Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 9 Apr 2017 15:26:21 +0000 (08:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 9 Apr 2017 15:26:21 +0000 (08:26 -0700)
Pull VFS fixes from Al Viro:
 "statx followup fixes and a fix for stack-smashing on alpha"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  alpha: fix stack smashing in old_adjtimex(2)
  statx: Include a mask for stx_attributes in struct statx
  statx: Reserve the top bit of the mask for future struct expansion
  xfs: report crtime and attribute flags to statx
  ext4: Add statx support
  statx: optimize copy of struct statx to userspace
  statx: remove incorrect part of vfs_statx() comment
  statx: reject unknown flags when using NULL path
  Documentation/filesystems: fix documentation for ->getattr()

14 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/porting
Documentation/filesystems/vfs.txt
arch/alpha/kernel/osf_sys.c
fs/ext4/ext4.h
fs/ext4/file.c
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/symlink.c
fs/stat.c
fs/xfs/xfs_iops.c
include/linux/stat.h
include/uapi/linux/stat.h
samples/statx/test-statx.c

index fdcfdd79682a00102b1c18c8b56e19e00a418d26..fe25787ff6d49748c3e1ecc494f17e401a60916a 100644 (file)
@@ -58,8 +58,7 @@ prototypes:
        int (*permission) (struct inode *, int, unsigned int);
        int (*get_acl)(struct inode *, int);
        int (*setattr) (struct dentry *, struct iattr *);
-       int (*getattr) (const struct path *, struct dentry *, struct kstat *,
-                       u32, unsigned int);
+       int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
        void (*update_time)(struct inode *, struct timespec *, int);
index 95280079c0b3af0c217270e6838b723eb818cbcd..5fb17f49f7a21907a2c446267cc952c749e42043 100644 (file)
@@ -600,3 +600,9 @@ in your dentry operations instead.
 [recommended]
        ->readlink is optional for symlinks.  Don't set, unless filesystem needs
        to fake something for readlink(2).
+--
+[mandatory]
+       ->getattr() is now passed a struct path rather than a vfsmount and
+       dentry separately, and it now has request_mask and query_flags arguments
+       to specify the fields and sync type requested by statx.  Filesystems not
+       supporting any statx-specific features may ignore the new arguments.
index 569211703721fe81e0cbbbd1cb979a10c276cf9e..94dd27ef4a766ca5414146fab1b76ea5aacb67de 100644 (file)
@@ -382,8 +382,7 @@ struct inode_operations {
        int (*permission) (struct inode *, int);
        int (*get_acl)(struct inode *, int);
        int (*setattr) (struct dentry *, struct iattr *);
-       int (*getattr) (const struct path *, struct dentry *, struct kstat *,
-                       u32, unsigned int);
+       int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        void (*update_time)(struct inode *, struct timespec *, int);
        int (*atomic_open)(struct inode *, struct dentry *, struct file *,
index 0b961093ca5cac69fe88598ed7ebc9c023a11a78..6d76e528ab8f2606ddde5e3b739598b86a7cb5b4 100644 (file)
@@ -1290,7 +1290,7 @@ SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p)
        /* copy relevant bits of struct timex. */
        if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) ||
            copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - 
-                          offsetof(struct timex32, time)))
+                          offsetof(struct timex32, tick)))
          return -EFAULT;
 
        ret = do_adjtimex(&txc);        
index f493af66659134dafce0e6078437834968ef2eb7..fb69ee2388dba0b83b4f29c57b37ec5cb0aefb34 100644 (file)
@@ -2466,6 +2466,7 @@ extern int  ext4_setattr(struct dentry *, struct iattr *);
 extern int  ext4_getattr(const struct path *, struct kstat *, u32, unsigned int);
 extern void ext4_evict_inode(struct inode *);
 extern void ext4_clear_inode(struct inode *);
+extern int  ext4_file_getattr(const struct path *, struct kstat *, u32, unsigned int);
 extern int  ext4_sync_inode(handle_t *, struct inode *);
 extern void ext4_dirty_inode(struct inode *, int);
 extern int ext4_change_inode_journal_flag(struct inode *, int);
