|
@@ -18,6 +18,15 @@
|
|
|
#include <linux/uaccess.h>
|
|
|
#include <asm/unistd.h>
|
|
|
|
|
|
+/**
|
|
|
+ * generic_fillattr - Fill in the basic attributes from the inode struct
|
|
|
+ * @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.
|
|
|
+ */
|
|
|
void generic_fillattr(struct inode *inode, struct kstat *stat)
|
|
|
{
|
|
|
stat->dev = inode->i_sb->s_dev;
|
|
@@ -33,81 +42,147 @@ void generic_fillattr(struct inode *inode, struct kstat *stat)
|
|
|
stat->ctime = inode->i_ctime;
|
|
|
stat->blksize = i_blocksize(inode);
|
|
|
stat->blocks = inode->i_blocks;
|
|
|
-}
|
|
|
|
|
|
+ if (IS_NOATIME(inode))
|
|
|
+ stat->result_mask &= ~STATX_ATIME;
|
|
|
+ if (IS_AUTOMOUNT(inode))
|
|
|
+ stat->attributes |= STATX_ATTR_AUTOMOUNT;
|
|
|
+}
|
|
|
EXPORT_SYMBOL(generic_fillattr);
|
|
|
|
|
|
/**
|
|
|
* vfs_getattr_nosec - getattr without security checks
|
|
|
* @path: file to get attributes from
|
|
|
* @stat: structure to return attributes in
|
|
|
+ * @request_mask: STATX_xxx flags indicating what the caller wants
|
|
|
+ * @query_flags: Query mode (KSTAT_QUERY_FLAGS)
|
|
|
*
|
|
|
* Get attributes without calling security_inode_getattr.
|
|
|
*
|
|
|
* Currently the only caller other than vfs_getattr is internal to the
|
|
|
- * filehandle lookup code, which uses only the inode number and returns
|
|
|
- * no attributes to any user. Any other code probably wants
|
|
|
- * vfs_getattr.
|
|
|
+ * filehandle lookup code, which uses only the inode number and returns no
|
|
|
+ * attributes to any user. Any other code probably wants vfs_getattr.
|
|
|
*/
|
|
|
-int vfs_getattr_nosec(struct path *path, struct kstat *stat)
|
|
|
+int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
|
|
|
+ u32 request_mask, unsigned int query_flags)
|
|
|
{
|
|
|
struct inode *inode = d_backing_inode(path->dentry);
|
|
|
|
|
|
+ memset(stat, 0, sizeof(*stat));
|
|
|
+ stat->result_mask |= STATX_BASIC_STATS;
|
|
|
+ request_mask &= STATX_ALL;
|
|
|
+ query_flags &= KSTAT_QUERY_FLAGS;
|
|
|
if (inode->i_op->getattr)
|
|
|
- return inode->i_op->getattr(path->mnt, path->dentry, stat);
|
|
|
+ return inode->i_op->getattr(path, stat, request_mask,
|
|
|
+ query_flags);
|
|
|
|
|
|
generic_fillattr(inode, stat);
|
|
|
return 0;
|
|
|
}
|
|
|
-
|
|
|
EXPORT_SYMBOL(vfs_getattr_nosec);
|
|
|
|
|
|
-int vfs_getattr(struct path *path, struct kstat *stat)
|
|
|
+/*
|
|
|
+ * vfs_getattr - Get the enhanced basic attributes of a file
|
|
|
+ * @path: The file of interest
|
|
|
+ * @stat: Where to return the statistics
|
|
|
+ * @request_mask: STATX_xxx flags indicating what the caller wants
|
|
|
+ * @query_flags: Query mode (KSTAT_QUERY_FLAGS)
|
|
|
+ *
|
|
|
+ * Ask the filesystem for a file's attributes. The caller must indicate in
|
|
|
+ * request_mask and query_flags to indicate what they want.
|
|
|
+ *
|
|
|
+ * If the file is remote, the filesystem can be forced to update the attributes
|
|
|
+ * from the backing store by passing AT_STATX_FORCE_SYNC in query_flags or can
|
|
|
+ * suppress the update by passing AT_STATX_DONT_SYNC.
|
|
|
+ *
|
|
|
+ * Bits must have been set in request_mask to indicate which attributes the
|
|
|
+ * caller wants retrieving. Any such attribute not requested may be returned
|
|
|
+ * anyway, but the value may be approximate, and, if remote, may not have been
|
|
|
+ * synchronised with the server.
|
|
|
+ *
|
|
|
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
|
|
|
+ */
|
|
|
+int vfs_getattr(const struct path *path, struct kstat *stat,
|
|
|
+ u32 request_mask, unsigned int query_flags)
|
|
|
{
|
|
|
int retval;
|
|
|
|
|
|
retval = security_inode_getattr(path);
|
|
|
if (retval)
|
|
|
return retval;
|
|
|
- return vfs_getattr_nosec(path, stat);
|
|
|
+ return vfs_getattr_nosec(path, stat, request_mask, query_flags);
|
|
|
}
|
|
|
-
|
|
|
EXPORT_SYMBOL(vfs_getattr);
|
|
|
|
|
|
-int vfs_fstat(unsigned int fd, struct kstat *stat)
|
|
|
+/**
|
|
|
+ * vfs_statx_fd - Get the enhanced basic attributes by file descriptor
|
|
|
+ * @fd: The file descriptor referring to the file of interest
|
|
|
+ * @stat: The result structure to fill in.
|
|
|
+ * @request_mask: STATX_xxx flags indicating what the caller wants
|
|
|
+ * @query_flags: Query mode (KSTAT_QUERY_FLAGS)
|
|
|
+ *
|
|
|
+ * This function is a wrapper around vfs_getattr(). The main difference is
|
|
|
+ * that it uses a file descriptor to determine the file location.
|
|
|
+ *
|
|
|
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
|
|
|
+ */
|
|
|
+int vfs_statx_fd(unsigned int fd, struct kstat *stat,
|
|
|
+ u32 request_mask, unsigned int query_flags)
|
|
|
{
|
|
|
struct fd f = fdget_raw(fd);
|
|
|
int error = -EBADF;
|
|
|
|
|
|
if (f.file) {
|
|
|
- error = vfs_getattr(&f.file->f_path, stat);
|
|
|
+ error = vfs_getattr(&f.file->f_path, stat,
|
|
|
+ request_mask, query_flags);
|
|
|
fdput(f);
|
|
|
}
|
|
|
return error;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(vfs_fstat);
|
|
|
+EXPORT_SYMBOL(vfs_statx_fd);
|
|
|
|
|
|
-int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
|
|
- int flag)
|
|
|
+/**
|
|
|
+ * vfs_statx - Get basic and extra attributes by filename
|
|
|
+ * @dfd: A file descriptor representing the base dir for a relative filename
|
|
|
+ * @filename: The name of the file of interest
|
|
|
+ * @flags: Flags to control the query
|
|
|
+ * @stat: The result structure to fill in.
|
|
|
+ * @request_mask: STATX_xxx flags indicating what the caller wants
|
|
|
+ *
|
|
|
+ * This function is a wrapper around vfs_getattr(). The main difference is
|
|
|
+ * that it uses a filename and base directory to determine the file location.
|
|
|
+ * 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,
|
|
|
+ struct kstat *stat, u32 request_mask)
|
|
|
{
|
|
|
struct path path;
|
|
|
int error = -EINVAL;
|
|
|
- unsigned int lookup_flags = 0;
|
|
|
+ unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
|
|
|
|
|
|
- if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
|
|
|
- AT_EMPTY_PATH)) != 0)
|
|
|
- goto out;
|
|
|
+ if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
|
|
|
+ AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- if (!(flag & AT_SYMLINK_NOFOLLOW))
|
|
|
- lookup_flags |= LOOKUP_FOLLOW;
|
|
|
- if (flag & AT_EMPTY_PATH)
|
|
|
+ if (flags & AT_SYMLINK_NOFOLLOW)
|
|
|
+ lookup_flags &= ~LOOKUP_FOLLOW;
|
|
|
+ if (flags & AT_NO_AUTOMOUNT)
|
|
|
+ lookup_flags &= ~LOOKUP_AUTOMOUNT;
|
|
|
+ if (flags & AT_EMPTY_PATH)
|
|
|
lookup_flags |= LOOKUP_EMPTY;
|
|
|
+
|
|
|
retry:
|
|
|
error = user_path_at(dfd, filename, lookup_flags, &path);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
|
|
|
- error = vfs_getattr(&path, stat);
|
|
|
+ error = vfs_getattr(&path, stat, request_mask, flags);
|
|
|
path_put(&path);
|
|
|
if (retry_estale(error, lookup_flags)) {
|
|
|
lookup_flags |= LOOKUP_REVAL;
|
|
@@ -116,19 +191,7 @@ retry:
|
|
|
out:
|
|
|
return error;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(vfs_fstatat);
|
|
|
-
|
|
|
-int vfs_stat(const char __user *name, struct kstat *stat)
|
|
|
-{
|
|
|
- return vfs_fstatat(AT_FDCWD, name, stat, 0);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(vfs_stat);
|
|
|
-
|
|
|
-int vfs_lstat(const char __user *name, struct kstat *stat)
|
|
|
-{
|
|
|
- return vfs_fstatat(AT_FDCWD, name, stat, AT_SYMLINK_NOFOLLOW);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(vfs_lstat);
|
|
|
+EXPORT_SYMBOL(vfs_statx);
|
|
|
|
|
|
|
|
|
#ifdef __ARCH_WANT_OLD_STAT
|
|
@@ -141,7 +204,7 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
|
|
|
{
|
|
|
static int warncount = 5;
|
|
|
struct __old_kernel_stat tmp;
|
|
|
-
|
|
|
+
|
|
|
if (warncount > 0) {
|
|
|
warncount--;
|
|
|
printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n",
|
|
@@ -166,7 +229,7 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta
|
|
|
#if BITS_PER_LONG == 32
|
|
|
if (stat->size > MAX_NON_LFS)
|
|
|
return -EOVERFLOW;
|
|
|
-#endif
|
|
|
+#endif
|
|
|
tmp.st_size = stat->size;
|
|
|
tmp.st_atime = stat->atime.tv_sec;
|
|
|
tmp.st_mtime = stat->mtime.tv_sec;
|
|
@@ -445,6 +508,81 @@ 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)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * sys_statx - System call to get enhanced stats
|
|
|
+ * @dfd: Base directory to pathwalk from *or* fd to stat.
|
|
|
+ * @filename: File to stat *or* NULL.
|
|
|
+ * @flags: AT_* flags to control pathwalk.
|
|
|
+ * @mask: Parts of statx struct actually required.
|
|
|
+ * @buffer: Result buffer.
|
|
|
+ *
|
|
|
+ * Note that if filename is NULL, then it does the equivalent of fstat() using
|
|
|
+ * dfd to indicate the file of interest.
|
|
|
+ */
|
|
|
+SYSCALL_DEFINE5(statx,
|
|
|
+ int, dfd, const char __user *, filename, unsigned, flags,
|
|
|
+ unsigned int, mask,
|
|
|
+ struct statx __user *, buffer)
|
|
|
+{
|
|
|
+ struct kstat stat;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ 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);
|
|
|
+ else
|
|
|
+ error = vfs_statx_fd(dfd, &stat, mask, flags);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ return statx_set_result(&stat, buffer);
|
|
|
+}
|
|
|
+
|
|
|
/* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
|
|
|
void __inode_add_bytes(struct inode *inode, loff_t bytes)
|
|
|
{
|