blob: 443270f635f43e525097ef2339a1f4572f4534d2 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * linux/fs/readdir.c
4 *
5 * Copyright (C) 1995 Linus Torvalds
6 */
7
Kevin Winchester85c9fe82010-08-09 17:20:22 -07008#include <linux/stddef.h>
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07009#include <linux/kernel.h>
Paul Gortmaker630d9c42011-11-16 23:57:37 -050010#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/time.h>
12#include <linux/mm.h>
13#include <linux/errno.h>
14#include <linux/stat.h>
15#include <linux/file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/fs.h>
Heinrich Schuchardtd4c7cf62014-06-04 16:05:41 -070017#include <linux/fsnotify.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/dirent.h>
19#include <linux/security.h>
20#include <linux/syscalls.h>
21#include <linux/unistd.h>
Al Viro0460b2a2017-04-08 18:10:08 -040022#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080024#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
Al Viro5c0ba4e2013-05-15 13:52:59 -040026int iterate_dir(struct file *file, struct dir_context *ctx)
Linus Torvalds1da177e2005-04-16 15:20:36 -070027{
Al Viro496ad9a2013-01-23 17:07:38 -050028 struct inode *inode = file_inode(file);
Al Viro61922692016-04-20 23:08:32 -040029 bool shared = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030 int res = -ENOTDIR;
Al Viro61922692016-04-20 23:08:32 -040031 if (file->f_op->iterate_shared)
32 shared = true;
33 else if (!file->f_op->iterate)
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 goto out;
35
36 res = security_file_permission(file, MAY_READ);
37 if (res)
38 goto out;
39
Kirill Tkhai0dc208b2017-09-29 19:06:48 +030040 if (shared)
41 res = down_read_killable(&inode->i_rwsem);
42 else
Al Viro00235412016-05-26 00:05:12 -040043 res = down_write_killable(&inode->i_rwsem);
Kirill Tkhai0dc208b2017-09-29 19:06:48 +030044 if (res)
45 goto out;
Liam R. Howlettda784512007-12-06 17:39:54 -050046
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 res = -ENOENT;
48 if (!IS_DEADDIR(inode)) {
Al Viro2233f312013-05-22 21:44:23 -040049 ctx->pos = file->f_pos;
Al Viro61922692016-04-20 23:08:32 -040050 if (shared)
51 res = file->f_op->iterate_shared(file, ctx);
52 else
53 res = file->f_op->iterate(file, ctx);
Al Viro2233f312013-05-22 21:44:23 -040054 file->f_pos = ctx->pos;
Heinrich Schuchardtd4c7cf62014-06-04 16:05:41 -070055 fsnotify_access(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 file_accessed(file);
57 }
Al Viro61922692016-04-20 23:08:32 -040058 if (shared)
59 inode_unlock_shared(inode);
60 else
61 inode_unlock(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062out:
63 return res;
64}
Al Viro5c0ba4e2013-05-15 13:52:59 -040065EXPORT_SYMBOL(iterate_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67/*
Linus Torvalds40696eb2019-10-05 11:32:52 -070068 * POSIX says that a dirent name cannot contain NULL or a '/'.
69 *
70 * It's not 100% clear what we should really do in this case.
71 * The filesystem is clearly corrupted, but returning a hard
72 * error means that you now don't see any of the other names
73 * either, so that isn't a perfect alternative.
74 *
75 * And if you return an error, what error do you use? Several
76 * filesystems seem to have decided on EUCLEAN being the error
77 * code for EFSCORRUPTED, and that may be the error to use. Or
78 * just EIO, which is perhaps more obvious to users.
79 *
80 * In order to see the other file names in the directory, the
81 * caller might want to make this a "soft" error: skip the
82 * entry, and return the error at the end instead.
83 *
84 * Note that this should likely do a "memchr(name, 0, len)"
85 * check too, since that would be filesystem corruption as
86 * well. However, that case can't actually confuse user space,
87 * which has to do a strlen() on the name anyway to find the
88 * filename length, and the above "soft error" worry means
89 * that it's probably better left alone until we have that
90 * issue clarified.
91 */
92static int verify_dirent_name(const char *name, int len)
93{
Linus Torvalds0643c3d2019-10-18 18:41:16 -040094 if (!len)
Linus Torvalds40696eb2019-10-05 11:32:52 -070095 return -EIO;
Linus Torvalds0643c3d2019-10-18 18:41:16 -040096 if (memchr(name, '/', len))
Linus Torvalds40696eb2019-10-05 11:32:52 -070097 return -EIO;
98 return 0;
99}
100
101/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 * Traditional linux readdir() handling..
103 *
104 * "count=1" is a special case, meaning that the buffer is one
105 * dirent-structure in size and that the code can't handle more
106 * anyway. Thus the special "fillonedir()" function for that
107 * case (the low-level handlers don't need to care about this).
108 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
110#ifdef __ARCH_WANT_OLD_READDIR
111
112struct old_linux_dirent {
113 unsigned long d_ino;
114 unsigned long d_offset;
115 unsigned short d_namlen;
116 char d_name[1];
117};
118
119struct readdir_callback {
Al Viro5c0ba4e2013-05-15 13:52:59 -0400120 struct dir_context ctx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 struct old_linux_dirent __user * dirent;
122 int result;
123};
124
Miklos Szerediac7576f2014-10-30 17:37:34 +0100125static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
126 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
Miklos Szerediac7576f2014-10-30 17:37:34 +0100128 struct readdir_callback *buf =
129 container_of(ctx, struct readdir_callback, ctx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 struct old_linux_dirent __user * dirent;
David Howellsafefdbb2006-10-03 01:13:46 -0700131 unsigned long d_ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 if (buf->result)
134 return -EINVAL;
David Howellsafefdbb2006-10-03 01:13:46 -0700135 d_ino = ino;
Al Viro8f3f6552008-08-12 00:28:24 -0400136 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
137 buf->result = -EOVERFLOW;
David Howellsafefdbb2006-10-03 01:13:46 -0700138 return -EOVERFLOW;
Al Viro8f3f6552008-08-12 00:28:24 -0400139 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 buf->result++;
141 dirent = buf->dirent;
142 if (!access_ok(VERIFY_WRITE, dirent,
143 (unsigned long)(dirent->d_name + namlen + 1) -
144 (unsigned long)dirent))
145 goto efault;
David Howellsafefdbb2006-10-03 01:13:46 -0700146 if ( __put_user(d_ino, &dirent->d_ino) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 __put_user(offset, &dirent->d_offset) ||
148 __put_user(namlen, &dirent->d_namlen) ||
149 __copy_to_user(dirent->d_name, name, namlen) ||
150 __put_user(0, dirent->d_name + namlen))
151 goto efault;
152 return 0;
153efault:
154 buf->result = -EFAULT;
155 return -EFAULT;
156}
157
Heiko Carstensd4e82042009-01-14 14:14:34 +0100158SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
159 struct old_linux_dirent __user *, dirent, unsigned int, count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160{
161 int error;
Al Viro63b6df12016-04-20 17:08:21 -0400162 struct fd f = fdget_pos(fd);
Al Viroac6614b2013-05-22 22:22:04 -0400163 struct readdir_callback buf = {
164 .ctx.actor = fillonedir,
165 .dirent = dirent
166 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
Al Viro2903ff02012-08-28 12:52:22 -0400168 if (!f.file)
Al Viro863ced72012-04-21 18:40:32 -0400169 return -EBADF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Al Viro5c0ba4e2013-05-15 13:52:59 -0400171 error = iterate_dir(f.file, &buf.ctx);
Al Viro53c9c5c2008-08-24 07:29:52 -0400172 if (buf.result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 error = buf.result;
174
Al Viro63b6df12016-04-20 17:08:21 -0400175 fdput_pos(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 return error;
177}
178
179#endif /* __ARCH_WANT_OLD_READDIR */
180
181/*
182 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
183 * interface.
184 */
185struct linux_dirent {
186 unsigned long d_ino;
187 unsigned long d_off;
188 unsigned short d_reclen;
189 char d_name[1];
190};
191
192struct getdents_callback {
Al Viro5c0ba4e2013-05-15 13:52:59 -0400193 struct dir_context ctx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 struct linux_dirent __user * current_dir;
195 struct linux_dirent __user * previous;
196 int count;
197 int error;
198};
199
Miklos Szerediac7576f2014-10-30 17:37:34 +0100200static int filldir(struct dir_context *ctx, const char *name, int namlen,
201 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 struct linux_dirent __user * dirent;
Miklos Szerediac7576f2014-10-30 17:37:34 +0100204 struct getdents_callback *buf =
205 container_of(ctx, struct getdents_callback, ctx);
David Howellsafefdbb2006-10-03 01:13:46 -0700206 unsigned long d_ino;
Kevin Winchester85c9fe82010-08-09 17:20:22 -0700207 int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
208 sizeof(long));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
Linus Torvalds40696eb2019-10-05 11:32:52 -0700210 buf->error = verify_dirent_name(name, namlen);
211 if (unlikely(buf->error))
212 return buf->error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 buf->error = -EINVAL; /* only used if we fail.. */
214 if (reclen > buf->count)
215 return -EINVAL;
David Howellsafefdbb2006-10-03 01:13:46 -0700216 d_ino = ino;
Al Viro8f3f6552008-08-12 00:28:24 -0400217 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
218 buf->error = -EOVERFLOW;
David Howellsafefdbb2006-10-03 01:13:46 -0700219 return -EOVERFLOW;
Al Viro8f3f6552008-08-12 00:28:24 -0400220 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 dirent = buf->previous;
222 if (dirent) {
Theodore Ts'o1f60fbe2016-04-23 22:50:07 -0400223 if (signal_pending(current))
224 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 if (__put_user(offset, &dirent->d_off))
226 goto efault;
227 }
228 dirent = buf->current_dir;
David Howellsafefdbb2006-10-03 01:13:46 -0700229 if (__put_user(d_ino, &dirent->d_ino))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 goto efault;
231 if (__put_user(reclen, &dirent->d_reclen))
232 goto efault;
233 if (copy_to_user(dirent->d_name, name, namlen))
234 goto efault;
235 if (__put_user(0, dirent->d_name + namlen))
236 goto efault;
237 if (__put_user(d_type, (char __user *) dirent + reclen - 1))
238 goto efault;
239 buf->previous = dirent;
240 dirent = (void __user *)dirent + reclen;
241 buf->current_dir = dirent;
242 buf->count -= reclen;
243 return 0;
244efault:
245 buf->error = -EFAULT;
246 return -EFAULT;
247}
248
Heiko Carstens20f37032009-01-14 14:14:23 +0100249SYSCALL_DEFINE3(getdents, unsigned int, fd,
250 struct linux_dirent __user *, dirent, unsigned int, count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251{
Al Viro2903ff02012-08-28 12:52:22 -0400252 struct fd f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 struct linux_dirent __user * lastdirent;
Al Viroac6614b2013-05-22 22:22:04 -0400254 struct getdents_callback buf = {
255 .ctx.actor = filldir,
256 .count = count,
257 .current_dir = dirent
258 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 int error;
260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 if (!access_ok(VERIFY_WRITE, dirent, count))
Al Viro863ced72012-04-21 18:40:32 -0400262 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
Al Viro63b6df12016-04-20 17:08:21 -0400264 f = fdget_pos(fd);
Al Viro2903ff02012-08-28 12:52:22 -0400265 if (!f.file)
Al Viro863ced72012-04-21 18:40:32 -0400266 return -EBADF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Al Viro5c0ba4e2013-05-15 13:52:59 -0400268 error = iterate_dir(f.file, &buf.ctx);
Al Viro53c9c5c2008-08-24 07:29:52 -0400269 if (error >= 0)
270 error = buf.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 lastdirent = buf.previous;
272 if (lastdirent) {
Al Virobb6f6192013-05-15 18:49:12 -0400273 if (put_user(buf.ctx.pos, &lastdirent->d_off))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 error = -EFAULT;
275 else
276 error = count - buf.count;
277 }
Al Viro63b6df12016-04-20 17:08:21 -0400278 fdput_pos(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 return error;
280}
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282struct getdents_callback64 {
Al Viro5c0ba4e2013-05-15 13:52:59 -0400283 struct dir_context ctx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 struct linux_dirent64 __user * current_dir;
285 struct linux_dirent64 __user * previous;
286 int count;
287 int error;
288};
289
Miklos Szerediac7576f2014-10-30 17:37:34 +0100290static int filldir64(struct dir_context *ctx, const char *name, int namlen,
291 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293 struct linux_dirent64 __user *dirent;
Miklos Szerediac7576f2014-10-30 17:37:34 +0100294 struct getdents_callback64 *buf =
295 container_of(ctx, struct getdents_callback64, ctx);
Kevin Winchester85c9fe82010-08-09 17:20:22 -0700296 int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
297 sizeof(u64));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Linus Torvalds40696eb2019-10-05 11:32:52 -0700299 buf->error = verify_dirent_name(name, namlen);
300 if (unlikely(buf->error))
301 return buf->error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 buf->error = -EINVAL; /* only used if we fail.. */
303 if (reclen > buf->count)
304 return -EINVAL;
305 dirent = buf->previous;
306 if (dirent) {
Theodore Ts'o1f60fbe2016-04-23 22:50:07 -0400307 if (signal_pending(current))
308 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 if (__put_user(offset, &dirent->d_off))
310 goto efault;
311 }
312 dirent = buf->current_dir;
313 if (__put_user(ino, &dirent->d_ino))
314 goto efault;
315 if (__put_user(0, &dirent->d_off))
316 goto efault;
317 if (__put_user(reclen, &dirent->d_reclen))
318 goto efault;
319 if (__put_user(d_type, &dirent->d_type))
320 goto efault;
321 if (copy_to_user(dirent->d_name, name, namlen))
322 goto efault;
323 if (__put_user(0, dirent->d_name + namlen))
324 goto efault;
325 buf->previous = dirent;
326 dirent = (void __user *)dirent + reclen;
327 buf->current_dir = dirent;
328 buf->count -= reclen;
329 return 0;
330efault:
331 buf->error = -EFAULT;
332 return -EFAULT;
333}
334
Dominik Brodowski454dab32018-03-13 21:34:04 +0100335int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
336 unsigned int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
Al Viro2903ff02012-08-28 12:52:22 -0400338 struct fd f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 struct linux_dirent64 __user * lastdirent;
Al Viroac6614b2013-05-22 22:22:04 -0400340 struct getdents_callback64 buf = {
341 .ctx.actor = filldir64,
342 .count = count,
343 .current_dir = dirent
344 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 int error;
346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 if (!access_ok(VERIFY_WRITE, dirent, count))
Al Viro863ced72012-04-21 18:40:32 -0400348 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Al Viro63b6df12016-04-20 17:08:21 -0400350 f = fdget_pos(fd);
Al Viro2903ff02012-08-28 12:52:22 -0400351 if (!f.file)
Al Viro863ced72012-04-21 18:40:32 -0400352 return -EBADF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
Al Viro5c0ba4e2013-05-15 13:52:59 -0400354 error = iterate_dir(f.file, &buf.ctx);
Al Viro53c9c5c2008-08-24 07:29:52 -0400355 if (error >= 0)
356 error = buf.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 lastdirent = buf.previous;
358 if (lastdirent) {
Al Virobb6f6192013-05-15 18:49:12 -0400359 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 if (__put_user(d_off, &lastdirent->d_off))
Al Viro53c9c5c2008-08-24 07:29:52 -0400361 error = -EFAULT;
362 else
363 error = count - buf.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 }
Al Viro63b6df12016-04-20 17:08:21 -0400365 fdput_pos(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 return error;
367}
Al Viro0460b2a2017-04-08 18:10:08 -0400368
Dominik Brodowski454dab32018-03-13 21:34:04 +0100369
370SYSCALL_DEFINE3(getdents64, unsigned int, fd,
371 struct linux_dirent64 __user *, dirent, unsigned int, count)
372{
373 return ksys_getdents64(fd, dirent, count);
374}
375
Al Viro0460b2a2017-04-08 18:10:08 -0400376#ifdef CONFIG_COMPAT
377struct compat_old_linux_dirent {
378 compat_ulong_t d_ino;
379 compat_ulong_t d_offset;
380 unsigned short d_namlen;
381 char d_name[1];
382};
383
384struct compat_readdir_callback {
385 struct dir_context ctx;
386 struct compat_old_linux_dirent __user *dirent;
387 int result;
388};
389
390static int compat_fillonedir(struct dir_context *ctx, const char *name,
391 int namlen, loff_t offset, u64 ino,
392 unsigned int d_type)
393{
394 struct compat_readdir_callback *buf =
395 container_of(ctx, struct compat_readdir_callback, ctx);
396 struct compat_old_linux_dirent __user *dirent;
397 compat_ulong_t d_ino;
398
399 if (buf->result)
400 return -EINVAL;
401 d_ino = ino;
402 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
403 buf->result = -EOVERFLOW;
404 return -EOVERFLOW;
405 }
406 buf->result++;
407 dirent = buf->dirent;
408 if (!access_ok(VERIFY_WRITE, dirent,
409 (unsigned long)(dirent->d_name + namlen + 1) -
410 (unsigned long)dirent))
411 goto efault;
412 if ( __put_user(d_ino, &dirent->d_ino) ||
413 __put_user(offset, &dirent->d_offset) ||
414 __put_user(namlen, &dirent->d_namlen) ||
415 __copy_to_user(dirent->d_name, name, namlen) ||
416 __put_user(0, dirent->d_name + namlen))
417 goto efault;
418 return 0;
419efault:
420 buf->result = -EFAULT;
421 return -EFAULT;
422}
423
424COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
425 struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
426{
427 int error;
428 struct fd f = fdget_pos(fd);
429 struct compat_readdir_callback buf = {
430 .ctx.actor = compat_fillonedir,
431 .dirent = dirent
432 };
433
434 if (!f.file)
435 return -EBADF;
436
437 error = iterate_dir(f.file, &buf.ctx);
438 if (buf.result)
439 error = buf.result;
440
441 fdput_pos(f);
442 return error;
443}
444
445struct compat_linux_dirent {
446 compat_ulong_t d_ino;
447 compat_ulong_t d_off;
448 unsigned short d_reclen;
449 char d_name[1];
450};
451
452struct compat_getdents_callback {
453 struct dir_context ctx;
454 struct compat_linux_dirent __user *current_dir;
455 struct compat_linux_dirent __user *previous;
456 int count;
457 int error;
458};
459
460static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
461 loff_t offset, u64 ino, unsigned int d_type)
462{
463 struct compat_linux_dirent __user * dirent;
464 struct compat_getdents_callback *buf =
465 container_of(ctx, struct compat_getdents_callback, ctx);
466 compat_ulong_t d_ino;
467 int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
468 namlen + 2, sizeof(compat_long_t));
469
470 buf->error = -EINVAL; /* only used if we fail.. */
471 if (reclen > buf->count)
472 return -EINVAL;
473 d_ino = ino;
474 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
475 buf->error = -EOVERFLOW;
476 return -EOVERFLOW;
477 }
478 dirent = buf->previous;
479 if (dirent) {
480 if (signal_pending(current))
481 return -EINTR;
482 if (__put_user(offset, &dirent->d_off))
483 goto efault;
484 }
485 dirent = buf->current_dir;
486 if (__put_user(d_ino, &dirent->d_ino))
487 goto efault;
488 if (__put_user(reclen, &dirent->d_reclen))
489 goto efault;
490 if (copy_to_user(dirent->d_name, name, namlen))
491 goto efault;
492 if (__put_user(0, dirent->d_name + namlen))
493 goto efault;
494 if (__put_user(d_type, (char __user *) dirent + reclen - 1))
495 goto efault;
496 buf->previous = dirent;
497 dirent = (void __user *)dirent + reclen;
498 buf->current_dir = dirent;
499 buf->count -= reclen;
500 return 0;
501efault:
502 buf->error = -EFAULT;
503 return -EFAULT;
504}
505
506COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
507 struct compat_linux_dirent __user *, dirent, unsigned int, count)
508{
509 struct fd f;
510 struct compat_linux_dirent __user * lastdirent;
511 struct compat_getdents_callback buf = {
512 .ctx.actor = compat_filldir,
513 .current_dir = dirent,
514 .count = count
515 };
516 int error;
517
518 if (!access_ok(VERIFY_WRITE, dirent, count))
519 return -EFAULT;
520
521 f = fdget_pos(fd);
522 if (!f.file)
523 return -EBADF;
524
525 error = iterate_dir(f.file, &buf.ctx);
526 if (error >= 0)
527 error = buf.error;
528 lastdirent = buf.previous;
529 if (lastdirent) {
530 if (put_user(buf.ctx.pos, &lastdirent->d_off))
531 error = -EFAULT;
532 else
533 error = count - buf.count;
534 }
535 fdput_pos(f);
536 return error;
537}
538#endif