Merge branch 'parisc-4.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
[sfrench/cifs-2.6.git] / fs / stat.c
index 3f14d1ef086805ee56590d32f678c5099131ef5a..fa0be59340cc9136eb8c508434e10289acc8fc97 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
 #include <linux/fs.h>
 #include <linux/namei.h>
 #include <linux/security.h>
+#include <linux/cred.h>
 #include <linux/syscalls.h>
 #include <linux/pagemap.h>
 
 #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 +43,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 +192,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 +205,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 +230,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 +509,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)
 {