index 8210c1f43556f4358e9b602a93d158e5c0780c44..cefa9835f275d9b062ae9b13765ea743edb53f64 100644 (file)
@@ -744,7 +744,7 @@ const struct file_operations ext4_file_operations = {
 
 const struct inode_operations ext4_file_inode_operations = {
        .setattr        = ext4_setattr,
-       .getattr        = ext4_getattr,
+       .getattr        = ext4_file_getattr,
        .listxattr      = ext4_listxattr,
        .get_acl        = ext4_get_acl,
        .set_acl        = ext4_set_acl,
index 4247d8d25687814dd1b844ea5555feeb43854d99..b9ffa9f4191f4cb3a122c215894ef76689fd9604 100644 (file)
@@ -5390,11 +5390,46 @@ err_out:
 int ext4_getattr(const struct path *path, struct kstat *stat,
                 u32 request_mask, unsigned int query_flags)
 {
-       struct inode *inode;
-       unsigned long long delalloc_blocks;
+       struct inode *inode = d_inode(path->dentry);
+       struct ext4_inode *raw_inode;
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       unsigned int flags;
+
+       if (EXT4_FITS_IN_INODE(raw_inode, ei, i_crtime)) {
+               stat->result_mask |= STATX_BTIME;
+               stat->btime.tv_sec = ei->i_crtime.tv_sec;
+               stat->btime.tv_nsec = ei->i_crtime.tv_nsec;
+       }
+
+       flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
+       if (flags & EXT4_APPEND_FL)
+               stat->attributes |= STATX_ATTR_APPEND;
+       if (flags & EXT4_COMPR_FL)
+               stat->attributes |= STATX_ATTR_COMPRESSED;
+       if (flags & EXT4_ENCRYPT_FL)
+               stat->attributes |= STATX_ATTR_ENCRYPTED;
+       if (flags & EXT4_IMMUTABLE_FL)
+               stat->attributes |= STATX_ATTR_IMMUTABLE;
+       if (flags & EXT4_NODUMP_FL)
+               stat->attributes |= STATX_ATTR_NODUMP;
+
+       stat->attributes_mask |= (STATX_ATTR_APPEND |
+                                 STATX_ATTR_COMPRESSED |
+                                 STATX_ATTR_ENCRYPTED |
+                                 STATX_ATTR_IMMUTABLE |
+                                 STATX_ATTR_NODUMP);
 
-       inode = d_inode(path->dentry);
        generic_fillattr(inode, stat);
+       return 0;
+}
+
+int ext4_file_getattr(const struct path *path, struct kstat *stat,
+                     u32 request_mask, unsigned int query_flags)
+{
+       struct inode *inode = d_inode(path->dentry);
+       u64 delalloc_blocks;
+
+       ext4_getattr(path, stat, request_mask, query_flags);
 
        /*
         * If there is inline data in the inode, the inode will normally not
index 6ad612c576fc733f8a6d95540c20d8e75995e1c6..07e5e140577176e13db84089eaa048713ddb4797 100644 (file)
@@ -3912,6 +3912,7 @@ const struct inode_operations ext4_dir_inode_operations = {
        .tmpfile        = ext4_tmpfile,
        .rename         = ext4_rename2,
        .setattr        = ext4_setattr,
+       .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
        .get_acl        = ext4_get_acl,
        .set_acl        = ext4_set_acl,
@@ -3920,6 +3921,7 @@ const struct inode_operations ext4_dir_inode_operations = {
 
 const struct inode_operations ext4_special_inode_operations = {
        .setattr        = ext4_setattr,
+       .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
        .get_acl        = ext4_get_acl,
        .set_acl        = ext4_set_acl,
index 73b184d161fc98dc6c870758243b1a03932cb85f..5c8fc53cb0e5a3127366b6324514407668f150b3 100644 (file)
@@ -85,17 +85,20 @@ errout:
 const struct inode_operations ext4_encrypted_symlink_inode_operations = {
        .get_link       = ext4_encrypted_get_link,
        .setattr        = ext4_setattr,
+       .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
 };
 
 const struct inode_operations ext4_symlink_inode_operations = {
        .get_link       = page_get_link,
        .setattr        = ext4_setattr,
+       .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
 };
 
 const struct inode_operations ext4_fast_symlink_inode_operations = {
        .get_link       = simple_get_link,
        .setattr        = ext4_setattr,
+       .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
 };
index fa0be59340cc9136eb8c508434e10289acc8fc97..c6c963b2546b4f2d0301f40706064498c31fd3b6 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -130,9 +130,13 @@ EXPORT_SYMBOL(vfs_getattr);
 int vfs_statx_fd(unsigned int fd, struct kstat *stat,
                 u32 request_mask, unsigned int query_flags)
 {
-       struct fd f = fdget_raw(fd);
+       struct fd f;
        int error = -EBADF;
 
+       if (query_flags & ~KSTAT_QUERY_FLAGS)
+               return -EINVAL;
+
+       f = fdget_raw(fd);
        if (f.file) {
                error = vfs_getattr(&f.file->f_path, stat,
                                    request_mask, query_flags);
@@ -155,9 +159,6 @@ EXPORT_SYMBOL(vfs_statx_fd);
  * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink
  * at the given name from being referenced.
  *
- * The caller must have preset stat->request_mask as for vfs_getattr().  The
- * flags are also used to load up stat->query_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,
@@ -509,46 +510,38 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
 }
 #endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
 
-static inline int __put_timestamp(struct timespec *kts,
-                                 struct statx_timestamp __user *uts)
-{
-       return (__put_user(kts->tv_sec,         &uts->tv_sec            ) ||
-               __put_user(kts->tv_nsec,        &uts->tv_nsec           ) ||
-               __put_user(0,                   &uts->__reserved        ));
-}
-
-/*
- * Set the statx results.
- */
-static long statx_set_result(struct kstat *stat, struct statx __user *buffer)
+static noinline_for_stack int
+cp_statx(const struct kstat *stat, struct statx __user *buffer)
 {
-       uid_t uid = from_kuid_munged(current_user_ns(), stat->uid);
-       gid_t gid = from_kgid_munged(current_user_ns(), stat->gid);
-
-       if (__put_user(stat->result_mask,       &buffer->stx_mask       ) ||
-           __put_user(stat->mode,              &buffer->stx_mode       ) ||
-           __clear_user(&buffer->__spare0, sizeof(buffer->__spare0))     ||
-           __put_user(stat->nlink,             &buffer->stx_nlink      ) ||
-           __put_user(uid,                     &buffer->stx_uid        ) ||
-           __put_user(gid,                     &buffer->stx_gid        ) ||
-           __put_user(stat->attributes,        &buffer->stx_attributes ) ||
-           __put_user(stat->blksize,           &buffer->stx_blksize    ) ||
-           __put_user(MAJOR(stat->rdev),       &buffer->stx_rdev_major ) ||
-           __put_user(MINOR(stat->rdev),       &buffer->stx_rdev_minor ) ||
-           __put_user(MAJOR(stat->dev),        &buffer->stx_dev_major  ) ||
-           __put_user(MINOR(stat->dev),        &buffer->stx_dev_minor  ) ||
-           __put_timestamp(&stat->atime,       &buffer->stx_atime      ) ||
-           __put_timestamp(&stat->btime,       &buffer->stx_btime      ) ||
-           __put_timestamp(&stat->ctime,       &buffer->stx_ctime      ) ||
-           __put_timestamp(&stat->mtime,       &buffer->stx_mtime      ) ||
-           __put_user(stat->ino,               &buffer->stx_ino        ) ||
-           __put_user(stat->size,              &buffer->stx_size       ) ||
-           __put_user(stat->blocks,            &buffer->stx_blocks     ) ||
-           __clear_user(&buffer->__spare1, sizeof(buffer->__spare1))     ||
-           __clear_user(&buffer->__spare2, sizeof(buffer->__spare2)))
-               return -EFAULT;
-
-       return 0;
+       struct statx tmp;
+
+       memset(&tmp, 0, sizeof(tmp));
+
+       tmp.stx_mask = stat->result_mask;
+       tmp.stx_blksize = stat->blksize;
+       tmp.stx_attributes = stat->attributes;
+       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);
+       tmp.stx_mode = stat->mode;
+       tmp.stx_ino = stat->ino;
+       tmp.stx_size = stat->size;
+       tmp.stx_blocks = stat->blocks;
+       tmp.stx_attributes_mask = stat->attributes_mask;
+       tmp.stx_atime.tv_sec = stat->atime.tv_sec;
+       tmp.stx_atime.tv_nsec = stat->atime.tv_nsec;
+       tmp.stx_btime.tv_sec = stat->btime.tv_sec;
+       tmp.stx_btime.tv_nsec = stat->btime.tv_nsec;
+       tmp.stx_ctime.tv_sec = stat->ctime.tv_sec;
+       tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec;
+       tmp.stx_mtime.tv_sec = stat->mtime.tv_sec;
+       tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec;
+       tmp.stx_rdev_major = MAJOR(stat->rdev);
+       tmp.stx_rdev_minor = MINOR(stat->rdev);
+       tmp.stx_dev_major = MAJOR(stat->dev);
+       tmp.stx_dev_minor = MINOR(stat->dev);
+
+       return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 }
 
 /**
@@ -570,10 +563,10 @@ SYSCALL_DEFINE5(statx,
        struct kstat stat;
        int error;
 
+       if (mask & STATX__RESERVED)
+               return -EINVAL;
        if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
                return -EINVAL;
-       if (!access_ok(VERIFY_WRITE, buffer, sizeof(*buffer)))
-               return -EFAULT;
 
        if (filename)
                error = vfs_statx(dfd, filename, flags, &stat, mask);
@@ -581,7 +574,8 @@ SYSCALL_DEFINE5(statx,
                error = vfs_statx_fd(dfd, &stat, mask, flags);
        if (error)
                return error;
-       return statx_set_result(&stat, buffer);
+
+       return cp_statx(&stat, buffer);
 }
 
 /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
index 229cc6a6d8ef03dc599b5195c5052796e2e19ba7..ebfc13350f9ae8b43b0229948588192560b51683 100644 (file)
@@ -516,6 +516,20 @@ xfs_vn_getattr(
        stat->blocks =
                XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks);
 
+       if (ip->i_d.di_version == 3) {
+               if (request_mask & STATX_BTIME) {
+                       stat->result_mask |= STATX_BTIME;
+                       stat->btime.tv_sec = ip->i_d.di_crtime.t_sec;
+                       stat->btime.tv_nsec = ip->i_d.di_crtime.t_nsec;
+               }
+       }
+
+       if (ip->i_d.di_flags & XFS_DIFLAG_IMMUTABLE)
+               stat->attributes |= STATX_ATTR_IMMUTABLE;
+       if (ip->i_d.di_flags & XFS_DIFLAG_APPEND)
+               stat->attributes |= STATX_ATTR_APPEND;
+       if (ip->i_d.di_flags & XFS_DIFLAG_NODUMP)
+               stat->attributes |= STATX_ATTR_NODUMP;
 
        switch (inode->i_mode & S_IFMT) {
        case S_IFBLK:
index c76e524fb34b6af362c82103662454750826ac9e..64b6b3aece21aee52a2b7c9246862afb41d606f1 100644 (file)
@@ -26,6 +26,7 @@ struct kstat {
        unsigned int    nlink;
        uint32_t        blksize;        /* Preferred I/O size */
        u64             attributes;
+       u64             attributes_mask;
 #define KSTAT_ATTR_FS_IOC_FLAGS                                \
        (STATX_ATTR_COMPRESSED |                        \
         STATX_ATTR_IMMUTABLE |                         \
index 51a6b86e370043f7b37f128c4896f5f588710643..d538897b8e08bb4358c56148c189fc7b8128a9da 100644 (file)
@@ -114,7 +114,7 @@ struct statx {
        __u64   stx_ino;        /* Inode number */
        __u64   stx_size;       /* File size */
        __u64   stx_blocks;     /* Number of 512-byte blocks allocated */
-       __u64   __spare1[1];
+       __u64   stx_attributes_mask; /* Mask to show what's supported in stx_attributes */
        /* 0x40 */
        struct statx_timestamp  stx_atime;      /* Last access time */
        struct statx_timestamp  stx_btime;      /* File creation time */
@@ -152,9 +152,10 @@ struct statx {
 #define STATX_BASIC_STATS      0x000007ffU     /* The stuff in the normal stat struct */
 #define STATX_BTIME            0x00000800U     /* Want/got stx_btime */
 #define STATX_ALL              0x00000fffU     /* All currently supported flags */
+#define STATX__RESERVED                0x80000000U     /* Reserved for future struct statx expansion */
 
 /*
- * Attributes to be found in stx_attributes
+ * Attributes to be found in stx_attributes and masked in stx_attributes_mask.
  *
  * These give information about the features or the state of a file that might
  * be of use to ordinary userspace programs such as GUIs or ls rather than
index 8571d766331dd1a513a3f772f3a7ce050909598b..d4d77b09412c416fbe96e648e179a3789347d5ea 100644 (file)
@@ -141,8 +141,8 @@ static void dump_statx(struct statx *stx)
        if (stx->stx_mask & STATX_BTIME)
                print_time(" Birth: ", &stx->stx_btime);
 
-       if (stx->stx_attributes) {
-               unsigned char bits;
+       if (stx->stx_attributes_mask) {
+               unsigned char bits, mbits;
                int loop, byte;
 
                static char attr_representation[64 + 1] =
@@ -160,14 +160,18 @@ static void dump_statx(struct statx *stx)
                printf("Attributes: %016llx (", stx->stx_attributes);
                for (byte = 64 - 8; byte >= 0; byte -= 8) {
                        bits = stx->stx_attributes >> byte;
+                       mbits = stx->stx_attributes_mask >> byte;
                        for (loop = 7; loop >= 0; loop--) {
                                int bit = byte + loop;
 
-                               if (bits & 0x80)
+                               if (!(mbits & 0x80))
+                                       putchar('.');   /* Not supported */
+                               else if (bits & 0x80)
                                        putchar(attr_representation[63 - bit]);
                                else
-                                       putchar('-');
+                                       putchar('-');   /* Not set */
                                bits <<= 1;
+                               mbits <<= 1;
                        }
                        if (byte)
                                putchar(' ');