Commit 2a610b8a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

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()
parents 78d91a75 a8e28440
...@@ -58,8 +58,7 @@ prototypes: ...@@ -58,8 +58,7 @@ prototypes:
int (*permission) (struct inode *, int, unsigned int); int (*permission) (struct inode *, int, unsigned int);
int (*get_acl)(struct inode *, int); int (*get_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *); int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (const struct path *, struct dentry *, struct kstat *, int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
void (*update_time)(struct inode *, struct timespec *, int); void (*update_time)(struct inode *, struct timespec *, int);
......
...@@ -600,3 +600,9 @@ in your dentry operations instead. ...@@ -600,3 +600,9 @@ in your dentry operations instead.
[recommended] [recommended]
->readlink is optional for symlinks. Don't set, unless filesystem needs ->readlink is optional for symlinks. Don't set, unless filesystem needs
to fake something for readlink(2). 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.
...@@ -382,8 +382,7 @@ struct inode_operations { ...@@ -382,8 +382,7 @@ struct inode_operations {
int (*permission) (struct inode *, int); int (*permission) (struct inode *, int);
int (*get_acl)(struct inode *, int); int (*get_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *); int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (const struct path *, struct dentry *, struct kstat *, int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t);
void (*update_time)(struct inode *, struct timespec *, int); void (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *, struct file *, int (*atomic_open)(struct inode *, struct dentry *, struct file *,
......
...@@ -1290,7 +1290,7 @@ SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p) ...@@ -1290,7 +1290,7 @@ SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p)
/* copy relevant bits of struct timex. */ /* copy relevant bits of struct timex. */
if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) || if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) ||
copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) -
offsetof(struct timex32, time))) offsetof(struct timex32, tick)))
return -EFAULT; return -EFAULT;
ret = do_adjtimex(&txc); ret = do_adjtimex(&txc);
......
...@@ -2466,6 +2466,7 @@ extern int ext4_setattr(struct dentry *, struct iattr *); ...@@ -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 int ext4_getattr(const struct path *, struct kstat *, u32, unsigned int);
extern void ext4_evict_inode(struct inode *); extern void ext4_evict_inode(struct inode *);
extern void ext4_clear_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 int ext4_sync_inode(handle_t *, struct inode *);
extern void ext4_dirty_inode(struct inode *, int); extern void ext4_dirty_inode(struct inode *, int);
extern int ext4_change_inode_journal_flag(struct inode *, int); extern int ext4_change_inode_journal_flag(struct inode *, int);
......
...@@ -744,7 +744,7 @@ const struct file_operations ext4_file_operations = { ...@@ -744,7 +744,7 @@ const struct file_operations ext4_file_operations = {
const struct inode_operations ext4_file_inode_operations = { const struct inode_operations ext4_file_inode_operations = {
.setattr = ext4_setattr, .setattr = ext4_setattr,
.getattr = ext4_getattr, .getattr = ext4_file_getattr,
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
.get_acl = ext4_get_acl, .get_acl = ext4_get_acl,
.set_acl = ext4_set_acl, .set_acl = ext4_set_acl,
......
...@@ -5390,11 +5390,46 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -5390,11 +5390,46 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
int ext4_getattr(const struct path *path, struct kstat *stat, int ext4_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags) u32 request_mask, unsigned int query_flags)
{ {
struct inode *inode; struct inode *inode = d_inode(path->dentry);
unsigned long long delalloc_blocks; 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); 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 * If there is inline data in the inode, the inode will normally not
......
...@@ -3912,6 +3912,7 @@ const struct inode_operations ext4_dir_inode_operations = { ...@@ -3912,6 +3912,7 @@ const struct inode_operations ext4_dir_inode_operations = {
.tmpfile = ext4_tmpfile, .tmpfile = ext4_tmpfile,
.rename = ext4_rename2, .rename = ext4_rename2,
.setattr = ext4_setattr, .setattr = ext4_setattr,
.getattr = ext4_getattr,
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
.get_acl = ext4_get_acl, .get_acl = ext4_get_acl,
.set_acl = ext4_set_acl, .set_acl = ext4_set_acl,
...@@ -3920,6 +3921,7 @@ const struct inode_operations ext4_dir_inode_operations = { ...@@ -3920,6 +3921,7 @@ const struct inode_operations ext4_dir_inode_operations = {
const struct inode_operations ext4_special_inode_operations = { const struct inode_operations ext4_special_inode_operations = {
.setattr = ext4_setattr, .setattr = ext4_setattr,
.getattr = ext4_getattr,
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
.get_acl = ext4_get_acl, .get_acl = ext4_get_acl,
.set_acl = ext4_set_acl, .set_acl = ext4_set_acl,
......
...@@ -85,17 +85,20 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry, ...@@ -85,17 +85,20 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry,
const struct inode_operations ext4_encrypted_symlink_inode_operations = { const struct inode_operations ext4_encrypted_symlink_inode_operations = {
.get_link = ext4_encrypted_get_link, .get_link = ext4_encrypted_get_link,
.setattr = ext4_setattr, .setattr = ext4_setattr,
.getattr = ext4_getattr,
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
}; };
const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_symlink_inode_operations = {
.get_link = page_get_link, .get_link = page_get_link,
.setattr = ext4_setattr, .setattr = ext4_setattr,
.getattr = ext4_getattr,
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
}; };
const struct inode_operations ext4_fast_symlink_inode_operations = { const struct inode_operations ext4_fast_symlink_inode_operations = {
.get_link = simple_get_link, .get_link = simple_get_link,
.setattr = ext4_setattr, .setattr = ext4_setattr,
.getattr = ext4_getattr,
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
}; };
...@@ -130,9 +130,13 @@ EXPORT_SYMBOL(vfs_getattr); ...@@ -130,9 +130,13 @@ EXPORT_SYMBOL(vfs_getattr);
int vfs_statx_fd(unsigned int fd, struct kstat *stat, int vfs_statx_fd(unsigned int fd, struct kstat *stat,
u32 request_mask, unsigned int query_flags) u32 request_mask, unsigned int query_flags)
{ {
struct fd f = fdget_raw(fd); struct fd f;
int error = -EBADF; int error = -EBADF;
if (query_flags & ~KSTAT_QUERY_FLAGS)
return -EINVAL;
f = fdget_raw(fd);
if (f.file) { if (f.file) {
error = vfs_getattr(&f.file->f_path, stat, error = vfs_getattr(&f.file->f_path, stat,
request_mask, query_flags); request_mask, query_flags);
...@@ -155,9 +159,6 @@ EXPORT_SYMBOL(vfs_statx_fd); ...@@ -155,9 +159,6 @@ EXPORT_SYMBOL(vfs_statx_fd);
* Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink
* at the given name from being referenced. * 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. * 0 will be returned on success, and a -ve error code if unsuccessful.
*/ */
int vfs_statx(int dfd, const char __user *filename, int flags, 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,46 +510,38 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
} }
#endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */ #endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
static inline int __put_timestamp(struct timespec *kts, static noinline_for_stack int
struct statx_timestamp __user *uts) cp_statx(const struct kstat *stat, struct statx __user *buffer)
{
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)
{ {
uid_t uid = from_kuid_munged(current_user_ns(), stat->uid); struct statx tmp;
gid_t gid = from_kgid_munged(current_user_ns(), stat->gid);
memset(&tmp, 0, sizeof(tmp));
if (__put_user(stat->result_mask, &buffer->stx_mask ) ||
__put_user(stat->mode, &buffer->stx_mode ) || tmp.stx_mask = stat->result_mask;
__clear_user(&buffer->__spare0, sizeof(buffer->__spare0)) || tmp.stx_blksize = stat->blksize;
__put_user(stat->nlink, &buffer->stx_nlink ) || tmp.stx_attributes = stat->attributes;
__put_user(uid, &buffer->stx_uid ) || tmp.stx_nlink = stat->nlink;
__put_user(gid, &buffer->stx_gid ) || tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid);
__put_user(stat->attributes, &buffer->stx_attributes ) || tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid);
__put_user(stat->blksize, &buffer->stx_blksize ) || tmp.stx_mode = stat->mode;
__put_user(MAJOR(stat->rdev), &buffer->stx_rdev_major ) || tmp.stx_ino = stat->ino;
__put_user(MINOR(stat->rdev), &buffer->stx_rdev_minor ) || tmp.stx_size = stat->size;
__put_user(MAJOR(stat->dev), &buffer->stx_dev_major ) || tmp.stx_blocks = stat->blocks;
__put_user(MINOR(stat->dev), &buffer->stx_dev_minor ) || tmp.stx_attributes_mask = stat->attributes_mask;
__put_timestamp(&stat->atime, &buffer->stx_atime ) || tmp.stx_atime.tv_sec = stat->atime.tv_sec;
__put_timestamp(&stat->btime, &buffer->stx_btime ) || tmp.stx_atime.tv_nsec = stat->atime.tv_nsec;
__put_timestamp(&stat->ctime, &buffer->stx_ctime ) || tmp.stx_btime.tv_sec = stat->btime.tv_sec;
__put_timestamp(&stat->mtime, &buffer->stx_mtime ) || tmp.stx_btime.tv_nsec = stat->btime.tv_nsec;
__put_user(stat->ino, &buffer->stx_ino ) || tmp.stx_ctime.tv_sec = stat->ctime.tv_sec;
__put_user(stat->size, &buffer->stx_size ) || tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec;
__put_user(stat->blocks, &buffer->stx_blocks ) || tmp.stx_mtime.tv_sec = stat->mtime.tv_sec;
__clear_user(&buffer->__spare1, sizeof(buffer->__spare1)) || tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec;
__clear_user(&buffer->__spare2, sizeof(buffer->__spare2))) tmp.stx_rdev_major = MAJOR(stat->rdev);
return -EFAULT; tmp.stx_rdev_minor = MINOR(stat->rdev);
tmp.stx_dev_major = MAJOR(stat->dev);
return 0; tmp.stx_dev_minor = MINOR(stat->dev);
return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
} }
/** /**
...@@ -570,10 +563,10 @@ SYSCALL_DEFINE5(statx, ...@@ -570,10 +563,10 @@ SYSCALL_DEFINE5(statx,
struct kstat stat; struct kstat stat;
int error; int error;
if (mask & STATX__RESERVED)
return -EINVAL;
if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
return -EINVAL; return -EINVAL;
if (!access_ok(VERIFY_WRITE, buffer, sizeof(*buffer)))
return -EFAULT;
if (filename) if (filename)
error = vfs_statx(dfd, filename, flags, &stat, mask); error = vfs_statx(dfd, filename, flags, &stat, mask);
...@@ -581,7 +574,8 @@ SYSCALL_DEFINE5(statx, ...@@ -581,7 +574,8 @@ SYSCALL_DEFINE5(statx,
error = vfs_statx_fd(dfd, &stat, mask, flags); error = vfs_statx_fd(dfd, &stat, mask, flags);
if (error) if (error)
return 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) */ /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
......
...@@ -516,6 +516,20 @@ xfs_vn_getattr( ...@@ -516,6 +516,20 @@ xfs_vn_getattr(
stat->blocks = stat->blocks =
XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks); 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) { switch (inode->i_mode & S_IFMT) {
case S_IFBLK: case S_IFBLK:
......
...@@ -26,6 +26,7 @@ struct kstat { ...@@ -26,6 +26,7 @@ struct kstat {
unsigned int nlink; unsigned int nlink;
uint32_t blksize; /* Preferred I/O size */ uint32_t blksize; /* Preferred I/O size */
u64 attributes; u64 attributes;
u64 attributes_mask;
#define KSTAT_ATTR_FS_IOC_FLAGS \ #define KSTAT_ATTR_FS_IOC_FLAGS \
(STATX_ATTR_COMPRESSED | \ (STATX_ATTR_COMPRESSED | \
STATX_ATTR_IMMUTABLE | \ STATX_ATTR_IMMUTABLE | \
......
...@@ -114,7 +114,7 @@ struct statx { ...@@ -114,7 +114,7 @@ struct statx {
__u64 stx_ino; /* Inode number */ __u64 stx_ino; /* Inode number */
__u64 stx_size; /* File size */ __u64 stx_size; /* File size */
__u64 stx_blocks; /* Number of 512-byte blocks allocated */ __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 */ /* 0x40 */
struct statx_timestamp stx_atime; /* Last access time */ struct statx_timestamp stx_atime; /* Last access time */
struct statx_timestamp stx_btime; /* File creation time */ struct statx_timestamp stx_btime; /* File creation time */
...@@ -152,9 +152,10 @@ struct statx { ...@@ -152,9 +152,10 @@ struct statx {
#define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */ #define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */
#define STATX_BTIME 0x00000800U /* Want/got stx_btime */ #define STATX_BTIME 0x00000800U /* Want/got stx_btime */
#define STATX_ALL 0x00000fffU /* All currently supported flags */ #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 * 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 * be of use to ordinary userspace programs such as GUIs or ls rather than
......
...@@ -141,8 +141,8 @@ static void dump_statx(struct statx *stx) ...@@ -141,8 +141,8 @@ static void dump_statx(struct statx *stx)
if (stx->stx_mask & STATX_BTIME) if (stx->stx_mask & STATX_BTIME)
print_time(" Birth: ", &stx->stx_btime); print_time(" Birth: ", &stx->stx_btime);
if (stx->stx_attributes) { if (stx->stx_attributes_mask) {
unsigned char bits; unsigned char bits, mbits;
int loop, byte; int loop, byte;
static char attr_representation[64 + 1] = static char attr_representation[64 + 1] =
...@@ -160,14 +160,18 @@ static void dump_statx(struct statx *stx) ...@@ -160,14 +160,18 @@ static void dump_statx(struct statx *stx)
printf("Attributes: %016llx (", stx->stx_attributes); printf("Attributes: %016llx (", stx->stx_attributes);
for (byte = 64 - 8; byte >= 0; byte -= 8) { for (byte = 64 - 8; byte >= 0; byte -= 8) {
bits = stx->stx_attributes >> byte; bits = stx->stx_attributes >> byte;
mbits = stx->stx_attributes_mask >> byte;
for (loop = 7; loop >= 0; loop--) { for (loop = 7; loop >= 0; loop--) {
int bit = byte + loop; int bit = byte + loop;
if (bits & 0x80) if (!(mbits & 0x80))
putchar('.'); /* Not supported */
else if (bits & 0x80)
putchar(attr_representation[63 - bit]); putchar(attr_representation[63 - bit]);
else else
putchar('-'); putchar('-'); /* Not set */
bits <<= 1; bits <<= 1;
mbits <<= 1;
} }
if (byte) if (byte)
putchar(' '); putchar(' ');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment