Root/fs/readdir.c

1/*
2 * linux/fs/readdir.c
3 *
4 * Copyright (C) 1995 Linus Torvalds
5 */
6
7#include <linux/stddef.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/errno.h>
13#include <linux/stat.h>
14#include <linux/file.h>
15#include <linux/fs.h>
16#include <linux/dirent.h>
17#include <linux/security.h>
18#include <linux/syscalls.h>
19#include <linux/unistd.h>
20
21#include <asm/uaccess.h>
22
23int vfs_readdir(struct file *file, filldir_t filler, void *buf)
24{
25    struct inode *inode = file->f_path.dentry->d_inode;
26    int res = -ENOTDIR;
27    if (!file->f_op || !file->f_op->readdir)
28        goto out;
29
30    res = security_file_permission(file, MAY_READ);
31    if (res)
32        goto out;
33
34    res = mutex_lock_killable(&inode->i_mutex);
35    if (res)
36        goto out;
37
38    res = -ENOENT;
39    if (!IS_DEADDIR(inode)) {
40        res = file->f_op->readdir(file, buf, filler);
41        file_accessed(file);
42    }
43    mutex_unlock(&inode->i_mutex);
44out:
45    return res;
46}
47
48EXPORT_SYMBOL(vfs_readdir);
49
50/*
51 * Traditional linux readdir() handling..
52 *
53 * "count=1" is a special case, meaning that the buffer is one
54 * dirent-structure in size and that the code can't handle more
55 * anyway. Thus the special "fillonedir()" function for that
56 * case (the low-level handlers don't need to care about this).
57 */
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(offsetof(struct linux_dirent, d_name) + namlen + 2,
156        sizeof(long));
157
158    buf->error = -EINVAL; /* only used if we fail.. */
159    if (reclen > buf->count)
160        return -EINVAL;
161    d_ino = ino;
162    if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
163        buf->error = -EOVERFLOW;
164        return -EOVERFLOW;
165    }
166    dirent = buf->previous;
167    if (dirent) {
168        if (__put_user(offset, &dirent->d_off))
169            goto efault;
170    }
171    dirent = buf->current_dir;
172    if (__put_user(d_ino, &dirent->d_ino))
173        goto efault;
174    if (__put_user(reclen, &dirent->d_reclen))
175        goto efault;
176    if (copy_to_user(dirent->d_name, name, namlen))
177        goto efault;
178    if (__put_user(0, dirent->d_name + namlen))
179        goto efault;
180    if (__put_user(d_type, (char __user *) dirent + reclen - 1))
181        goto efault;
182    buf->previous = dirent;
183    dirent = (void __user *)dirent + reclen;
184    buf->current_dir = dirent;
185    buf->count -= reclen;
186    return 0;
187efault:
188    buf->error = -EFAULT;
189    return -EFAULT;
190}
191
192SYSCALL_DEFINE3(getdents, unsigned int, fd,
193        struct linux_dirent __user *, dirent, unsigned int, count)
194{
195    struct file * file;
196    struct linux_dirent __user * lastdirent;
197    struct getdents_callback buf;
198    int error;
199
200    error = -EFAULT;
201    if (!access_ok(VERIFY_WRITE, dirent, count))
202        goto out;
203
204    error = -EBADF;
205    file = fget(fd);
206    if (!file)
207        goto out;
208
209    buf.current_dir = dirent;
210    buf.previous = NULL;
211    buf.count = count;
212    buf.error = 0;
213
214    error = vfs_readdir(file, filldir, &buf);
215    if (error >= 0)
216        error = buf.error;
217    lastdirent = buf.previous;
218    if (lastdirent) {
219        if (put_user(file->f_pos, &lastdirent->d_off))
220            error = -EFAULT;
221        else
222            error = count - buf.count;
223    }
224    fput(file);
225out:
226    return error;
227}
228
229struct getdents_callback64 {
230    struct linux_dirent64 __user * current_dir;
231    struct linux_dirent64 __user * previous;
232    int count;
233    int error;
234};
235
236static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
237             u64 ino, unsigned int d_type)
238{
239    struct linux_dirent64 __user *dirent;
240    struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
241    int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
242        sizeof(u64));
243
244    buf->error = -EINVAL; /* only used if we fail.. */
245    if (reclen > buf->count)
246        return -EINVAL;
247    dirent = buf->previous;
248    if (dirent) {
249        if (__put_user(offset, &dirent->d_off))
250            goto efault;
251    }
252    dirent = buf->current_dir;
253    if (__put_user(ino, &dirent->d_ino))
254        goto efault;
255    if (__put_user(0, &dirent->d_off))
256        goto efault;
257    if (__put_user(reclen, &dirent->d_reclen))
258        goto efault;
259    if (__put_user(d_type, &dirent->d_type))
260        goto efault;
261    if (copy_to_user(dirent->d_name, name, namlen))
262        goto efault;
263    if (__put_user(0, dirent->d_name + namlen))
264        goto efault;
265    buf->previous = dirent;
266    dirent = (void __user *)dirent + reclen;
267    buf->current_dir = dirent;
268    buf->count -= reclen;
269    return 0;
270efault:
271    buf->error = -EFAULT;
272    return -EFAULT;
273}
274
275SYSCALL_DEFINE3(getdents64, unsigned int, fd,
276        struct linux_dirent64 __user *, dirent, unsigned int, count)
277{
278    struct file * file;
279    struct linux_dirent64 __user * lastdirent;
280    struct getdents_callback64 buf;
281    int error;
282
283    error = -EFAULT;
284    if (!access_ok(VERIFY_WRITE, dirent, count))
285        goto out;
286
287    error = -EBADF;
288    file = fget(fd);
289    if (!file)
290        goto out;
291
292    buf.current_dir = dirent;
293    buf.previous = NULL;
294    buf.count = count;
295    buf.error = 0;
296
297    error = vfs_readdir(file, filldir64, &buf);
298    if (error >= 0)
299        error = buf.error;
300    lastdirent = buf.previous;
301    if (lastdirent) {
302        typeof(lastdirent->d_off) d_off = file->f_pos;
303        if (__put_user(d_off, &lastdirent->d_off))
304            error = -EFAULT;
305        else
306            error = count - buf.count;
307    }
308    fput(file);
309out:
310    return error;
311}
312

Archive Download this file



interactive