From 0e6f0c4069e34ae66ce960879b5a88ae0ce9aab8 Mon Sep 17 00:00:00 2001 From: Rafael Aquini Date: Mon, 11 May 2026 10:39:37 -0400 Subject: [PATCH] fs: add S_ANON_INODE JIRA: https://issues.redhat.com/browse/RHEL-171616 Conflicts: * mm/readahead.c: in order to reduce differences and make the porting cleaner we are folding the hunk from 6348be02eead ("fdget(), trivial conversions") commit 19bbfe7b5fcc04d8711e8e1352acc77c1a5c3955 Author: Christian Brauner Date: Mon Apr 21 10:27:40 2025 +0200 fs: add S_ANON_INODE This makes it easy to detect proper anonymous inodes and to ensure that we can detect them in codepaths such as readahead(). Readahead on anonymous inodes didn't work because they didn't have a proper mode. Now that they have we need to retain EINVAL being returned otherwise LTP will fail. We also need to ensure that ioctls aren't simply fired like they are for regular files so things like inotify inodes continue to correctly call their own ioctl handlers as in [1]. Reported-by: Xilin Wu Link: https://lore.kernel.org/3A9139D5CD543962+89831381-31b9-4392-87ec-a84a5b3507d8@radxa.com [1] Link: https://lore.kernel.org/7a1a7076-ff6b-4cb0-94e7-7218a0a44028@sirena.org.uk Signed-off-by: Christian Brauner Signed-off-by: Rafael Aquini diff --git a/fs/ioctl.c b/fs/ioctl.c index 6e0c954388d4..4dbd5627af8f 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -822,7 +822,8 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, return ioctl_fioasync(fd, filp, argp); case FIOQSIZE: - if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || + if (S_ISDIR(inode->i_mode) || + (S_ISREG(inode->i_mode) && !IS_ANON_FILE(inode)) || S_ISLNK(inode->i_mode)) { loff_t res = inode_get_bytes(inode); return copy_to_user(argp, &res, sizeof(res)) ? @@ -857,7 +858,7 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, return ioctl_file_dedupe_range(filp, argp); case FIONREAD: - if (!S_ISREG(inode->i_mode)) + if (!S_ISREG(inode->i_mode) || IS_ANON_FILE(inode)) return vfs_ioctl(filp, cmd, arg); return put_user(i_size_read(inode) - filp->f_pos, @@ -882,7 +883,7 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, return ioctl_get_fs_sysfs_path(filp, argp); default: - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode) && !IS_ANON_FILE(inode)) return file_ioctl(filp, cmd, argp); break; } diff --git a/fs/libfs.c b/fs/libfs.c index 4e9de5aa4d7f..c4a305967619 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1660,7 +1660,7 @@ struct inode *alloc_anon_inode(struct super_block *s) inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); - inode->i_flags |= S_PRIVATE; + inode->i_flags |= S_PRIVATE | S_ANON_INODE; simple_inode_init_ts(inode); return inode; } diff --git a/fs/pidfs.c b/fs/pidfs.c index d6c0ed79ea24..7e10fac8e623 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -804,7 +804,7 @@ static int pidfs_init_inode(struct inode *inode, void *data) const struct pid *pid = data; inode->i_private = data; - inode->i_flags |= S_PRIVATE; + inode->i_flags |= S_PRIVATE | S_ANON_INODE; inode->i_mode |= S_IRWXU; inode->i_op = &pidfs_inode_operations; inode->i_fop = &pidfs_file_operations; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3e14c4eb64f4..fa47510429f0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2319,6 +2319,7 @@ struct super_operations { #define S_CASEFOLD (1 << 15) /* Casefolded file */ #define S_VERITY (1 << 16) /* Verity file (using fs/verity/) */ #define S_KERNEL_FILE (1 << 17) /* File is in use by the kernel (eg. fs/cachefiles) */ +#define S_ANON_INODE (1 << 19) /* Inode is an anonymous inode */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -2375,6 +2376,7 @@ static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags #define IS_WHITEOUT(inode) (S_ISCHR(inode->i_mode) && \ (inode)->i_rdev == WHITEOUT_DEV) +#define IS_ANON_FILE(inode) ((inode)->i_flags & S_ANON_INODE) static inline bool HAS_UNMAPPED_ID(struct mnt_idmap *idmap, struct inode *inode) diff --git a/mm/readahead.c b/mm/readahead.c index 2dbe5993b6aa..a3547f923249 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -678,29 +678,34 @@ EXPORT_SYMBOL_GPL(page_cache_async_ra); ssize_t ksys_readahead(int fd, loff_t offset, size_t count) { - ssize_t ret; - struct fd f; + struct file *file; + const struct inode *inode; - ret = -EBADF; - f = fdget(fd); - if (!fd_file(f) || !(fd_file(f)->f_mode & FMODE_READ)) - goto out; + CLASS(fd, f)(fd); + if (fd_empty(f)) + return -EBADF; + + file = fd_file(f); + if (!(file->f_mode & FMODE_READ)) + return -EBADF; /* * The readahead() syscall is intended to run only on files * that can execute readahead. If readahead is not possible * on this file, then we must return -EINVAL. */ - ret = -EINVAL; - if (!fd_file(f)->f_mapping || !fd_file(f)->f_mapping->a_ops || - (!S_ISREG(file_inode(fd_file(f))->i_mode) && - !S_ISBLK(file_inode(fd_file(f))->i_mode))) - goto out; - - ret = vfs_fadvise(fd_file(f), offset, count, POSIX_FADV_WILLNEED); -out: - fdput(f); - return ret; + if (!file->f_mapping) + return -EINVAL; + if (!file->f_mapping->a_ops) + return -EINVAL; + + inode = file_inode(file); + if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) + return -EINVAL; + if (IS_ANON_FILE(inode)) + return -EINVAL; + + return vfs_fadvise(fd_file(f), offset, count, POSIX_FADV_WILLNEED); } SYSCALL_DEFINE3(readahead, int, fd, loff_t, offset, size_t, count) -- 2.50.1 (Apple Git-155)