diff options
| -rw-r--r-- | Documentation/filesystems/Locking | 3 | ||||
| -rw-r--r-- | Documentation/filesystems/porting | 6 | ||||
| -rw-r--r-- | Documentation/filesystems/vfs.txt | 3 | ||||
| -rw-r--r-- | arch/alpha/kernel/osf_sys.c | 2 | ||||
| -rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
| -rw-r--r-- | fs/ext4/file.c | 2 | ||||
| -rw-r--r-- | fs/ext4/inode.c | 41 | ||||
| -rw-r--r-- | fs/ext4/namei.c | 2 | ||||
| -rw-r--r-- | fs/ext4/symlink.c | 3 | ||||
| -rw-r--r-- | fs/stat.c | 86 | ||||
| -rw-r--r-- | fs/xfs/xfs_iops.c | 14 | ||||
| -rw-r--r-- | include/linux/stat.h | 1 | ||||
| -rw-r--r-- | include/uapi/linux/stat.h | 5 | ||||
| -rw-r--r-- | samples/statx/test-statx.c | 12 |
14 files changed, 120 insertions, 61 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index fdcfdd79682a..fe25787ff6d4 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking | |||
| @@ -58,8 +58,7 @@ prototypes: | |||
| 58 | int (*permission) (struct inode *, int, unsigned int); | 58 | int (*permission) (struct inode *, int, unsigned int); |
| 59 | int (*get_acl)(struct inode *, int); | 59 | int (*get_acl)(struct inode *, int); |
| 60 | int (*setattr) (struct dentry *, struct iattr *); | 60 | int (*setattr) (struct dentry *, struct iattr *); |
| 61 | int (*getattr) (const struct path *, struct dentry *, struct kstat *, | 61 | int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); |
| 62 | u32, unsigned int); | ||
| 63 | ssize_t (*listxattr) (struct dentry *, char *, size_t); | 62 | ssize_t (*listxattr) (struct dentry *, char *, size_t); |
| 64 | int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); | 63 | int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); |
| 65 | void (*update_time)(struct inode *, struct timespec *, int); | 64 | void (*update_time)(struct inode *, struct timespec *, int); |
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 95280079c0b3..5fb17f49f7a2 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting | |||
| @@ -600,3 +600,9 @@ in your dentry operations instead. | |||
| 600 | [recommended] | 600 | [recommended] |
| 601 | ->readlink is optional for symlinks. Don't set, unless filesystem needs | 601 | ->readlink is optional for symlinks. Don't set, unless filesystem needs |
| 602 | to fake something for readlink(2). | 602 | to fake something for readlink(2). |
| 603 | -- | ||
| 604 | [mandatory] | ||
| 605 | ->getattr() is now passed a struct path rather than a vfsmount and | ||
| 606 | dentry separately, and it now has request_mask and query_flags arguments | ||
| 607 | to specify the fields and sync type requested by statx. Filesystems not | ||
| 608 | supporting any statx-specific features may ignore the new arguments. | ||
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 569211703721..94dd27ef4a76 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
| @@ -382,8 +382,7 @@ struct inode_operations { | |||
| 382 | int (*permission) (struct inode *, int); | 382 | int (*permission) (struct inode *, int); |
| 383 | int (*get_acl)(struct inode *, int); | 383 | int (*get_acl)(struct inode *, int); |
| 384 | int (*setattr) (struct dentry *, struct iattr *); | 384 | int (*setattr) (struct dentry *, struct iattr *); |
| 385 | int (*getattr) (const struct path *, struct dentry *, struct kstat *, | 385 | int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); |
| 386 | u32, unsigned int); | ||
| 387 | ssize_t (*listxattr) (struct dentry *, char *, size_t); | 386 | ssize_t (*listxattr) (struct dentry *, char *, size_t); |
| 388 | void (*update_time)(struct inode *, struct timespec *, int); | 387 | void (*update_time)(struct inode *, struct timespec *, int); |
| 389 | int (*atomic_open)(struct inode *, struct dentry *, struct file *, | 388 | int (*atomic_open)(struct inode *, struct dentry *, struct file *, |
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 0b961093ca5c..6d76e528ab8f 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c | |||
| @@ -1290,7 +1290,7 @@ SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p) | |||
| 1290 | /* copy relevant bits of struct timex. */ | 1290 | /* copy relevant bits of struct timex. */ |
| 1291 | if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) || | 1291 | if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) || |
| 1292 | copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - | 1292 | copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - |
| 1293 | offsetof(struct timex32, time))) | 1293 | offsetof(struct timex32, tick))) |
| 1294 | return -EFAULT; | 1294 | return -EFAULT; |
| 1295 | 1295 | ||
| 1296 | ret = do_adjtimex(&txc); | 1296 | ret = do_adjtimex(&txc); |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f493af666591..fb69ee2388db 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
| @@ -2466,6 +2466,7 @@ extern int ext4_setattr(struct dentry *, struct iattr *); | |||
| 2466 | extern int ext4_getattr(const struct path *, struct kstat *, u32, unsigned int); | 2466 | extern int ext4_getattr(const struct path *, struct kstat *, u32, unsigned int); |
| 2467 | extern void ext4_evict_inode(struct inode *); | 2467 | extern void ext4_evict_inode(struct inode *); |
| 2468 | extern void ext4_clear_inode(struct inode *); | 2468 | extern void ext4_clear_inode(struct inode *); |
| 2469 | extern int ext4_file_getattr(const struct path *, struct kstat *, u32, unsigned int); | ||
| 2469 | extern int ext4_sync_inode(handle_t *, struct inode *); | 2470 | extern int ext4_sync_inode(handle_t *, struct inode *); |
| 2470 | extern void ext4_dirty_inode(struct inode *, int); | 2471 | extern void ext4_dirty_inode(struct inode *, int); |
| 2471 | extern int ext4_change_inode_journal_flag(struct inode *, int); | 2472 | extern int ext4_change_inode_journal_flag(struct inode *, int); |
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 8210c1f43556..cefa9835f275 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
| @@ -744,7 +744,7 @@ const struct file_operations ext4_file_operations = { | |||
| 744 | 744 | ||
| 745 | const struct inode_operations ext4_file_inode_operations = { | 745 | const struct inode_operations ext4_file_inode_operations = { |
| 746 | .setattr = ext4_setattr, | 746 | .setattr = ext4_setattr, |
| 747 | .getattr = ext4_getattr, | 747 | .getattr = ext4_file_getattr, |
| 748 | .listxattr = ext4_listxattr, | 748 | .listxattr = ext4_listxattr, |
| 749 | .get_acl = ext4_get_acl, | 749 | .get_acl = ext4_get_acl, |
| 750 | .set_acl = ext4_set_acl, | 750 | .set_acl = ext4_set_acl, |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 4247d8d25687..b9ffa9f4191f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
| @@ -5390,11 +5390,46 @@ err_out: | |||
| 5390 | int ext4_getattr(const struct path *path, struct kstat *stat, | 5390 | int ext4_getattr(const struct path *path, struct kstat *stat, |
| 5391 | u32 request_mask, unsigned int query_flags) | 5391 | u32 request_mask, unsigned int query_flags) |
| 5392 | { | 5392 | { |
| 5393 | struct inode *inode; | 5393 | struct inode *inode = d_inode(path->dentry); |
| 5394 | unsigned long long delalloc_blocks; | 5394 | struct ext4_inode *raw_inode; |
| 5395 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
| 5396 | unsigned int flags; | ||
| 5397 | |||
| 5398 | if (EXT4_FITS_IN_INODE(raw_inode, ei, i_crtime)) { | ||
| 5399 | stat->result_mask |= STATX_BTIME; | ||
| 5400 | stat->btime.tv_sec = ei->i_crtime.tv_sec; | ||
| 5401 | stat->btime.tv_nsec = ei->i_crtime.tv_nsec; | ||
| 5402 | } | ||
| 5403 | |||
| 5404 | flags = ei->i_flags & EXT4_FL_USER_VISIBLE; | ||
| 5405 | if (flags & EXT4_APPEND_FL) | ||
| 5406 | stat->attributes |= STATX_ATTR_APPEND; | ||
| 5407 | if (flags & EXT4_COMPR_FL) | ||
| 5408 | stat->attributes |= STATX_ATTR_COMPRESSED; | ||
| 5409 | if (flags & EXT4_ENCRYPT_FL) | ||
| 5410 | stat->attributes |= STATX_ATTR_ENCRYPTED; | ||
| 5411 | if (flags & EXT4_IMMUTABLE_FL) | ||
| 5412 | stat->attributes |= STATX_ATTR_IMMUTABLE; | ||
| 5413 | if (flags & EXT4_NODUMP_FL) | ||
| 5414 | stat->attributes |= STATX_ATTR_NODUMP; | ||
| 5415 | |||
| 5416 | stat->attributes_mask |= (STATX_ATTR_APPEND | | ||
| 5417 | STATX_ATTR_COMPRESSED | | ||
| 5418 | STATX_ATTR_ENCRYPTED | | ||
| 5419 | STATX_ATTR_IMMUTABLE | | ||
| 5420 | STATX_ATTR_NODUMP); | ||
| 5395 | 5421 | ||
| 5396 | inode = d_inode(path->dentry); | ||
| 5397 | generic_fillattr(inode, stat); | 5422 | generic_fillattr(inode, stat); |
| 5423 | return 0; | ||
| 5424 | } | ||
| 5425 | |||
| 5426 | int ext4_file_getattr(const struct path *path, struct kstat *stat, | ||
| 5427 | u32 request_mask, unsigned int query_flags) | ||
| 5428 | { | ||
| 5429 | struct inode *inode = d_inode(path->dentry); | ||
| 5430 | u64 delalloc_blocks; | ||
| 5431 | |||
| 5432 | ext4_getattr(path, stat, request_mask, query_flags); | ||
| 5398 | 5433 | ||
| 5399 | /* | 5434 | /* |
| 5400 | * If there is inline data in the inode, the inode will normally not | 5435 | * If there is inline data in the inode, the inode will normally not |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 6ad612c576fc..07e5e1405771 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
| @@ -3912,6 +3912,7 @@ const struct inode_operations ext4_dir_inode_operations = { | |||
| 3912 | .tmpfile = ext4_tmpfile, | 3912 | .tmpfile = ext4_tmpfile, |
| 3913 | .rename = ext4_rename2, | 3913 | .rename = ext4_rename2, |
| 3914 | .setattr = ext4_setattr, | 3914 | .setattr = ext4_setattr, |
| 3915 | .getattr = ext4_getattr, | ||
| 3915 | .listxattr = ext4_listxattr, | 3916 | .listxattr = ext4_listxattr, |
| 3916 | .get_acl = ext4_get_acl, | 3917 | .get_acl = ext4_get_acl, |
| 3917 | .set_acl = ext4_set_acl, | 3918 | .set_acl = ext4_set_acl, |
| @@ -3920,6 +3921,7 @@ const struct inode_operations ext4_dir_inode_operations = { | |||
| 3920 | 3921 | ||
| 3921 | const struct inode_operations ext4_special_inode_operations = { | 3922 | const struct inode_operations ext4_special_inode_operations = { |
| 3922 | .setattr = ext4_setattr, | 3923 | .setattr = ext4_setattr, |
| 3924 | .getattr = ext4_getattr, | ||
| 3923 | .listxattr = ext4_listxattr, | 3925 | .listxattr = ext4_listxattr, |
| 3924 | .get_acl = ext4_get_acl, | 3926 | .get_acl = ext4_get_acl, |
| 3925 | .set_acl = ext4_set_acl, | 3927 | .set_acl = ext4_set_acl, |
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 73b184d161fc..5c8fc53cb0e5 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c | |||
| @@ -85,17 +85,20 @@ errout: | |||
| 85 | const struct inode_operations ext4_encrypted_symlink_inode_operations = { | 85 | const struct inode_operations ext4_encrypted_symlink_inode_operations = { |
| 86 | .get_link = ext4_encrypted_get_link, | 86 | .get_link = ext4_encrypted_get_link, |
| 87 | .setattr = ext4_setattr, | 87 | .setattr = ext4_setattr, |
| 88 | .getattr = ext4_getattr, | ||
| 88 | .listxattr = ext4_listxattr, | 89 | .listxattr = ext4_listxattr, |
| 89 | }; | 90 | }; |
| 90 | 91 | ||
| 91 | const struct inode_operations ext4_symlink_inode_operations = { | 92 | const struct inode_operations ext4_symlink_inode_operations = { |
| 92 | .get_link = page_get_link, | 93 | .get_link = page_get_link, |
| 93 | .setattr = ext4_setattr, | 94 | .setattr = ext4_setattr, |
| 95 | .getattr = ext4_getattr, | ||
| 94 | .listxattr = ext4_listxattr, | 96 | .listxattr = ext4_listxattr, |
| 95 | }; | 97 | }; |
| 96 | 98 | ||
| 97 | const struct inode_operations ext4_fast_symlink_inode_operations = { | 99 | const struct inode_operations ext4_fast_symlink_inode_operations = { |
| 98 | .get_link = simple_get_link, | 100 | .get_link = simple_get_link, |
| 99 | .setattr = ext4_setattr, | 101 | .setattr = ext4_setattr, |
| 102 | .getattr = ext4_getattr, | ||
| 100 | .listxattr = ext4_listxattr, | 103 | .listxattr = ext4_listxattr, |
| 101 | }; | 104 | }; |
| @@ -130,9 +130,13 @@ EXPORT_SYMBOL(vfs_getattr); | |||
| 130 | int vfs_statx_fd(unsigned int fd, struct kstat *stat, | 130 | int vfs_statx_fd(unsigned int fd, struct kstat *stat, |
| 131 | u32 request_mask, unsigned int query_flags) | 131 | u32 request_mask, unsigned int query_flags) |
| 132 | { | 132 | { |
| 133 | struct fd f = fdget_raw(fd); | 133 | struct fd f; |
| 134 | int error = -EBADF; | 134 | int error = -EBADF; |
| 135 | 135 | ||
| 136 | if (query_flags & ~KSTAT_QUERY_FLAGS) | ||
| 137 | return -EINVAL; | ||
| 138 | |||
| 139 | f = fdget_raw(fd); | ||
| 136 | if (f.file) { | 140 | if (f.file) { |
| 137 | error = vfs_getattr(&f.file->f_path, stat, | 141 | error = vfs_getattr(&f.file->f_path, stat, |
| 138 | request_mask, query_flags); | 142 | request_mask, query_flags); |
| @@ -155,9 +159,6 @@ EXPORT_SYMBOL(vfs_statx_fd); | |||
| 155 | * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink | 159 | * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink |
| 156 | * at the given name from being referenced. | 160 | * at the given name from being referenced. |
| 157 | * | 161 | * |
| 158 | * The caller must have preset stat->request_mask as for vfs_getattr(). The | ||
| 159 | * flags are also used to load up stat->query_flags. | ||
| 160 | * | ||
| 161 | * 0 will be returned on success, and a -ve error code if unsuccessful. | 162 | * 0 will be returned on success, and a -ve error code if unsuccessful. |
| 162 | */ | 163 | */ |
| 163 | int vfs_statx(int dfd, const char __user *filename, int flags, | 164 | int vfs_statx(int dfd, const char __user *filename, int flags, |
| @@ -509,46 +510,38 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename, | |||
| 509 | } | 510 | } |
| 510 | #endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */ | 511 | #endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */ |
| 511 | 512 | ||
| 512 | static inline int __put_timestamp(struct timespec *kts, | 513 | static noinline_for_stack int |
| 513 | struct statx_timestamp __user *uts) | 514 | cp_statx(const struct kstat *stat, struct statx __user *buffer) |
| 514 | { | ||
| 515 | return (__put_user(kts->tv_sec, &uts->tv_sec ) || | ||
| 516 | __put_user(kts->tv_nsec, &uts->tv_nsec ) || | ||
| 517 | __put_user(0, &uts->__reserved )); | ||
| 518 | } | ||
| 519 | |||
| 520 | /* | ||
| 521 | * Set the statx results. | ||
| 522 | */ | ||
| 523 | static long statx_set_result(struct kstat *stat, struct statx __user *buffer) | ||
| 524 | { | 515 | { |
| 525 | uid_t uid = from_kuid_munged(current_user_ns(), stat->uid); | 516 | struct statx tmp; |
| 526 | gid_t gid = from_kgid_munged(current_user_ns(), stat->gid); | 517 | |
| 527 | 518 | memset(&tmp, 0, sizeof(tmp)); | |
| 528 | if (__put_user(stat->result_mask, &buffer->stx_mask ) || | 519 | |
| 529 | __put_user(stat->mode, &buffer->stx_mode ) || | 520 | tmp.stx_mask = stat->result_mask; |
| 530 | __clear_user(&buffer->__spare0, sizeof(buffer->__spare0)) || | 521 | tmp.stx_blksize = stat->blksize; |
| 531 | __put_user(stat->nlink, &buffer->stx_nlink ) || | 522 | tmp.stx_attributes = stat->attributes; |
| 532 | __put_user(uid, &buffer->stx_uid ) || | 523 | tmp.stx_nlink = stat->nlink; |
| 533 | __put_user(gid, &buffer->stx_gid ) || | 524 | tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid); |
| 534 | __put_user(stat->attributes, &buffer->stx_attributes ) || | 525 | tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid); |
| 535 | __put_user(stat->blksize, &buffer->stx_blksize ) || | 526 | tmp.stx_mode = stat->mode; |
| 536 | __put_user(MAJOR(stat->rdev), &buffer->stx_rdev_major ) || | 527 | tmp.stx_ino = stat->ino; |
| 537 | __put_user(MINOR(stat->rdev), &buffer->stx_rdev_minor ) || | 528 | tmp.stx_size = stat->size; |
| 538 | __put_user(MAJOR(stat->dev), &buffer->stx_dev_major ) || | 529 | tmp.stx_blocks = stat->blocks; |
| 539 | __put_user(MINOR(stat->dev), &buffer->stx_dev_minor ) || | 530 | tmp.stx_attributes_mask = stat->attributes_mask; |
| 540 | __put_timestamp(&stat->atime, &buffer->stx_atime ) || | 531 | tmp.stx_atime.tv_sec = stat->atime.tv_sec; |
| 541 | __put_timestamp(&stat->btime, &buffer->stx_btime ) || | 532 | tmp.stx_atime.tv_nsec = stat->atime.tv_nsec; |
| 542 | __put_timestamp(&stat->ctime, &buffer->stx_ctime ) || | 533 | tmp.stx_btime.tv_sec = stat->btime.tv_sec; |
| 543 | __put_timestamp(&stat->mtime, &buffer->stx_mtime ) || | 534 | tmp.stx_btime.tv_nsec = stat->btime.tv_nsec; |
| 544 | __put_user(stat->ino, &buffer->stx_ino ) || | 535 | tmp.stx_ctime.tv_sec = stat->ctime.tv_sec; |
| 545 | __put_user(stat->size, &buffer->stx_size ) || | 536 | tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec; |
| 546 | __put_user(stat->blocks, &buffer->stx_blocks ) || | 537 | tmp.stx_mtime.tv_sec = stat->mtime.tv_sec; |
| 547 | __clear_user(&buffer->__spare1, sizeof(buffer->__spare1)) || | 538 | tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec; |
| 548 | __clear_user(&buffer->__spare2, sizeof(buffer->__spare2))) | 539 | tmp.stx_rdev_major = MAJOR(stat->rdev); |
| 549 | return -EFAULT; | 540 | tmp.stx_rdev_minor = MINOR(stat->rdev); |
| 550 | 541 | tmp.stx_dev_major = MAJOR(stat->dev); | |
| 551 | return 0; | 542 | tmp.stx_dev_minor = MINOR(stat->dev); |
| 543 | |||
| 544 | return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0; | ||
| 552 | } | 545 | } |
| 553 | 546 | ||
| 554 | /** | 547 | /** |
| @@ -570,10 +563,10 @@ SYSCALL_DEFINE5(statx, | |||
| 570 | struct kstat stat; | 563 | struct kstat stat; |
| 571 | int error; | 564 | int error; |
| 572 | 565 | ||
| 566 | if (mask & STATX__RESERVED) | ||
| 567 | return -EINVAL; | ||
| 573 | if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) | 568 | if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) |
| 574 | return -EINVAL; | 569 | return -EINVAL; |
| 575 | if (!access_ok(VERIFY_WRITE, buffer, sizeof(*buffer))) | ||
| 576 | return -EFAULT; | ||
| 577 | 570 | ||
| 578 | if (filename) | 571 | if (filename) |
| 579 | error = vfs_statx(dfd, filename, flags, &stat, mask); | 572 | error = vfs_statx(dfd, filename, flags, &stat, mask); |
| @@ -581,7 +574,8 @@ SYSCALL_DEFINE5(statx, | |||
| 581 | error = vfs_statx_fd(dfd, &stat, mask, flags); | 574 | error = vfs_statx_fd(dfd, &stat, mask, flags); |
| 582 | if (error) | 575 | if (error) |
| 583 | return error; | 576 | return error; |
| 584 | return statx_set_result(&stat, buffer); | 577 | |
| 578 | return cp_statx(&stat, buffer); | ||
| 585 | } | 579 | } |
| 586 | 580 | ||
| 587 | /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */ | 581 | /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */ |
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 229cc6a6d8ef..ebfc13350f9a 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c | |||
| @@ -516,6 +516,20 @@ xfs_vn_getattr( | |||
| 516 | stat->blocks = | 516 | stat->blocks = |
| 517 | XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks); | 517 | XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks); |
| 518 | 518 | ||
| 519 | if (ip->i_d.di_version == 3) { | ||
| 520 | if (request_mask & STATX_BTIME) { | ||
| 521 | stat->result_mask |= STATX_BTIME; | ||
| 522 | stat->btime.tv_sec = ip->i_d.di_crtime.t_sec; | ||
| 523 | stat->btime.tv_nsec = ip->i_d.di_crtime.t_nsec; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | if (ip->i_d.di_flags & XFS_DIFLAG_IMMUTABLE) | ||
| 528 | stat->attributes |= STATX_ATTR_IMMUTABLE; | ||
| 529 | if (ip->i_d.di_flags & XFS_DIFLAG_APPEND) | ||
| 530 | stat->attributes |= STATX_ATTR_APPEND; | ||
| 531 | if (ip->i_d.di_flags & XFS_DIFLAG_NODUMP) | ||
| 532 | stat->attributes |= STATX_ATTR_NODUMP; | ||
| 519 | 533 | ||
| 520 | switch (inode->i_mode & S_IFMT) { | 534 | switch (inode->i_mode & S_IFMT) { |
| 521 | case S_IFBLK: | 535 | case S_IFBLK: |
diff --git a/include/linux/stat.h b/include/linux/stat.h index c76e524fb34b..64b6b3aece21 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h | |||
| @@ -26,6 +26,7 @@ struct kstat { | |||
| 26 | unsigned int nlink; | 26 | unsigned int nlink; |
| 27 | uint32_t blksize; /* Preferred I/O size */ | 27 | uint32_t blksize; /* Preferred I/O size */ |
| 28 | u64 attributes; | 28 | u64 attributes; |
| 29 | u64 attributes_mask; | ||
| 29 | #define KSTAT_ATTR_FS_IOC_FLAGS \ | 30 | #define KSTAT_ATTR_FS_IOC_FLAGS \ |
| 30 | (STATX_ATTR_COMPRESSED | \ | 31 | (STATX_ATTR_COMPRESSED | \ |
| 31 | STATX_ATTR_IMMUTABLE | \ | 32 | STATX_ATTR_IMMUTABLE | \ |
diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h index 51a6b86e3700..d538897b8e08 100644 --- a/include/uapi/linux/stat.h +++ b/include/uapi/linux/stat.h | |||
| @@ -114,7 +114,7 @@ struct statx { | |||
| 114 | __u64 stx_ino; /* Inode number */ | 114 | __u64 stx_ino; /* Inode number */ |
| 115 | __u64 stx_size; /* File size */ | 115 | __u64 stx_size; /* File size */ |
| 116 | __u64 stx_blocks; /* Number of 512-byte blocks allocated */ | 116 | __u64 stx_blocks; /* Number of 512-byte blocks allocated */ |
| 117 | __u64 __spare1[1]; | 117 | __u64 stx_attributes_mask; /* Mask to show what's supported in stx_attributes */ |
| 118 | /* 0x40 */ | 118 | /* 0x40 */ |
| 119 | struct statx_timestamp stx_atime; /* Last access time */ | 119 | struct statx_timestamp stx_atime; /* Last access time */ |
| 120 | struct statx_timestamp stx_btime; /* File creation time */ | 120 | struct statx_timestamp stx_btime; /* File creation time */ |
| @@ -152,9 +152,10 @@ struct statx { | |||
| 152 | #define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */ | 152 | #define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */ |
| 153 | #define STATX_BTIME 0x00000800U /* Want/got stx_btime */ | 153 | #define STATX_BTIME 0x00000800U /* Want/got stx_btime */ |
| 154 | #define STATX_ALL 0x00000fffU /* All currently supported flags */ | 154 | #define STATX_ALL 0x00000fffU /* All currently supported flags */ |
| 155 | #define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */ | ||
| 155 | 156 | ||
| 156 | /* | 157 | /* |
| 157 | * Attributes to be found in stx_attributes | 158 | * Attributes to be found in stx_attributes and masked in stx_attributes_mask. |
| 158 | * | 159 | * |
| 159 | * These give information about the features or the state of a file that might | 160 | * These give information about the features or the state of a file that might |
| 160 | * be of use to ordinary userspace programs such as GUIs or ls rather than | 161 | * be of use to ordinary userspace programs such as GUIs or ls rather than |
diff --git a/samples/statx/test-statx.c b/samples/statx/test-statx.c index 8571d766331d..d4d77b09412c 100644 --- a/samples/statx/test-statx.c +++ b/samples/statx/test-statx.c | |||
| @@ -141,8 +141,8 @@ static void dump_statx(struct statx *stx) | |||
| 141 | if (stx->stx_mask & STATX_BTIME) | 141 | if (stx->stx_mask & STATX_BTIME) |
| 142 | print_time(" Birth: ", &stx->stx_btime); | 142 | print_time(" Birth: ", &stx->stx_btime); |
| 143 | 143 | ||
| 144 | if (stx->stx_attributes) { | 144 | if (stx->stx_attributes_mask) { |
| 145 | unsigned char bits; | 145 | unsigned char bits, mbits; |
| 146 | int loop, byte; | 146 | int loop, byte; |
| 147 | 147 | ||
| 148 | static char attr_representation[64 + 1] = | 148 | static char attr_representation[64 + 1] = |
| @@ -160,14 +160,18 @@ static void dump_statx(struct statx *stx) | |||
| 160 | printf("Attributes: %016llx (", stx->stx_attributes); | 160 | printf("Attributes: %016llx (", stx->stx_attributes); |
| 161 | for (byte = 64 - 8; byte >= 0; byte -= 8) { | 161 | for (byte = 64 - 8; byte >= 0; byte -= 8) { |
| 162 | bits = stx->stx_attributes >> byte; | 162 | bits = stx->stx_attributes >> byte; |
| 163 | mbits = stx->stx_attributes_mask >> byte; | ||
| 163 | for (loop = 7; loop >= 0; loop--) { | 164 | for (loop = 7; loop >= 0; loop--) { |
| 164 | int bit = byte + loop; | 165 | int bit = byte + loop; |
| 165 | 166 | ||
| 166 | if (bits & 0x80) | 167 | if (!(mbits & 0x80)) |
| 168 | putchar('.'); /* Not supported */ | ||
| 169 | else if (bits & 0x80) | ||
| 167 | putchar(attr_representation[63 - bit]); | 170 | putchar(attr_representation[63 - bit]); |
| 168 | else | 171 | else |
| 169 | putchar('-'); | 172 | putchar('-'); /* Not set */ |
| 170 | bits <<= 1; | 173 | bits <<= 1; |
| 174 | mbits <<= 1; | ||
| 171 | } | 175 | } |
| 172 | if (byte) | 176 | if (byte) |
| 173 | putchar(' '); | 177 | putchar(' '); |
