=======
TESTING
=======
The following test program can be used to test the xstat system call:
/* Test the xstat() system call
*
* Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#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
#define AT_NO_AUTOMOUNT 0x1000
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_INODE_FLAGS 0x00004000ULL
#define XSTAT_REQUEST__EXTENDED_STATS 0x00007fffULL
#define XSTAT_REQUEST__ALL_STATS 0x00007fffULL
#define XSTAT_REQUEST__EXTRA_STATS (XSTAT_REQUEST__ALL_STATS & ~XSTAT_REQUEST__EXTENDED_STATS)
};
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 long long st_result_mask;
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_inode_flags;
unsigned long long st_extra_results[0];
};
#define FS__STANDARD_FL 0x00000000ffffffffULL
#define FS_SPECIAL_FL 0x0000000100000000ULL
#define FS_AUTOMOUNT_FL 0x0000000200000000ULL
#define FS_AUTOMOUNT_ANY_FL 0x0000000400000000ULL
#define FS_REMOTE_FL 0x0000000800000000ULL
#define FS_ENCRYPTED_FL 0x0000001000000000ULL
#define FS_SYSTEM_FL 0x0000002000000000ULL
#define FS_TEMPORARY_FL 0x0000004000000000ULL
#define FS_OFFLINE_FL 0x0000008000000000ULL
#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);
if (xst->st_result_mask & XSTAT_REQUEST_INODE_FLAGS) {
unsigned char bits;
int loop, byte;
static char flag_representation[64 + 1] =
"????????"
"????????"
"????????"
"otserAaS"
"????????"
"????ehTD"
"tj?IE?XZ"
"AdaiScus"
;
printf("Inode flags: %016llx (", xst->st_inode_flags);
for (byte = 64 - 8; byte >= 0; byte -= 8) {
bits = xst->st_inode_flags >> byte;
for (loop = 7; loop >= 0; loop--) {
int bit = byte + loop;
if (bits & 0x80)
putchar(flag_representation[63 - bit]);
else
putchar('-');
bits <<= 1;
}
if (byte)
putchar(' ');
}
printf(")\n");
}
}
int main(int argc, char **argv)
{
struct xstat_parameters params;
union {
struct xstat xst;
unsigned long long raw[4096 / 8];
} buffer;
int ret, atflag = AT_SYMLINK_NOFOLLOW;
unsigned long long query = XSTAT_REQUEST__ALL_STATS;
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;
}
if (strcmp(*argv, "-A") == 0) {
atflag |= AT_NO_AUTOMOUNT;
continue;
}
memset(&buffer, 0xbf, sizeof(buffer));
params.request_mask = query;
ret = xstat(AT_FDCWD, *argv, atflag, ¶ms, &buffer.xst,
sizeof(buffer));
printf("xstat(%s) = %d\n", *argv, ret);
if (ret < 0) {
perror(*argv);
exit(1);
}
dump_xstat(&buffer.xst);
ret = (ret + 7) / 8;
if (ret > sizeof(buffer.xst) / 8) {
unsigned offset, print_offset = 1, col = 0;
if (ret > sizeof(buffer) / 8)
ret = sizeof(buffer) / 8;
for (offset = sizeof(buffer.xst) / 8; offset < ret; offset++) {
if (print_offset) {
printf("%04x: ", offset * 8);
print_offset = 0;
}
printf("%016llx", buffer.raw[offset]);
col++;
if ((col & 3) == 0) {
printf("\n");
print_offset = 1;
} else {
printf(" ");
}
}
if (!print_offset)
printf("\n");
}
}
return 0;
}
Just compile and run, passing it paths to the files you want to examine:
[root@andromeda ~]# /tmp/xstat /proc/$$
xstat(/proc/2074) = 160
results=47ef
Size: 0 Blocks: 0 IO Block: 1024 directory
Device: 00:03 Inode: 9072 Links: 7
Access: (0555/dr-xr-xr-x) Uid: 0
Gid: 0
Access: 2010-07-14 16:50:46.609336272+0100
Modify: 2010-07-14 16:50:46.609336272+0100
Change: 2010-07-14 16:50:46.609336272+0100
Inode flags: 0000000100000000 (-------- -------- -------- -------S -------- -------- -------- --------)
[root@andromeda ~]# /tmp/xstat /afs/archive/linuxdev/fedora9/x86_64/kernel-devel-2.6.25.10-86.fc9.x86_64.rpm
xstat(/afs/archive/linuxdev/fedora9/x86_64/kernel-devel-2.6.25.10-86.fc9.x86_64.rpm) = 160
results=77ef
Size: 5413882 Blocks: 0 IO Block: 4096 regular file
Device: 00:15 Inode: 2288 Links: 1
Access: (0644/-rw-r--r--) Uid: 75338
Gid: 0
Access: 2008-11-05 19:47:22.000000000+0000
Modify: 2008-11-05 19:47:22.000000000+0000
Change: 2008-11-05 19:47:22.000000000+0000
Inode version: 795h
Data version: 2h
Inode flags: 0000000800000000 (-------- -------- -------- ----r--- -------- -------- -------- --------)
Signed-off-by: David Howells <dhowells@redhat.com>
---
arch/x86/include/asm/unistd_32.h | 4
arch/x86/include/asm/unistd_64.h | 4
fs/stat.c | 322 +++++++++++++++++++++++++++++++++++---
include/linux/fcntl.h | 1
include/linux/fs.h | 4
include/linux/stat.h | 119 ++++++++++++++
include/linux/syscalls.h | 9 +
7 files changed, 436 insertions(+), 27 deletions(-)
@@ -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
@@ -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
@@ -18,6 +18,15 @@
#include <asm/uaccess.h>
#include <asm/unistd.h>
+/**
+ * generic_fillattr - Fill in the basic attributes from the inode struct
+ * @inode: Inode to use as the source
+ * @stat: Where to fill in the attributes
+ *
+ * Fill in the basic attributes in the kstat structure from data that's to be
+ * found on the VFS inode structure. This is the default if no getattr inode
+ * operation is supplied.
+ */
void generic_fillattr(struct inode *inode, struct kstat *stat)
{
stat->dev = inode->i_sb->s_dev;
@@ -33,11 +42,37 @@ 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->inode_flags = inode->i_sb->s_type->inode_flags;
+ 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;
+ if (stat->inode_flags)
+ stat->result_mask |= XSTAT_REQUEST_INODE_FLAGS;
}
-
EXPORT_SYMBOL(generic_fillattr);
-int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+/**
+ * vfs_xgetattr - Get the extended attributes of a file
+ * @mnt: The mountpoint to which the dentry belongs
+ * @dentry: The file of interest
+ * @stat: Where to return the statistics
+ *
+ * Ask the filesystem for a file's attributes. The caller must have preset
+ * stat->request_mask and stat->query_flags to indicate what they want.
+ *
+ * If the file is remote, the filesystem can be forced to update the attributes
+ * from the backing store by passing AT_FORCE_ATTR_SYNC in query_flags.
+ *
+ * Bits must have been set in stat->request_mask to indicate which attributes
+ * the caller wants retrieving. Only attributes from the set
+ * XSTAT_REQUEST__EXTENDED_STATS can be retrieved through this interface. Any
+ * such attribute not requested may be returned anyway, but the value may be
+ * approximate, and, if remote, may not have been synchronised with the server.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_xgetattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
int retval;
@@ -46,61 +81,176 @@ int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
if (retval)
return retval;
+ stat->result_mask = 0;
if (inode->i_op->getattr)
return inode->i_op->getattr(mnt, dentry, stat);
generic_fillattr(inode, stat);
return 0;
}
+EXPORT_SYMBOL(vfs_xgetattr);
+/**
+ * vfs_getattr - Get the basic attributes of a file
+ * @mnt: The mountpoint to which the dentry belongs
+ * @dentry: The file of interest
+ * @stat: Where to return the statistics
+ *
+ * Ask the filesystem for a file's attributes. If remote, the filesystem isn't
+ * forced to update its files from the backing store. Only the basic set of
+ * attributes will be retrieved; anyone wanting more must use vfs_getxattr(),
+ * as must anyone who wants to force attributes to be sync'd with the server.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+ stat->query_flags = 0;
+ stat->request_mask = XSTAT_REQUEST__BASIC_STATS;
+ return vfs_xgetattr(mnt, dentry, stat);
+}
EXPORT_SYMBOL(vfs_getattr);
-int vfs_fstat(unsigned int fd, struct kstat *stat)
+/**
+ * vfs_fxstat - Get extended attributes by file descriptor
+ * @fd: The file descriptor refering to the file of interest
+ * @stat: The result structure to fill in.
+ *
+ * This function is a wrapper around vfs_xgetattr(). The main difference is
+ * that it uses a file descriptor to determine the file location.
+ *
+ * The caller must have preset stat->query_flags and stat->request_mask as for
+ * vfs_xgetattr().
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_fxstat(unsigned int fd, struct kstat *stat)
{
struct file *f = fget(fd);
int error = -EBADF;
+ if (stat->query_flags & ~KSTAT_QUERY_FLAGS)
+ return -EINVAL;
if (f) {
- error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat);
+ error = vfs_xgetattr(f->f_path.mnt, f->f_path.dentry, stat);
fput(f);
}
return error;
}
+EXPORT_SYMBOL(vfs_fxstat);
+
+/**
+ * vfs_fstat - Get basic attributes by file descriptor
+ * @fd: The file descriptor refering to the file of interest
+ * @stat: The result structure to fill in.
+ *
+ * This function is a wrapper around vfs_getattr(). The main difference is
+ * that it uses a file descriptor to determine the file location.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_fstat(unsigned int fd, struct kstat *stat)
+{
+ stat->query_flags = 0;
+ stat->request_mask = XSTAT_REQUEST__BASIC_STATS;
+ return vfs_fxstat(fd, stat);
+}
EXPORT_SYMBOL(vfs_fstat);
-int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- int flag)
+/**
+ * vfs_xstat - Get extended attributes by filename
+ * @dfd: A file descriptor representing the base dir for a relative filename
+ * @filename: The name of the file of interest
+ * @flags: Flags to control the query
+ * @stat: The result structure to fill in.
+ *
+ * This function is a wrapper around vfs_xgetattr(). The main difference is
+ * that it uses a filename and base directory to determine the file location.
+ * Additionally, the addition of AT_SYMLINK_NOFOLLOW to flags will prevent a
+ * symlink at the given name from being referenced.
+ *
+ * The caller must have preset stat->request_mask as for vfs_xgetattr(). The
+ * flags are also used to load up stat->query_flags.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_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 & KSTAT_QUERY_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_xgetattr(path.mnt, path.dentry, stat);
+ path_put(&path);
+ }
return error;
}
+EXPORT_SYMBOL(vfs_xstat);
+
+/**
+ * vfs_fstatat - Get basic attributes by filename
+ * @dfd: A file descriptor representing the base dir for a relative filename
+ * @filename: The name of the file of interest
+ * @flags: Flags to control the query
+ * @stat: The result structure to fill in.
+ *
+ * This function is a wrapper around vfs_xstat(). The difference is that it
+ * preselects basic stats only. The flags are used to load up
+ * stat->query_flags in addition to indicating symlink handling during path
+ * resolution.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ int flags)
+{
+ stat->request_mask = XSTAT_REQUEST__BASIC_STATS;
+ return vfs_xstat(dfd, filename, flags, stat);
+}
EXPORT_SYMBOL(vfs_fstatat);
-int vfs_stat(const char __user *name, struct kstat *stat)
+/**
+ * vfs_stat - Get basic attributes by filename
+ * @filename: The name of the file of interest
+ * @stat: The result structure to fill in.
+ *
+ * This function is a wrapper around vfs_xstat(). The difference is that it
+ * preselects basic stats only, terminal symlinks are followed regardless and a
+ * remote filesystem can't be forced to query the server. If such is desired,
+ * vfs_xstat() should be used instead.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
+int vfs_stat(const char __user *filename, struct kstat *stat)
{
- return vfs_fstatat(AT_FDCWD, name, stat, 0);
+ stat->request_mask = XSTAT_REQUEST__BASIC_STATS;
+ return vfs_xstat(AT_FDCWD, filename, 0, stat);
}
EXPORT_SYMBOL(vfs_stat);
+/**
+ * vfs_stat - Get basic attributes by filename, without following terminal symlink
+ * @filename: The name of the file of interest
+ * @stat: The result structure to fill in.
+ *
+ * This function is a wrapper around vfs_xstat(). The difference is that it
+ * preselects basic stats only, terminal symlinks are note followed regardless
+ * and a remote filesystem can't be forced to query the server. If such is
+ * desired, vfs_xstat() should be used instead.
+ *
+ * 0 will be returned on success, and a -ve error code if unsuccessful.
+ */
int vfs_lstat(const char __user *name, struct kstat *stat)
{
- return vfs_fstatat(AT_FDCWD, name, stat, AT_SYMLINK_NOFOLLOW);
+ return vfs_xstat(AT_FDCWD, name, AT_SYMLINK_NOFOLLOW, stat);
}
EXPORT_SYMBOL(vfs_lstat);
@@ -115,7 +265,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 +290,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 +372,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 +558,130 @@ 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__BASIC_STATS;
+ }
+ stat->result_mask = 0;
+ return 0;
+}
+
+/*
+ * Set the xstat results.
+ *
+ * If the buffer size was 0, we just return the size of the buffer needed to
+ * return the full result.
+ *
+ * If bufsize indicates a buffer of insufficient size to hold the full result,
+ * we return -E2BIG.
+ *
+ * Otherwise we copy the extended stats to userspace and return the amount of
+ * data written into the buffer (or -EFAULT).
+ */
+static long xstat_set_result(struct kstat *stat,
+ struct xstat __user *buffer, size_t bufsize)
+{
+ struct xstat tmp;
+ size_t result_size = sizeof(tmp);
+
+ if (bufsize == 0)
+ return result_size;
+ if (bufsize < result_size)
+ return -E2BIG;
+
+ /* 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;
+ if (tmp.st_result_mask & XSTAT_REQUEST_INODE_FLAGS)
+ tmp.st_inode_flags = stat->inode_flags;
+
+ if (copy_to_user(buffer, &tmp, result_size) != 0)
+ return -EFAULT;
+ return result_size;
+}
+
+/*
+ * 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;
+ stat.query_flags = flags;
+ error = vfs_fxstat(fd, &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)
{
@@ -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__
@@ -1735,6 +1735,7 @@ int sync_inode(struct inode *inode, struct writeback_control *wbc);
struct file_system_type {
const char *name;
int fs_flags;
+ u64 inode_flags; /* base inode_flags for generic_getattr() */
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
void (*kill_sb) (struct super_block *);
@@ -2341,6 +2342,7 @@ extern const struct inode_operations page_symlink_inode_operations;
extern int generic_readlink(struct dentry *, char __user *, int);
extern void generic_fillattr(struct inode *, struct kstat *);
extern int vfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+extern int vfs_xgetattr(struct vfsmount *, struct dentry *, struct kstat *);
void __inode_add_bytes(struct inode *inode, loff_t bytes);
void inode_add_bytes(struct inode *inode, loff_t bytes);
void inode_sub_bytes(struct inode *inode, loff_t bytes);
@@ -2353,6 +2355,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);
@@ -46,6 +46,114 @@
#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_INODE_FLAGS 0x00004000ULL /* want/got st_inode_flags */
+#define XSTAT_REQUEST__EXTENDED_STATS 0x00007fffULL /* the stuff in the xstat struct */
+#define XSTAT_REQUEST__ALL_STATS 0x00007fffULL /* 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 long long st_result_mask; /* what results were written */
+ 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_inode_flags; /* inode flags (!= BSD st_flags) */
+ unsigned long long st_extra_results[0]; /* extra requested results */
+};
+
+#define FS__STANDARD_FL 0x00000000ffffffffULL /* As for user visible FS_IOC_GETFLAGS */
+#define FS_SPECIAL_FL 0x0000000100000000ULL /* Special file as found in procfs/sysfs */
+#define FS_AUTOMOUNT_FL 0x0000000200000000ULL /* Specific automount point */
+#define FS_AUTOMOUNT_ANY_FL 0x0000000400000000ULL /* Unspecific automount directory */
+#define FS_REMOTE_FL 0x0000000800000000ULL /* File is remote */
+#define FS_ENCRYPTED_FL 0x0000001000000000ULL /* File is encrypted */
+#define FS_HIDDEN_FL 0x0000002000000000ULL /* File is marked hidden (DOS+) */
+#define FS_SYSTEM_FL 0x0000004000000000ULL /* File is marked system (DOS+) */
+#define FS_ARCHIVE_FL 0x0000008000000000ULL /* File is marked archive (DOS+) */
+#define FS_TEMPORARY_FL 0x0000010000000000ULL /* File is temporary (NTFS/CIFS) */
+#define FS_OFFLINE_FL 0x0000020000000000ULL /* File is offline (CIFS) */
+#define FS_REPARSE_POINT_FL 0x0000040000000000ULL /* Reparse point (NTFS/CIFS) */
+
#ifdef __KERNEL__
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
@@ -60,6 +168,8 @@
#include <linux/time.h>
struct kstat {
+ u64 request_mask; /* what fields the user asked for */
+ u64 result_mask; /* what fields the user got */
u64 ino;
dev_t dev;
umode_t mode;
@@ -67,14 +177,19 @@ 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 gen; /* inode generation */
+ u64 data_version;
+ u64 inode_flags; /* inode flags (!= BSD st_flags) */
};
#endif
-
#endif
@@ -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