]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs: add S_ANON_INODE
authorChristian Brauner <brauner@kernel.org>
Mon, 21 Apr 2025 08:27:40 +0000 (10:27 +0200)
committerChristian Brauner <brauner@kernel.org>
Mon, 21 Apr 2025 11:20:14 +0000 (13:20 +0200)
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 <sophon@radxa.com>
Link: https://lore.kernel.org/3A9139D5CD543962+89831381-31b9-4392-87ec-a84a5b3507d8@radxa.com
Link: https://lore.kernel.org/7a1a7076-ff6b-4cb0-94e7-7218a0a44028@sirena.org.uk
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/ioctl.c
fs/libfs.c
fs/pidfs.c
include/linux/fs.h
mm/readahead.c

index c91fd2b46a77f6f1ea99aab56b4d92b6d9a8c534..03d9a11f22475b420d72dcf4a223fe2215fc8fc9 100644 (file)
@@ -821,7 +821,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)) ?
@@ -856,7 +857,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,
@@ -881,7 +882,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;
        }
index e1146620346e229f021adefad48d0041b4e89ec9..e28da9574a652bdb5862c067a867d96599e0b5b0 100644 (file)
@@ -1656,7 +1656,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;
 }
index 10b4ee454ccab3b0ab604ed9ae24da08a1be0cba..2ac6f5cd861d692daaa6393c77f33b7052568c17 100644 (file)
@@ -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;
index 016b0fe1536e3630eccb10df3f6f93393c97966b..cf7208500cee303af282415d0ded3699f8861f7a 100644 (file)
@@ -2344,6 +2344,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
@@ -2400,6 +2401,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)
index 6a4e96b69702b3eca0d984b7e909f2cc3ed33fa9..20d36d6b055ed542fb9fea3eaef7b146b6bf7de5 100644 (file)
@@ -690,9 +690,15 @@ EXPORT_SYMBOL_GPL(page_cache_async_ra);
 
 ssize_t ksys_readahead(int fd, loff_t offset, size_t count)
 {
+       struct file *file;
+       const struct inode *inode;
+
        CLASS(fd, f)(fd);
+       if (fd_empty(f))
+               return -EBADF;
 
-       if (fd_empty(f) || !(fd_file(f)->f_mode & FMODE_READ))
+       file = fd_file(f);
+       if (!(file->f_mode & FMODE_READ))
                return -EBADF;
 
        /*
@@ -700,9 +706,15 @@ ssize_t ksys_readahead(int fd, loff_t offset, size_t count)
         * that can execute readahead. If readahead is not possible
         * on this file, then we must return -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)))
+       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);