1/* ntfsdir.c - directory functions
2 *
3 * Copyright (c) 2006 Troeglazov Gerasim (3dEyes**)
4 *
5 * This program/include file is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program/include file is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program (in the main directory of the Linux-NTFS
17 * distribution in the file COPYING); if not, write to the Free Software
18 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21
22#include "ntfsdir.h"
23
24#include <ctype.h>
25#include <dirent.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <time.h>
32#include <unistd.h>
33#include <sys/stat.h>
34
35
36static int
37_ntfs_dirent_filler(void *_dirent, const ntfschar *name,
38	const int name_len, const int name_type, const s64 pos, const MFT_REF mref,
39	const unsigned dt_type)
40{
41	char *filename = NULL;
42	dircookie *cookie = (dircookie*)_dirent;
43
44	if (name_type == FILE_NAME_DOS)
45		return 0;
46
47	if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user
48		|| cookie->show_sys_files) {
49		int len = ntfs_ucstombs(name, name_len, &filename, 0);
50		if (len >= 0 && filename != NULL) {
51			cache_entry* new_entry =
52				(cache_entry*)ntfs_calloc(sizeof(cache_entry));
53			if (new_entry == NULL) {
54				free(filename);
55				return -1;
56			}
57
58			new_entry->ent =
59				(struct dirent*)ntfs_calloc(sizeof(struct dirent) + len);
60			new_entry->ent->d_dev = cookie->dev_id;
61			new_entry->ent->d_ino = MREF(mref);
62			memcpy(new_entry->ent->d_name,filename, len + 1);
63			new_entry->ent->d_reclen =  sizeof(struct dirent) + len;
64
65			if (cookie->cache_root == NULL || cookie->entry == NULL) {
66				cookie->cache_root = new_entry;
67				cookie->entry = cookie->cache_root;
68				cookie->entry->next = NULL;
69			} else {
70				cookie->entry->next = (void*)new_entry;
71				cookie->entry = cookie->entry->next;
72				cookie->entry->next = NULL;
73			}
74
75		   	free(filename);
76		   	return 0;
77		}
78		return -1;
79	}
80	return 0;
81}
82
83
84status_t
85fs_free_dircookie(fs_volume *_vol, fs_vnode *vnode, void *_cookie)
86{
87	nspace		*ns = (nspace*)_vol->private_volume;
88	dircookie	*cookie = (dircookie*)_cookie;
89
90	LOCK_VOL(ns);
91	TRACE("fs_free_dircookie - ENTER\n");
92
93	if (cookie != NULL) {
94		cache_entry *entry = cookie->cache_root;
95		while (entry != NULL) {
96			cache_entry *next = entry->next;
97			free(entry->ent);
98			free(entry);
99			entry = next;
100		}
101		free(cookie);
102	}
103
104	TRACE("fs_free_dircookie - EXIT\n");
105	UNLOCK_VOL(ns);
106
107	return B_NO_ERROR;
108}
109
110
111status_t
112fs_opendir(fs_volume *_vol, fs_vnode *_node, void** _cookie)
113{
114	nspace		*ns = (nspace*)_vol->private_volume;
115	vnode		*node = (vnode*)_node->private_node;
116	dircookie	*cookie = NULL;
117	ntfs_inode	*ni = NULL;
118	int			result = B_NO_ERROR;
119
120	LOCK_VOL(ns);
121	TRACE("fs_opendir - ENTER\n");
122
123	ni = ntfs_inode_open(ns->ntvol, node->vnid);
124	if (ni == NULL) {
125		result = ENOENT;
126		goto exit;
127	}
128
129	if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
130		result = EMFILE;
131		goto exit;
132	}
133
134	cookie = (dircookie*)ntfs_calloc(sizeof(dircookie));
135	if (cookie != NULL) {
136		cookie->pos = 0;
137		cookie->dev_id = ns->id;
138		cookie->show_sys_files = ns->show_sys_files;
139		cookie->cache_root = NULL;
140		cookie->entry = cookie->cache_root;
141		*_cookie = (void*)cookie;
142	} else
143		result = ENOMEM;
144
145exit:
146	if (ni != NULL)
147		ntfs_inode_close(ni);
148
149	TRACE("fs_opendir - EXIT\n");
150	UNLOCK_VOL(ns);
151
152	return result;
153}
154
155
156status_t
157fs_closedir(fs_volume *_vol, fs_vnode *_node, void *cookie)
158{
159	return B_NO_ERROR;
160}
161
162
163status_t
164fs_readdir(fs_volume *_vol, fs_vnode *_node, void *_cookie, struct dirent *buf,
165	size_t bufsize, uint32 *num)
166{
167	nspace		*ns = (nspace*)_vol->private_volume;
168	vnode		*node = (vnode*)_node->private_node;
169	dircookie	*cookie = (dircookie*)_cookie;
170	ntfs_inode	*ni = NULL;
171
172	uint32 		nameLength = bufsize - sizeof(struct dirent), realLen;
173	int 		result = B_NO_ERROR;
174
175	LOCK_VOL(ns);
176	TRACE("fs_readdir - ENTER (sizeof(buf)=%d, bufsize=%d, num=%d\n",
177		sizeof(buf), bufsize, *num);
178
179	if (!ns || !node || !cookie || !num || bufsize < sizeof(*buf)) {
180	 	result = EINVAL;
181		goto exit;
182	}
183
184	ni = ntfs_inode_open(ns->ntvol, node->vnid);
185	if (ni == NULL) {
186		TRACE("fs_readdir - dir not opened\n");
187		result = ENOENT;
188		goto exit;
189	}
190
191	if (cookie->cache_root == NULL) {
192		cookie->entry = NULL;
193		result = ntfs_readdir(ni, &cookie->pos, cookie,
194			(ntfs_filldir_t)_ntfs_dirent_filler);
195		cookie->entry = cookie->cache_root;
196		if (result) {
197			result = ENOENT;
198			goto exit;
199		}
200	}
201
202	if (cookie->entry == NULL) {
203		result = ENOENT;
204		goto exit;
205	}
206
207	if (cookie->entry->ent == NULL) {
208		result = ENOENT;
209		goto exit;
210	}
211
212	realLen = nameLength > 255 ? 255 : nameLength;
213
214	buf->d_dev = ns->id;
215	buf->d_ino = cookie->entry->ent->d_ino;
216	strlcpy(buf->d_name, cookie->entry->ent->d_name, realLen + 1);
217	buf->d_reclen = sizeof(struct dirent) + realLen;
218
219	cookie->entry = (cache_entry*)cookie->entry->next;
220
221	TRACE("fs_readdir - FILE: [%s]\n",buf->d_name);
222
223exit:
224	if (ni != NULL)
225		ntfs_inode_close(ni);
226
227	if (result == B_NO_ERROR)
228		*num = 1;
229	else
230		*num = 0;
231
232	if (result == ENOENT)
233		result = B_NO_ERROR;
234
235	TRACE("fs_readdir - EXIT num=%d result (%s)\n",*num, strerror(result));
236	UNLOCK_VOL(ns);
237
238	return result;
239}
240
241
242status_t
243fs_rewinddir(fs_volume *_vol, fs_vnode *_node, void *_cookie)
244{
245	nspace		*ns = (nspace*)_vol->private_volume;
246	dircookie	*cookie = (dircookie*)_cookie;
247	int			result = EINVAL;
248
249	LOCK_VOL(ns);
250	TRACE("fs_rewinddir - ENTER\n");
251
252	if (cookie != NULL) {
253		cache_entry *entry = cookie->cache_root;
254		while (entry != NULL) {
255			cache_entry *next = entry->next;
256			free(entry->ent);
257			free(entry);
258			entry = next;
259		}
260		cookie->pos = 0;
261		cookie->dev_id = ns->id;
262		cookie->show_sys_files = ns->show_sys_files;
263		cookie->cache_root = NULL;
264		cookie->entry = cookie->cache_root;
265		result = B_NO_ERROR;
266	}
267
268	TRACE("fs_rewinddir - EXIT, result is %s\n", strerror(result));
269	UNLOCK_VOL(ns);
270
271	return result;
272}
273