1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/readdir.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1995 Linus Torvalds
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
885c9fe8fSKevin Winchester #include <linux/stddef.h>
9022a1692SMilind Arun Choudhary #include <linux/kernel.h>
10630d9c47SPaul Gortmaker #include <linux/export.h>
111da177e4SLinus Torvalds #include <linux/time.h>
121da177e4SLinus Torvalds #include <linux/mm.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/stat.h>
151da177e4SLinus Torvalds #include <linux/file.h>
161da177e4SLinus Torvalds #include <linux/fs.h>
17d4c7cf6cSHeinrich Schuchardt #include <linux/fsnotify.h>
181da177e4SLinus Torvalds #include <linux/dirent.h>
191da177e4SLinus Torvalds #include <linux/security.h>
201da177e4SLinus Torvalds #include <linux/syscalls.h>
211da177e4SLinus Torvalds #include <linux/unistd.h>
220460b2a2SAl Viro #include <linux/compat.h>
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
241da177e4SLinus Torvalds
259f79b78eSLinus Torvalds #include <asm/unaligned.h>
269f79b78eSLinus Torvalds
279f79b78eSLinus Torvalds /*
28*3e327154SLinus Torvalds * Some filesystems were never converted to '->iterate_shared()'
29*3e327154SLinus Torvalds * and their directory iterators want the inode lock held for
30*3e327154SLinus Torvalds * writing. This wrapper allows for converting from the shared
31*3e327154SLinus Torvalds * semantics to the exclusive inode use.
32*3e327154SLinus Torvalds */
wrap_directory_iterator(struct file * file,struct dir_context * ctx,int (* iter)(struct file *,struct dir_context *))33*3e327154SLinus Torvalds int wrap_directory_iterator(struct file *file,
34*3e327154SLinus Torvalds struct dir_context *ctx,
35*3e327154SLinus Torvalds int (*iter)(struct file *, struct dir_context *))
36*3e327154SLinus Torvalds {
37*3e327154SLinus Torvalds struct inode *inode = file_inode(file);
38*3e327154SLinus Torvalds int ret;
39*3e327154SLinus Torvalds
40*3e327154SLinus Torvalds /*
41*3e327154SLinus Torvalds * We'd love to have an 'inode_upgrade_trylock()' operation,
42*3e327154SLinus Torvalds * see the comment in mmap_upgrade_trylock() in mm/memory.c.
43*3e327154SLinus Torvalds *
44*3e327154SLinus Torvalds * But considering this is for "filesystems that never got
45*3e327154SLinus Torvalds * converted", it really doesn't matter.
46*3e327154SLinus Torvalds *
47*3e327154SLinus Torvalds * Also note that since we have to return with the lock held
48*3e327154SLinus Torvalds * for reading, we can't use the "killable()" locking here,
49*3e327154SLinus Torvalds * since we do need to get the lock even if we're dying.
50*3e327154SLinus Torvalds *
51*3e327154SLinus Torvalds * We could do the write part killably and then get the read
52*3e327154SLinus Torvalds * lock unconditionally if it mattered, but see above on why
53*3e327154SLinus Torvalds * this does the very simplistic conversion.
54*3e327154SLinus Torvalds */
55*3e327154SLinus Torvalds up_read(&inode->i_rwsem);
56*3e327154SLinus Torvalds down_write(&inode->i_rwsem);
57*3e327154SLinus Torvalds
58*3e327154SLinus Torvalds /*
59*3e327154SLinus Torvalds * Since we dropped the inode lock, we should do the
60*3e327154SLinus Torvalds * DEADDIR test again. See 'iterate_dir()' below.
61*3e327154SLinus Torvalds *
62*3e327154SLinus Torvalds * Note that we don't need to re-do the f_pos games,
63*3e327154SLinus Torvalds * since the file must be locked wrt f_pos anyway.
64*3e327154SLinus Torvalds */
65*3e327154SLinus Torvalds ret = -ENOENT;
66*3e327154SLinus Torvalds if (!IS_DEADDIR(inode))
67*3e327154SLinus Torvalds ret = iter(file, ctx);
68*3e327154SLinus Torvalds
69*3e327154SLinus Torvalds downgrade_write(&inode->i_rwsem);
70*3e327154SLinus Torvalds return ret;
71*3e327154SLinus Torvalds }
72*3e327154SLinus Torvalds EXPORT_SYMBOL(wrap_directory_iterator);
73*3e327154SLinus Torvalds
74*3e327154SLinus Torvalds /*
759f79b78eSLinus Torvalds * Note the "unsafe_put_user() semantics: we goto a
769f79b78eSLinus Torvalds * label for errors.
779f79b78eSLinus Torvalds */
789f79b78eSLinus Torvalds #define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \
799f79b78eSLinus Torvalds char __user *dst = (_dst); \
809f79b78eSLinus Torvalds const char *src = (_src); \
819f79b78eSLinus Torvalds size_t len = (_len); \
82c512c691SLinus Torvalds unsafe_put_user(0, dst+len, label); \
83c512c691SLinus Torvalds unsafe_copy_to_user(dst, src, len, label); \
849f79b78eSLinus Torvalds } while (0)
859f79b78eSLinus Torvalds
869f79b78eSLinus Torvalds
iterate_dir(struct file * file,struct dir_context * ctx)875c0ba4e0SAl Viro int iterate_dir(struct file *file, struct dir_context *ctx)
881da177e4SLinus Torvalds {
89496ad9aaSAl Viro struct inode *inode = file_inode(file);
901da177e4SLinus Torvalds int res = -ENOTDIR;
91*3e327154SLinus Torvalds
92*3e327154SLinus Torvalds if (!file->f_op->iterate_shared)
931da177e4SLinus Torvalds goto out;
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds res = security_file_permission(file, MAY_READ);
961da177e4SLinus Torvalds if (res)
971da177e4SLinus Torvalds goto out;
981da177e4SLinus Torvalds
990dc208b5SKirill Tkhai res = down_read_killable(&inode->i_rwsem);
10000235411SAl Viro if (res)
10100235411SAl Viro goto out;
102da784511SLiam R. Howlett
1031da177e4SLinus Torvalds res = -ENOENT;
1041da177e4SLinus Torvalds if (!IS_DEADDIR(inode)) {
105bb6f619bSAl Viro ctx->pos = file->f_pos;
10661922694SAl Viro res = file->f_op->iterate_shared(file, ctx);
107bb6f619bSAl Viro file->f_pos = ctx->pos;
108d4c7cf6cSHeinrich Schuchardt fsnotify_access(file);
1091da177e4SLinus Torvalds file_accessed(file);
1101da177e4SLinus Torvalds }
11161922694SAl Viro inode_unlock_shared(inode);
1121da177e4SLinus Torvalds out:
1131da177e4SLinus Torvalds return res;
1141da177e4SLinus Torvalds }
1155c0ba4e0SAl Viro EXPORT_SYMBOL(iterate_dir);
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds /*
1188a23eb80SLinus Torvalds * POSIX says that a dirent name cannot contain NULL or a '/'.
1198a23eb80SLinus Torvalds *
1208a23eb80SLinus Torvalds * It's not 100% clear what we should really do in this case.
1218a23eb80SLinus Torvalds * The filesystem is clearly corrupted, but returning a hard
1228a23eb80SLinus Torvalds * error means that you now don't see any of the other names
1238a23eb80SLinus Torvalds * either, so that isn't a perfect alternative.
1248a23eb80SLinus Torvalds *
1258a23eb80SLinus Torvalds * And if you return an error, what error do you use? Several
1268a23eb80SLinus Torvalds * filesystems seem to have decided on EUCLEAN being the error
1278a23eb80SLinus Torvalds * code for EFSCORRUPTED, and that may be the error to use. Or
1288a23eb80SLinus Torvalds * just EIO, which is perhaps more obvious to users.
1298a23eb80SLinus Torvalds *
1308a23eb80SLinus Torvalds * In order to see the other file names in the directory, the
1318a23eb80SLinus Torvalds * caller might want to make this a "soft" error: skip the
1328a23eb80SLinus Torvalds * entry, and return the error at the end instead.
1338a23eb80SLinus Torvalds *
1348a23eb80SLinus Torvalds * Note that this should likely do a "memchr(name, 0, len)"
1358a23eb80SLinus Torvalds * check too, since that would be filesystem corruption as
1368a23eb80SLinus Torvalds * well. However, that case can't actually confuse user space,
1378a23eb80SLinus Torvalds * which has to do a strlen() on the name anyway to find the
1388a23eb80SLinus Torvalds * filename length, and the above "soft error" worry means
1398a23eb80SLinus Torvalds * that it's probably better left alone until we have that
1408a23eb80SLinus Torvalds * issue clarified.
1412c6b7bcdSLinus Torvalds *
1422c6b7bcdSLinus Torvalds * Note the PATH_MAX check - it's arbitrary but the real
1432c6b7bcdSLinus Torvalds * kernel limit on a possible path component, not NAME_MAX,
1442c6b7bcdSLinus Torvalds * which is the technical standard limit.
1458a23eb80SLinus Torvalds */
verify_dirent_name(const char * name,int len)1468a23eb80SLinus Torvalds static int verify_dirent_name(const char *name, int len)
1478a23eb80SLinus Torvalds {
1482c6b7bcdSLinus Torvalds if (len <= 0 || len >= PATH_MAX)
1498a23eb80SLinus Torvalds return -EIO;
150b9959c7aSLinus Torvalds if (memchr(name, '/', len))
1518a23eb80SLinus Torvalds return -EIO;
1528a23eb80SLinus Torvalds return 0;
1538a23eb80SLinus Torvalds }
1548a23eb80SLinus Torvalds
1558a23eb80SLinus Torvalds /*
1561da177e4SLinus Torvalds * Traditional linux readdir() handling..
1571da177e4SLinus Torvalds *
1581da177e4SLinus Torvalds * "count=1" is a special case, meaning that the buffer is one
1591da177e4SLinus Torvalds * dirent-structure in size and that the code can't handle more
1601da177e4SLinus Torvalds * anyway. Thus the special "fillonedir()" function for that
1611da177e4SLinus Torvalds * case (the low-level handlers don't need to care about this).
1621da177e4SLinus Torvalds */
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds #ifdef __ARCH_WANT_OLD_READDIR
1651da177e4SLinus Torvalds
1661da177e4SLinus Torvalds struct old_linux_dirent {
1671da177e4SLinus Torvalds unsigned long d_ino;
1681da177e4SLinus Torvalds unsigned long d_offset;
1691da177e4SLinus Torvalds unsigned short d_namlen;
1702507135eSGustavo A. R. Silva char d_name[];
1711da177e4SLinus Torvalds };
1721da177e4SLinus Torvalds
1731da177e4SLinus Torvalds struct readdir_callback {
1745c0ba4e0SAl Viro struct dir_context ctx;
1751da177e4SLinus Torvalds struct old_linux_dirent __user * dirent;
1761da177e4SLinus Torvalds int result;
1771da177e4SLinus Torvalds };
1781da177e4SLinus Torvalds
fillonedir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)17925885a35SAl Viro static bool fillonedir(struct dir_context *ctx, const char *name, int namlen,
180ac7576f4SMiklos Szeredi loff_t offset, u64 ino, unsigned int d_type)
1811da177e4SLinus Torvalds {
182ac7576f4SMiklos Szeredi struct readdir_callback *buf =
183ac7576f4SMiklos Szeredi container_of(ctx, struct readdir_callback, ctx);
1841da177e4SLinus Torvalds struct old_linux_dirent __user * dirent;
185afefdbb2SDavid Howells unsigned long d_ino;
1861da177e4SLinus Torvalds
1871da177e4SLinus Torvalds if (buf->result)
18825885a35SAl Viro return false;
1890c93ac69SLinus Torvalds buf->result = verify_dirent_name(name, namlen);
19025885a35SAl Viro if (buf->result)
19125885a35SAl Viro return false;
192afefdbb2SDavid Howells d_ino = ino;
1938f3f655dSAl Viro if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
1948f3f655dSAl Viro buf->result = -EOVERFLOW;
19525885a35SAl Viro return false;
1968f3f655dSAl Viro }
1971da177e4SLinus Torvalds buf->result++;
1981da177e4SLinus Torvalds dirent = buf->dirent;
199391b7461SAl Viro if (!user_write_access_begin(dirent,
2001da177e4SLinus Torvalds (unsigned long)(dirent->d_name + namlen + 1) -
2011da177e4SLinus Torvalds (unsigned long)dirent))
2021da177e4SLinus Torvalds goto efault;
203391b7461SAl Viro unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
204391b7461SAl Viro unsafe_put_user(offset, &dirent->d_offset, efault_end);
205391b7461SAl Viro unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
206391b7461SAl Viro unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
207391b7461SAl Viro user_write_access_end();
20825885a35SAl Viro return true;
209391b7461SAl Viro efault_end:
210391b7461SAl Viro user_write_access_end();
2111da177e4SLinus Torvalds efault:
2121da177e4SLinus Torvalds buf->result = -EFAULT;
21325885a35SAl Viro return false;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds
SYSCALL_DEFINE3(old_readdir,unsigned int,fd,struct old_linux_dirent __user *,dirent,unsigned int,count)216d4e82042SHeiko Carstens SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
217d4e82042SHeiko Carstens struct old_linux_dirent __user *, dirent, unsigned int, count)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds int error;
22063b6df14SAl Viro struct fd f = fdget_pos(fd);
221ac6614b7SAl Viro struct readdir_callback buf = {
222ac6614b7SAl Viro .ctx.actor = fillonedir,
223ac6614b7SAl Viro .dirent = dirent
224ac6614b7SAl Viro };
2251da177e4SLinus Torvalds
2262903ff01SAl Viro if (!f.file)
227863ced7fSAl Viro return -EBADF;
2281da177e4SLinus Torvalds
2295c0ba4e0SAl Viro error = iterate_dir(f.file, &buf.ctx);
23053c9c5c0SAl Viro if (buf.result)
2311da177e4SLinus Torvalds error = buf.result;
2321da177e4SLinus Torvalds
23363b6df14SAl Viro fdput_pos(f);
2341da177e4SLinus Torvalds return error;
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds
2371da177e4SLinus Torvalds #endif /* __ARCH_WANT_OLD_READDIR */
2381da177e4SLinus Torvalds
2391da177e4SLinus Torvalds /*
2401da177e4SLinus Torvalds * New, all-improved, singing, dancing, iBCS2-compliant getdents()
2411da177e4SLinus Torvalds * interface.
2421da177e4SLinus Torvalds */
2431da177e4SLinus Torvalds struct linux_dirent {
2441da177e4SLinus Torvalds unsigned long d_ino;
2451da177e4SLinus Torvalds unsigned long d_off;
2461da177e4SLinus Torvalds unsigned short d_reclen;
2472507135eSGustavo A. R. Silva char d_name[];
2481da177e4SLinus Torvalds };
2491da177e4SLinus Torvalds
2501da177e4SLinus Torvalds struct getdents_callback {
2515c0ba4e0SAl Viro struct dir_context ctx;
2521da177e4SLinus Torvalds struct linux_dirent __user * current_dir;
2533c2659bdSLinus Torvalds int prev_reclen;
2541da177e4SLinus Torvalds int count;
2551da177e4SLinus Torvalds int error;
2561da177e4SLinus Torvalds };
2571da177e4SLinus Torvalds
filldir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)25825885a35SAl Viro static bool filldir(struct dir_context *ctx, const char *name, int namlen,
259ac7576f4SMiklos Szeredi loff_t offset, u64 ino, unsigned int d_type)
2601da177e4SLinus Torvalds {
2613c2659bdSLinus Torvalds struct linux_dirent __user *dirent, *prev;
262ac7576f4SMiklos Szeredi struct getdents_callback *buf =
263ac7576f4SMiklos Szeredi container_of(ctx, struct getdents_callback, ctx);
264afefdbb2SDavid Howells unsigned long d_ino;
26585c9fe8fSKevin Winchester int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
26685c9fe8fSKevin Winchester sizeof(long));
2673c2659bdSLinus Torvalds int prev_reclen;
2681da177e4SLinus Torvalds
2698a23eb80SLinus Torvalds buf->error = verify_dirent_name(name, namlen);
2708a23eb80SLinus Torvalds if (unlikely(buf->error))
27125885a35SAl Viro return false;
2721da177e4SLinus Torvalds buf->error = -EINVAL; /* only used if we fail.. */
2731da177e4SLinus Torvalds if (reclen > buf->count)
27425885a35SAl Viro return false;
275afefdbb2SDavid Howells d_ino = ino;
2768f3f655dSAl Viro if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
2778f3f655dSAl Viro buf->error = -EOVERFLOW;
27825885a35SAl Viro return false;
2798f3f655dSAl Viro }
2803c2659bdSLinus Torvalds prev_reclen = buf->prev_reclen;
2813c2659bdSLinus Torvalds if (prev_reclen && signal_pending(current))
28225885a35SAl Viro return false;
2831da177e4SLinus Torvalds dirent = buf->current_dir;
2843c2659bdSLinus Torvalds prev = (void __user *) dirent - prev_reclen;
28541cd7805SChristophe Leroy if (!user_write_access_begin(prev, reclen + prev_reclen))
2863c2659bdSLinus Torvalds goto efault;
2873c2659bdSLinus Torvalds
2883c2659bdSLinus Torvalds /* This might be 'dirent->d_off', but if so it will get overwritten */
2893c2659bdSLinus Torvalds unsafe_put_user(offset, &prev->d_off, efault_end);
2909f79b78eSLinus Torvalds unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
2919f79b78eSLinus Torvalds unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
2929f79b78eSLinus Torvalds unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
2939f79b78eSLinus Torvalds unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
29441cd7805SChristophe Leroy user_write_access_end();
2959f79b78eSLinus Torvalds
2963c2659bdSLinus Torvalds buf->current_dir = (void __user *)dirent + reclen;
2973c2659bdSLinus Torvalds buf->prev_reclen = reclen;
2981da177e4SLinus Torvalds buf->count -= reclen;
29925885a35SAl Viro return true;
3009f79b78eSLinus Torvalds efault_end:
30141cd7805SChristophe Leroy user_write_access_end();
3021da177e4SLinus Torvalds efault:
3031da177e4SLinus Torvalds buf->error = -EFAULT;
30425885a35SAl Viro return false;
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds
SYSCALL_DEFINE3(getdents,unsigned int,fd,struct linux_dirent __user *,dirent,unsigned int,count)30720f37034SHeiko Carstens SYSCALL_DEFINE3(getdents, unsigned int, fd,
30820f37034SHeiko Carstens struct linux_dirent __user *, dirent, unsigned int, count)
3091da177e4SLinus Torvalds {
3102903ff01SAl Viro struct fd f;
311ac6614b7SAl Viro struct getdents_callback buf = {
312ac6614b7SAl Viro .ctx.actor = filldir,
313ac6614b7SAl Viro .count = count,
314ac6614b7SAl Viro .current_dir = dirent
315ac6614b7SAl Viro };
3161da177e4SLinus Torvalds int error;
3171da177e4SLinus Torvalds
31863b6df14SAl Viro f = fdget_pos(fd);
3192903ff01SAl Viro if (!f.file)
320863ced7fSAl Viro return -EBADF;
3211da177e4SLinus Torvalds
3225c0ba4e0SAl Viro error = iterate_dir(f.file, &buf.ctx);
32353c9c5c0SAl Viro if (error >= 0)
3241da177e4SLinus Torvalds error = buf.error;
3253c2659bdSLinus Torvalds if (buf.prev_reclen) {
3263c2659bdSLinus Torvalds struct linux_dirent __user * lastdirent;
3273c2659bdSLinus Torvalds lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
3283c2659bdSLinus Torvalds
329bb6f619bSAl Viro if (put_user(buf.ctx.pos, &lastdirent->d_off))
3301da177e4SLinus Torvalds error = -EFAULT;
3311da177e4SLinus Torvalds else
3321da177e4SLinus Torvalds error = count - buf.count;
3331da177e4SLinus Torvalds }
33463b6df14SAl Viro fdput_pos(f);
3351da177e4SLinus Torvalds return error;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds
3381da177e4SLinus Torvalds struct getdents_callback64 {
3395c0ba4e0SAl Viro struct dir_context ctx;
3401da177e4SLinus Torvalds struct linux_dirent64 __user * current_dir;
3413c2659bdSLinus Torvalds int prev_reclen;
3421da177e4SLinus Torvalds int count;
3431da177e4SLinus Torvalds int error;
3441da177e4SLinus Torvalds };
3451da177e4SLinus Torvalds
filldir64(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)34625885a35SAl Viro static bool filldir64(struct dir_context *ctx, const char *name, int namlen,
347ac7576f4SMiklos Szeredi loff_t offset, u64 ino, unsigned int d_type)
3481da177e4SLinus Torvalds {
3493c2659bdSLinus Torvalds struct linux_dirent64 __user *dirent, *prev;
350ac7576f4SMiklos Szeredi struct getdents_callback64 *buf =
351ac7576f4SMiklos Szeredi container_of(ctx, struct getdents_callback64, ctx);
35285c9fe8fSKevin Winchester int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
35385c9fe8fSKevin Winchester sizeof(u64));
3543c2659bdSLinus Torvalds int prev_reclen;
3551da177e4SLinus Torvalds
3568a23eb80SLinus Torvalds buf->error = verify_dirent_name(name, namlen);
3578a23eb80SLinus Torvalds if (unlikely(buf->error))
35825885a35SAl Viro return false;
3591da177e4SLinus Torvalds buf->error = -EINVAL; /* only used if we fail.. */
3601da177e4SLinus Torvalds if (reclen > buf->count)
36125885a35SAl Viro return false;
3623c2659bdSLinus Torvalds prev_reclen = buf->prev_reclen;
3633c2659bdSLinus Torvalds if (prev_reclen && signal_pending(current))
36425885a35SAl Viro return false;
3651da177e4SLinus Torvalds dirent = buf->current_dir;
3663c2659bdSLinus Torvalds prev = (void __user *)dirent - prev_reclen;
36741cd7805SChristophe Leroy if (!user_write_access_begin(prev, reclen + prev_reclen))
3683c2659bdSLinus Torvalds goto efault;
3693c2659bdSLinus Torvalds
3703c2659bdSLinus Torvalds /* This might be 'dirent->d_off', but if so it will get overwritten */
3713c2659bdSLinus Torvalds unsafe_put_user(offset, &prev->d_off, efault_end);
3729f79b78eSLinus Torvalds unsafe_put_user(ino, &dirent->d_ino, efault_end);
3739f79b78eSLinus Torvalds unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
3749f79b78eSLinus Torvalds unsafe_put_user(d_type, &dirent->d_type, efault_end);
3759f79b78eSLinus Torvalds unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
37641cd7805SChristophe Leroy user_write_access_end();
3779f79b78eSLinus Torvalds
3783c2659bdSLinus Torvalds buf->prev_reclen = reclen;
3793c2659bdSLinus Torvalds buf->current_dir = (void __user *)dirent + reclen;
3801da177e4SLinus Torvalds buf->count -= reclen;
38125885a35SAl Viro return true;
3823c2659bdSLinus Torvalds
3839f79b78eSLinus Torvalds efault_end:
38441cd7805SChristophe Leroy user_write_access_end();
3851da177e4SLinus Torvalds efault:
3861da177e4SLinus Torvalds buf->error = -EFAULT;
38725885a35SAl Viro return false;
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds
SYSCALL_DEFINE3(getdents64,unsigned int,fd,struct linux_dirent64 __user *,dirent,unsigned int,count)390fb2da16cSChristoph Hellwig SYSCALL_DEFINE3(getdents64, unsigned int, fd,
391fb2da16cSChristoph Hellwig struct linux_dirent64 __user *, dirent, unsigned int, count)
3921da177e4SLinus Torvalds {
3932903ff01SAl Viro struct fd f;
394ac6614b7SAl Viro struct getdents_callback64 buf = {
395ac6614b7SAl Viro .ctx.actor = filldir64,
396ac6614b7SAl Viro .count = count,
397ac6614b7SAl Viro .current_dir = dirent
398ac6614b7SAl Viro };
3991da177e4SLinus Torvalds int error;
4001da177e4SLinus Torvalds
40163b6df14SAl Viro f = fdget_pos(fd);
4022903ff01SAl Viro if (!f.file)
403863ced7fSAl Viro return -EBADF;
4041da177e4SLinus Torvalds
4055c0ba4e0SAl Viro error = iterate_dir(f.file, &buf.ctx);
40653c9c5c0SAl Viro if (error >= 0)
4071da177e4SLinus Torvalds error = buf.error;
4083c2659bdSLinus Torvalds if (buf.prev_reclen) {
4093c2659bdSLinus Torvalds struct linux_dirent64 __user * lastdirent;
410bb6f619bSAl Viro typeof(lastdirent->d_off) d_off = buf.ctx.pos;
4113c2659bdSLinus Torvalds
4123c2659bdSLinus Torvalds lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
4135fb15141SAl Viro if (put_user(d_off, &lastdirent->d_off))
41453c9c5c0SAl Viro error = -EFAULT;
41553c9c5c0SAl Viro else
4161da177e4SLinus Torvalds error = count - buf.count;
4171da177e4SLinus Torvalds }
41863b6df14SAl Viro fdput_pos(f);
4191da177e4SLinus Torvalds return error;
4201da177e4SLinus Torvalds }
4210460b2a2SAl Viro
4220460b2a2SAl Viro #ifdef CONFIG_COMPAT
4230460b2a2SAl Viro struct compat_old_linux_dirent {
4240460b2a2SAl Viro compat_ulong_t d_ino;
4250460b2a2SAl Viro compat_ulong_t d_offset;
4260460b2a2SAl Viro unsigned short d_namlen;
4272507135eSGustavo A. R. Silva char d_name[];
4280460b2a2SAl Viro };
4290460b2a2SAl Viro
4300460b2a2SAl Viro struct compat_readdir_callback {
4310460b2a2SAl Viro struct dir_context ctx;
4320460b2a2SAl Viro struct compat_old_linux_dirent __user *dirent;
4330460b2a2SAl Viro int result;
4340460b2a2SAl Viro };
4350460b2a2SAl Viro
compat_fillonedir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)43625885a35SAl Viro static bool compat_fillonedir(struct dir_context *ctx, const char *name,
4370460b2a2SAl Viro int namlen, loff_t offset, u64 ino,
4380460b2a2SAl Viro unsigned int d_type)
4390460b2a2SAl Viro {
4400460b2a2SAl Viro struct compat_readdir_callback *buf =
4410460b2a2SAl Viro container_of(ctx, struct compat_readdir_callback, ctx);
4420460b2a2SAl Viro struct compat_old_linux_dirent __user *dirent;
4430460b2a2SAl Viro compat_ulong_t d_ino;
4440460b2a2SAl Viro
4450460b2a2SAl Viro if (buf->result)
44625885a35SAl Viro return false;
4470c93ac69SLinus Torvalds buf->result = verify_dirent_name(name, namlen);
44825885a35SAl Viro if (buf->result)
44925885a35SAl Viro return false;
4500460b2a2SAl Viro d_ino = ino;
4510460b2a2SAl Viro if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
4520460b2a2SAl Viro buf->result = -EOVERFLOW;
45325885a35SAl Viro return false;
4540460b2a2SAl Viro }
4550460b2a2SAl Viro buf->result++;
4560460b2a2SAl Viro dirent = buf->dirent;
457391b7461SAl Viro if (!user_write_access_begin(dirent,
4580460b2a2SAl Viro (unsigned long)(dirent->d_name + namlen + 1) -
4590460b2a2SAl Viro (unsigned long)dirent))
4600460b2a2SAl Viro goto efault;
461391b7461SAl Viro unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
462391b7461SAl Viro unsafe_put_user(offset, &dirent->d_offset, efault_end);
463391b7461SAl Viro unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
464391b7461SAl Viro unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
465391b7461SAl Viro user_write_access_end();
46625885a35SAl Viro return true;
467391b7461SAl Viro efault_end:
468391b7461SAl Viro user_write_access_end();
4690460b2a2SAl Viro efault:
4700460b2a2SAl Viro buf->result = -EFAULT;
47125885a35SAl Viro return false;
4720460b2a2SAl Viro }
4730460b2a2SAl Viro
COMPAT_SYSCALL_DEFINE3(old_readdir,unsigned int,fd,struct compat_old_linux_dirent __user *,dirent,unsigned int,count)4740460b2a2SAl Viro COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
4750460b2a2SAl Viro struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
4760460b2a2SAl Viro {
4770460b2a2SAl Viro int error;
4780460b2a2SAl Viro struct fd f = fdget_pos(fd);
4790460b2a2SAl Viro struct compat_readdir_callback buf = {
4800460b2a2SAl Viro .ctx.actor = compat_fillonedir,
4810460b2a2SAl Viro .dirent = dirent
4820460b2a2SAl Viro };
4830460b2a2SAl Viro
4840460b2a2SAl Viro if (!f.file)
4850460b2a2SAl Viro return -EBADF;
4860460b2a2SAl Viro
4870460b2a2SAl Viro error = iterate_dir(f.file, &buf.ctx);
4880460b2a2SAl Viro if (buf.result)
4890460b2a2SAl Viro error = buf.result;
4900460b2a2SAl Viro
4910460b2a2SAl Viro fdput_pos(f);
4920460b2a2SAl Viro return error;
4930460b2a2SAl Viro }
4940460b2a2SAl Viro
4950460b2a2SAl Viro struct compat_linux_dirent {
4960460b2a2SAl Viro compat_ulong_t d_ino;
4970460b2a2SAl Viro compat_ulong_t d_off;
4980460b2a2SAl Viro unsigned short d_reclen;
4992507135eSGustavo A. R. Silva char d_name[];
5000460b2a2SAl Viro };
5010460b2a2SAl Viro
5020460b2a2SAl Viro struct compat_getdents_callback {
5030460b2a2SAl Viro struct dir_context ctx;
5040460b2a2SAl Viro struct compat_linux_dirent __user *current_dir;
50582af599bSAl Viro int prev_reclen;
5060460b2a2SAl Viro int count;
5070460b2a2SAl Viro int error;
5080460b2a2SAl Viro };
5090460b2a2SAl Viro
compat_filldir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)51025885a35SAl Viro static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen,
5110460b2a2SAl Viro loff_t offset, u64 ino, unsigned int d_type)
5120460b2a2SAl Viro {
51382af599bSAl Viro struct compat_linux_dirent __user *dirent, *prev;
5140460b2a2SAl Viro struct compat_getdents_callback *buf =
5150460b2a2SAl Viro container_of(ctx, struct compat_getdents_callback, ctx);
5160460b2a2SAl Viro compat_ulong_t d_ino;
5170460b2a2SAl Viro int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
5180460b2a2SAl Viro namlen + 2, sizeof(compat_long_t));
51982af599bSAl Viro int prev_reclen;
5200460b2a2SAl Viro
52182af599bSAl Viro buf->error = verify_dirent_name(name, namlen);
52282af599bSAl Viro if (unlikely(buf->error))
52325885a35SAl Viro return false;
5240460b2a2SAl Viro buf->error = -EINVAL; /* only used if we fail.. */
5250460b2a2SAl Viro if (reclen > buf->count)
52625885a35SAl Viro return false;
5270460b2a2SAl Viro d_ino = ino;
5280460b2a2SAl Viro if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
5290460b2a2SAl Viro buf->error = -EOVERFLOW;
53025885a35SAl Viro return false;
5310460b2a2SAl Viro }
53282af599bSAl Viro prev_reclen = buf->prev_reclen;
53382af599bSAl Viro if (prev_reclen && signal_pending(current))
53425885a35SAl Viro return false;
5350460b2a2SAl Viro dirent = buf->current_dir;
53682af599bSAl Viro prev = (void __user *) dirent - prev_reclen;
53782af599bSAl Viro if (!user_write_access_begin(prev, reclen + prev_reclen))
5380460b2a2SAl Viro goto efault;
53982af599bSAl Viro
54082af599bSAl Viro unsafe_put_user(offset, &prev->d_off, efault_end);
54182af599bSAl Viro unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
54282af599bSAl Viro unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
54382af599bSAl Viro unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
54482af599bSAl Viro unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
54582af599bSAl Viro user_write_access_end();
54682af599bSAl Viro
54782af599bSAl Viro buf->prev_reclen = reclen;
54882af599bSAl Viro buf->current_dir = (void __user *)dirent + reclen;
5490460b2a2SAl Viro buf->count -= reclen;
55025885a35SAl Viro return true;
55182af599bSAl Viro efault_end:
55282af599bSAl Viro user_write_access_end();
5530460b2a2SAl Viro efault:
5540460b2a2SAl Viro buf->error = -EFAULT;
55525885a35SAl Viro return false;
5560460b2a2SAl Viro }
5570460b2a2SAl Viro
COMPAT_SYSCALL_DEFINE3(getdents,unsigned int,fd,struct compat_linux_dirent __user *,dirent,unsigned int,count)5580460b2a2SAl Viro COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
5590460b2a2SAl Viro struct compat_linux_dirent __user *, dirent, unsigned int, count)
5600460b2a2SAl Viro {
5610460b2a2SAl Viro struct fd f;
5620460b2a2SAl Viro struct compat_getdents_callback buf = {
5630460b2a2SAl Viro .ctx.actor = compat_filldir,
5640460b2a2SAl Viro .current_dir = dirent,
5650460b2a2SAl Viro .count = count
5660460b2a2SAl Viro };
5670460b2a2SAl Viro int error;
5680460b2a2SAl Viro
5690460b2a2SAl Viro f = fdget_pos(fd);
5700460b2a2SAl Viro if (!f.file)
5710460b2a2SAl Viro return -EBADF;
5720460b2a2SAl Viro
5730460b2a2SAl Viro error = iterate_dir(f.file, &buf.ctx);
5740460b2a2SAl Viro if (error >= 0)
5750460b2a2SAl Viro error = buf.error;
57682af599bSAl Viro if (buf.prev_reclen) {
57782af599bSAl Viro struct compat_linux_dirent __user * lastdirent;
57882af599bSAl Viro lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
57982af599bSAl Viro
5800460b2a2SAl Viro if (put_user(buf.ctx.pos, &lastdirent->d_off))
5810460b2a2SAl Viro error = -EFAULT;
5820460b2a2SAl Viro else
5830460b2a2SAl Viro error = count - buf.count;
5840460b2a2SAl Viro }
5850460b2a2SAl Viro fdput_pos(f);
5860460b2a2SAl Viro return error;
5870460b2a2SAl Viro }
5880460b2a2SAl Viro #endif
589