VisionFive2 Linux kernel

StarFive Tech Linux Kernel for VisionFive (JH7110) boards (mirror)

More than 9999 Commits   32 Branches   54 Tags
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