~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to fs/readdir.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
23
int 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);
 
44
out:
 
45
        return res;
 
46
}
 
47
 
 
48
EXPORT_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
 
 
61
struct 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
 
 
68
struct readdir_callback {
 
69
        struct old_linux_dirent __user * dirent;
 
70
        int result;
 
71
};
 
72
 
 
73
static 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;
 
100
efault:
 
101
        buf->result = -EFAULT;
 
102
        return -EFAULT;
 
103
}
 
104
 
 
105
SYSCALL_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);
 
125
out:
 
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
 */
 
135
struct linux_dirent {
 
136
        unsigned long   d_ino;
 
137
        unsigned long   d_off;
 
138
        unsigned short  d_reclen;
 
139
        char            d_name[1];
 
140
};
 
141
 
 
142
struct getdents_callback {
 
143
        struct linux_dirent __user * current_dir;
 
144
        struct linux_dirent __user * previous;
 
145
        int count;
 
146
        int error;
 
147
};
 
148
 
 
149
static 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;
 
187
efault:
 
188
        buf->error = -EFAULT;
 
189
        return -EFAULT;
 
190
}
 
191
 
 
192
SYSCALL_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);
 
225
out:
 
226
        return error;
 
227
}
 
228
 
 
229
struct getdents_callback64 {
 
230
        struct linux_dirent64 __user * current_dir;
 
231
        struct linux_dirent64 __user * previous;
 
232
        int count;
 
233
        int error;
 
234
};
 
235
 
 
236
static 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;
 
270
efault:
 
271
        buf->error = -EFAULT;
 
272
        return -EFAULT;
 
273
}
 
274
 
 
275
SYSCALL_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);
 
309
out:
 
310
        return error;
 
311
}