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, ¶ms, &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(¶ms, _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/