b24413180f560 (Greg Kroah-Hartman 2017-11-01 15:07:57 +0100 1) // SPDX-License-Identifier: GPL-2.0
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 2) /*
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 3) * linux/fs/readdir.c
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 4) *
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 5) * Copyright (C) 1995 Linus Torvalds
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 6) */
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 7)
85c9fe8fcaf63 (Kevin Winchester 2010-08-09 17:20:22 -0700 8) #include <linux/stddef.h>
022a1692444cd (Milind Arun Choudhary 2007-05-08 00:29:02 -0700 9) #include <linux/kernel.h>
630d9c47274aa (Paul Gortmaker 2011-11-16 23:57:37 -0500 10) #include <linux/export.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 11) #include <linux/time.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 12) #include <linux/mm.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 13) #include <linux/errno.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 14) #include <linux/stat.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 15) #include <linux/file.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 16) #include <linux/fs.h>
d4c7cf6cffb1b (Heinrich Schuchardt 2014-06-04 16:05:41 -0700 17) #include <linux/fsnotify.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 18) #include <linux/dirent.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 19) #include <linux/security.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 20) #include <linux/syscalls.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 21) #include <linux/unistd.h>
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 22) #include <linux/compat.h>
7c0f6ba682b9c (Linus Torvalds 2016-12-24 11:46:01 -0800 23) #include <linux/uaccess.h>
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 24)
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 25) #include <asm/unaligned.h>
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 26)
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 27) /*
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 28) * Note the "unsafe_put_user() semantics: we goto a
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 29) * label for errors.
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 30) */
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 31) #define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 32) char __user *dst = (_dst); \
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 33) const char *src = (_src); \
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 34) size_t len = (_len); \
c512c69187197 (Linus Torvalds 2019-10-07 12:56:48 -0700 35) unsafe_put_user(0, dst+len, label); \
c512c69187197 (Linus Torvalds 2019-10-07 12:56:48 -0700 36) unsafe_copy_to_user(dst, src, len, label); \
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 37) } while (0)
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 38)
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 39)
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 40) int iterate_dir(struct file *file, struct dir_context *ctx)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 41) {
496ad9aa8ef44 (Al Viro 2013-01-23 17:07:38 -0500 42) struct inode *inode = file_inode(file);
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 43) bool shared = false;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 44) int res = -ENOTDIR;
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 45) if (file->f_op->iterate_shared)
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 46) shared = true;
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 47) else if (!file->f_op->iterate)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 48) goto out;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 49)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 50) res = security_file_permission(file, MAY_READ);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 51) if (res)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 52) goto out;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 53)
0dc208b5d5fee (Kirill Tkhai 2017-09-29 19:06:48 +0300 54) if (shared)
0dc208b5d5fee (Kirill Tkhai 2017-09-29 19:06:48 +0300 55) res = down_read_killable(&inode->i_rwsem);
0dc208b5d5fee (Kirill Tkhai 2017-09-29 19:06:48 +0300 56) else
002354112f1e3 (Al Viro 2016-05-26 00:05:12 -0400 57) res = down_write_killable(&inode->i_rwsem);
0dc208b5d5fee (Kirill Tkhai 2017-09-29 19:06:48 +0300 58) if (res)
0dc208b5d5fee (Kirill Tkhai 2017-09-29 19:06:48 +0300 59) goto out;
da78451190bda (Liam R. Howlett 2007-12-06 17:39:54 -0500 60)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 61) res = -ENOENT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 62) if (!IS_DEADDIR(inode)) {
2233f31aade39 (Al Viro 2013-05-22 21:44:23 -0400 63) ctx->pos = file->f_pos;
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 64) if (shared)
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 65) res = file->f_op->iterate_shared(file, ctx);
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 66) else
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 67) res = file->f_op->iterate(file, ctx);
2233f31aade39 (Al Viro 2013-05-22 21:44:23 -0400 68) file->f_pos = ctx->pos;
d4c7cf6cffb1b (Heinrich Schuchardt 2014-06-04 16:05:41 -0700 69) fsnotify_access(file);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 70) file_accessed(file);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 71) }
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 72) if (shared)
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 73) inode_unlock_shared(inode);
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 74) else
6192269444ebf (Al Viro 2016-04-20 23:08:32 -0400 75) inode_unlock(inode);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 76) out:
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 77) return res;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 78) }
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 79) EXPORT_SYMBOL(iterate_dir);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 80)
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 81) /*
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 82) * POSIX says that a dirent name cannot contain NULL or a '/'.
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 83) *
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 84) * It's not 100% clear what we should really do in this case.
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 85) * The filesystem is clearly corrupted, but returning a hard
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 86) * error means that you now don't see any of the other names
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 87) * either, so that isn't a perfect alternative.
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 88) *
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 89) * And if you return an error, what error do you use? Several
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 90) * filesystems seem to have decided on EUCLEAN being the error
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 91) * code for EFSCORRUPTED, and that may be the error to use. Or
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 92) * just EIO, which is perhaps more obvious to users.
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 93) *
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 94) * In order to see the other file names in the directory, the
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 95) * caller might want to make this a "soft" error: skip the
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 96) * entry, and return the error at the end instead.
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 97) *
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 98) * Note that this should likely do a "memchr(name, 0, len)"
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 99) * check too, since that would be filesystem corruption as
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 100) * well. However, that case can't actually confuse user space,
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 101) * which has to do a strlen() on the name anyway to find the
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 102) * filename length, and the above "soft error" worry means
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 103) * that it's probably better left alone until we have that
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 104) * issue clarified.
2c6b7bcd74720 (Linus Torvalds 2020-01-23 10:05:05 -0800 105) *
2c6b7bcd74720 (Linus Torvalds 2020-01-23 10:05:05 -0800 106) * Note the PATH_MAX check - it's arbitrary but the real
2c6b7bcd74720 (Linus Torvalds 2020-01-23 10:05:05 -0800 107) * kernel limit on a possible path component, not NAME_MAX,
2c6b7bcd74720 (Linus Torvalds 2020-01-23 10:05:05 -0800 108) * which is the technical standard limit.
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 109) */
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 110) static int verify_dirent_name(const char *name, int len)
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 111) {
2c6b7bcd74720 (Linus Torvalds 2020-01-23 10:05:05 -0800 112) if (len <= 0 || len >= PATH_MAX)
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 113) return -EIO;
b9959c7a347d6 (Linus Torvalds 2019-10-18 18:41:16 -0400 114) if (memchr(name, '/', len))
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 115) return -EIO;
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 116) return 0;
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 117) }
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 118)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 119) /*
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 120) * Traditional linux readdir() handling..
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 121) *
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 122) * "count=1" is a special case, meaning that the buffer is one
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 123) * dirent-structure in size and that the code can't handle more
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 124) * anyway. Thus the special "fillonedir()" function for that
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 125) * case (the low-level handlers don't need to care about this).
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 126) */
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 127)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 128) #ifdef __ARCH_WANT_OLD_READDIR
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 129)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 130) struct old_linux_dirent {
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 131) unsigned long d_ino;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 132) unsigned long d_offset;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 133) unsigned short d_namlen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 134) char d_name[1];
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 135) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 136)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 137) struct readdir_callback {
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 138) struct dir_context ctx;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 139) struct old_linux_dirent __user * dirent;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 140) int result;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 141) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 142)
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 143) static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 144) loff_t offset, u64 ino, unsigned int d_type)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 145) {
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 146) struct readdir_callback *buf =
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 147) container_of(ctx, struct readdir_callback, ctx);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 148) struct old_linux_dirent __user * dirent;
afefdbb28a0a2 (David Howells 2006-10-03 01:13:46 -0700 149) unsigned long d_ino;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 150)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 151) if (buf->result)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 152) return -EINVAL;
0c93ac69407d6 (Linus Torvalds 2021-04-17 09:27:04 -0700 153) buf->result = verify_dirent_name(name, namlen);
0c93ac69407d6 (Linus Torvalds 2021-04-17 09:27:04 -0700 154) if (buf->result < 0)
0c93ac69407d6 (Linus Torvalds 2021-04-17 09:27:04 -0700 155) return buf->result;
afefdbb28a0a2 (David Howells 2006-10-03 01:13:46 -0700 156) d_ino = ino;
8f3f655da7288 (Al Viro 2008-08-12 00:28:24 -0400 157) if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
8f3f655da7288 (Al Viro 2008-08-12 00:28:24 -0400 158) buf->result = -EOVERFLOW;
afefdbb28a0a2 (David Howells 2006-10-03 01:13:46 -0700 159) return -EOVERFLOW;
8f3f655da7288 (Al Viro 2008-08-12 00:28:24 -0400 160) }
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 161) buf->result++;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 162) dirent = buf->dirent;
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 163) if (!user_write_access_begin(dirent,
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 164) (unsigned long)(dirent->d_name + namlen + 1) -
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 165) (unsigned long)dirent))
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 166) goto efault;
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 167) unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 168) unsafe_put_user(offset, &dirent->d_offset, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 169) unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 170) unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 171) user_write_access_end();
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 172) return 0;
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 173) efault_end:
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 174) user_write_access_end();
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 175) efault:
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 176) buf->result = -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 177) return -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 178) }
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 179)
d4e82042c4cfa (Heiko Carstens 2009-01-14 14:14:34 +0100 180) SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
d4e82042c4cfa (Heiko Carstens 2009-01-14 14:14:34 +0100 181) struct old_linux_dirent __user *, dirent, unsigned int, count)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 182) {
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 183) int error;
63b6df14134dd (Al Viro 2016-04-20 17:08:21 -0400 184) struct fd f = fdget_pos(fd);
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 185) struct readdir_callback buf = {
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 186) .ctx.actor = fillonedir,
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 187) .dirent = dirent
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 188) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 189)
2903ff019b346 (Al Viro 2012-08-28 12:52:22 -0400 190) if (!f.file)
863ced7fe762f (Al Viro 2012-04-21 18:40:32 -0400 191) return -EBADF;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 192)
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 193) error = iterate_dir(f.file, &buf.ctx);
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 194) if (buf.result)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 195) error = buf.result;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 196)
63b6df14134dd (Al Viro 2016-04-20 17:08:21 -0400 197) fdput_pos(f);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 198) return error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 199) }
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 200)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 201) #endif /* __ARCH_WANT_OLD_READDIR */
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 202)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 203) /*
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 204) * New, all-improved, singing, dancing, iBCS2-compliant getdents()
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 205) * interface.
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 206) */
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 207) struct linux_dirent {
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 208) unsigned long d_ino;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 209) unsigned long d_off;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 210) unsigned short d_reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 211) char d_name[1];
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 212) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 213)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 214) struct getdents_callback {
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 215) struct dir_context ctx;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 216) struct linux_dirent __user * current_dir;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 217) int prev_reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 218) int count;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 219) int error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 220) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 221)
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 222) static int filldir(struct dir_context *ctx, const char *name, int namlen,
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 223) loff_t offset, u64 ino, unsigned int d_type)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 224) {
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 225) struct linux_dirent __user *dirent, *prev;
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 226) struct getdents_callback *buf =
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 227) container_of(ctx, struct getdents_callback, ctx);
afefdbb28a0a2 (David Howells 2006-10-03 01:13:46 -0700 228) unsigned long d_ino;
85c9fe8fcaf63 (Kevin Winchester 2010-08-09 17:20:22 -0700 229) int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
85c9fe8fcaf63 (Kevin Winchester 2010-08-09 17:20:22 -0700 230) sizeof(long));
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 231) int prev_reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 232)
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 233) buf->error = verify_dirent_name(name, namlen);
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 234) if (unlikely(buf->error))
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 235) return buf->error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 236) buf->error = -EINVAL; /* only used if we fail.. */
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 237) if (reclen > buf->count)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 238) return -EINVAL;
afefdbb28a0a2 (David Howells 2006-10-03 01:13:46 -0700 239) d_ino = ino;
8f3f655da7288 (Al Viro 2008-08-12 00:28:24 -0400 240) if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
8f3f655da7288 (Al Viro 2008-08-12 00:28:24 -0400 241) buf->error = -EOVERFLOW;
afefdbb28a0a2 (David Howells 2006-10-03 01:13:46 -0700 242) return -EOVERFLOW;
8f3f655da7288 (Al Viro 2008-08-12 00:28:24 -0400 243) }
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 244) prev_reclen = buf->prev_reclen;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 245) if (prev_reclen && signal_pending(current))
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 246) return -EINTR;
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 247) dirent = buf->current_dir;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 248) prev = (void __user *) dirent - prev_reclen;
41cd780524674 (Christophe Leroy 2020-04-03 07:20:51 +0000 249) if (!user_write_access_begin(prev, reclen + prev_reclen))
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 250) goto efault;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 251)
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 252) /* This might be 'dirent->d_off', but if so it will get overwritten */
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 253) unsafe_put_user(offset, &prev->d_off, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 254) unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 255) unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 256) unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 257) unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
41cd780524674 (Christophe Leroy 2020-04-03 07:20:51 +0000 258) user_write_access_end();
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 259)
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 260) buf->current_dir = (void __user *)dirent + reclen;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 261) buf->prev_reclen = reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 262) buf->count -= reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 263) return 0;
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 264) efault_end:
41cd780524674 (Christophe Leroy 2020-04-03 07:20:51 +0000 265) user_write_access_end();
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 266) efault:
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 267) buf->error = -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 268) return -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 269) }
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 270)
20f37034fb966 (Heiko Carstens 2009-01-14 14:14:23 +0100 271) SYSCALL_DEFINE3(getdents, unsigned int, fd,
20f37034fb966 (Heiko Carstens 2009-01-14 14:14:23 +0100 272) struct linux_dirent __user *, dirent, unsigned int, count)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 273) {
2903ff019b346 (Al Viro 2012-08-28 12:52:22 -0400 274) struct fd f;
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 275) struct getdents_callback buf = {
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 276) .ctx.actor = filldir,
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 277) .count = count,
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 278) .current_dir = dirent
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 279) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 280) int error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 281)
63b6df14134dd (Al Viro 2016-04-20 17:08:21 -0400 282) f = fdget_pos(fd);
2903ff019b346 (Al Viro 2012-08-28 12:52:22 -0400 283) if (!f.file)
863ced7fe762f (Al Viro 2012-04-21 18:40:32 -0400 284) return -EBADF;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 285)
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 286) error = iterate_dir(f.file, &buf.ctx);
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 287) if (error >= 0)
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 288) error = buf.error;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 289) if (buf.prev_reclen) {
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 290) struct linux_dirent __user * lastdirent;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 291) lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 292)
bb6f619b3a49f (Al Viro 2013-05-15 18:49:12 -0400 293) if (put_user(buf.ctx.pos, &lastdirent->d_off))
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 294) error = -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 295) else
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 296) error = count - buf.count;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 297) }
63b6df14134dd (Al Viro 2016-04-20 17:08:21 -0400 298) fdput_pos(f);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 299) return error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 300) }
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 301)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 302) struct getdents_callback64 {
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 303) struct dir_context ctx;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 304) struct linux_dirent64 __user * current_dir;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 305) int prev_reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 306) int count;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 307) int error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 308) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 309)
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 310) static int filldir64(struct dir_context *ctx, const char *name, int namlen,
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 311) loff_t offset, u64 ino, unsigned int d_type)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 312) {
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 313) struct linux_dirent64 __user *dirent, *prev;
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 314) struct getdents_callback64 *buf =
ac7576f4b1da8 (Miklos Szeredi 2014-10-30 17:37:34 +0100 315) container_of(ctx, struct getdents_callback64, ctx);
85c9fe8fcaf63 (Kevin Winchester 2010-08-09 17:20:22 -0700 316) int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
85c9fe8fcaf63 (Kevin Winchester 2010-08-09 17:20:22 -0700 317) sizeof(u64));
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 318) int prev_reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 319)
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 320) buf->error = verify_dirent_name(name, namlen);
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 321) if (unlikely(buf->error))
8a23eb804ca4f (Linus Torvalds 2019-10-05 11:32:52 -0700 322) return buf->error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 323) buf->error = -EINVAL; /* only used if we fail.. */
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 324) if (reclen > buf->count)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 325) return -EINVAL;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 326) prev_reclen = buf->prev_reclen;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 327) if (prev_reclen && signal_pending(current))
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 328) return -EINTR;
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 329) dirent = buf->current_dir;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 330) prev = (void __user *)dirent - prev_reclen;
41cd780524674 (Christophe Leroy 2020-04-03 07:20:51 +0000 331) if (!user_write_access_begin(prev, reclen + prev_reclen))
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 332) goto efault;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 333)
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 334) /* This might be 'dirent->d_off', but if so it will get overwritten */
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 335) unsafe_put_user(offset, &prev->d_off, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 336) unsafe_put_user(ino, &dirent->d_ino, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 337) unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 338) unsafe_put_user(d_type, &dirent->d_type, efault_end);
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 339) unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
41cd780524674 (Christophe Leroy 2020-04-03 07:20:51 +0000 340) user_write_access_end();
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 341)
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 342) buf->prev_reclen = reclen;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 343) buf->current_dir = (void __user *)dirent + reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 344) buf->count -= reclen;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 345) return 0;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 346)
9f79b78ef7443 (Linus Torvalds 2016-05-21 21:59:07 -0700 347) efault_end:
41cd780524674 (Christophe Leroy 2020-04-03 07:20:51 +0000 348) user_write_access_end();
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 349) efault:
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 350) buf->error = -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 351) return -EFAULT;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 352) }
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 353)
fb2da16cd70a5 (Christoph Hellwig 2020-07-14 09:02:07 +0200 354) SYSCALL_DEFINE3(getdents64, unsigned int, fd,
fb2da16cd70a5 (Christoph Hellwig 2020-07-14 09:02:07 +0200 355) struct linux_dirent64 __user *, dirent, unsigned int, count)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 356) {
2903ff019b346 (Al Viro 2012-08-28 12:52:22 -0400 357) struct fd f;
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 358) struct getdents_callback64 buf = {
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 359) .ctx.actor = filldir64,
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 360) .count = count,
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 361) .current_dir = dirent
ac6614b76478e (Al Viro 2013-05-22 22:22:04 -0400 362) };
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 363) int error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 364)
63b6df14134dd (Al Viro 2016-04-20 17:08:21 -0400 365) f = fdget_pos(fd);
2903ff019b346 (Al Viro 2012-08-28 12:52:22 -0400 366) if (!f.file)
863ced7fe762f (Al Viro 2012-04-21 18:40:32 -0400 367) return -EBADF;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 368)
5c0ba4e0762e6 (Al Viro 2013-05-15 13:52:59 -0400 369) error = iterate_dir(f.file, &buf.ctx);
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 370) if (error >= 0)
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 371) error = buf.error;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 372) if (buf.prev_reclen) {
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 373) struct linux_dirent64 __user * lastdirent;
bb6f619b3a49f (Al Viro 2013-05-15 18:49:12 -0400 374) typeof(lastdirent->d_off) d_off = buf.ctx.pos;
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 375)
3c2659bd1db81 (Linus Torvalds 2020-01-22 12:37:25 -0800 376) lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
5fb1514164de2 (Al Viro 2020-02-18 22:34:07 -0500 377) if (put_user(d_off, &lastdirent->d_off))
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 378) error = -EFAULT;
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 379) else
53c9c5c0e32c6 (Al Viro 2008-08-24 07:29:52 -0400 380) error = count - buf.count;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 381) }
63b6df14134dd (Al Viro 2016-04-20 17:08:21 -0400 382) fdput_pos(f);
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 383) return error;
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 384) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 385)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 386) #ifdef CONFIG_COMPAT
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 387) struct compat_old_linux_dirent {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 388) compat_ulong_t d_ino;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 389) compat_ulong_t d_offset;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 390) unsigned short d_namlen;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 391) char d_name[1];
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 392) };
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 393)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 394) struct compat_readdir_callback {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 395) struct dir_context ctx;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 396) struct compat_old_linux_dirent __user *dirent;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 397) int result;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 398) };
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 399)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 400) static int compat_fillonedir(struct dir_context *ctx, const char *name,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 401) int namlen, loff_t offset, u64 ino,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 402) unsigned int d_type)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 403) {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 404) struct compat_readdir_callback *buf =
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 405) container_of(ctx, struct compat_readdir_callback, ctx);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 406) struct compat_old_linux_dirent __user *dirent;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 407) compat_ulong_t d_ino;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 408)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 409) if (buf->result)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 410) return -EINVAL;
0c93ac69407d6 (Linus Torvalds 2021-04-17 09:27:04 -0700 411) buf->result = verify_dirent_name(name, namlen);
0c93ac69407d6 (Linus Torvalds 2021-04-17 09:27:04 -0700 412) if (buf->result < 0)
0c93ac69407d6 (Linus Torvalds 2021-04-17 09:27:04 -0700 413) return buf->result;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 414) d_ino = ino;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 415) if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 416) buf->result = -EOVERFLOW;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 417) return -EOVERFLOW;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 418) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 419) buf->result++;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 420) dirent = buf->dirent;
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 421) if (!user_write_access_begin(dirent,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 422) (unsigned long)(dirent->d_name + namlen + 1) -
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 423) (unsigned long)dirent))
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 424) goto efault;
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 425) unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 426) unsafe_put_user(offset, &dirent->d_offset, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 427) unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 428) unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 429) user_write_access_end();
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 430) return 0;
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 431) efault_end:
391b7461d4a14 (Al Viro 2020-02-18 14:39:56 -0500 432) user_write_access_end();
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 433) efault:
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 434) buf->result = -EFAULT;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 435) return -EFAULT;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 436) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 437)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 438) COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 439) struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 440) {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 441) int error;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 442) struct fd f = fdget_pos(fd);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 443) struct compat_readdir_callback buf = {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 444) .ctx.actor = compat_fillonedir,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 445) .dirent = dirent
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 446) };
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 447)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 448) if (!f.file)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 449) return -EBADF;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 450)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 451) error = iterate_dir(f.file, &buf.ctx);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 452) if (buf.result)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 453) error = buf.result;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 454)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 455) fdput_pos(f);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 456) return error;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 457) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 458)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 459) struct compat_linux_dirent {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 460) compat_ulong_t d_ino;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 461) compat_ulong_t d_off;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 462) unsigned short d_reclen;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 463) char d_name[1];
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 464) };
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 465)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 466) struct compat_getdents_callback {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 467) struct dir_context ctx;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 468) struct compat_linux_dirent __user *current_dir;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 469) int prev_reclen;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 470) int count;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 471) int error;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 472) };
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 473)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 474) static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 475) loff_t offset, u64 ino, unsigned int d_type)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 476) {
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 477) struct compat_linux_dirent __user *dirent, *prev;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 478) struct compat_getdents_callback *buf =
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 479) container_of(ctx, struct compat_getdents_callback, ctx);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 480) compat_ulong_t d_ino;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 481) int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 482) namlen + 2, sizeof(compat_long_t));
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 483) int prev_reclen;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 484)
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 485) buf->error = verify_dirent_name(name, namlen);
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 486) if (unlikely(buf->error))
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 487) return buf->error;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 488) buf->error = -EINVAL; /* only used if we fail.. */
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 489) if (reclen > buf->count)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 490) return -EINVAL;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 491) d_ino = ino;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 492) if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 493) buf->error = -EOVERFLOW;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 494) return -EOVERFLOW;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 495) }
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 496) prev_reclen = buf->prev_reclen;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 497) if (prev_reclen && signal_pending(current))
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 498) return -EINTR;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 499) dirent = buf->current_dir;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 500) prev = (void __user *) dirent - prev_reclen;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 501) if (!user_write_access_begin(prev, reclen + prev_reclen))
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 502) goto efault;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 503)
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 504) unsafe_put_user(offset, &prev->d_off, efault_end);
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 505) unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 506) unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 507) unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 508) unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 509) user_write_access_end();
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 510)
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 511) buf->prev_reclen = reclen;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 512) buf->current_dir = (void __user *)dirent + reclen;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 513) buf->count -= reclen;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 514) return 0;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 515) efault_end:
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 516) user_write_access_end();
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 517) efault:
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 518) buf->error = -EFAULT;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 519) return -EFAULT;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 520) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 521)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 522) COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 523) struct compat_linux_dirent __user *, dirent, unsigned int, count)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 524) {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 525) struct fd f;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 526) struct compat_getdents_callback buf = {
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 527) .ctx.actor = compat_filldir,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 528) .current_dir = dirent,
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 529) .count = count
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 530) };
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 531) int error;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 532)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 533) f = fdget_pos(fd);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 534) if (!f.file)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 535) return -EBADF;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 536)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 537) error = iterate_dir(f.file, &buf.ctx);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 538) if (error >= 0)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 539) error = buf.error;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 540) if (buf.prev_reclen) {
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 541) struct compat_linux_dirent __user * lastdirent;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 542) lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
82af599b70365 (Al Viro 2020-02-18 22:33:09 -0500 543)
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 544) if (put_user(buf.ctx.pos, &lastdirent->d_off))
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 545) error = -EFAULT;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 546) else
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 547) error = count - buf.count;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 548) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 549) fdput_pos(f);
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 550) return error;
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 551) }
0460b2a28b4b3 (Al Viro 2017-04-08 18:10:08 -0400 552) #endif