Linux 6.9-rc1
[linux-2.6-microblaze.git] / fs / stat.c
index 9ced886..77cdc69 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -5,6 +5,7 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/blkdev.h>
 #include <linux/export.h>
 #include <linux/mm.h>
 #include <linux/errno.h>
@@ -17,6 +18,7 @@
 #include <linux/syscalls.h>
 #include <linux/pagemap.h>
 #include <linux/compat.h>
+#include <linux/iversion.h>
 
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
 
 /**
  * generic_fillattr - Fill in the basic attributes from the inode struct
- * @mnt_userns:        user namespace of the mount the inode was found from
- * @inode:     Inode to use as the source
- * @stat:      Where to fill in the attributes
+ * @idmap:             idmap of the mount the inode was found from
+ * @request_mask:      statx request_mask
+ * @inode:             Inode to use as the source
+ * @stat:              Where to fill in the attributes
  *
  * Fill in the basic attributes in the kstat structure from data that's to be
  * found on the VFS inode structure.  This is the default if no getattr inode
  * operation is supplied.
  *
- * If the inode has been found through an idmapped mount the user namespace of
- * the vfsmount must be passed through @mnt_userns. This function will then
- * take care to map the inode according to @mnt_userns before filling in the
+ * If the inode has been found through an idmapped mount the idmap of
+ * the vfsmount must be passed through @idmap. This function will then
+ * take care to map the inode according to @idmap before filling in the
  * uid and gid filds. On non-idmapped mounts or if permission checking is to be
- * performed on the raw inode simply passs init_user_ns.
+ * performed on the raw inode simply pass @nop_mnt_idmap.
  */
-void generic_fillattr(struct user_namespace *mnt_userns, struct inode *inode,
-                     struct kstat *stat)
+void generic_fillattr(struct mnt_idmap *idmap, u32 request_mask,
+                     struct inode *inode, struct kstat *stat)
 {
+       vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);
+       vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
+
        stat->dev = inode->i_sb->s_dev;
        stat->ino = inode->i_ino;
        stat->mode = inode->i_mode;
        stat->nlink = inode->i_nlink;
-       stat->uid = i_uid_into_mnt(mnt_userns, inode);
-       stat->gid = i_gid_into_mnt(mnt_userns, inode);
+       stat->uid = vfsuid_into_kuid(vfsuid);
+       stat->gid = vfsgid_into_kgid(vfsgid);
        stat->rdev = inode->i_rdev;
        stat->size = i_size_read(inode);
-       stat->atime = inode->i_atime;
-       stat->mtime = inode->i_mtime;
-       stat->ctime = inode->i_ctime;
+       stat->atime = inode_get_atime(inode);
+       stat->mtime = inode_get_mtime(inode);
+       stat->ctime = inode_get_ctime(inode);
        stat->blksize = i_blocksize(inode);
        stat->blocks = inode->i_blocks;
+
+       if ((request_mask & STATX_CHANGE_COOKIE) && IS_I_VERSION(inode)) {
+               stat->result_mask |= STATX_CHANGE_COOKIE;
+               stat->change_cookie = inode_query_iversion(inode);
+       }
+
 }
 EXPORT_SYMBOL(generic_fillattr);
 
@@ -93,7 +105,7 @@ EXPORT_SYMBOL(generic_fill_statx_attr);
 int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
                      u32 request_mask, unsigned int query_flags)
 {
-       struct user_namespace *mnt_userns;
+       struct mnt_idmap *idmap;
        struct inode *inode = d_backing_inode(path->dentry);
 
        memset(stat, 0, sizeof(*stat));
@@ -118,12 +130,13 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
        stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |
                                  STATX_ATTR_DAX);
 
-       mnt_userns = mnt_user_ns(path->mnt);
+       idmap = mnt_idmap(path->mnt);
        if (inode->i_op->getattr)
-               return inode->i_op->getattr(mnt_userns, path, stat,
-                                           request_mask, query_flags);
+               return inode->i_op->getattr(idmap, path, stat,
+                                           request_mask,
+                                           query_flags | AT_GETATTR_NOSEC);
 
-       generic_fillattr(mnt_userns, inode, stat);
+       generic_fillattr(idmap, request_mask, inode, stat);
        return 0;
 }
 EXPORT_SYMBOL(vfs_getattr_nosec);
