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			if (entry->ent != NULL)
98				free(entry->ent);
99			free(entry);
100			entry = next;
101		}
102		free(cookie);
103	}
104
105	TRACE("fs_free_dircookie - EXIT\n");
106	UNLOCK_VOL(ns);
107
108	return B_NO_ERROR;
109}
110
111
112status_t
113fs_opendir(fs_volume *_vol, fs_vnode *_node, void** _cookie)
114{
115	nspace		*ns = (nspace*)_vol->private_volume;
116	vnode		*node = (vnode*)_node->private_node;
117	dircookie	*cookie = NULL;
118	ntfs_inode	*ni = NULL;
119	int			result = B_NO_ERROR;
120
121	LOCK_VOL(ns);
122	TRACE("fs_opendir - ENTER\n");
123
124	ni = ntfs_inode_open(ns->ntvol, node->vnid);
125	if (ni == NULL) {
126		result = ENOENT;
127		goto exit;
128	}
129
130	if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
131		result = EMFILE;
132		goto exit;
133	}
134
135	cookie = (dircookie*)ntfs_calloc(sizeof(dircookie));
136	if (cookie != NULL) {
137		cookie->pos = 0;
138		cookie->dev_id = ns->id;
139		cookie->show_sys_files = ns->show_sys_files;
140		cookie->cache_root = NULL;
141		cookie->entry = cookie->cache_root;
142		*_cookie = (void*)cookie;
143	} else
144		result = ENOMEM;
145
146exit:
147	if (ni != NULL)
148		ntfs_inode_close(ni);
149
150	TRACE("fs_opendir - EXIT\n");
151	UNLOCK_VOL(ns);
152
153	return result;
154}
155
156
157status_t
158fs_closedir(fs_volume *_vol, fs_vnode *_node, void *cookie)
159{
160	return B_NO_ERROR;
161}
162
163
164status_t
165fs_readdir(fs_volume *_vol, fs_vnode *_node, void *_cookie, struct dirent *buf,
166	size_t bufsize, uint32 *num)
167{
168	nspace		*ns = (nspace*)_vol->private_volume;
169	vnode		*node = (vnode*)_node->private_node;
170	dircookie	*cookie = (dircookie*)_cookie;
171	ntfs_inode	*ni = NULL;
172
173	uint32 		nameLength = bufsize - sizeof(struct dirent), realLen;
174	int 		result = B_NO_ERROR;
175
176	LOCK_VOL(ns);
177	TRACE("fs_readdir - ENTER (sizeof(buf)=%d, bufsize=%d, num=%d\n",
178		sizeof(buf), bufsize, *num);
179
180	if (!ns || !node || !cookie || !num || bufsize < sizeof(*buf)) {
181	 	result = EINVAL;
182		goto exit;
183	}
184
185	ni = ntfs_inode_open(ns->ntvol, node->vnid);
186	if (ni == NULL) {
187		TRACE("fs_readdir - dir not opened\n");
188		result = ENOENT;
189		goto exit;
190	}
191
192	if(cookie->cache_root == NULL) {
193		cookie->entry = NULL;
194		result = ntfs_readdir(ni, &cookie->pos, cookie,
195			(ntfs_filldir_t)_ntfs_dirent_filler);
196		cookie->entry = cookie->cache_root;
197		if(result) {
198			result = ENOENT;
199			goto exit;
200		}
201	}
202
203	if(cookie->entry == NULL) {
204		result = ENOENT;
205		goto exit;
206	}
207
208	if(cookie->entry->ent == NULL) {
209		result = ENOENT;
210		goto exit;
211	}
212
213	realLen = nameLength > 255 ? 255 : nameLength;
214
215	buf->d_dev = ns->id;
216	buf->d_ino = cookie->entry->ent->d_ino;
217	strlcpy(buf->d_name, cookie->entry->ent->d_name, realLen + 1);
218	buf->d_reclen = sizeof(struct dirent) + realLen;
219
220	cookie->entry = (cache_entry*)cookie->entry->next;
221
222	TRACE("fs_readdir - FILE: [%s]\n",buf->d_name);
223
224exit:
225	if (ni != NULL)
226		ntfs_inode_close(ni);
227
228	if (result == B_NO_ERROR)
229		*num = 1;
230	else
231		*num = 0;
232
233	if (result == ENOENT)
234		result = B_NO_ERROR;
235
236	TRACE("fs_readdir - EXIT num=%d result (%s)\n",*num, strerror(result));
237	UNLOCK_VOL(ns);
238
239	return result;
240}
241
242
243status_t
244fs_rewinddir(fs_volume *_vol, fs_vnode *_node, void *_cookie)
245{
246	nspace		*ns = (nspace*)_vol->private_volume;
247	dircookie	*cookie = (dircookie*)_cookie;
248	int			result = EINVAL;
249
250	LOCK_VOL(ns);
251	TRACE("fs_rewinddir - ENTER\n");
252
253	if (cookie != NULL) {
254		cache_entry *entry = cookie->cache_root;
255		while (entry != NULL) {
256			cache_entry *next = entry->next;
257			if (entry->ent != NULL)
258				free(entry->ent);
259			free(entry);
260			entry = next;
261		}
262		cookie->pos = 0;
263		cookie->dev_id = ns->id;
264		cookie->show_sys_files = ns->show_sys_files;
265		cookie->cache_root = NULL;
266		cookie->entry = cookie->cache_root;
267		result = B_NO_ERROR;
268	}
269
270	TRACE("fs_rewinddir - EXIT, result is %s\n", strerror(result));
271	UNLOCK_VOL(ns);
272
273	return result;
274}
275