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()
This commit is contained in:
commit
2a610b8aa8
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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 *,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
80
fs/stat.c
80
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)
|
||||
static noinline_for_stack int
|
||||
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 ));
|
||||
}
|
||||
struct statx tmp;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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 ) ||
|
||||
__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;
|
||||
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 0;
|
||||
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) */
|
||||
|
@ -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:
|
||||
|
@ -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 | \
|
||||
|
@ -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
|
||||
|
@ -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(' ');
|
||||
|
Loading…
Reference in New Issue
Block a user