1/*
2 *  linux/fs/readdir.c
3 *
4 *  Copyright (C) 1995  Linus Torvalds
5 */
6
7#include <linux/sched.h>
8#include <linux/mm.h>
9#include <linux/errno.h>
10#include <linux/stat.h>
11#include <linux/file.h>
12#include <linux/smp_lock.h>
13
14#include <asm/uaccess.h>
15
16int vfs_readdir(struct file *file, filldir_t filler, void *buf)
17{
18	struct inode *inode = file->f_dentry->d_inode;
19	int res = -ENOTDIR;
20	if (!file->f_op || !file->f_op->readdir)
21		goto out;
22	down(&inode->i_sem);
23	down(&inode->i_zombie);
24	res = -ENOENT;
25	if (!IS_DEADDIR(inode)) {
26		lock_kernel();
27		res = file->f_op->readdir(file, buf, filler);
28		unlock_kernel();
29	}
30	up(&inode->i_zombie);
31	up(&inode->i_sem);
32out:
33	return res;
34}
35
36int dcache_dir_open(struct inode *inode, struct file *file)
37{
38	static struct qstr cursor_name = {len:1, name:"."};
39
40	file->private_data = d_alloc(file->f_dentry, &cursor_name);
41
42	return file->private_data ? 0 : -ENOMEM;
43}
44
45int dcache_dir_close(struct inode *inode, struct file *file)
46{
47	dput(file->private_data);
48	return 0;
49}
50
51loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
52{
53	down(&file->f_dentry->d_inode->i_sem);
54	switch (origin) {
55		case 1:
56			offset += file->f_pos;
57		case 0:
58			if (offset >= 0)
59				break;
60		default:
61			up(&file->f_dentry->d_inode->i_sem);
62			return -EINVAL;
63	}
64	if (offset != file->f_pos) {
65		file->f_pos = offset;
66		if (file->f_pos >= 2) {
67			struct list_head *p;
68			struct dentry *cursor = file->private_data;
69			loff_t n = file->f_pos - 2;
70
71			spin_lock(&dcache_lock);
72			p = file->f_dentry->d_subdirs.next;
73			while (n && p != &file->f_dentry->d_subdirs) {
74				struct dentry *next;
75				next = list_entry(p, struct dentry, d_child);
76				if (!list_empty(&next->d_hash) && next->d_inode)
77					n--;
78				p = p->next;
79			}
80			list_del(&cursor->d_child);
81			list_add_tail(&cursor->d_child, p);
82			spin_unlock(&dcache_lock);
83		}
84	}
85	up(&file->f_dentry->d_inode->i_sem);
86	return offset;
87}
88
89int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync)
90{
91	return 0;
92}
93
94/*
95 * Directory is locked and all positive dentries in it are safe, since
96 * for ramfs-type trees they can't go away without unlink() or rmdir(),
97 * both impossible due to the lock on directory.
98 */
99
100int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
101{
102	struct dentry *dentry = filp->f_dentry;
103	struct dentry *cursor = filp->private_data;
104	struct list_head *p, *q = &cursor->d_child;
105	ino_t ino;
106	int i = filp->f_pos;
107
108	switch (i) {
109		case 0:
110			ino = dentry->d_inode->i_ino;
111			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
112				break;
113			filp->f_pos++;
114			i++;
115			/* fallthrough */
116		case 1:
117			spin_lock(&dcache_lock);
118			ino = dentry->d_parent->d_inode->i_ino;
119			spin_unlock(&dcache_lock);
120			if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
121				break;
122			filp->f_pos++;
123			i++;
124			/* fallthrough */
125		default:
126			spin_lock(&dcache_lock);
127			if (filp->f_pos == 2) {
128				list_del(q);
129				list_add(q, &dentry->d_subdirs);
130			}
131			for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
132				struct dentry *next;
133				next = list_entry(p, struct dentry, d_child);
134				if (list_empty(&next->d_hash) || !next->d_inode)
135					continue;
136
137				spin_unlock(&dcache_lock);
138				if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
139					return 0;
140				spin_lock(&dcache_lock);
141				/* next is still alive */
142				list_del(q);
143				list_add(q, p);
144				p = q;
145				filp->f_pos++;
146			}
147			spin_unlock(&dcache_lock);
148	}
149	return 0;
150}
151
152struct file_operations dcache_dir_ops = {
153	open:		dcache_dir_open,
154	release:	dcache_dir_close,
155	llseek:		dcache_dir_lseek,
156	read:		generic_read_dir,
157	readdir:	dcache_readdir,
158	fsync:		dcache_dir_fsync,
159};
160
161/*
162 * Traditional linux readdir() handling..
163 *
164 * "count=1" is a special case, meaning that the buffer is one
165 * dirent-structure in size and that the code can't handle more
166 * anyway. Thus the special "fillonedir()" function for that
167 * case (the low-level handlers don't need to care about this).
168 */
169#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
170#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
171
172#ifndef __ia64__
173
174struct old_linux_dirent {
175	unsigned long	d_ino;
176	unsigned long	d_offset;
177	unsigned short	d_namlen;
178	char		d_name[1];
179};
180
181struct readdir_callback {
182	struct old_linux_dirent * dirent;
183	int count;
184};
185
186static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
187		      ino_t ino, unsigned int d_type)
188{
189	struct readdir_callback * buf = (struct readdir_callback *) __buf;
190	struct old_linux_dirent * dirent;
191
192	if (buf->count)
193		return -EINVAL;
194	buf->count++;
195	dirent = buf->dirent;
196	put_user(ino, &dirent->d_ino);
197	put_user(offset, &dirent->d_offset);
198	put_user(namlen, &dirent->d_namlen);
199	copy_to_user(dirent->d_name, name, namlen);
200	put_user(0, dirent->d_name + namlen);
201	return 0;
202}
203
204asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
205{
206	int error;
207	struct file * file;
208	struct readdir_callback buf;
209
210	error = -EBADF;
211	file = fget(fd);
212	if (!file)
213		goto out;
214
215	buf.count = 0;
216	buf.dirent = dirent;
217
218	error = vfs_readdir(file, fillonedir, &buf);
219	if (error >= 0)
220		error = buf.count;
221
222	fput(file);
223out:
224	return error;
225}
226
227#endif /* !__ia64__ */
228
229/*
230 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
231 * interface.
232 */
233struct linux_dirent {
234	unsigned long	d_ino;
235	unsigned long	d_off;
236	unsigned short	d_reclen;
237	char		d_name[1];
238};
239
240struct getdents_callback {
241	struct linux_dirent * current_dir;
242	struct linux_dirent * previous;
243	int count;
244	int error;
245};
246
247static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
248		   ino_t ino, unsigned int d_type)
249{
250	struct linux_dirent * dirent;
251	struct getdents_callback * buf = (struct getdents_callback *) __buf;
252	int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
253
254	buf->error = -EINVAL;	/* only used if we fail.. */
255	if (reclen > buf->count)
256		return -EINVAL;
257	dirent = buf->previous;
258	if (dirent)
259		put_user(offset, &dirent->d_off);
260	dirent = buf->current_dir;
261	buf->previous = dirent;
262	put_user(ino, &dirent->d_ino);
263	put_user(reclen, &dirent->d_reclen);
264	copy_to_user(dirent->d_name, name, namlen);
265	put_user(0, dirent->d_name + namlen);
266	((char *) dirent) += reclen;
267	buf->current_dir = dirent;
268	buf->count -= reclen;
269	return 0;
270}
271
272asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
273{
274	struct file * file;
275	struct linux_dirent * lastdirent;
276	struct getdents_callback buf;
277	int error;
278
279	error = -EBADF;
280	file = fget(fd);
281	if (!file)
282		goto out;
283
284	buf.current_dir = (struct linux_dirent *) dirent;
285	buf.previous = NULL;
286	buf.count = count;
287	buf.error = 0;
288
289	error = vfs_readdir(file, filldir, &buf);
290	if (error < 0)
291		goto out_putf;
292	error = buf.error;
293	lastdirent = buf.previous;
294	if (lastdirent) {
295		put_user(file->f_pos, &lastdirent->d_off);
296		error = count - buf.count;
297	}
298
299out_putf:
300	fput(file);
301out:
302	return error;
303}
304
305/*
306 * And even better one including d_type field and 64bit d_ino and d_off.
307 */
308struct linux_dirent64 {
309	u64		d_ino;
310	s64		d_off;
311	unsigned short	d_reclen;
312	unsigned char	d_type;
313	char		d_name[0];
314};
315
316#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
317
318struct getdents_callback64 {
319	struct linux_dirent64 * current_dir;
320	struct linux_dirent64 * previous;
321	int count;
322	int error;
323};
324
325static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
326		     ino_t ino, unsigned int d_type)
327{
328	struct linux_dirent64 * dirent, d;
329	struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
330	int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
331
332	buf->error = -EINVAL;	/* only used if we fail.. */
333	if (reclen > buf->count)
334		return -EINVAL;
335	dirent = buf->previous;
336	if (dirent) {
337		d.d_off = offset;
338		copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
339	}
340	dirent = buf->current_dir;
341	buf->previous = dirent;
342	memset(&d, 0, NAME_OFFSET(&d));
343	d.d_ino = ino;
344	d.d_reclen = reclen;
345	d.d_type = d_type;
346	copy_to_user(dirent, &d, NAME_OFFSET(&d));
347	copy_to_user(dirent->d_name, name, namlen);
348	put_user(0, dirent->d_name + namlen);
349	((char *) dirent) += reclen;
350	buf->current_dir = dirent;
351	buf->count -= reclen;
352	return 0;
353}
354
355asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
356{
357	struct file * file;
358	struct linux_dirent64 * lastdirent;
359	struct getdents_callback64 buf;
360	int error;
361
362	error = -EBADF;
363	file = fget(fd);
364	if (!file)
365		goto out;
366
367	buf.current_dir = (struct linux_dirent64 *) dirent;
368	buf.previous = NULL;
369	buf.count = count;
370	buf.error = 0;
371
372	error = vfs_readdir(file, filldir64, &buf);
373	if (error < 0)
374		goto out_putf;
375	error = buf.error;
376	lastdirent = buf.previous;
377	if (lastdirent) {
378		struct linux_dirent64 d;
379		d.d_off = file->f_pos;
380		copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
381		error = count - buf.count;
382	}
383
384out_putf:
385	fput(file);
386out:
387	return error;
388}
389