From dab741e0e02bd3c4f5e2e97be74b39df2523fc6e Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Thu, 27 Aug 2020 11:09:46 -0600 Subject: [PATCH 01/10] Add a "nosymfollow" mount option. For mounts that have the new "nosymfollow" option, don't follow symlinks when resolving paths. The new option is similar in spirit to the existing "nodev", "noexec", and "nosuid" options, as well as to the LOOKUP_NO_SYMLINKS resolve flag in the openat2(2) syscall. Various BSD variants have been supporting the "nosymfollow" mount option for a long time with equivalent implementations. Note that symlinks may still be created on file systems mounted with the "nosymfollow" option present. readlink() remains functional, so user space code that is aware of symlinks can still choose to follow them explicitly. Setting the "nosymfollow" mount option helps prevent privileged writers from modifying files unintentionally in case there is an unexpected link along the accessed path. The "nosymfollow" option is thus useful as a defensive measure for systems that need to deal with untrusted file systems in privileged contexts. More information on the history and motivation for this patch can be found here: https://sites.google.com/a/chromium.org/dev/chromium-os/chromiumos-design-docs/hardening-against-malicious-stateful-data#TOC-Restricting-symlink-traversal Signed-off-by: Mattias Nissler Signed-off-by: Ross Zwisler Reviewed-by: Aleksa Sarai Signed-off-by: Al Viro --- fs/namei.c | 3 ++- fs/namespace.c | 2 ++ fs/proc_namespace.c | 1 + fs/statfs.c | 2 ++ include/linux/mount.h | 3 ++- include/linux/statfs.h | 1 + include/uapi/linux/mount.h | 1 + 7 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index e99e2a9da0f7..33e8c79bc761 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1626,7 +1626,8 @@ static const char *pick_link(struct nameidata *nd, struct path *link, return ERR_PTR(error); } - if (unlikely(nd->flags & LOOKUP_NO_SYMLINKS)) + if (unlikely(nd->flags & LOOKUP_NO_SYMLINKS) || + unlikely(link->mnt->mnt_flags & MNT_NOSYMFOLLOW)) return ERR_PTR(-ELOOP); if (!(nd->flags & LOOKUP_RCU)) { diff --git a/fs/namespace.c b/fs/namespace.c index bae0e95b3713..6408788a649e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3160,6 +3160,8 @@ int path_mount(const char *dev_name, struct path *path, mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME); if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY; + if (flags & MS_NOSYMFOLLOW) + mnt_flags |= MNT_NOSYMFOLLOW; /* The default atime for remount is preservation */ if ((flags & MS_REMOUNT) && diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 3059a9394c2d..e59d4bb3a89e 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -70,6 +70,7 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) { MNT_NOATIME, ",noatime" }, { MNT_NODIRATIME, ",nodiratime" }, { MNT_RELATIME, ",relatime" }, + { MNT_NOSYMFOLLOW, ",nosymfollow" }, { 0, NULL } }; const struct proc_fs_opts *fs_infop; diff --git a/fs/statfs.c b/fs/statfs.c index 2616424012ea..59f33752c131 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -29,6 +29,8 @@ static int flags_by_mnt(int mnt_flags) flags |= ST_NODIRATIME; if (mnt_flags & MNT_RELATIME) flags |= ST_RELATIME; + if (mnt_flags & MNT_NOSYMFOLLOW) + flags |= ST_NOSYMFOLLOW; return flags; } diff --git a/include/linux/mount.h b/include/linux/mount.h index de657bd211fa..aaf343b38671 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -30,6 +30,7 @@ struct fs_context; #define MNT_NODIRATIME 0x10 #define MNT_RELATIME 0x20 #define MNT_READONLY 0x40 /* does the user want this to be r/o? */ +#define MNT_NOSYMFOLLOW 0x80 #define MNT_SHRINKABLE 0x100 #define MNT_WRITE_HOLD 0x200 @@ -46,7 +47,7 @@ struct fs_context; #define MNT_SHARED_MASK (MNT_UNBINDABLE) #define MNT_USER_SETTABLE_MASK (MNT_NOSUID | MNT_NODEV | MNT_NOEXEC \ | MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME \ - | MNT_READONLY) + | MNT_READONLY | MNT_NOSYMFOLLOW) #define MNT_ATIME_MASK (MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME ) #define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \ diff --git a/include/linux/statfs.h b/include/linux/statfs.h index 9bc69edb8f18..fac4356ea1bf 100644 --- a/include/linux/statfs.h +++ b/include/linux/statfs.h @@ -40,6 +40,7 @@ struct kstatfs { #define ST_NOATIME 0x0400 /* do not update access times */ #define ST_NODIRATIME 0x0800 /* do not update directory access times */ #define ST_RELATIME 0x1000 /* update atime relative to mtime/ctime */ +#define ST_NOSYMFOLLOW 0x2000 /* do not follow symlinks */ struct dentry; extern int vfs_get_fsid(struct dentry *dentry, __kernel_fsid_t *fsid); diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h index 96a0240f23fe..dd8306ea336c 100644 --- a/include/uapi/linux/mount.h +++ b/include/uapi/linux/mount.h @@ -16,6 +16,7 @@ #define MS_REMOUNT 32 /* Alter flags of a mounted FS */ #define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */ #define MS_DIRSYNC 128 /* Directory modifications are synchronous */ +#define MS_NOSYMFOLLOW 256 /* Do not follow symlinks */ #define MS_NOATIME 1024 /* Do not update access times. */ #define MS_NODIRATIME 2048 /* Do not update directory access times */ #define MS_BIND 4096 From 8ec90bfd1aeb39f44c3e704aaa9ca88c52e6866a Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Thu, 27 Aug 2020 11:09:47 -0600 Subject: [PATCH 02/10] selftests: mount: add nosymfollow tests Add tests for the new 'nosymfollow' mount option. We test to make sure that symlink traversal fails with ELOOP when 'nosymfollow' is set, but that readlink(2) and realpath(3) still work as expected. We also verify that statfs(2) correctly returns ST_NOSYMFOLLOW when we are mounted with the 'nosymfollow' option. Signed-off-by: Ross Zwisler Signed-off-by: Al Viro --- tools/testing/selftests/mount/.gitignore | 1 + tools/testing/selftests/mount/Makefile | 4 +- .../selftests/mount/nosymfollow-test.c | 218 ++++++++++++++++++ .../selftests/mount/run_nosymfollow.sh | 4 + ...n_tests.sh => run_unprivileged_remount.sh} | 0 5 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/mount/nosymfollow-test.c create mode 100755 tools/testing/selftests/mount/run_nosymfollow.sh rename tools/testing/selftests/mount/{run_tests.sh => run_unprivileged_remount.sh} (100%) diff --git a/tools/testing/selftests/mount/.gitignore b/tools/testing/selftests/mount/.gitignore index 0bc64a6d4c18..17f2d8415162 100644 --- a/tools/testing/selftests/mount/.gitignore +++ b/tools/testing/selftests/mount/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only unprivileged-remount-test +nosymfollow-test diff --git a/tools/testing/selftests/mount/Makefile b/tools/testing/selftests/mount/Makefile index 026890744215..2d9454841644 100644 --- a/tools/testing/selftests/mount/Makefile +++ b/tools/testing/selftests/mount/Makefile @@ -3,7 +3,7 @@ CFLAGS = -Wall \ -O2 -TEST_PROGS := run_tests.sh -TEST_GEN_FILES := unprivileged-remount-test +TEST_PROGS := run_unprivileged_remount.sh run_nosymfollow.sh +TEST_GEN_FILES := unprivileged-remount-test nosymfollow-test include ../lib.mk diff --git a/tools/testing/selftests/mount/nosymfollow-test.c b/tools/testing/selftests/mount/nosymfollow-test.c new file mode 100644 index 000000000000..650d6d80a1d2 --- /dev/null +++ b/tools/testing/selftests/mount/nosymfollow-test.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MS_NOSYMFOLLOW +# define MS_NOSYMFOLLOW 256 /* Do not follow symlinks */ +#endif + +#ifndef ST_NOSYMFOLLOW +# define ST_NOSYMFOLLOW 0x2000 /* Do not follow symlinks */ +#endif + +#define DATA "/tmp/data" +#define LINK "/tmp/symlink" +#define TMP "/tmp" + +static void die(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, + va_list ap) +{ + ssize_t written; + char buf[4096]; + int buf_len; + int fd; + + buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); + if (buf_len < 0) + die("vsnprintf failed: %s\n", strerror(errno)); + + if (buf_len >= sizeof(buf)) + die("vsnprintf output truncated\n"); + + fd = open(filename, O_WRONLY); + if (fd < 0) { + if ((errno == ENOENT) && enoent_ok) + return; + die("open of %s failed: %s\n", filename, strerror(errno)); + } + + written = write(fd, buf, buf_len); + if (written != buf_len) { + if (written >= 0) { + die("short write to %s\n", filename); + } else { + die("write to %s failed: %s\n", + filename, strerror(errno)); + } + } + + if (close(fd) != 0) + die("close of %s failed: %s\n", filename, strerror(errno)); +} + +static void maybe_write_file(char *filename, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vmaybe_write_file(true, filename, fmt, ap); + va_end(ap); +} + +static void write_file(char *filename, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vmaybe_write_file(false, filename, fmt, ap); + va_end(ap); +} + +static void create_and_enter_ns(void) +{ + uid_t uid = getuid(); + gid_t gid = getgid(); + + if (unshare(CLONE_NEWUSER) != 0) + die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno)); + + maybe_write_file("/proc/self/setgroups", "deny"); + write_file("/proc/self/uid_map", "0 %d 1", uid); + write_file("/proc/self/gid_map", "0 %d 1", gid); + + if (setgid(0) != 0) + die("setgid(0) failed %s\n", strerror(errno)); + if (setuid(0) != 0) + die("setuid(0) failed %s\n", strerror(errno)); + + if (unshare(CLONE_NEWNS) != 0) + die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno)); +} + +static void setup_symlink(void) +{ + int data, err; + + data = creat(DATA, O_RDWR); + if (data < 0) + die("creat failed: %s\n", strerror(errno)); + + err = symlink(DATA, LINK); + if (err < 0) + die("symlink failed: %s\n", strerror(errno)); + + if (close(data) != 0) + die("close of %s failed: %s\n", DATA, strerror(errno)); +} + +static void test_link_traversal(bool nosymfollow) +{ + int link; + + link = open(LINK, 0, O_RDWR); + if (nosymfollow) { + if ((link != -1 || errno != ELOOP)) { + die("link traversal unexpected result: %d, %s\n", + link, strerror(errno)); + } + } else { + if (link < 0) + die("link traversal failed: %s\n", strerror(errno)); + + if (close(link) != 0) + die("close of link failed: %s\n", strerror(errno)); + } +} + +static void test_readlink(void) +{ + char buf[4096]; + ssize_t ret; + + bzero(buf, sizeof(buf)); + + ret = readlink(LINK, buf, sizeof(buf)); + if (ret < 0) + die("readlink failed: %s\n", strerror(errno)); + if (strcmp(buf, DATA) != 0) + die("readlink strcmp failed: '%s' '%s'\n", buf, DATA); +} + +static void test_realpath(void) +{ + char *path = realpath(LINK, NULL); + + if (!path) + die("realpath failed: %s\n", strerror(errno)); + if (strcmp(path, DATA) != 0) + die("realpath strcmp failed\n"); + + free(path); +} + +static void test_statfs(bool nosymfollow) +{ + struct statfs buf; + int ret; + + ret = statfs(TMP, &buf); + if (ret) + die("statfs failed: %s\n", strerror(errno)); + + if (nosymfollow) { + if ((buf.f_flags & ST_NOSYMFOLLOW) == 0) + die("ST_NOSYMFOLLOW not set on %s\n", TMP); + } else { + if ((buf.f_flags & ST_NOSYMFOLLOW) != 0) + die("ST_NOSYMFOLLOW set on %s\n", TMP); + } +} + +static void run_tests(bool nosymfollow) +{ + test_link_traversal(nosymfollow); + test_readlink(); + test_realpath(); + test_statfs(nosymfollow); +} + +int main(int argc, char **argv) +{ + create_and_enter_ns(); + + if (mount("testing", TMP, "ramfs", 0, NULL) != 0) + die("mount failed: %s\n", strerror(errno)); + + setup_symlink(); + run_tests(false); + + if (mount("testing", TMP, "ramfs", MS_REMOUNT|MS_NOSYMFOLLOW, NULL) != 0) + die("remount failed: %s\n", strerror(errno)); + + run_tests(true); + + return EXIT_SUCCESS; +} diff --git a/tools/testing/selftests/mount/run_nosymfollow.sh b/tools/testing/selftests/mount/run_nosymfollow.sh new file mode 100755 index 000000000000..5fbbf03043a2 --- /dev/null +++ b/tools/testing/selftests/mount/run_nosymfollow.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +./nosymfollow-test diff --git a/tools/testing/selftests/mount/run_tests.sh b/tools/testing/selftests/mount/run_unprivileged_remount.sh similarity index 100% rename from tools/testing/selftests/mount/run_tests.sh rename to tools/testing/selftests/mount/run_unprivileged_remount.sh From aabf59432c51be174994ecfe280f75ac139b5550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Sun, 6 Sep 2020 22:39:49 +0000 Subject: [PATCH 03/10] fs: Remove duplicated flag O_NDELAY occurring twice in VALID_OPEN_FLAGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The O_NDELAY flag occurs twice in the VALID_OPEN_FLAGS definition, this change removes the duplicate. There is no change to the functionality. Note, that the flags O_NONBLOCK and O_NDELAY are not duplicates, as values of these flags are platform dependent, and on platforms like Sparc O_NONBLOCK and O_NDELAY are not the same. This has been done that way to maintain the ABI compatibility with Solaris since the Sparc port was first introduced. This change resolves the following Coccinelle warning: include/linux/fcntl.h:11:13-21: duplicated argument to & or | Signed-off-by: Krzysztof WilczyƄski Signed-off-by: Al Viro --- include/linux/fcntl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index 7bcdcf4f6ab2..921e750843e6 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -8,7 +8,7 @@ /* List of all valid flags for the open/openat flags argument: */ #define VALID_OPEN_FLAGS \ (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \ - O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \ + O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \ FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) From 6d1349c769ea28543bdde20a658cbc93c3bc936d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 18 Sep 2020 16:45:50 -0400 Subject: [PATCH 04/10] [PATCH] reduce boilerplate in fsid handling Get rid of boilerplate in most of ->statfs() instances... Signed-off-by: Al Viro --- fs/9p/vfs_super.c | 3 +-- fs/adfs/super.c | 3 +-- fs/affs/super.c | 3 +-- fs/befs/linuxvfs.c | 3 +-- fs/bfs/inode.c | 3 +-- fs/ceph/super.c | 3 +-- fs/cramfs/inode.c | 3 +-- fs/efs/super.c | 3 +-- fs/erofs/super.c | 3 +-- fs/exfat/super.c | 3 +-- fs/ext2/super.c | 3 +-- fs/ext4/super.c | 3 +-- fs/f2fs/super.c | 3 +-- fs/fat/inode.c | 3 +-- fs/hfs/super.c | 3 +-- fs/hfsplus/super.c | 3 +-- fs/hpfs/super.c | 3 +-- fs/isofs/inode.c | 3 +-- fs/minix/inode.c | 3 +-- fs/nilfs2/super.c | 3 +-- fs/ntfs/super.c | 3 +-- fs/omfs/inode.c | 3 +-- fs/qnx4/inode.c | 3 +-- fs/qnx6/inode.c | 3 +-- fs/romfs/super.c | 3 +-- fs/squashfs/super.c | 3 +-- fs/sysv/inode.c | 3 +-- fs/udf/super.c | 3 +-- fs/ufs/super.c | 3 +-- fs/xfs/xfs_super.c | 3 +-- fs/zonefs/super.c | 3 +-- include/linux/statfs.h | 5 +++++ 32 files changed, 36 insertions(+), 62 deletions(-) diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 74df32be4c6a..124855b3324d 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -258,8 +258,7 @@ static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = rs.bavail; buf->f_files = rs.files; buf->f_ffree = rs.ffree; - buf->f_fsid.val[0] = rs.fsid & 0xFFFFFFFFUL; - buf->f_fsid.val[1] = (rs.fsid >> 32) & 0xFFFFFFFFUL; + buf->f_fsid = u64_to_fsid(rs.fsid); buf->f_namelen = rs.namelen; } if (res != -ENOSYS) diff --git a/fs/adfs/super.c b/fs/adfs/super.c index d553bb5bc17a..bdbd26e571ed 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -210,8 +210,7 @@ static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_namelen = sbi->s_namelen; buf->f_bsize = sb->s_blocksize; buf->f_ffree = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/affs/super.c b/fs/affs/super.c index 47107c6712a6..890b44874f0f 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -620,8 +620,7 @@ affs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = AFFS_SB(sb)->s_partition_size - AFFS_SB(sb)->s_reserved; buf->f_bfree = free; buf->f_bavail = free; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = AFFSNAMEMAX; return 0; } diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 2482032021ca..c1ba13d19024 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -963,8 +963,7 @@ befs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = buf->f_bfree; buf->f_files = 0; /* UNKNOWN */ buf->f_ffree = 0; /* UNKNOWN */ - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = BEFS_NAME_LEN; befs_debug(sb, "<--- %s", __func__); diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index f8ce1368218b..3ac7611ef7ce 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -229,8 +229,7 @@ static int bfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = buf->f_bavail = info->si_freeb; buf->f_files = info->si_lasti + 1 - BFS_ROOT_INO; buf->f_ffree = info->si_freei; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = BFS_NAMELEN; return 0; } diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 7ec0e6d03d10..cafb252a9d98 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -104,8 +104,7 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) le64_to_cpu(*((__le64 *)&monc->monmap->fsid + 1)); mutex_unlock(&monc->mutex); - buf->f_fsid.val[0] = fsid & 0xffffffff; - buf->f_fsid.val[1] = fsid >> 32; + buf->f_fsid = u64_to_fsid(fsid); return 0; } diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 912308600d39..4b90cfd1ec36 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -690,8 +690,7 @@ static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = 0; buf->f_files = CRAMFS_SB(sb)->files; buf->f_ffree = 0; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = CRAMFS_MAXPATHLEN; return 0; } diff --git a/fs/efs/super.c b/fs/efs/super.c index a4a945d0ac6a..62b155b9366b 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -342,8 +342,7 @@ static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) { sbi->inode_blocks * (EFS_BLOCKSIZE / sizeof(struct efs_dinode)); buf->f_ffree = sbi->inode_free; /* free inodes */ - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */ return 0; diff --git a/fs/erofs/super.c b/fs/erofs/super.c index ddaa516c008a..744089ab134c 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -563,8 +563,7 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_namelen = EROFS_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 3b6a1659892f..ea17e8ed5067 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -89,8 +89,7 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */ buf->f_bfree = buf->f_blocks - sbi->used_clusters; buf->f_bavail = buf->f_bfree; - buf->f_fsid.val[0] = (unsigned int)id; - buf->f_fsid.val[1] = (unsigned int)(id >> 32); + buf->f_fsid = u64_to_fsid(id); /* Unicode utf16 255 characters */ buf->f_namelen = EXFAT_MAX_FILE_LEN * NLS_MAX_CHARSET_SIZE; return 0; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index dda860562ca3..4911c59679bb 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1455,8 +1455,7 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) buf->f_namelen = EXT2_NAME_LEN; fsid = le64_to_cpup((void *)es->s_uuid) ^ le64_to_cpup((void *)es->s_uuid + sizeof(u64)); - buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL; - buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL; + buf->f_fsid = u64_to_fsid(fsid); spin_unlock(&sbi->s_lock); return 0; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 0907f907c47d..eef0a7d81e0d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -5787,8 +5787,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_namelen = EXT4_NAME_LEN; fsid = le64_to_cpup((void *)es->s_uuid) ^ le64_to_cpup((void *)es->s_uuid + sizeof(u64)); - buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL; - buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL; + buf->f_fsid = u64_to_fsid(fsid); #ifdef CONFIG_QUOTA if (ext4_test_inode_flag(dentry->d_inode, EXT4_INODE_PROJINHERIT) && diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index dfa072fa8081..088872f4e568 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1422,8 +1422,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) } buf->f_namelen = F2FS_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); #ifdef CONFIG_QUOTA if (is_inode_flag_set(dentry->d_inode, FI_PROJ_INHERIT) && diff --git a/fs/fat/inode.c b/fs/fat/inode.c index a0cf99debb1e..bab9b202b496 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -836,8 +836,7 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = sbi->max_cluster - FAT_START_ENT; buf->f_bfree = sbi->free_clusters; buf->f_bavail = sbi->free_clusters; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = (sbi->options.isvfat ? FAT_LFN_LEN : 12) * NLS_MAX_CHARSET_SIZE; diff --git a/fs/hfs/super.c b/fs/hfs/super.c index c33324686d89..44d07c9e3a7f 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -104,8 +104,7 @@ static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = buf->f_bfree; buf->f_files = HFS_SB(sb)->fs_ablocks; buf->f_ffree = HFS_SB(sb)->free_ablocks; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = HFS_NAMELEN; return 0; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 129dca3f4b78..807119ae5adf 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -320,8 +320,7 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = buf->f_bfree; buf->f_files = 0xFFFFFFFF; buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = HFSPLUS_MAX_STRLEN; return 0; diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 0a677a9aaf34..a7dbfc892022 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -192,8 +192,7 @@ static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = sbi->sb_n_free; buf->f_files = sbi->sb_dirband_size / 4; buf->f_ffree = hpfs_get_free_dnodes(s); - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = 254; hpfs_unlock(s); diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 78f5c96c76f3..ec90773527ee 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1038,8 +1038,7 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = 0; buf->f_files = ISOFS_SB(sb)->s_ninodes; buf->f_ffree = 0; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_namelen = NAME_MAX; return 0; } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 7b09a9158e40..34f546404aa1 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -383,8 +383,7 @@ static int minix_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_files = sbi->s_ninodes; buf->f_ffree = minix_count_free_inodes(sb); buf->f_namelen = sbi->s_namelen; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 2eee5fb1a882..4abd928b0bc8 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -651,8 +651,7 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_files = nmaxinodes; buf->f_ffree = nfreeinodes; buf->f_namelen = NILFS_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 7dc3bc604f78..0d7e948cb29c 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2643,8 +2643,7 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs) * the least significant 32-bits in f_fsid[0] and the most significant * 32-bits in f_fsid[1]. */ - sfs->f_fsid.val[0] = vol->serial_no & 0xffffffff; - sfs->f_fsid.val[1] = (vol->serial_no >> 32) & 0xffffffff; + sfs->f_fsid = u64_to_fsid(vol->serial_no); /* Maximum length of filenames. */ sfs->f_namelen = NTFS_MAX_NAME_LEN; return 0; diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index b76ec6b88ded..68aa38a48308 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -282,8 +282,7 @@ static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = sbi->s_num_blocks; buf->f_files = sbi->s_num_blocks; buf->f_namelen = OMFS_NAMELEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); buf->f_bfree = buf->f_bavail = buf->f_ffree = omfs_count_free(s); diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index e8da1cde87b9..3fb7fc819b4f 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -137,8 +137,7 @@ static int qnx4_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = qnx4_count_free_blocks(sb); buf->f_bavail = buf->f_bfree; buf->f_namelen = QNX4_NAME_MAX; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c index 755293c8c71a..61191f7bdf62 100644 --- a/fs/qnx6/inode.c +++ b/fs/qnx6/inode.c @@ -166,8 +166,7 @@ static int qnx6_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_ffree = fs32_to_cpu(sbi, sbi->sb->sb_free_inodes); buf->f_bavail = buf->f_bfree; buf->f_namelen = QNX6_LONG_NAME_MAX; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/romfs/super.c b/fs/romfs/super.c index e582d001f792..d69c8e4f4973 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -415,8 +415,7 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = buf->f_bavail = buf->f_ffree; buf->f_blocks = (romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 0cc4ceec0562..d6c6593ec169 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -380,8 +380,7 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_files = msblk->inodes; buf->f_ffree = 0; buf->f_namelen = SQUASHFS_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 02b1d9d0c182..be47263b8605 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -98,8 +98,7 @@ static int sysv_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_files = sbi->s_ninodes; buf->f_ffree = sysv_count_free_inodes(sb); buf->f_namelen = SYSV_NAMELEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/udf/super.c b/fs/udf/super.c index 1c42f544096d..d40ecaf3c2c2 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -2411,8 +2411,7 @@ static int udf_statfs(struct dentry *dentry, struct kstatfs *buf) + buf->f_bfree; buf->f_ffree = buf->f_bfree; buf->f_namelen = UDF_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); return 0; } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index e3b69fb280e8..983558b572c7 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1431,8 +1431,7 @@ static int ufs_statfs(struct dentry *dentry, struct kstatfs *buf) ? (buf->f_bfree - uspi->s_root_blocks) : 0; buf->f_files = uspi->s_ncg * uspi->s_ipg; buf->f_namelen = UFS_MAXNAMLEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_fsid = u64_to_fsid(id); mutex_unlock(&UFS_SB(sb)->s_lock); diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 71ac6c1cdc36..2e04dfc373c1 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -794,8 +794,7 @@ xfs_fs_statfs( statp->f_namelen = MAXNAMELEN - 1; id = huge_encode_dev(mp->m_ddev_targp->bt_dev); - statp->f_fsid.val[0] = (u32)id; - statp->f_fsid.val[1] = (u32)(id >> 32); + statp->f_fsid = u64_to_fsid(id); icount = percpu_counter_sum(&mp->m_icount); ifree = percpu_counter_sum(&mp->m_ifree); diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index 8ec7c8f109d7..763f48541c68 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -932,8 +932,7 @@ static int zonefs_statfs(struct dentry *dentry, struct kstatfs *buf) fsid = le64_to_cpup((void *)sbi->s_uuid.b) ^ le64_to_cpup((void *)sbi->s_uuid.b + sizeof(u64)); - buf->f_fsid.val[0] = (u32)fsid; - buf->f_fsid.val[1] = (u32)(fsid >> 32); + buf->f_fsid = u64_to_fsid(fsid); return 0; } diff --git a/include/linux/statfs.h b/include/linux/statfs.h index fac4356ea1bf..20f695b90aab 100644 --- a/include/linux/statfs.h +++ b/include/linux/statfs.h @@ -45,4 +45,9 @@ struct kstatfs { struct dentry; extern int vfs_get_fsid(struct dentry *dentry, __kernel_fsid_t *fsid); +static inline __kernel_fsid_t u64_to_fsid(u64 v) +{ + return (__kernel_fsid_t){.val = {(u32)v, (u32)(v>>32)}}; +} + #endif From a7c9df0446d295daee68bb16c9b3746f3d21b1ef Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Mon, 14 Sep 2020 18:57:01 +0100 Subject: [PATCH 05/10] fs: omfs: use kmemdup() rather than kmalloc+memcpy Issue identified with Coccinelle. Signed-off-by: Alex Dewar Acked-by: Bob Copeland Signed-off-by: Al Viro --- fs/omfs/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index 68aa38a48308..ce93ccca8639 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -362,12 +362,11 @@ static int omfs_get_imap(struct super_block *sb) bh = sb_bread(sb, block++); if (!bh) goto nomem_free; - *ptr = kmalloc(sb->s_blocksize, GFP_KERNEL); + *ptr = kmemdup(bh->b_data, sb->s_blocksize, GFP_KERNEL); if (!*ptr) { brelse(bh); goto nomem_free; } - memcpy(*ptr, bh->b_data, sb->s_blocksize); if (count < sb->s_blocksize) memset((void *)*ptr + count, 0xff, sb->s_blocksize - count); From da9aa5d96bfe49e903ce2bc01cfb8a776c2619e9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 26 Sep 2020 09:03:57 +0200 Subject: [PATCH 06/10] fs: remove vfs_statx_fd vfs_statx_fd is only used to implement vfs_fstat. Remove vfs_statx_fd and just implement vfs_fstat directly. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/stat.c | 22 +++++++--------------- include/linux/fs.h | 7 +------ 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index 44f8ad346db4..2683a051ce07 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -126,35 +126,27 @@ int vfs_getattr(const struct path *path, struct kstat *stat, EXPORT_SYMBOL(vfs_getattr); /** - * vfs_statx_fd - Get the enhanced basic attributes by file descriptor + * vfs_fstat - Get the basic attributes by file descriptor * @fd: The file descriptor referring to the file of interest * @stat: The result structure to fill in. - * @request_mask: STATX_xxx flags indicating what the caller wants - * @query_flags: Query mode (KSTAT_QUERY_FLAGS) * * This function is a wrapper around vfs_getattr(). The main difference is * that it uses a file descriptor to determine the file location. * * 0 will be returned on success, and a -ve error code if unsuccessful. */ -int vfs_statx_fd(unsigned int fd, struct kstat *stat, - u32 request_mask, unsigned int query_flags) +int vfs_fstat(int fd, struct kstat *stat) { struct fd f; - int error = -EBADF; - - if (query_flags & ~KSTAT_QUERY_FLAGS) - return -EINVAL; + int error; f = fdget_raw(fd); - if (f.file) { - error = vfs_getattr(&f.file->f_path, stat, - request_mask, query_flags); - fdput(f); - } + if (!f.file) + return -EBADF; + error = vfs_getattr(&f.file->f_path, stat, STATX_BASIC_STATS, 0); + fdput(f); return error; } -EXPORT_SYMBOL(vfs_statx_fd); static inline unsigned vfs_stat_set_lookup_flags(unsigned *lookup_flags, int flags) diff --git a/include/linux/fs.h b/include/linux/fs.h index e019ea2f1347..f100d9f711a3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3163,7 +3163,7 @@ extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); extern int vfs_statx(int, const char __user *, int, struct kstat *, u32); -extern int vfs_statx_fd(unsigned int, struct kstat *, u32, unsigned int); +int vfs_fstat(int fd, struct kstat *stat); static inline int vfs_stat(const char __user *filename, struct kstat *stat) { @@ -3181,11 +3181,6 @@ static inline int vfs_fstatat(int dfd, const char __user *filename, return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT, stat, STATX_BASIC_STATS); } -static inline int vfs_fstat(int fd, struct kstat *stat) -{ - return vfs_statx_fd(fd, stat, STATX_BASIC_STATS, 0); -} - extern const char *vfs_get_link(struct dentry *, struct delayed_call *); extern int vfs_readlink(struct dentry *, char __user *, int); From 0b2c6693b4220595e9cff95d829d5d5bc5d544dc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 26 Sep 2020 09:03:58 +0200 Subject: [PATCH 07/10] fs: implement vfs_stat and vfs_lstat in terms of vfs_fstatat Go through vfs_fstatat instead of duplicating the *stat to statx mapping three times. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- include/linux/fs.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index f100d9f711a3..b43c9ad7c3c2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3165,22 +3165,20 @@ extern int iterate_dir(struct file *, struct dir_context *); extern int vfs_statx(int, const char __user *, int, struct kstat *, u32); int vfs_fstat(int fd, struct kstat *stat); -static inline int vfs_stat(const char __user *filename, struct kstat *stat) -{ - return vfs_statx(AT_FDCWD, filename, AT_NO_AUTOMOUNT, - stat, STATX_BASIC_STATS); -} -static inline int vfs_lstat(const char __user *name, struct kstat *stat) -{ - return vfs_statx(AT_FDCWD, name, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, - stat, STATX_BASIC_STATS); -} static inline int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int flags) { return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT, stat, STATX_BASIC_STATS); } +static inline int vfs_stat(const char __user *filename, struct kstat *stat) +{ + return vfs_fstatat(AT_FDCWD, filename, stat, 0); +} +static inline int vfs_lstat(const char __user *name, struct kstat *stat) +{ + return vfs_fstatat(AT_FDCWD, name, stat, AT_SYMLINK_NOFOLLOW); +} extern const char *vfs_get_link(struct dentry *, struct delayed_call *); extern int vfs_readlink(struct dentry *, char __user *, int); From 09f1bde4017e9c34749da2918b3926799c77bce8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 26 Sep 2020 09:03:59 +0200 Subject: [PATCH 08/10] fs: move vfs_fstatat out of line This allows to keep vfs_statx static in fs/stat.c to prepare for the following changes. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/stat.c | 9 +++++++-- include/linux/fs.h | 9 ++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index 2683a051ce07..ddf0176d4dbc 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -181,7 +181,7 @@ static inline unsigned vfs_stat_set_lookup_flags(unsigned *lookup_flags, * * 0 will be returned on success, and a -ve error code if unsuccessful. */ -int vfs_statx(int dfd, const char __user *filename, int flags, +static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask) { struct path path; @@ -209,8 +209,13 @@ int vfs_statx(int dfd, const char __user *filename, int flags, out: return error; } -EXPORT_SYMBOL(vfs_statx); +int vfs_fstatat(int dfd, const char __user *filename, + struct kstat *stat, int flags) +{ + return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT, + stat, STATX_BASIC_STATS); +} #ifdef __ARCH_WANT_OLD_STAT diff --git a/include/linux/fs.h b/include/linux/fs.h index b43c9ad7c3c2..1ac68236bc09 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3162,15 +3162,10 @@ extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); -extern int vfs_statx(int, const char __user *, int, struct kstat *, u32); +int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, + int flags); int vfs_fstat(int fd, struct kstat *stat); -static inline int vfs_fstatat(int dfd, const char __user *filename, - struct kstat *stat, int flags) -{ - return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT, - stat, STATX_BASIC_STATS); -} static inline int vfs_stat(const char __user *filename, struct kstat *stat) { return vfs_fstatat(AT_FDCWD, filename, stat, 0); From b3f0515004a5ee41d65218303804bd57fdda027e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 26 Sep 2020 09:04:00 +0200 Subject: [PATCH 09/10] fs: remove vfs_stat_set_lookup_flags The function really obsfucates checking for valid flags and setting the lookup flags. The fact that it returns -EINVAL through and unsigned return value, which is then used as boolean really doesn't help either. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/stat.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index ddf0176d4dbc..8acc4b14ac24 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -148,24 +148,6 @@ int vfs_fstat(int fd, struct kstat *stat) return error; } -static inline unsigned vfs_stat_set_lookup_flags(unsigned *lookup_flags, - int flags) -{ - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | - AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0) - return -EINVAL; - - *lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT; - if (flags & AT_SYMLINK_NOFOLLOW) - *lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_NO_AUTOMOUNT) - *lookup_flags &= ~LOOKUP_AUTOMOUNT; - if (flags & AT_EMPTY_PATH) - *lookup_flags |= LOOKUP_EMPTY; - - return 0; -} - /** * vfs_statx - Get basic and extra attributes by filename * @dfd: A file descriptor representing the base dir for a relative filename @@ -185,11 +167,20 @@ static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask) { struct path path; - int error = -EINVAL; - unsigned lookup_flags; + unsigned lookup_flags = 0; + int error; - if (vfs_stat_set_lookup_flags(&lookup_flags, flags)) + if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | + KSTAT_QUERY_FLAGS)) return -EINVAL; + + if (!(flags & AT_SYMLINK_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + if (!(flags & AT_NO_AUTOMOUNT)) + lookup_flags |= LOOKUP_AUTOMOUNT; + if (flags & AT_EMPTY_PATH) + lookup_flags |= LOOKUP_EMPTY; + retry: error = user_path_at(dfd, filename, lookup_flags, &path); if (error) From f2d077ff1b5c17008cff5dc27e7356a694e55462 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 26 Sep 2020 09:04:01 +0200 Subject: [PATCH 10/10] fs: remove KSTAT_QUERY_FLAGS KSTAT_QUERY_FLAGS expands to AT_STATX_SYNC_TYPE, which itself already is a mask. Remove the double name, especially given that the prefix is a little confusing vs the normal AT_* flags. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/stat.c | 8 ++++---- include/linux/stat.h | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index 8acc4b14ac24..dacecdda2e79 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -56,7 +56,7 @@ EXPORT_SYMBOL(generic_fillattr); * @path: file to get attributes from * @stat: structure to return attributes in * @request_mask: STATX_xxx flags indicating what the caller wants - * @query_flags: Query mode (KSTAT_QUERY_FLAGS) + * @query_flags: Query mode (AT_STATX_SYNC_TYPE) * * Get attributes without calling security_inode_getattr. * @@ -71,7 +71,7 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, memset(stat, 0, sizeof(*stat)); stat->result_mask |= STATX_BASIC_STATS; - query_flags &= KSTAT_QUERY_FLAGS; + query_flags &= AT_STATX_SYNC_TYPE; /* allow the fs to override these if it really wants to */ /* SB_NOATIME means filesystem supplies dummy atime value */ @@ -97,7 +97,7 @@ EXPORT_SYMBOL(vfs_getattr_nosec); * @path: The file of interest * @stat: Where to return the statistics * @request_mask: STATX_xxx flags indicating what the caller wants - * @query_flags: Query mode (KSTAT_QUERY_FLAGS) + * @query_flags: Query mode (AT_STATX_SYNC_TYPE) * * Ask the filesystem for a file's attributes. The caller must indicate in * request_mask and query_flags to indicate what they want. @@ -171,7 +171,7 @@ static int vfs_statx(int dfd, const char __user *filename, int flags, int error; if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | - KSTAT_QUERY_FLAGS)) + AT_STATX_SYNC_TYPE)) return -EINVAL; if (!(flags & AT_SYMLINK_NOFOLLOW)) diff --git a/include/linux/stat.h b/include/linux/stat.h index 56614af83d4a..fff27e603814 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -19,8 +19,6 @@ #include #include -#define KSTAT_QUERY_FLAGS (AT_STATX_SYNC_TYPE) - struct kstat { u32 result_mask; /* What fields the user got */ umode_t mode;