]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - fs/readdir.c
give readdir(2)/getdents(2)/etc. uniform exclusion with lseek()
[mirror_ubuntu-bionic-kernel.git] / fs / readdir.c
CommitLineData
1da177e4
LT
1/*
2 * linux/fs/readdir.c
3 *
4 * Copyright (C) 1995 Linus Torvalds
5 */
6
85c9fe8f 7#include <linux/stddef.h>
022a1692 8#include <linux/kernel.h>
630d9c47 9#include <linux/export.h>
1da177e4
LT
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>
1da177e4 15#include <linux/fs.h>
d4c7cf6c 16#include <linux/fsnotify.h>
1da177e4
LT
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
5c0ba4e0 24int iterate_dir(struct file *file, struct dir_context *ctx)
1da177e4 25{
496ad9aa 26 struct inode *inode = file_inode(file);
1da177e4 27 int res = -ENOTDIR;
72c2d531 28 if (!file->f_op->iterate)
1da177e4
LT
29 goto out;
30
31 res = security_file_permission(file, MAY_READ);
32 if (res)
33 goto out;
34
9902af79
AV
35 inode_lock(inode);
36 // res = mutex_lock_killable(&inode->i_mutex);
37 // if (res)
38 // goto out;
da784511 39
1da177e4
LT
40 res = -ENOENT;
41 if (!IS_DEADDIR(inode)) {
2233f31a
AV
42 ctx->pos = file->f_pos;
43 res = file->f_op->iterate(file, ctx);
44 file->f_pos = ctx->pos;
d4c7cf6c 45 fsnotify_access(file);
1da177e4
LT
46 file_accessed(file);
47 }
5955102c 48 inode_unlock(inode);
1da177e4
LT
49out:
50 return res;
51}
5c0ba4e0 52EXPORT_SYMBOL(iterate_dir);
1da177e4
LT
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 */
1da177e4
LT
62
63#ifdef __ARCH_WANT_OLD_READDIR
64
65struct 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
72struct readdir_callback {
5c0ba4e0 73 struct dir_context ctx;
1da177e4
LT
74 struct old_linux_dirent __user * dirent;
75 int result;
76};
77
ac7576f4
MS
78static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
79 loff_t offset, u64 ino, unsigned int d_type)
1da177e4 80{
ac7576f4
MS
81 struct readdir_callback *buf =
82 container_of(ctx, struct readdir_callback, ctx);
1da177e4 83 struct old_linux_dirent __user * dirent;
afefdbb2 84 unsigned long d_ino;
1da177e4
LT
85
86 if (buf->result)
87 return -EINVAL;
afefdbb2 88 d_ino = ino;
8f3f655d
AV
89 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
90 buf->result = -EOVERFLOW;
afefdbb2 91 return -EOVERFLOW;
8f3f655d 92 }
1da177e4
LT
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;
afefdbb2 99 if ( __put_user(d_ino, &dirent->d_ino) ||
1da177e4
LT
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;
106efault:
107 buf->result = -EFAULT;
108 return -EFAULT;
109}
110
d4e82042
HC
111SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
112 struct old_linux_dirent __user *, dirent, unsigned int, count)
1da177e4
LT
113{
114 int error;
63b6df14 115 struct fd f = fdget_pos(fd);
ac6614b7
AV
116 struct readdir_callback buf = {
117 .ctx.actor = fillonedir,
118 .dirent = dirent
119 };
1da177e4 120
2903ff01 121 if (!f.file)
863ced7f 122 return -EBADF;
1da177e4 123
5c0ba4e0 124 error = iterate_dir(f.file, &buf.ctx);
53c9c5c0 125 if (buf.result)
1da177e4
LT
126 error = buf.result;
127
63b6df14 128 fdput_pos(f);
1da177e4
LT
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 */
138struct linux_dirent {
139 unsigned long d_ino;
140 unsigned long d_off;
141 unsigned short d_reclen;
142 char d_name[1];
143};
144
145struct getdents_callback {
5c0ba4e0 146 struct dir_context ctx;
1da177e4
LT
147 struct linux_dirent __user * current_dir;
148 struct linux_dirent __user * previous;
149 int count;
150 int error;
151};
152
ac7576f4
MS
153static int filldir(struct dir_context *ctx, const char *name, int namlen,
154 loff_t offset, u64 ino, unsigned int d_type)
1da177e4
LT
155{
156 struct linux_dirent __user * dirent;
ac7576f4
MS
157 struct getdents_callback *buf =
158 container_of(ctx, struct getdents_callback, ctx);
afefdbb2 159 unsigned long d_ino;
85c9fe8f
KW
160 int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
161 sizeof(long));
1da177e4
LT
162
163 buf->error = -EINVAL; /* only used if we fail.. */
164 if (reclen > buf->count)
165 return -EINVAL;
afefdbb2 166 d_ino = ino;
8f3f655d
AV
167 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
168 buf->error = -EOVERFLOW;
afefdbb2 169 return -EOVERFLOW;
8f3f655d 170 }
1da177e4
LT
171 dirent = buf->previous;
172 if (dirent) {
173 if (__put_user(offset, &dirent->d_off))
174 goto efault;
175 }
176 dirent = buf->current_dir;
afefdbb2 177 if (__put_user(d_ino, &dirent->d_ino))
1da177e4
LT
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;
192efault:
193 buf->error = -EFAULT;
194 return -EFAULT;
195}
196
20f37034
HC
197SYSCALL_DEFINE3(getdents, unsigned int, fd,
198 struct linux_dirent __user *, dirent, unsigned int, count)
1da177e4 199{
2903ff01 200 struct fd f;
1da177e4 201 struct linux_dirent __user * lastdirent;
ac6614b7
AV
202 struct getdents_callback buf = {
203 .ctx.actor = filldir,
204 .count = count,
205 .current_dir = dirent
206 };
1da177e4
LT
207 int error;
208
1da177e4 209 if (!access_ok(VERIFY_WRITE, dirent, count))
863ced7f 210 return -EFAULT;
1da177e4 211
63b6df14 212 f = fdget_pos(fd);
2903ff01 213 if (!f.file)
863ced7f 214 return -EBADF;
1da177e4 215
5c0ba4e0 216 error = iterate_dir(f.file, &buf.ctx);
53c9c5c0
AV
217 if (error >= 0)
218 error = buf.error;
1da177e4
LT
219 lastdirent = buf.previous;
220 if (lastdirent) {
bb6f619b 221 if (put_user(buf.ctx.pos, &lastdirent->d_off))
1da177e4
LT
222 error = -EFAULT;
223 else
224 error = count - buf.count;
225 }
63b6df14 226 fdput_pos(f);
1da177e4
LT
227 return error;
228}
229
1da177e4 230struct getdents_callback64 {
5c0ba4e0 231 struct dir_context ctx;
1da177e4
LT
232 struct linux_dirent64 __user * current_dir;
233 struct linux_dirent64 __user * previous;
234 int count;
235 int error;
236};
237
ac7576f4
MS
238static int filldir64(struct dir_context *ctx, const char *name, int namlen,
239 loff_t offset, u64 ino, unsigned int d_type)
1da177e4
LT
240{
241 struct linux_dirent64 __user *dirent;
ac7576f4
MS
242 struct getdents_callback64 *buf =
243 container_of(ctx, struct getdents_callback64, ctx);
85c9fe8f
KW
244 int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
245 sizeof(u64));
1da177e4
LT
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;
273efault:
274 buf->error = -EFAULT;
275 return -EFAULT;
276}
277
20f37034
HC
278SYSCALL_DEFINE3(getdents64, unsigned int, fd,
279 struct linux_dirent64 __user *, dirent, unsigned int, count)
1da177e4 280{
2903ff01 281 struct fd f;
1da177e4 282 struct linux_dirent64 __user * lastdirent;
ac6614b7
AV
283 struct getdents_callback64 buf = {
284 .ctx.actor = filldir64,
285 .count = count,
286 .current_dir = dirent
287 };
1da177e4
LT
288 int error;
289
1da177e4 290 if (!access_ok(VERIFY_WRITE, dirent, count))
863ced7f 291 return -EFAULT;
1da177e4 292
63b6df14 293 f = fdget_pos(fd);
2903ff01 294 if (!f.file)
863ced7f 295 return -EBADF;
1da177e4 296
5c0ba4e0 297 error = iterate_dir(f.file, &buf.ctx);
53c9c5c0
AV
298 if (error >= 0)
299 error = buf.error;
1da177e4
LT
300 lastdirent = buf.previous;
301 if (lastdirent) {
bb6f619b 302 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
1da177e4 303 if (__put_user(d_off, &lastdirent->d_off))
53c9c5c0
AV
304 error = -EFAULT;
305 else
306 error = count - buf.count;
1da177e4 307 }
63b6df14 308 fdput_pos(f);
1da177e4
LT
309 return error;
310}