Re: [PATCH] Add a pair of system calls to make extended file stats available [ver #3]

From: Michael Kerrisk
Date: Sun Jul 04 2010 - 00:33:41 EST


hi David,

On Fri, Jul 2, 2010 at 7:36 AM, Michael Kerrisk <mtk.manpages@xxxxxxxxx> wrote:
> Hi David,
>
> [Please CC linux-api@ on patches that change the API/ABI]
>
> On Thu, Jul 1, 2010 at 1:36 AM, David Howells <dhowells@xxxxxxxxxx> wrote:
>> Add a pair of system calls to make extended file stats available, including
>> file creation time, inode version and data version where available through the
>> underlying filesystem.
>
> Just some random thoughts here. I've not tried to guess the overhead
> of these ideas...
>
> * Include information from the "inode_info" structure, most notably
> i_flags, but perhaps other info as well.

I see you put a patch for the above for comment. Thanks.

> * Return a bit mask indicating the presence of additional information
> associated with the i-node. Here, I am thinking of flags that indicate
> that the file has any of the following: capabilities, an ACL, and
> extended attributes (obviously a superset of the previous). I could
> imagine some apps that, having got the xstat info, would be interested
> to obtain some of this other info.

What did you think about the above idea?

Cheers,

Michael


> Obviously, the above only make sense if the overhead of providing the
> extra information is low.
>
>> [This depends on the previously posted pair of patches to (a) constify a number
>>  of syscall string and buffer arguments and (b) rearrange AFS's use of
>>  i_version and i_generation].
>>
>> The following structures are defined for their use:
>>
>>        struct xstat_parameters {
>>                unsigned long long      request_mask;
>>        };
>>
>>        struct xstat_dev {
>>                unsigned int            major, minor;
>>        };
>>
>>        struct xstat_time {
>>                unsigned long long      tv_sec, tv_nsec;
>>        };
>>
>>        struct xstat {
>>                unsigned int            st_mode;
>>                unsigned int            st_nlink;
>>                unsigned int            st_uid;
>>                unsigned int            st_gid;
>>                struct xstat_dev        st_rdev;
>>                struct xstat_dev        st_dev;
>>                struct xstat_time       st_atime;
>>                struct xstat_time       st_mtime;
>>                struct xstat_time       st_ctime;
>>                struct xstat_time       st_btime;
>>                unsigned long long      st_ino;
>>                unsigned long long      st_size;
>>                unsigned long long      st_blksize;
>>                unsigned long long      st_blocks;
>>                unsigned long long      st_gen;
>>                unsigned long long      st_data_version;
>>                unsigned long long      st_result_mask;
>>                unsigned long long      st_extra_results[0];
>>        };
>>
>> where st_btime is the file creation time, st_gen is the inode generation
>> (i_generation), st_data_version is the data version number (i_version),
>> request_mask and st_result_mask are bitmasks of data desired/provided and
>> st_extra_results[] is where as-yet undefined fields are appended.
>>
>> The defined bits in request_mask and st_result_mask are:
>>
>>        XSTAT_REQUEST_MODE              Want/got st_mode
>>        XSTAT_REQUEST_NLINK             Want/got st_nlink
>>        XSTAT_REQUEST_UID               Want/got st_uid
>>        XSTAT_REQUEST_GID               Want/got st_gid
>>        XSTAT_REQUEST_RDEV              Want/got st_rdev
>>        XSTAT_REQUEST_ATIME             Want/got st_atime
>>        XSTAT_REQUEST_MTIME             Want/got st_mtime
>>        XSTAT_REQUEST_CTIME             Want/got st_ctime
>>        XSTAT_REQUEST_INO               Want/got st_ino
>>        XSTAT_REQUEST_SIZE              Want/got st_size
>>        XSTAT_REQUEST_BLOCKS            Want/got st_blocks
>>        XSTAT_REQUEST__BASIC_STATS      The stuff in the normal stat struct
>>        XSTAT_REQUEST_BTIME             Want/got st_btime
>>        XSTAT_REQUEST_GEN               Want/got st_gen
>>        XSTAT_REQUEST_DATA_VERSION      Want/got st_data_version
>>        XSTAT_REQUEST__EXTENDED_STATS   The stuff in the xstat struct
>>        XSTAT_REQUEST__ALL_STATS        The defined set of requestables
>>
>> The system calls are:
>>
>>        ssize_t ret = xstat(int dfd,
>>                            const char *filename,
>>                            unsigned flags,
>>                            const struct xstat_parameters *params,
>>                            struct xstat *buffer,
>>                            size_t buflen);
>>
>>        ssize_t ret = fxstat(unsigned fd,
>>                             unsigned flags,
>>                             const struct xstat_parameters *params,
>>                             struct xstat *buffer,
>>                             size_t buflen);
>>
>>
>> The dfd, filename, flags and fd parameters indicate the file to query.  There
>> is no equivalent of lstat() as that can be emulated with xstat() by passing
>> AT_SYMLINK_NOFOLLOW in flags.
>>
>> AT_FORCE_ATTR_SYNC can also be set in flags.  This will require a network
>> filesystem to synchronise its attributes with the server.
>>
>> When the system call is executed, the request_mask bitmask is read from the
>> parameter block to work out what the user is requesting.  If params is NULL,
>> then request_mask will be assumed to be XSTAT_REQUEST__GET_ANYWAY.
>>
>> The request_mask should be set by the caller to specify extra results that the
>> caller may desire.  These come in a number of classes:
>>
>>  (0) dev, blksize.
>>
>>     These are local data and are always available.
>>
>>  (1) mode, nlinks, uid, gid, [amc]time, ino, size, blocks.
>>
>>     These will be returned whether the caller asks for them or not.  The
>>     corresponding bits in result_mask will be set to indicate their presence.
>>
>>     If the caller didn't ask for them, then they may be approximated.  For
>>     example, NFS won't waste any time updating them from the server, unless as
>>     a byproduct of updating something requested.
>>
>>  (2) rdev.
>>
>>     As for class (1), but this won't be returned if the file is not a blockdev
>>     or chardev.  The bit will be cleared if the value is not returned.
>>
>>  (3) File creation time, inode generation and data version.
>>
>>     These will be returned if available whether the caller asked for them or
>>     not.  The corresponding bits in result_mask will be set or cleared as
>>     appropriate to indicate their presence.
>>
>>     If the caller didn't ask for them, then they may be approximated.  For
>>     example, NFS won't waste any time updating them from the server, unless
>>     as a byproduct of updating something requested.
>>
>>  (4) Extra results.
>>
>>     These will only be returned if the caller asked for them by setting their
>>     bits in request_mask.  They will be placed in the buffer after the xstat
>>     struct in ascending result_mask bit order.  Any bit set in request_mask
>>     mask will be left set in result_mask if the result is available and
>>     cleared otherwise.
>>
>>     The pointer into the results list will be rounded up to the nearest 8-byte
>>     boundary after each result is written in.  The size of each extra result
>>     is specific to the definition for that result.
>>
>>     No extra results are currently defined.
>>
>> If the buffer is insufficiently big, the syscall returns the amount of space it
>> will need to write the complete result set and returns a partial result in the
>> buffer.
>>
>> At the moment, this will only work on x86_64 as it requires system calls to be
>> wired up.
>>
>>
>> ===========
>> FILESYSTEMS
>> ===========
>>
>> The following filesystems have been modified to make use of this facility:
>>
>>  (*) Ext4.  This will return the creation time and inode version number for all
>>     files.  It will, however, only return the data version number for
>>     directories unless the I_VERSION option is set on the filesystem.
>>
>>  (*) AFS.  This will return the vnode ID uniquifier as the inode version and
>>     the AFS data version number as the data version.  There is no file
>>     creation time available.
>>
>>     AFS should go to the server if AT_FORCE_ATTR_SYNC is specified.
>>
>>  (*) NFS.  This will return the change attribute if NFSv4 only.  No other extra
>>     values are returned at this time.
>>
>>     If AT_FORCE_ATTR_SYNC is set or mtime, ctime or data_version (NFSv4 only)
>>     are asked for then the outstanding writes will be written to the server
>>     first.
>>
>>     If AT_FORCE_ATTR_SYNC is set or atime is requested then the attributes
>>     will be reread unconditionally, otherwise if any of data version (NFSv4
>>     only) XSTAT_REQUEST__BASIC_STATS are requested, then the attributes will
>>     be reread if the cached attributes have expired.
>>
>>
>> =======
>> TESTING
>> =======
>>
>> The following test program can be used to test the xstat system call:
>>
>>        #define _GNU_SOURCE
>>        #define _ATFILE_SOURCE
>>        #include <stdio.h>
>>        #include <stdlib.h>
>>        #include <string.h>
>>        #include <unistd.h>
>>        #include <fcntl.h>
>>        #include <time.h>
>>        #include <sys/syscall.h>
>>        #include <sys/stat.h>
>>        #include <sys/types.h>
>>
>>        #define AT_FORCE_ATTR_SYNC      0x800
>>
>>        struct xstat_parameters {
>>                unsigned long long      request_mask;
>>        #define XSTAT_REQUEST_MODE              0x00000001ULL
>>        #define XSTAT_REQUEST_NLINK             0x00000002ULL
>>        #define XSTAT_REQUEST_UID               0x00000004ULL
>>        #define XSTAT_REQUEST_GID               0x00000008ULL
>>        #define XSTAT_REQUEST_RDEV              0x00000010ULL
>>        #define XSTAT_REQUEST_ATIME             0x00000020ULL
>>        #define XSTAT_REQUEST_MTIME             0x00000040ULL
>>        #define XSTAT_REQUEST_CTIME             0x00000080ULL
>>        #define XSTAT_REQUEST_INO               0x00000100ULL
>>        #define XSTAT_REQUEST_SIZE              0x00000200ULL
>>        #define XSTAT_REQUEST_BLOCKS            0x00000400ULL
>>        #define XSTAT_REQUEST__BASIC_STATS      0x000007ffULL
>>        #define XSTAT_REQUEST_BTIME             0x00000800ULL
>>        #define XSTAT_REQUEST_GEN               0x00001000ULL
>>        #define XSTAT_REQUEST_DATA_VERSION      0x00002000ULL
>>        #define XSTAT_REQUEST__EXTENDED_STATS   0x00003fffULL
>>        #define XSTAT_REQUEST__ALL_STATS        0x00003fffULL
>>        };
>>
>>        struct xstat_dev {
>>                unsigned int    major;
>>                unsigned int    minor;
>>        };
>>
>>        struct xstat_time {
>>                unsigned long long      tv_sec;
>>                unsigned long long      tv_nsec;
>>        };
>>
>>        struct xstat {
>>                unsigned int            st_mode;
>>                unsigned int            st_nlink;
>>                unsigned int            st_uid;
>>                unsigned int            st_gid;
>>                struct xstat_dev        st_rdev;
>>                struct xstat_dev        st_dev;
>>                struct xstat_time       st_atim;
>>                struct xstat_time       st_mtim;
>>                struct xstat_time       st_ctim;
>>                struct xstat_time       st_btim;
>>                unsigned long long      st_ino;
>>                unsigned long long      st_size;
>>                unsigned long long      st_blksize;
>>                unsigned long long      st_blocks;
>>                unsigned long long      st_gen;
>>                unsigned long long      st_data_version;
>>                unsigned long long      st_result_mask;
>>                unsigned long long      st_extra_results[0];
>>        };
>>
>>        #define __NR_xstat                              300
>>        #define __NR_fxstat                             301
>>
>>        static __attribute__((unused))
>>        ssize_t xstat(int dfd, const char *filename, unsigned flags,
>>                      struct xstat_parameters *params,
>>                      struct xstat *buffer, size_t bufsize)
>>        {
>>                return syscall(__NR_xstat, dfd, filename, flags,
>>                               params, buffer, bufsize);
>>        }
>>
>>        static __attribute__((unused))
>>        ssize_t fxstat(int fd, unsigned flags,
>>                       struct xstat_parameters *params,
>>                       struct xstat *buffer, size_t bufsize)
>>        {
>>                return syscall(__NR_fxstat, fd, flags,
>>                               params, buffer, bufsize);
>>        }
>>
>>        static void print_time(const char *field, const struct xstat_time *xstm)
>>        {
>>                struct tm tm;
>>                time_t tim;
>>                char buffer[100];
>>                int len;
>>
>>                tim = xstm->tv_sec;
>>                if (!localtime_r(&tim, &tm)) {
>>                        perror("localtime_r");
>>                        exit(1);
>>                }
>>                len = strftime(buffer, 100, "%F %T", &tm);
>>                if (len == 0) {
>>                        perror("strftime");
>>                        exit(1);
>>                }
>>                printf("%s", field);
>>                fwrite(buffer, 1, len, stdout);
>>                printf(".%09llu", xstm->tv_nsec);
>>                len = strftime(buffer, 100, "%z", &tm);
>>                if (len == 0) {
>>                        perror("strftime2");
>>                        exit(1);
>>                }
>>                fwrite(buffer, 1, len, stdout);
>>                printf("\n");
>>        }
>>
>>        static void dump_xstat(struct xstat *xst)
>>        {
>>                char buffer[256], ft;
>>
>>                printf("results=%llx\n", xst->st_result_mask);
>>
>>                printf(" ");
>>                if (xst->st_result_mask & XSTAT_REQUEST_SIZE)
>>                        printf(" Size: %-15llu", xst->st_size);
>>                if (xst->st_result_mask & XSTAT_REQUEST_BLOCKS)
>>                        printf(" Blocks: %-10llu", xst->st_blocks);
>>                printf(" IO Block: %-6llu ", xst->st_blksize);
>>                if (xst->st_result_mask & XSTAT_REQUEST_MODE) {
>>                        switch (xst->st_mode & S_IFMT) {
>>                        case S_IFIFO:   printf(" FIFO\n");                      ft = 'p'; break;
>>                        case S_IFCHR:   printf(" character special file\n");    ft = 'c'; break;
>>                        case S_IFDIR:   printf(" directory\n");                 ft = 'd'; break;
>>                        case S_IFBLK:   printf(" block special file\n");        ft = 'b'; break;
>>                        case S_IFREG:   printf(" regular file\n");              ft = '-'; break;
>>                        case S_IFLNK:   printf(" symbolic link\n");             ft = 'l'; break;
>>                        case S_IFSOCK:  printf(" socket\n");                    ft = 's'; break;
>>                        default:
>>                                printf("unknown type (%o)\n", xst->st_mode & S_IFMT);
>>                                ft = '?';
>>                                break;
>>                        }
>>                }
>>
>>                sprintf(buffer, "%02x:%02x", xst->st_dev.major, xst->st_dev.minor);
>>                printf("Device: %-15s", buffer);
>>                if (xst->st_result_mask & XSTAT_REQUEST_INO)
>>                        printf(" Inode: %-11llu", xst->st_ino);
>>                if (xst->st_result_mask & XSTAT_REQUEST_SIZE)
>>                        printf(" Links: %-5u", xst->st_nlink);
>>                if (xst->st_result_mask & XSTAT_REQUEST_RDEV)
>>                        printf(" Device type: %u,%u",
>>                               xst->st_rdev.major, xst->st_rdev.minor);
>>                printf("\n");
>>
>>                if (xst->st_result_mask & XSTAT_REQUEST_MODE)
>>                        printf("Access: (%04o/%c%c%c%c%c%c%c%c%c%c)  ",
>>                               xst->st_mode & 07777,
>>                               ft,
>>                               xst->st_mode & S_IRUSR ? 'r' : '-',
>>                               xst->st_mode & S_IWUSR ? 'w' : '-',
>>                               xst->st_mode & S_IXUSR ? 'x' : '-',
>>                               xst->st_mode & S_IRGRP ? 'r' : '-',
>>                               xst->st_mode & S_IWGRP ? 'w' : '-',
>>                               xst->st_mode & S_IXGRP ? 'x' : '-',
>>                               xst->st_mode & S_IROTH ? 'r' : '-',
>>                               xst->st_mode & S_IWOTH ? 'w' : '-',
>>                               xst->st_mode & S_IXOTH ? 'x' : '-');
>>                if (xst->st_result_mask & XSTAT_REQUEST_UID)
>>                        printf("Uid: %d   \n", xst->st_uid);
>>                if (xst->st_result_mask & XSTAT_REQUEST_GID)
>>                        printf("Gid: %u\n", xst->st_gid);
>>
>>                if (xst->st_result_mask & XSTAT_REQUEST_ATIME)
>>                        print_time("Access: ", &xst->st_atim);
>>                if (xst->st_result_mask & XSTAT_REQUEST_MTIME)
>>                        print_time("Modify: ", &xst->st_mtim);
>>                if (xst->st_result_mask & XSTAT_REQUEST_CTIME)
>>                        print_time("Change: ", &xst->st_ctim);
>>                if (xst->st_result_mask & XSTAT_REQUEST_BTIME)
>>                        print_time("Create: ", &xst->st_btim);
>>
>>                if (xst->st_result_mask & XSTAT_REQUEST_GEN)
>>                        printf("Inode version: %llxh\n", xst->st_gen);
>>                if (xst->st_result_mask & XSTAT_REQUEST_DATA_VERSION)
>>                        printf("Data version: %llxh\n", xst->st_data_version);
>>        }
>>
>>        int main(int argc, char **argv)
>>        {
>>                struct xstat_parameters params;
>>                struct xstat xst;
>>                int ret, atflag = AT_SYMLINK_NOFOLLOW;
>>
>>                unsigned long long query =
>>                        XSTAT_REQUEST__BASIC_STATS |
>>                        XSTAT_REQUEST_BTIME |
>>                        XSTAT_REQUEST_GEN |
>>                        XSTAT_REQUEST_DATA_VERSION;
>>
>>                for (argv++; *argv; argv++) {
>>                        if (strcmp(*argv, "-F") == 0) {
>>                                atflag |= AT_FORCE_ATTR_SYNC;
>>                                continue;
>>                        }
>>                        if (strcmp(*argv, "-L") == 0) {
>>                                atflag &= ~AT_SYMLINK_NOFOLLOW;
>>                                continue;
>>                        }
>>                        if (strcmp(*argv, "-O") == 0) {
>>                                query &= ~XSTAT_REQUEST__BASIC_STATS;
>>                                continue;
>>                        }
>>
>>                        memset(&xst, 0xbf, sizeof(xst));
>>                        params.request_mask = query;
>>                        ret = xstat(AT_FDCWD, *argv, atflag, &params, &xst, sizeof(xst));
>>                        printf("xstat(%s) = %d\n", *argv, ret);
>>                        if (ret < 0) {
>>                                perror(*argv);
>>                                exit(1);
>>                        }
>>
>>                        dump_xstat(&xst);
>>                }
>>                return 0;
>>        }
>>
>> Just compile and run, passing it paths to the files you want to examine:
>>
>>        [root@andromeda ~]# /tmp/xstat -O /dev/tty
>>        xstat(/dev/tty) = 152
>>        results=7ff
>>          Size: 0               Blocks: 0          IO Block: 4096    character special file
>>        Device: 00:0f           Inode: 246         Links: 1     Device type: 5,0
>>        Access: (0666/crw-rw-rw-)  Uid: 0
>>        Gid: 5
>>        Access: 2010-06-30 16:25:01.813517001+0100
>>        Modify: 2010-06-30 16:25:01.813517001+0100
>>        Change: 2010-06-30 16:25:01.813517001+0100
>>
>>        [root@andromeda ~]# /tmp/xstat /var/cache/fscache/cache/
>>        xstat(/var/cache/fscache/cache/) = 152
>>        results=3fef
>>          Size: 4096            Blocks: 16         IO Block: 4096    directory
>>        Device: 08:06           Inode: 130561      Links: 3
>>        Access: (0700/drwx------)  Uid: 0
>>        Gid: 0
>>        Access: 2010-06-29 18:16:33.680703545+0100
>>        Modify: 2010-06-29 18:16:20.132786632+0100
>>        Change: 2010-06-29 18:16:20.132786632+0100
>>        Create: 2010-06-25 15:17:39.471199293+0100
>>        Inode version: f585ab70h
>>        Data version: 2h
>>
>> Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
>> ---
>>
>>  arch/x86/include/asm/unistd_32.h |    4 +
>>  arch/x86/include/asm/unistd_64.h |    4 +
>>  fs/afs/inode.c                   |   11 +-
>>  fs/ecryptfs/inode.c              |    2
>>  fs/ext4/ext4.h                   |    2
>>  fs/ext4/file.c                   |    2
>>  fs/ext4/inode.c                  |   27 +++++-
>>  fs/ext4/namei.c                  |    2
>>  fs/ext4/symlink.c                |    2
>>  fs/nfs/inode.c                   |   46 +++++++---
>>  fs/nfsd/nfs3proc.c               |    2
>>  fs/nfsd/nfs3xdr.c                |    4 +
>>  fs/nfsd/nfs4xdr.c                |    4 +
>>  fs/nfsd/nfsproc.c                |    6 +
>>  fs/nfsd/nfsxdr.c                 |    2
>>  fs/stat.c                        |  175 ++++++++++++++++++++++++++++++++++----
>>  include/linux/fcntl.h            |    1
>>  include/linux/fs.h               |    2
>>  include/linux/stat.h             |  103 ++++++++++++++++++++++
>>  include/linux/syscalls.h         |    9 ++
>>  20 files changed, 368 insertions(+), 42 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h
>> index beb9b5f..a9953cc 100644
>> --- a/arch/x86/include/asm/unistd_32.h
>> +++ b/arch/x86/include/asm/unistd_32.h
>> @@ -343,10 +343,12 @@
>>  #define __NR_rt_tgsigqueueinfo 335
>>  #define __NR_perf_event_open   336
>>  #define __NR_recvmmsg          337
>> +#define __NR_xstat             338
>> +#define __NR_fxstat            339
>>
>>  #ifdef __KERNEL__
>>
>> -#define NR_syscalls 338
>> +#define NR_syscalls 340
>>
>>  #define __ARCH_WANT_IPC_PARSE_VERSION
>>  #define __ARCH_WANT_OLD_READDIR
>> diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h
>> index ff4307b..c90d240 100644
>> --- a/arch/x86/include/asm/unistd_64.h
>> +++ b/arch/x86/include/asm/unistd_64.h
>> @@ -663,6 +663,10 @@ __SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo)
>>  __SYSCALL(__NR_perf_event_open, sys_perf_event_open)
>>  #define __NR_recvmmsg                          299
>>  __SYSCALL(__NR_recvmmsg, sys_recvmmsg)
>> +#define __NR_xstat                             300
>> +__SYSCALL(__NR_xstat, sys_xstat)
>> +#define __NR_fxstat                            301
>> +__SYSCALL(__NR_fxstat, sys_fxstat)
>>
>>  #ifndef __NO_STUBS
>>  #define __ARCH_WANT_OLD_READDIR
>> diff --git a/fs/afs/inode.c b/fs/afs/inode.c
>> index ee3190a..f624c5a 100644
>> --- a/fs/afs/inode.c
>> +++ b/fs/afs/inode.c
>> @@ -300,16 +300,17 @@ error_unlock:
>>  /*
>>  * read the attributes of an inode
>>  */
>> -int afs_getattr(struct vfsmount *mnt, struct dentry *dentry,
>> -                     struct kstat *stat)
>> +int afs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
>>  {
>> -       struct inode *inode;
>> -
>> -       inode = dentry->d_inode;
>> +       struct inode *inode = dentry->d_inode;
>>
>>        _enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
>>
>>        generic_fillattr(inode, stat);
>> +
>> +       stat->result_mask |= XSTAT_REQUEST_GEN | XSTAT_REQUEST_DATA_VERSION;
>> +       stat->gen = inode->i_generation;
>> +       stat->data_version = inode->i_version;
>>        return 0;
>>  }
>>
>> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
>> index 31ef525..0b02272 100644
>> --- a/fs/ecryptfs/inode.c
>> +++ b/fs/ecryptfs/inode.c
>> @@ -994,6 +994,8 @@ int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
>>        struct kstat lower_stat;
>>        int rc;
>>
>> +       lower_stat.query_flags = stat->query_flags;
>> +       lower_stat.request_mask = stat->request_mask | XSTAT_REQUEST_BLOCKS;
>>        rc = vfs_getattr(ecryptfs_dentry_to_lower_mnt(dentry),
>>                         ecryptfs_dentry_to_lower(dentry), &lower_stat);
>>        if (!rc) {
>> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
>> index 19a4de5..96823f3 100644
>> --- a/fs/ext4/ext4.h
>> +++ b/fs/ext4/ext4.h
>> @@ -1571,6 +1571,8 @@ extern int  ext4_write_inode(struct inode *, struct writeback_control *);
>>  extern int  ext4_setattr(struct dentry *, struct iattr *);
>>  extern int  ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
>>                                struct kstat *stat);
>> +extern int  ext4_file_getattr(struct vfsmount *mnt, struct dentry *dentry,
>> +                               struct kstat *stat);
>>  extern void ext4_delete_inode(struct inode *);
>>  extern int  ext4_sync_inode(handle_t *, struct inode *);
>>  extern void ext4_dirty_inode(struct inode *);
>> diff --git a/fs/ext4/file.c b/fs/ext4/file.c
>> index 5313ae4..18c29ab 100644
>> --- a/fs/ext4/file.c
>> +++ b/fs/ext4/file.c
>> @@ -150,7 +150,7 @@ const struct file_operations ext4_file_operations = {
>>  const struct inode_operations ext4_file_inode_operations = {
>>        .truncate       = ext4_truncate,
>>        .setattr        = ext4_setattr,
>> -       .getattr        = ext4_getattr,
>> +       .getattr        = ext4_file_getattr,
>>  #ifdef CONFIG_EXT4_FS_XATTR
>>        .setxattr       = generic_setxattr,
>>        .getxattr       = generic_getxattr,
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index 42272d6..f9a730a 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
>> @@ -5550,12 +5550,33 @@ err_out:
>>  int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
>>                 struct kstat *stat)
>>  {
>> -       struct inode *inode;
>> -       unsigned long delalloc_blocks;
>> +       struct inode *inode = dentry->d_inode;
>>
>> -       inode = dentry->d_inode;
>>        generic_fillattr(inode, stat);
>>
>> +       stat->result_mask |= XSTAT_REQUEST_BTIME;
>> +       stat->btime.tv_sec = EXT4_I(inode)->i_crtime.tv_sec;
>> +       stat->btime.tv_nsec = EXT4_I(inode)->i_crtime.tv_nsec;
>> +
>> +       if (inode->i_ino != EXT4_ROOT_INO) {
>> +               stat->result_mask |= XSTAT_REQUEST_GEN;
>> +               stat->gen = inode->i_generation;
>> +       }
>> +       if (S_ISDIR(inode->i_mode) || test_opt(inode->i_sb, I_VERSION)) {
>> +               stat->result_mask |= XSTAT_REQUEST_DATA_VERSION;
>> +               stat->data_version = inode->i_version;
>> +       }
>> +       return 0;
>> +}
>> +
>> +int ext4_file_getattr(struct vfsmount *mnt, struct dentry *dentry,
>> +                     struct kstat *stat)
>> +{
>> +       struct inode *inode = dentry->d_inode;
>> +       unsigned long delalloc_blocks;
>> +
>> +       ext4_getattr(mnt, dentry, stat);
>> +
>>        /*
>>         * We can't update i_blocks if the block allocation is delayed
>>         * otherwise in the case of system crash before the real block
>> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
>> index a43e661..0f776c7 100644
>> --- a/fs/ext4/namei.c
>> +++ b/fs/ext4/namei.c
>> @@ -2542,6 +2542,7 @@ const struct inode_operations ext4_dir_inode_operations = {
>>        .mknod          = ext4_mknod,
>>        .rename         = ext4_rename,
>>        .setattr        = ext4_setattr,
>> +       .getattr        = ext4_getattr,
>>  #ifdef CONFIG_EXT4_FS_XATTR
>>        .setxattr       = generic_setxattr,
>>        .getxattr       = generic_getxattr,
>> @@ -2554,6 +2555,7 @@ const struct inode_operations ext4_dir_inode_operations = {
>>
>>  const struct inode_operations ext4_special_inode_operations = {
>>        .setattr        = ext4_setattr,
>> +       .getattr        = ext4_getattr,
>>  #ifdef CONFIG_EXT4_FS_XATTR
>>        .setxattr       = generic_setxattr,
>>        .getxattr       = generic_getxattr,
>> diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
>> index ed9354a..d8fe7fb 100644
>> --- a/fs/ext4/symlink.c
>> +++ b/fs/ext4/symlink.c
>> @@ -35,6 +35,7 @@ const struct inode_operations ext4_symlink_inode_operations = {
>>        .follow_link    = page_follow_link_light,
>>        .put_link       = page_put_link,
>>        .setattr        = ext4_setattr,
>> +       .getattr        = ext4_getattr,
>>  #ifdef CONFIG_EXT4_FS_XATTR
>>        .setxattr       = generic_setxattr,
>>        .getxattr       = generic_getxattr,
>> @@ -47,6 +48,7 @@ const struct inode_operations ext4_fast_symlink_inode_operations = {
>>        .readlink       = generic_readlink,
>>        .follow_link    = ext4_follow_link,
>>        .setattr        = ext4_setattr,
>> +       .getattr        = ext4_getattr,
>>  #ifdef CONFIG_EXT4_FS_XATTR
>>        .setxattr       = generic_setxattr,
>>        .getxattr       = generic_getxattr,
>> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
>> index 099b351..8c6de96 100644
>> --- a/fs/nfs/inode.c
>> +++ b/fs/nfs/inode.c
>> @@ -495,11 +495,21 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
>>  int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
>>  {
>>        struct inode *inode = dentry->d_inode;
>> +       unsigned force = stat->query_flags & AT_FORCE_ATTR_SYNC;
>>        int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
>>        int err;
>>
>> -       /* Flush out writes to the server in order to update c/mtime.  */
>> -       if (S_ISREG(inode->i_mode)) {
>> +       if (NFS_SERVER(inode)->nfs_client->rpc_ops->version < 4)
>> +               stat->request_mask &= ~XSTAT_REQUEST_DATA_VERSION;
>> +
>> +       /* Flush out writes to the server in order to update c/mtime
>> +        * or data version if the user wants them */
>> +       if ((force || stat->request_mask & (XSTAT_REQUEST_MTIME |
>> +                                           XSTAT_REQUEST_CTIME |
>> +                                           XSTAT_REQUEST_DATA_VERSION
>> +                                           )) &&
>> +           S_ISREG(inode->i_mode)
>> +           ) {
>>                err = filemap_write_and_wait(inode->i_mapping);
>>                if (err)
>>                        goto out;
>> @@ -514,18 +524,30 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
>>         *  - NFS never sets MS_NOATIME or MS_NODIRATIME so there is
>>         *    no point in checking those.
>>         */
>> -       if ((mnt->mnt_flags & MNT_NOATIME) ||
>> -           ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
>> +       if (!(stat->request_mask & XSTAT_REQUEST_ATIME) ||
>> +           (mnt->mnt_flags & MNT_NOATIME) ||
>> +           ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
>>                need_atime = 0;
>>
>> -       if (need_atime)
>> -               err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
>> -       else
>> -               err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
>> -       if (!err) {
>> -               generic_fillattr(inode, stat);
>> -               stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
>> +       if (force || stat->request_mask & (XSTAT_REQUEST__BASIC_STATS |
>> +                                          XSTAT_REQUEST_DATA_VERSION)
>> +           ) {
>> +               if (force || need_atime)
>> +                       err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
>> +               else
>> +                       err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
>> +               if (err)
>> +                       goto out;
>>        }
>> +
>> +       generic_fillattr(inode, stat);
>> +       stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
>> +
>> +       if (stat->request_mask & XSTAT_REQUEST_DATA_VERSION) {
>> +               stat->data_version = NFS_I(inode)->change_attr;
>> +               stat->result_mask |= XSTAT_REQUEST_DATA_VERSION;
>> +       }
>> +
>>  out:
>>        return err;
>>  }
>> @@ -770,7 +792,7 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
>>  static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
>>  {
>>        struct nfs_inode *nfsi = NFS_I(inode);
>> -
>> +
>>        if (mapping->nrpages != 0) {
>>                int ret = invalidate_inode_pages2(mapping);
>>                if (ret < 0)
>> diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
>> index 3d68f45..310ff05 100644
>> --- a/fs/nfsd/nfs3proc.c
>> +++ b/fs/nfsd/nfs3proc.c
>> @@ -55,6 +55,8 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
>>        if (nfserr)
>>                RETURN_STATUS(nfserr);
>>
>> +       resp->stat.query_flags = 0;
>> +       resp->stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        err = vfs_getattr(resp->fh.fh_export->ex_path.mnt,
>>                          resp->fh.fh_dentry, &resp->stat);
>>        nfserr = nfserrno(err);
>> diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
>> index 2a533a0..eaa3c3b 100644
>> --- a/fs/nfsd/nfs3xdr.c
>> +++ b/fs/nfsd/nfs3xdr.c
>> @@ -205,6 +205,8 @@ encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
>>                int err;
>>                struct kstat stat;
>>
>> +               stat.query_flags = 0;
>> +               stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>                err = vfs_getattr(fhp->fh_export->ex_path.mnt, dentry, &stat);
>>                if (!err) {
>>                        *p++ = xdr_one;         /* attributes follow */
>> @@ -257,6 +259,8 @@ void fill_post_wcc(struct svc_fh *fhp)
>>        if (fhp->fh_post_saved)
>>                printk("nfsd: inode locked twice during operation.\n");
>>
>> +       fhp->fh_post_attr.query_flags = 0;
>> +       fhp->fh_post_attr.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry,
>>                        &fhp->fh_post_attr);
>>        fhp->fh_post_change = fhp->fh_dentry->d_inode->i_version;
>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>> index ac17a70..e9d1b59 100644
>> --- a/fs/nfsd/nfs4xdr.c
>> +++ b/fs/nfsd/nfs4xdr.c
>> @@ -1769,6 +1769,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
>>                        goto out;
>>        }
>>
>> +       stat.query_flags = 0;
>> +       stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        err = vfs_getattr(exp->ex_path.mnt, dentry, &stat);
>>        if (err)
>>                goto out_nfserr;
>> @@ -2139,6 +2141,8 @@ out_acl:
>>                                if (path.dentry != path.mnt->mnt_root)
>>                                        break;
>>                        }
>> +                       stat.query_flags = 0;
>> +                       stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>                        err = vfs_getattr(path.mnt, path.dentry, &stat);
>>                        path_put(&path);
>>                        if (err)
>> diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
>> index a047ad6..7c0e74b 100644
>> --- a/fs/nfsd/nfsproc.c
>> +++ b/fs/nfsd/nfsproc.c
>> @@ -26,6 +26,8 @@ static __be32
>>  nfsd_return_attrs(__be32 err, struct nfsd_attrstat *resp)
>>  {
>>        if (err) return err;
>> +       resp->stat.query_flags = 0;
>> +       resp->stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        return nfserrno(vfs_getattr(resp->fh.fh_export->ex_path.mnt,
>>                                    resp->fh.fh_dentry,
>>                                    &resp->stat));
>> @@ -34,6 +36,8 @@ static __be32
>>  nfsd_return_dirop(__be32 err, struct nfsd_diropres *resp)
>>  {
>>        if (err) return err;
>> +       resp->stat.query_flags = 0;
>> +       resp->stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        return nfserrno(vfs_getattr(resp->fh.fh_export->ex_path.mnt,
>>                                    resp->fh.fh_dentry,
>>                                    &resp->stat));
>> @@ -150,6 +154,8 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
>>                                  &resp->count);
>>
>>        if (nfserr) return nfserr;
>> +       resp->stat.query_flags = 0;
>> +       resp->stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        return nfserrno(vfs_getattr(resp->fh.fh_export->ex_path.mnt,
>>                                    resp->fh.fh_dentry,
>>                                    &resp->stat));
>> diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
>> index 4ce005d..a595fb6 100644
>> --- a/fs/nfsd/nfsxdr.c
>> +++ b/fs/nfsd/nfsxdr.c
>> @@ -197,6 +197,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
>>  __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
>>  {
>>        struct kstat stat;
>> +       stat.query_flags = 0;
>> +       stat.request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>>        vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry, &stat);
>>        return encode_fattr(rqstp, p, fhp, &stat);
>>  }
>> diff --git a/fs/stat.c b/fs/stat.c
>> index 12e90e2..2fb1527 100644
>> --- a/fs/stat.c
>> +++ b/fs/stat.c
>> @@ -33,6 +33,9 @@ void generic_fillattr(struct inode *inode, struct kstat *stat)
>>        stat->size = i_size_read(inode);
>>        stat->blocks = inode->i_blocks;
>>        stat->blksize = (1 << inode->i_blkbits);
>> +       stat->result_mask |= XSTAT_REQUEST__BASIC_STATS & ~XSTAT_REQUEST_RDEV;
>> +       if (unlikely(S_ISBLK(stat->mode) || S_ISCHR(stat->mode)))
>> +               stat->result_mask |= XSTAT_REQUEST_RDEV;
>>  }
>>
>>  EXPORT_SYMBOL(generic_fillattr);
>> @@ -42,6 +45,8 @@ int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
>>        struct inode *inode = dentry->d_inode;
>>        int retval;
>>
>> +       stat->result_mask = 0;
>> +
>>        retval = security_inode_getattr(mnt, dentry);
>>        if (retval)
>>                return retval;
>> @@ -55,41 +60,64 @@ int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
>>
>>  EXPORT_SYMBOL(vfs_getattr);
>>
>> -int vfs_fstat(unsigned int fd, struct kstat *stat)
>> +/*
>> + * VFS entrypoint to get extended stats by file descriptor
>> + */
>> +int vfs_fxstat(unsigned int fd, int flags, struct kstat *stat)
>>  {
>>        struct file *f = fget(fd);
>>        int error = -EBADF;
>>
>> +       if (flags & ~KSTAT_QUERY_FLAGS)
>> +               return -EINVAL;
>> +       stat->query_flags = flags;
>> +
>>        if (f) {
>>                error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat);
>>                fput(f);
>>        }
>>        return error;
>>  }
>> +EXPORT_SYMBOL(vfs_fxstat);
>> +
>> +int vfs_fstat(unsigned int fd, struct kstat *stat)
>> +{
>> +       stat->request_mask = XSTAT_REQUEST__BASIC_STATS;
>> +       return vfs_fxstat(fd, 0, stat);
>> +}
>>  EXPORT_SYMBOL(vfs_fstat);
>>
>> -int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
>> -               int flag)
>> +/*
>> + * VFS entrypoint to get extended stats by filename
>> + */
>> +int vfs_xstat(int dfd, const char __user *filename, int flags,
>> +             struct kstat *stat)
>>  {
>>        struct path path;
>> -       int error = -EINVAL;
>> -       int lookup_flags = 0;
>> +       int error, lookup_flags;
>>
>> -       if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
>> -               goto out;
>> +       if (flags & ~(AT_SYMLINK_NOFOLLOW | KSTAT_QUERY_FLAGS))
>> +               return -EINVAL;
>>
>> -       if (!(flag & AT_SYMLINK_NOFOLLOW))
>> -               lookup_flags |= LOOKUP_FOLLOW;
>> +       stat->query_flags = flags;
>> +       lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
>>
>>        error = user_path_at(dfd, filename, lookup_flags, &path);
>> -       if (error)
>> -               goto out;
>> -
>> -       error = vfs_getattr(path.mnt, path.dentry, stat);
>> -       path_put(&path);
>> -out:
>> +       if (!error) {
>> +               error = vfs_getattr(path.mnt, path.dentry, stat);
>> +               path_put(&path);
>> +       }
>>        return error;
>>  }
>> +EXPORT_SYMBOL(vfs_xstat);
>> +
>> +int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
>> +               int flags)
>> +{
>> +       stat->request_mask = XSTAT_REQUEST__BASIC_STATS;
>> +       stat->query_flags = 0;
>> +       return vfs_xstat(dfd, filename, flags, stat);
>> +}
>>  EXPORT_SYMBOL(vfs_fstatat);
>>
>>  int vfs_stat(const char __user *name, struct kstat *stat)
>> @@ -115,7 +143,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",
>> @@ -140,7 +168,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;
>> @@ -222,7 +250,7 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
>>  #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;
>> @@ -408,6 +436,117 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
>>  }
>>  #endif /* __ARCH_WANT_STAT64 */
>>
>> +/*
>> + * Get the xstat parameters if supplied
>> + */
>> +static int xstat_get_params(struct xstat_parameters __user *_params,
>> +                           struct kstat *stat)
>> +{
>> +       struct xstat_parameters params;
>> +
>> +       memset(stat, 0xde, sizeof(*stat));      // DEBUGGING
>> +
>> +       if (_params) {
>> +               if (copy_from_user(&params, _params, sizeof(params)) != 0)
>> +                       return -EFAULT;
>> +               stat->request_mask =
>> +                       params.request_mask & XSTAT_REQUEST__ALL_STATS;
>> +       } else {
>> +               stat->request_mask = XSTAT_REQUEST__EXTENDED_STATS;
>> +       }
>> +       stat->result_mask = 0;
>> +       return 0;
>> +}
>> +
>> +/*
>> + * copy the extended stats to userspace and return the amount of data written
>> + * into the buffer
>> + */
>> +static long xstat_set_result(struct kstat *stat,
>> +                            struct xstat __user *buffer, size_t bufsize)
>> +{
>> +       struct xstat tmp;
>> +       size_t copy;
>> +
>> +       /* transfer the fixed results */
>> +       memset(&tmp, 0, sizeof(tmp));
>> +       tmp.st_result_mask      = stat->result_mask;
>> +       tmp.st_mode             = stat->mode;
>> +       tmp.st_nlink            = stat->nlink;
>> +       tmp.st_uid              = stat->uid;
>> +       tmp.st_gid              = stat->gid;
>> +       tmp.st_blksize          = stat->blksize;
>> +       tmp.st_rdev.major       = MAJOR(stat->rdev);
>> +       tmp.st_rdev.minor       = MINOR(stat->rdev);
>> +       tmp.st_dev.major        = MAJOR(stat->dev);
>> +       tmp.st_dev.minor        = MINOR(stat->dev);
>> +       tmp.st_atime.tv_sec     = stat->atime.tv_sec;
>> +       tmp.st_atime.tv_nsec    = stat->atime.tv_nsec;
>> +       tmp.st_mtime.tv_sec     = stat->mtime.tv_sec;
>> +       tmp.st_mtime.tv_nsec    = stat->mtime.tv_nsec;
>> +       tmp.st_ctime.tv_sec     = stat->ctime.tv_sec;
>> +       tmp.st_ctime.tv_nsec    = stat->ctime.tv_nsec;
>> +       tmp.st_ino              = stat->ino;
>> +       tmp.st_size             = stat->size;
>> +       tmp.st_blocks           = stat->blocks;
>> +
>> +       if (tmp.st_result_mask & XSTAT_REQUEST_BTIME) {
>> +               tmp.st_btime.tv_sec     = stat->btime.tv_sec;
>> +               tmp.st_btime.tv_nsec    = stat->btime.tv_nsec;
>> +       }
>> +       if (tmp.st_result_mask & XSTAT_REQUEST_GEN)
>> +               tmp.st_gen              = stat->gen;
>> +       if (tmp.st_result_mask & XSTAT_REQUEST_DATA_VERSION)
>> +               tmp.st_data_version     = stat->data_version;
>> +
>> +       copy = sizeof(tmp);
>> +       if (copy > bufsize)
>> +               copy = bufsize;
>> +       if (copy_to_user(buffer, &tmp, copy) != 0)
>> +               return -EFAULT;
>> +       return sizeof(tmp);
>> +}
>> +
>> +/*
>> + * System call to get extended stats by path
>> + */
>> +SYSCALL_DEFINE6(xstat,
>> +               int, dfd, const char __user *, filename, unsigned, atflag,
>> +               struct xstat_parameters __user *, params,
>> +               struct xstat __user *, buffer, size_t, bufsize)
>> +{
>> +       struct kstat stat;
>> +       int error;
>> +
>> +       error = xstat_get_params(params, &stat);
>> +       if (error != 0)
>> +               return error;
>> +       error = vfs_xstat(dfd, filename, atflag, &stat);
>> +       if (error)
>> +               return error;
>> +       return xstat_set_result(&stat, buffer, bufsize);
>> +}
>> +
>> +/*
>> + * System call to get extended stats by file descriptor
>> + */
>> +SYSCALL_DEFINE5(fxstat, unsigned int, fd, unsigned int, flags,
>> +               struct xstat_parameters __user *, params,
>> +               struct xstat __user *, buffer, size_t, bufsize)
>> +{
>> +       struct kstat stat;
>> +       int error;
>> +
>> +       error = xstat_get_params(params, &stat);
>> +       if (error < 0)
>> +               return error;
>> +       error = vfs_fxstat(fd, flags, &stat);
>> +       if (error)
>> +               return error;
>> +
>> +       return xstat_set_result(&stat, buffer, bufsize);
>> +}
>> +
>>  /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
>>  void __inode_add_bytes(struct inode *inode, loff_t bytes)
>>  {
>> diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
>> index afc00af..bcf8083 100644
>> --- a/include/linux/fcntl.h
>> +++ b/include/linux/fcntl.h
>> @@ -45,6 +45,7 @@
>>  #define AT_REMOVEDIR           0x200   /* Remove directory instead of
>>                                            unlinking file.  */
>>  #define AT_SYMLINK_FOLLOW      0x400   /* Follow symbolic links.  */
>> +#define AT_FORCE_ATTR_SYNC     0x800   /* Force the attributes to be sync'd with the server */
>>
>>  #ifdef __KERNEL__
>>
>> diff --git a/include/linux/fs.h b/include/linux/fs.h
>> index a18bcea..9ce2119 100644
>> --- a/include/linux/fs.h
>> +++ b/include/linux/fs.h
>> @@ -2343,6 +2343,8 @@ extern int vfs_stat(const char __user *, struct kstat *);
>>  extern int vfs_lstat(const char __user *, struct kstat *);
>>  extern int vfs_fstat(unsigned int, struct kstat *);
>>  extern int vfs_fstatat(int , const char __user *, struct kstat *, int);
>> +extern int vfs_xstat(int, const char __user *, int, struct kstat *);
>> +extern int vfs_xfstat(unsigned int, struct kstat *);
>>
>>  extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
>>                    unsigned long arg);
>> diff --git a/include/linux/stat.h b/include/linux/stat.h
>> index 611c398..e0b89e4 100644
>> --- a/include/linux/stat.h
>> +++ b/include/linux/stat.h
>> @@ -46,6 +46,99 @@
>>
>>  #endif
>>
>> +/*
>> + * Extended stat structures
>> + */
>> +struct xstat_parameters {
>> +       /* Query request/result mask
>> +        *
>> +        * Bits should be set in request_mask to request particular items
>> +        * before calling xstat() or fxstat().
>> +        *
>> +        * For each item in the set XSTAT_REQUEST__EXTENDED_STATS:
>> +        *
>> +        * - if not available at all, the bit will be cleared before returning
>> +        *   and the field will be cleared; otherwise,
>> +        *
>> +        * - if AT_FORCE_ATTR_SYNC is set, then the datum will be synchronised
>> +        *   to the server and the bit will be set on return; otherwise,
>> +        *
>> +        * - if requested, the datum will be synchronised to a server or other
>> +        *   hardware if out of date before being returned, and the bit will be
>> +        *   set on return; otherwise,
>> +        *
>> +        * - if not requested, but available in approximate form without any
>> +        *   effort, it will be filled in anyway, and the bit will be set upon
>> +        *   return (it might not be up to date, however, and no attempt will
>> +        *   be made to synchronise the internal state first); otherwise,
>> +        *
>> +        * - the bit will be cleared before returning, and the field will be
>> +         *   cleared.
>> +        *
>> +        * For each item not in the set XSTAT_REQUEST__EXTENDED_STATS
>> +        *
>> +        * - if not available at all, the bit will be cleared, and no result
>> +         *   data will be returned; otherwise,
>> +        *
>> +        * - if requested, the datum will be synchronised to a server or other
>> +        *   hardware before being appended if necessary, and the bit will be
>> +        *   set on return; otherwise,
>> +        *
>> +        * - the bit will be cleared, and no result data will be returned.
>> +        *
>> +        * Items in XSTAT_REQUEST__BASIC_STATS may be marked unavailable on
>> +        * return, but they will have a value installed for compatibility
>> +        * purposes.
>> +        */
>> +       unsigned long long      request_mask;
>> +#define XSTAT_REQUEST_MODE             0x00000001ULL   /* want/got st_mode */
>> +#define XSTAT_REQUEST_NLINK            0x00000002ULL   /* want/got st_nlink */
>> +#define XSTAT_REQUEST_UID              0x00000004ULL   /* want/got st_uid */
>> +#define XSTAT_REQUEST_GID              0x00000008ULL   /* want/got st_gid */
>> +#define XSTAT_REQUEST_RDEV             0x00000010ULL   /* want/got st_rdev */
>> +#define XSTAT_REQUEST_ATIME            0x00000020ULL   /* want/got st_atime */
>> +#define XSTAT_REQUEST_MTIME            0x00000040ULL   /* want/got st_mtime */
>> +#define XSTAT_REQUEST_CTIME            0x00000080ULL   /* want/got st_ctime */
>> +#define XSTAT_REQUEST_INO              0x00000100ULL   /* want/got st_ino */
>> +#define XSTAT_REQUEST_SIZE             0x00000200ULL   /* want/got st_size */
>> +#define XSTAT_REQUEST_BLOCKS           0x00000400ULL   /* want/got st_blocks */
>> +#define XSTAT_REQUEST__BASIC_STATS     0x000007ffULL   /* the stuff in the normal stat struct */
>> +#define XSTAT_REQUEST_BTIME            0x00000800ULL   /* want/got st_btime */
>> +#define XSTAT_REQUEST_GEN              0x00001000ULL   /* want/got st_gen */
>> +#define XSTAT_REQUEST_DATA_VERSION     0x00002000ULL   /* want/got st_data_version */
>> +#define XSTAT_REQUEST__EXTENDED_STATS  0x00003fffULL   /* the stuff in the xstat struct */
>> +#define XSTAT_REQUEST__ALL_STATS       0x00003fffULL   /* the defined set of requestables */
>> +};
>> +
>> +struct xstat_dev {
>> +       unsigned int            major, minor;
>> +};
>> +
>> +struct xstat_time {
>> +       unsigned long long      tv_sec, tv_nsec;
>> +};
>> +
>> +struct xstat {
>> +       unsigned int            st_mode;        /* file mode */
>> +       unsigned int            st_nlink;       /* number of hard links */
>> +       unsigned int            st_uid;         /* user ID of owner */
>> +       unsigned int            st_gid;         /* group ID of owner */
>> +       struct xstat_dev        st_rdev;        /* device ID of special file */
>> +       struct xstat_dev        st_dev;         /* ID of device containing file */
>> +       struct xstat_time       st_atime;       /* last access time */
>> +       struct xstat_time       st_mtime;       /* last data modification time */
>> +       struct xstat_time       st_ctime;       /* last attribute change time */
>> +       struct xstat_time       st_btime;       /* file creation time */
>> +       unsigned long long      st_ino;         /* inode number */
>> +       unsigned long long      st_size;        /* file size */
>> +       unsigned long long      st_blksize;     /* block size for filesystem I/O */
>> +       unsigned long long      st_blocks;      /* number of 512-byte blocks allocated */
>> +       unsigned long long      st_gen;         /* inode generation number */
>> +       unsigned long long      st_data_version; /* data version number */
>> +       unsigned long long      st_result_mask; /* what requests were written */
>> +       unsigned long long      st_extra_results[0]; /* extra requested results */
>> +};
>> +
>>  #ifdef __KERNEL__
>>  #define S_IRWXUGO      (S_IRWXU|S_IRWXG|S_IRWXO)
>>  #define S_IALLUGO      (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
>> @@ -67,14 +160,20 @@ struct kstat {
>>        uid_t           uid;
>>        gid_t           gid;
>>        dev_t           rdev;
>> +       unsigned int    query_flags;            /* operational flags */
>> +#define KSTAT_QUERY_FLAGS (AT_FORCE_ATTR_SYNC)
>>        loff_t          size;
>> -       struct timespec  atime;
>> +       struct timespec atime;
>>        struct timespec mtime;
>>        struct timespec ctime;
>> +       struct timespec btime;                  /* file creation time */
>>        unsigned long   blksize;
>>        unsigned long long      blocks;
>> +       u64             request_mask;           /* what fields the user asked for */
>> +       u64             result_mask;            /* what fields the user got */
>> +       u64             gen;                    /* inode generation */
>> +       u64             data_version;
>>  };
>>
>>  #endif
>> -
>>  #endif
>> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
>> index 8812a63..5d68b4c 100644
>> --- a/include/linux/syscalls.h
>> +++ b/include/linux/syscalls.h
>> @@ -44,6 +44,8 @@ struct shmid_ds;
>>  struct sockaddr;
>>  struct stat;
>>  struct stat64;
>> +struct xstat_parameters;
>> +struct xstat;
>>  struct statfs;
>>  struct statfs64;
>>  struct __sysctl_args;
>> @@ -824,4 +826,11 @@ asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len,
>>                        unsigned long fd, unsigned long pgoff);
>>  asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg);
>>
>> +asmlinkage long sys_xstat(int, const char __user *, unsigned,
>> +                         struct xstat_parameters __user *,
>> +                         struct xstat __user *, size_t);
>> +asmlinkage long sys_fxstat(unsigned, unsigned,
>> +                          struct xstat_parameters __user *,
>> +                          struct xstat __user *, size_t);
>> +
>>  #endif
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
>> the body of a message to majordomo@xxxxxxxxxxxxxxx
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>
>
>
> --
> Michael Kerrisk Linux man-pages maintainer;
> http://www.kernel.org/doc/man-pages/
> Author of "The Linux Programming Interface", http://blog.man7.org/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-api" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface" http://blog.man7.org/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/