@@ -154,6 +167,9 @@ int vfs_getattr(const struct path *path, struct kstat *stat,
 {
        int retval;
 
+       if (WARN_ON_ONCE(query_flags & AT_GETATTR_NOSEC))
+               return -EPERM;
+
        retval = security_inode_getattr(path);
        if (retval)
                return retval;
@@ -230,11 +246,27 @@ retry:
                goto out;
 
        error = vfs_getattr(&path, stat, request_mask, flags);
-       stat->mnt_id = real_mount(path.mnt)->mnt_id;
-       stat->result_mask |= STATX_MNT_ID;
+
+       if (request_mask & STATX_MNT_ID_UNIQUE) {
+               stat->mnt_id = real_mount(path.mnt)->mnt_id_unique;
+               stat->result_mask |= STATX_MNT_ID_UNIQUE;
+       } else {
+               stat->mnt_id = real_mount(path.mnt)->mnt_id;
+               stat->result_mask |= STATX_MNT_ID;
+       }
+
        if (path.mnt->mnt_root == path.dentry)
                stat->attributes |= STATX_ATTR_MOUNT_ROOT;
        stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
+
+       /* Handle STATX_DIOALIGN for block devices. */
+       if (request_mask & STATX_DIOALIGN) {
+               struct inode *inode = d_backing_inode(path.dentry);
+
+               if (S_ISBLK(inode->i_mode))
+                       bdev_statx_dioalign(inode, stat);
+       }
+
        path_put(&path);
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
@@ -251,6 +283,23 @@ int vfs_fstatat(int dfd, const char __user *filename,
        int statx_flags = flags | AT_NO_AUTOMOUNT;
        struct filename *name;
 
+       /*
+        * Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH)
+        *
+        * If AT_EMPTY_PATH is set, we expect the common case to be that
+        * empty path, and avoid doing all the extra pathname work.
+        */
+       if (dfd >= 0 && flags == AT_EMPTY_PATH) {
+               char c;
+
+               ret = get_user(c, filename);
+               if (unlikely(ret))
+                       return ret;
+
+               if (likely(!c))
+                       return vfs_fstat(dfd, stat);
+       }
+
        name = getname_flags(filename, getname_statx_lookup_flags(statx_flags), NULL);
        ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
        putname(name);
@@ -342,12 +391,6 @@ SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, stat
 
 #ifdef __ARCH_WANT_NEW_STAT
 
-#if BITS_PER_LONG == 32
-#  define choose_32_64(a,b) a
-#else
-#  define choose_32_64(a,b) b
-#endif
-
 #ifndef INIT_STRUCT_STAT_PADDING
 #  define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
 #endif
@@ -587,9 +630,11 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
 
        memset(&tmp, 0, sizeof(tmp));
 
-       tmp.stx_mask = stat->result_mask;
+       /* STATX_CHANGE_COOKIE is kernel-only for now */
+       tmp.stx_mask = stat->result_mask & ~STATX_CHANGE_COOKIE;
        tmp.stx_blksize = stat->blksize;
-       tmp.stx_attributes = stat->attributes;
+       /* STATX_ATTR_CHANGE_MONOTONIC is kernel-only for now */
+       tmp.stx_attributes = stat->attributes & ~STATX_ATTR_CHANGE_MONOTONIC;
        tmp.stx_nlink = stat->nlink;
        tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid);
        tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid);
@@ -611,6 +656,8 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
        tmp.stx_dev_major = MAJOR(stat->dev);
        tmp.stx_dev_minor = MINOR(stat->dev);
        tmp.stx_mnt_id = stat->mnt_id;
+       tmp.stx_dio_mem_align = stat->dio_mem_align;
+       tmp.stx_dio_offset_align = stat->dio_offset_align;
 
        return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 }
@@ -626,6 +673,11 @@ int do_statx(int dfd, struct filename *filename, unsigned int flags,
        if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
                return -EINVAL;
 
+       /* STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests
+        * from userland.
+        */
+       mask &= ~STATX_CHANGE_COOKIE;
+
        error = vfs_statx(dfd, filename, flags, &stat, mask);
        if (error)
                return error;