Root/fs/readdir.c

1/*
2 * linux/fs/readdir.c
3 *
4 * Copyright (C) 1995 Linus Torvalds
5 */
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/time.h>
10#include <linux/mm.h>
11#include <linux/errno.h>
12#include <linux/stat.h>
13#include <linux/file.h>
14#include <linux/fs.h>
15#include <linux/dirent.h>
16#include <linux/security.h>
17#include <linux/syscalls.h>
18#include <linux/unistd.h>
19
20#include <asm/uaccess.h>
21
22int vfs_readdir(struct file *file, filldir_t filler, void *buf)
23{
24    struct inode *inode = file->f_path.dentry->d_inode;
25    int res = -ENOTDIR;
26    if (!file->f_op || !file->f_op->readdir)
27        goto out;
28
29    res = security_file_permission(file, MAY_READ);
30    if (res)
31        goto out;
32
33    res = mutex_lock_killable(&inode->i_mutex);
34    if (res)
35        goto out;
36
37    res = -ENOENT;
38    if (!IS_DEADDIR(inode)) {
39        res = file->f_op->readdir(file, buf, filler);
40        file_accessed(file);
41    }
42    mutex_unlock(&inode->i_mutex);
43out:
44    return res;
45}
46
47EXPORT_SYMBOL(vfs_readdir);
48
49/*
50 * Traditional linux readdir() handling..
51 *
52 * "count=1" is a special case, meaning that the buffer is one
53 * dirent-structure in size and that the code can't handle more
54 * anyway. Thus the special "fillonedir()" function for that
55 * case (the low-level handlers don't need to care about this).
56 */
57#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
58
59#ifdef __ARCH_WANT_OLD_READDIR
60
61struct old_linux_dirent {
62    unsigned long d_ino;
63    unsigned long d_offset;
64    unsigned short d_namlen;
65    char d_name[1];
66};
67
68struct readdir_callback {
69    struct old_linux_dirent __user * dirent;
70    int result;
71};
72
73static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
74              u64 ino, unsigned int d_type)
75{
76    struct readdir_callback * buf = (struct readdir_callback *) __buf;
77    struct old_linux_dirent __user * dirent;
78    unsigned long d_ino;
79
80    if (buf->result)
81        return -EINVAL;
82    d_ino = ino;
83    if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
84        buf->result = -EOVERFLOW;
85        return -EOVERFLOW;
86    }
87    buf->result++;
88    dirent = buf->dirent;
89    if (!access_ok(VERIFY_WRITE, dirent,
90            (unsigned long)(dirent->d_name + namlen + 1) -
91                (unsigned long)dirent))
92        goto efault;
93    if ( __put_user(d_ino, &dirent->d_ino) ||
94        __put_user(offset, &dirent->d_offset) ||
95        __put_user(namlen, &dirent->d_namlen) ||
96        __copy_to_user(dirent->d_name, name, namlen) ||
97        __put_user(0, dirent->d_name + namlen))
98        goto efault;
99    return 0;
100efault:
101    buf->result = -EFAULT;
102    return -EFAULT;
103}
104
105SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
106        struct old_linux_dirent __user *, dirent, unsigned int, count)
107{
108    int error;
109    struct file * file;
110    struct readdir_callback buf;
111
112    error = -EBADF;
113    file = fget(fd);
114    if (!file)
115        goto out;
116
117    buf.result = 0;
118    buf.dirent = dirent;
119
120    error = vfs_readdir(file, fillonedir, &buf);
121    if (buf.result)
122        error = buf.result;
123
124    fput(file);
125out:
126    return error;
127}
128
129#endif /* __ARCH_WANT_OLD_READDIR */
130
131/*
132 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
133 * interface.
134 */
135struct linux_dirent {
136    unsigned long d_ino;
137    unsigned long d_off;
138    unsigned short d_reclen;
139    char d_name[1];
140};
141
142struct getdents_callback {
143    struct linux_dirent __user * current_dir;
144    struct linux_dirent __user * previous;
145    int count;
146    int error;
147};
148
149static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
150           u64 ino, unsigned int d_type)
151{
152    struct linux_dirent __user * dirent;
153    struct getdents_callback * buf = (struct getdents_callback *) __buf;
154    unsigned long d_ino;
155    int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(long));
156
157    buf->error = -EINVAL; /* only used if we fail.. */
158    if (reclen > buf->count)
159        return -EINVAL;
160    d_ino = ino;
161    if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
162        buf->error = -EOVERFLOW;
163        return -EOVERFLOW;
164    }
165    dirent = buf->previous;
166    if (dirent) {
167        if (__put_user(offset, &dirent->d_off))
168            goto efault;
169    }
170    dirent = buf->current_dir;
171    if (__put_user(d_ino, &dirent->d_ino))
172        goto efault;
173    if (__put_user(reclen, &dirent->d_reclen))
174        goto efault;
175    if (copy_to_user(dirent->d_name, name, namlen))
176        goto efault;
177    if (__put_user(0, dirent->d_name + namlen))
178        goto efault;
179    if (__put_user(d_type, (char __user *) dirent + reclen - 1))
180        goto efault;
181    buf->previous = dirent;
182    dirent = (void __user *)dirent + reclen;
183    buf->current_dir = dirent;
184    buf->count -= reclen;
185    return 0;
186efault:
187    buf->error = -EFAULT;
188    return -EFAULT;
189}
190
191SYSCALL_DEFINE3(getdents, unsigned int, fd,
192        struct linux_dirent __user *, dirent, unsigned int, count)
193{
194    struct file * file;
195    struct linux_dirent __user * lastdirent;
196    struct getdents_callback buf;
197    int error;
198
199    error = -EFAULT;
200    if (!access_ok(VERIFY_WRITE, dirent, count))
201        goto out;
202
203    error = -EBADF;
204    file = fget(fd);
205    if (!file)
206        goto out;
207
208    buf.current_dir = dirent;
209    buf.previous = NULL;
210    buf.count = count;
211    buf.error = 0;
212
213    error = vfs_readdir(file, filldir, &buf);
214    if (error >= 0)
215        error = buf.error;
216    lastdirent = buf.previous;
217    if (lastdirent) {
218        if (put_user(file->f_pos, &lastdirent->d_off))
219            error = -EFAULT;
220        else
221            error = count - buf.count;
222    }
223    fput(file);
224out:
225    return error;
226}
227
228struct getdents_callback64 {
229    struct linux_dirent64 __user * current_dir;
230    struct linux_dirent64 __user * previous;
231    int count;
232    int error;
233};
234
235static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
236             u64 ino, unsigned int d_type)
237{
238    struct linux_dirent64 __user *dirent;
239    struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
240    int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(u64));
241
242    buf->error = -EINVAL; /* only used if we fail.. */
243    if (reclen > buf->count)
244        return -EINVAL;
245    dirent = buf->previous;
246    if (dirent) {
247        if (__put_user(offset, &dirent->d_off))
248            goto efault;
249    }
250    dirent = buf->current_dir;
251    if (__put_user(ino, &dirent->d_ino))
252        goto efault;
253    if (__put_user(0, &dirent->d_off))
254        goto efault;
255    if (__put_user(reclen, &dirent->d_reclen))
256        goto efault;
257    if (__put_user(d_type, &dirent->d_type))
258        goto efault;
259    if (copy_to_user(dirent->d_name, name, namlen))
260        goto efault;
261    if (__put_user(0, dirent->d_name + namlen))
262        goto efault;
263    buf->previous = dirent;
264    dirent = (void __user *)dirent + reclen;
265    buf->current_dir = dirent;
266    buf->count -= reclen;
267    return 0;
268efault:
269    buf->error = -EFAULT;
270    return -EFAULT;
271}
272
273SYSCALL_DEFINE3(getdents64, unsigned int, fd,
274        struct linux_dirent64 __user *, dirent, unsigned int, count)
275{
276    struct file * file;
277    struct linux_dirent64 __user * lastdirent;
278    struct getdents_callback64 buf;
279    int error;
280
281    error = -EFAULT;
282    if (!access_ok(VERIFY_WRITE, dirent, count))
283        goto out;
284
285    error = -EBADF;
286    file = fget(fd);
287    if (!file)
288        goto out;
289
290    buf.current_dir = dirent;
291    buf.previous = NULL;
292    buf.count = count;
293    buf.error = 0;
294
295    error = vfs_readdir(file, filldir64, &buf);
296    if (error >= 0)
297        error = buf.error;
298    lastdirent = buf.previous;
299    if (lastdirent) {
300        typeof(lastdirent->d_off) d_off = file->f_pos;
301        if (__put_user(d_off, &lastdirent->d_off))
302            error = -EFAULT;
303        else
304            error = count - buf.count;
305    }
306    fput(file);
307out:
308    return error;
309}
310

Archive Download this file



interactive