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