Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752183AbcKRCsX (ORCPT ); Thu, 17 Nov 2016 21:48:23 -0500 Received: from mail-it0-f66.google.com ([209.85.214.66]:36520 "EHLO mail-it0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751495AbcKRCsU (ORCPT ); Thu, 17 Nov 2016 21:48:20 -0500 Subject: Re: [PATCH 1/4] statx: Add a system call to make enhanced file info available Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) Content-Type: multipart/signed; boundary="Apple-Mail=_2FC85ADF-D8BB-4DEF-9C80-C77F96F0EEE5"; protocol="application/pgp-signature"; micalg=pgp-sha256 X-Pgp-Agent: GPGMail From: Andreas Dilger In-Reply-To: <1479407964.4556.5.camel@redhat.com> Date: Thu, 17 Nov 2016 19:32:08 -0700 Cc: David Howells , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Message-Id: <2D8B8AAF-2016-45BD-9D70-A532DDDB23D2@dilger.ca> References: <147938969703.13574.10295364502230379833.stgit@warthog.procyon.org.uk> <147938970382.13574.11581172952175034619.stgit@warthog.procyon.org.uk> <1479407964.4556.5.camel@redhat.com> To: Jeff Layton X-Mailer: Apple Mail (2.3124) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 51491 Lines: 1589 --Apple-Mail=_2FC85ADF-D8BB-4DEF-9C80-C77F96F0EEE5 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii > On Nov 17, 2016, at 11:39 AM, Jeff Layton wrote: >=20 > On Thu, 2016-11-17 at 13:35 +0000, David Howells wrote: >> Add a system call to make extended file information available, = including >> file creation, data version and some attribute flags where available >> through the underlying filesystem. >>=20 >>=20 >> =3D=3D=3D=3D=3D=3D=3D=3D >> OVERVIEW >> =3D=3D=3D=3D=3D=3D=3D=3D >>=20 >> The idea was initially proposed as a set of xattrs that could be = retrieved >> with getxattr(), but the general preferance proved to be for a new = syscall >> with an extended stat structure. >>=20 >> This has a number of uses: >>=20 >> (1) Better support for the y2038 problem [Arnd Bergmann]. >>=20 >> (2) Creation time: The SMB protocol carries the creation time, which = could >> be exported by Samba, which will in turn help CIFS make use of >> FS-Cache as that can be used for coherency data. >>=20 >> This is also specified in NFSv4 as a recommended attribute and = could >> be exported by NFSD [Steve French]. >>=20 >> (3) Lightweight stat: Ask for just those details of interest, and = allow a >> netfs (such as NFS) to approximate anything not of interest, = possibly >> without going to the server [Trond Myklebust, Ulrich Drepper, = Andreas >> Dilger]. >>=20 >> (4) Heavyweight stat: Force a netfs to go to the server, even if it = thinks >> its cached attributes are up to date [Trond Myklebust]. >>=20 >> (5) Data version number: Could be used by userspace NFS servers = [Aneesh >> Kumar]. >>=20 >> Can also be used to modify fill_post_wcc() in NFSD which = retrieves >> i_version directly, but has just called vfs_getattr(). It could = get >> it from the kstat struct if it used vfs_xgetattr() instead. >>=20 >> (6) BSD stat compatibility: Including more fields from the BSD stat = such >> as creation time (st_btime) and inode generation number (st_gen) >> [Jeremy Allison, Bernd Schubert]. >>=20 >> (7) Inode generation number: Useful for FUSE and userspace NFS = servers >> [Bernd Schubert]. This was asked for but later deemed = unnecessary >> with the open-by-handle capability available >>=20 >> (8) Extra coherency data may be useful in making backups [Andreas = Dilger]. >>=20 >> (9) Allow the filesystem to indicate what it can/cannot provide: A >> filesystem can now say it doesn't support a standard stat feature = if >> that isn't available, so if, for instance, inode numbers or UIDs = don't >> exist or are fabricated locally... >>=20 >> (10) Make the fields a consistent size on all arches and make them = large. >>=20 >> (11) Store a 16-byte volume ID in the superblock that can be returned = in >> struct xstat [Steve French]. >>=20 >> (12) Include granularity fields in the time data to indicate the >> granularity of each of the times (NFSv4 time_delta) [Steve = French]. >>=20 >> (13) FS_IOC_GETFLAGS value. These could be translated to BSD's = st_flags. >> Note that the Linux IOC flags are a mess and filesystems such as = Ext4 >> define flags that aren't in linux/fs.h, so translation in the = kernel >> may be a necessity (or, possibly, we provide the filesystem type = too). >>=20 >> (14) Mask of features available on file (eg: ACLs, seclabel) [Brad = Boyer, >> Michael Kerrisk]. >>=20 >> (15) Spare space, request flags and information flags are provided = for >> future expansion. >>=20 >> Note that not all of the above are implemented here. >>=20 >>=20 >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> NEW SYSTEM CALL >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>=20 >> The new system call is: >>=20 >> int ret =3D statx(int dfd, >> const char *filename, >> unsigned int flags, >> unsigned int mask, >> struct statx *buffer); >>=20 >> The dfd, filename and flags parameters indicate the file to query. = There >> is no equivalent of lstat() as that can be emulated with statx() by = passing >> AT_SYMLINK_NOFOLLOW in flags. There is also no equivalent of fstat() = as >> that can be emulated by passing a NULL filename to statx() with the = fd of >> interest in dfd. >>=20 >> AT_STATX_FORCE_SYNC can be set in flags. This will require a network >> filesystem to synchronise its attributes with the server. >>=20 >> AT_STATX_DONT_SYNC can be set in flags. This will suppress = synchronisation >> with the server in a network filesystem. The resulting values should = be >> considered approximate. >>=20 >> If neither AT_STATX_*_SYNC flag is set, the behaviour is the default = for >> stat() on that filesystem. >>=20 >=20 > We also need to specify here what happens if both bits are set. Should > that be -EINVAL? If that is the case, then it doesn't make sense to have two = contradictory flags. Pick a default behaviour (i.e. return what is known on the client), and if this is 100% accurate (e.g. local filesystem or filesystem with = strong coherency) then it can optionally set the SYNC flag in the returned = flags. If the application needs 100% accurate size info, then it can set the = SYNC flag in the request and the filesystem may need to do extra work to = fetch accurate data from the server. >> mask is a bitmask indicating the fields in struct statx that are of >> interest to the caller. The user should set this to = STATX_BASIC_STATS to >> get the basic set returned by stat(). It should be note that asking = for >> more information may entail more I/O operations. >>=20 >> buffer points to the destination for the data. This must be 256 = bytes in >> size. >>=20 >>=20 >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> MAIN ATTRIBUTES RECORD >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>=20 >> The following structures are defined in which to return the main = attribute >> set: >>=20 >> struct statx { >> __u32 stx_mask; >> __u32 stx_blksize; >> __u64 stx_attributes; >> __u32 stx_nlink; >> __u32 stx_uid; >> __u32 stx_gid; >> __u16 stx_mode; >> __u16 __spare0[1]; >> __u64 stx_ino; >> __u64 stx_size; >> __u64 stx_blocks; >> __u64 stx_version; >> __s64 stx_atime; >> __s64 stx_btime; >> __s64 stx_ctime; >> __s64 stx_mtime; >> __s32 stx_atime_ns; >> __s32 stx_btime_ns; >> __s32 stx_ctime_ns; >> __s32 stx_mtime_ns; >> __u32 stx_rdev_major; >> __u32 stx_rdev_minor; >> __u32 stx_dev_major; >> __u32 stx_dev_minor; >> __u64 __spare1[16]; >> }; >>=20 >> The defined bits in request_mask and stx_mask are: >>=20 >> STATX_TYPE Want/got stx_mode & S_IFMT >> STATX_MODE Want/got stx_mode & ~S_IFMT >> STATX_NLINK Want/got stx_nlink >> STATX_UID Want/got stx_uid >> STATX_GID Want/got stx_gid >> STATX_ATIME Want/got stx_atime{,_ns} >> STATX_MTIME Want/got stx_mtime{,_ns} >> STATX_CTIME Want/got stx_ctime{,_ns} >> STATX_INO Want/got stx_ino >> STATX_SIZE Want/got stx_size >> STATX_BLOCKS Want/got stx_blocks >> STATX_BASIC_STATS [The stuff in the normal stat struct] >> STATX_BTIME Want/got stx_btime{,_ns} >> STATX_VERSION Want/got stx_version >> STATX_ALL [All currently available stuff] >>=20 >> stx_btime is the file creation time; stx_version is the data version = number >> (i_version); stx_mask is a bitmask indicating the data provided; and >> __spares*[] are where as-yet undefined fields can be placed. >>=20 >> Time fields are split into separate seconds and nanoseconds fields to = make >> packing easier and the granularities can be queried with the = filesystem >> info system call. Note that times will be negative if before 1970; = in such >> a case, the nanosecond fields will also be negative if not zero. >>=20 >> The bits defined in the stx_attributes field convey information about = a >> file, how it is accessed, where it is and what it does. The = following >> attributes map to FS_*_FL flags and are the same numerical value: >>=20 >> STATX_ATTR_COMPRESSED File is compressed by the fs >> STATX_ATTR_IMMUTABLE File is marked immutable >> STATX_ATTR_APPEND File is append-only >> STATX_ATTR_NODUMP File is not to be dumped >> STATX_ATTR_ENCRYPTED File requires key to decrypt in = fs >>=20 >> The supported flags are listed by: >>=20 >> STATX_ATTR_FS_IOC_FLAGS >>=20 >> [Are any other IOC flags of sufficient general interest to be exposed >> through this interface?] >>=20 >> New flags include: >>=20 >> STATX_ATTR_NONUNIX_OWNERSHIP File doesn't have Unixy = ownership >> STATX_ATTR_HAS_ACL File has an ACL >> STATX_ATTR_KERNEL_API File is kernel API (eg: = procfs/sysfs) >> STATX_ATTR_REMOTE File is remote and needs network >> STATX_ATTR_FABRICATED File was made up by fs >> STATX_ATTR_AUTOMOUNT Object is an automount trigger >> STATX_ATTR_UNLISTED_DENTS Dir: Lookup may find files = getdents doesn't >>=20 >> These are for the use of GUI tools that might want to mark files = specially, >> depending on what they are. >>=20 >> Fields in struct statx come in a number of classes: >>=20 >> (0) stx_dev_*, stx_blksize. >>=20 >> These are local system information and are always available. >>=20 >> (1) stx_mode, stx_nlinks, stx_uid, stx_gid, stx_[amc]time*, stx_ino, >> stx_size, stx_blocks. >>=20 >> These will be returned whether the caller asks for them or not. = The >> corresponding bits in stx_mask will be set to indicate whether = they >> actually have valid values. >>=20 >> 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. >>=20 >> If the values don't actually exist for the underlying object = (such as >> UID or GID on a DOS file), then the bit won't be set in the = stx_mask, >> even if the caller asked for the value. In such a case, the = returned >> value will be a fabrication. >>=20 >> Note that there are instances where the type might not be valid, = for >> instance Windows reparse points. >>=20 >> (2) stx_rdev_*. >>=20 >> This will be set only if stx_mode indicates we're looking at a >> blockdev or a chardev, otherwise will be 0. >>=20 >> (3) stx_btime*, stx_version. >>=20 >> Similar to (1), except these will be set to 0 if they don't = exist. >>=20 >>=20 >> =3D=3D=3D=3D=3D=3D=3D >> TESTING >> =3D=3D=3D=3D=3D=3D=3D >>=20 >> The following test program can be used to test the statx system call: >>=20 >> samples/statx/test-statx.c >>=20 >> Just compile and run, passing it paths to the files you want to = examine. >> The file is built automatically if CONFIG_SAMPLES is enabled. >>=20 >> Here's some example output. Firstly, an NFS directory that crosses = to >> another FSID. Note that the FABRICATED and AUTOMOUNT info flags are = set. >> The former because the directory is invented locally as we don't see = the >> underlying dir on the server, the latter because transiting this = directory >> will cause d_automount to be invoked by the VFS. >>=20 >> [root@andromeda tmp]# ./samples/statx/test-statx -A = /warthog/data >> statx(/warthog/data) =3D 0 >> results=3D17ff >> Size: 4096 Blocks: 8 IO Block: 1048576 = directory >> Device: 00:26 Inode: 1703937 Links: 124 >> Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041 >> Access: 2016-11-10 15:52:11.219935864+0000 >> Modify: 2016-11-10 08:07:32.482314928+0000 >> Change: 2016-11-10 08:07:32.482314928+0000 >> Data version: 58242ac41cbf8ab0h >> Attributes: 000000000000e000 (-------- -------- -------- = -------- -------- -------- mfr----- --------) >> IO-blocksize: blksize=3D1048576 >>=20 >> Secondly, the result of automounting on that directory. >>=20 >> [root@andromeda tmp]# ./samples/statx/test-statx /warthog/data >> statx(/warthog/data) =3D 0 >> results=3D17ff >> Size: 4096 Blocks: 8 IO Block: 1048576 = directory >> Device: 00:27 Inode: 2 Links: 124 >> Access: (3777/drwxrwxrwx) Uid: 0 Gid: 4041 >> Access: 2016-11-10 15:52:11.219935864+0000 >> Modify: 2016-11-10 08:07:32.482314928+0000 >> Change: 2016-11-10 08:07:32.482314928+0000 >> Data version: 58242ac41cbf8ab0h >> Attributes: 0000000000002000 (-------- -------- -------- = -------- -------- -------- --r----- --------) >> IO-blocksize: blksize=3D1048576 >>=20 >> Signed-off-by: David Howells >> --- >>=20 >> arch/x86/entry/syscalls/syscall_32.tbl | 1 >> arch/x86/entry/syscalls/syscall_64.tbl | 1 >> fs/exportfs/expfs.c | 4 >> fs/stat.c | 294 = +++++++++++++++++++++++++++++--- >> include/linux/fs.h | 5 - >> include/linux/stat.h | 19 +- >> include/linux/syscalls.h | 3 >> include/uapi/linux/fcntl.h | 2 >> include/uapi/linux/stat.h | 124 +++++++++++++ >> samples/Kconfig | 5 + >> samples/Makefile | 3 >> samples/statx/Makefile | 10 + >> samples/statx/test-statx.c | 248 = +++++++++++++++++++++++++++ >> 13 files changed, 679 insertions(+), 40 deletions(-) >> create mode 100644 samples/statx/Makefile >> create mode 100644 samples/statx/test-statx.c >>=20 >> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl = b/arch/x86/entry/syscalls/syscall_32.tbl >> index 2b3618542544..9ba050fe47f3 100644 >> --- a/arch/x86/entry/syscalls/syscall_32.tbl >> +++ b/arch/x86/entry/syscalls/syscall_32.tbl >> @@ -389,3 +389,4 @@ >> 380 i386 pkey_mprotect sys_pkey_mprotect >> 381 i386 pkey_alloc sys_pkey_alloc >> 382 i386 pkey_free sys_pkey_free >> +383 i386 statx sys_statx >> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl = b/arch/x86/entry/syscalls/syscall_64.tbl >> index e93ef0b38db8..5aef183e2f85 100644 >> --- a/arch/x86/entry/syscalls/syscall_64.tbl >> +++ b/arch/x86/entry/syscalls/syscall_64.tbl >> @@ -338,6 +338,7 @@ >> 329 common pkey_mprotect sys_pkey_mprotect >> 330 common pkey_alloc sys_pkey_alloc >> 331 common pkey_free sys_pkey_free >> +332 common statx sys_statx >>=20 >> # >> # x32-specific system call numbers start at 512 to avoid cache impact >> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c >> index a4b531be9168..2acc31751248 100644 >> --- a/fs/exportfs/expfs.c >> +++ b/fs/exportfs/expfs.c >> @@ -299,7 +299,9 @@ static int get_name(const struct path *path, char = *name, struct dentry *child) >> * filesystem supports 64-bit inode numbers. So we need to >> * actually call ->getattr, not just read i_ino: >> */ >> - error =3D vfs_getattr_nosec(&child_path, &stat); >> + stat.query_flags =3D 0; >> + stat.request_mask =3D STATX_BASIC_STATS; >> + error =3D vfs_xgetattr_nosec(&child_path, &stat); >> if (error) >> return error; >> buffer.ino =3D stat.ino; >> diff --git a/fs/stat.c b/fs/stat.c >> index bc045c7994e1..78c0a5086038 100644 >> --- a/fs/stat.c >> +++ b/fs/stat.c >> @@ -18,6 +18,15 @@ >> #include >> #include >>=20 >> +/** >> + * 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 =3D inode->i_sb->s_dev; >> @@ -27,87 +36,189 @@ void generic_fillattr(struct inode *inode, = struct kstat *stat) >> stat->uid =3D inode->i_uid; >> stat->gid =3D inode->i_gid; >> stat->rdev =3D inode->i_rdev; >> - stat->size =3D i_size_read(inode); >> - stat->atime =3D inode->i_atime; >> stat->mtime =3D inode->i_mtime; >> stat->ctime =3D inode->i_ctime; >> - stat->blksize =3D (1 << inode->i_blkbits); >> + stat->size =3D i_size_read(inode); >> stat->blocks =3D inode->i_blocks; >> -} >> + stat->blksize =3D 1 << inode->i_blkbits; >>=20 >> + stat->result_mask |=3D STATX_BASIC_STATS; >> + if (IS_NOATIME(inode)) >> + stat->result_mask &=3D ~STATX_ATIME; >> + else >> + stat->atime =3D inode->i_atime; >> + >> + if (IS_AUTOMOUNT(inode)) >> + stat->attributes |=3D STATX_ATTR_AUTOMOUNT; >> +} >> EXPORT_SYMBOL(generic_fillattr); >>=20 >> /** >> - * vfs_getattr_nosec - getattr without security checks >> + * vfs_xgetattr_nosec - getattr without security checks >> * @path: file to get attributes from >> * @stat: structure to return attributes in >> * >> * Get attributes without calling security_inode_getattr. >> * >> - * Currently the only caller other than vfs_getattr is internal to = the >> - * filehandle lookup code, which uses only the inode number and = returns >> - * no attributes to any user. Any other code probably wants >> - * vfs_getattr. >> + * Currently the only caller other than vfs_xgetattr is internal to = the >> + * filehandle lookup code, which uses only the inode number and = returns no >> + * attributes to any user. Any other code probably wants = vfs_xgetattr. >> + * >> + * The caller must set stat->request_mask to indicate what they want = and >> + * stat->query_flags to indicate whether the server should be = queried. >> */ >> -int vfs_getattr_nosec(struct path *path, struct kstat *stat) >> +int vfs_xgetattr_nosec(struct path *path, struct kstat *stat) >> { >> struct inode *inode =3D d_backing_inode(path->dentry); >>=20 >> + stat->query_flags &=3D ~KSTAT_QUERY_FLAGS; >> + >> + stat->result_mask =3D 0; >> + stat->attributes =3D 0; >> if (inode->i_op->getattr) >> return inode->i_op->getattr(path->mnt, path->dentry, = stat); >>=20 >> generic_fillattr(inode, stat); >> return 0; >> } >> +EXPORT_SYMBOL(vfs_xgetattr_nosec); >>=20 >> -EXPORT_SYMBOL(vfs_getattr_nosec); >> - >> -int vfs_getattr(struct path *path, struct kstat *stat) >> +/* >> + * vfs_xgetattr - Get the enhanced basic attributes of a file >> + * @path: 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 or can >> + * suppress the update by passing AT_NO_ATTR_SYNC. >> + * >> + * Bits must have been set in stat->request_mask to indicate which = attributes >> + * the caller wants retrieving. Any such attribute not requested = may be >> + * returned anyway, but the value may be approximate, and, if = remote, may not >> + * have been synchronised with the server. >> + * >> + * 0 will be returned on success, and a -ve error code if = unsuccessful. >> + */ >> +int vfs_xgetattr(struct path *path, struct kstat *stat) >> { >> int retval; >>=20 >> retval =3D security_inode_getattr(path); >> if (retval) >> return retval; >> - return vfs_getattr_nosec(path, stat); >> + return vfs_xgetattr_nosec(path, stat); >> } >> +EXPORT_SYMBOL(vfs_xgetattr); >>=20 >> +/** >> + * vfs_getattr - Get the basic attributes of a file >> + * @path: 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_xgetattr(), >> + * 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 path *path, struct kstat *stat) >> +{ >> + stat->query_flags =3D 0; >> + stat->request_mask =3D STATX_BASIC_STATS; >> + return vfs_xgetattr(path, stat); >> +} >> EXPORT_SYMBOL(vfs_getattr); >>=20 >> -int vfs_fstat(unsigned int fd, struct kstat *stat) >> +/** >> + * vfs_fstatx - Get the enhanced basic attributes by file descriptor >> + * @fd: The file descriptor referring to the file of interest >> + * @stat: The result structure to fill in. >> + * >> + * 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_fstatx(unsigned int fd, struct kstat *stat) >> { >> struct fd f =3D fdget_raw(fd); >> int error =3D -EBADF; >>=20 >> if (f.file) { >> - error =3D vfs_getattr(&f.file->f_path, stat); >> + error =3D vfs_xgetattr(&f.file->f_path, stat); >> fdput(f); >> } >> return error; >> } >> +EXPORT_SYMBOL(vfs_fstatx); >> + >> +/** >> + * vfs_fstat - Get basic attributes by file descriptor >> + * @fd: The file descriptor referring 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 =3D 0; >> + stat->request_mask =3D STATX_BASIC_STATS; >> + return vfs_fstatx(fd, stat); >> +} >> EXPORT_SYMBOL(vfs_fstat); >>=20 >> -int vfs_fstatat(int dfd, const char __user *filename, struct kstat = *stat, >> - int flag) >> +/** >> + * vfs_statx - Get basic and extra attributes by filename >> + * @dfd: A file descriptor representing the base dir for a relative = filename >> + * @filename: The name of the file of interest >> + * @flags: Flags to control the query >> + * @stat: The result structure to fill in. >> + * >> + * 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_statx(int dfd, const char __user *filename, int flags, >> + struct kstat *stat) >> { >> struct path path; >> int error =3D -EINVAL; >> - unsigned int lookup_flags =3D 0; >> + unsigned int lookup_flags =3D LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT; >>=20 >> - if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | >> - AT_EMPTY_PATH)) !=3D 0) >> - goto out; >> + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | >> + AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) !=3D 0) >> + return -EINVAL; >>=20 >> - if (!(flag & AT_SYMLINK_NOFOLLOW)) >> - lookup_flags |=3D LOOKUP_FOLLOW; >> - if (flag & AT_EMPTY_PATH) >> + if (flags & AT_SYMLINK_NOFOLLOW) >> + lookup_flags &=3D ~LOOKUP_FOLLOW; >> + if (flags & AT_NO_AUTOMOUNT) >> + lookup_flags &=3D ~LOOKUP_AUTOMOUNT; >> + if (flags & AT_EMPTY_PATH) >> lookup_flags |=3D LOOKUP_EMPTY; >> + stat->query_flags =3D flags; >> + >> retry: >> error =3D user_path_at(dfd, filename, lookup_flags, &path); >> if (error) >> goto out; >>=20 >> - error =3D vfs_getattr(&path, stat); >> + error =3D vfs_xgetattr(&path, stat); >> path_put(&path); >> if (retry_estale(error, lookup_flags)) { >> lookup_flags |=3D LOOKUP_REVAL; >> @@ -116,17 +227,65 @@ int vfs_fstatat(int dfd, const char __user = *filename, struct kstat *stat, >> out: >> return error; >> } >> +EXPORT_SYMBOL(vfs_statx); >> + >> +/** >> + * 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_statx(). 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 =3D STATX_BASIC_STATS; >> + return vfs_statx(dfd, filename, flags, stat); >> +} >> EXPORT_SYMBOL(vfs_fstatat); >>=20 >> -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_statx(). 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_statx() 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 =3D STATX_BASIC_STATS; >> + return vfs_statx(AT_FDCWD, filename, 0, stat); >> } >> EXPORT_SYMBOL(vfs_stat); >>=20 >> +/** >> + * vfs_lstat - Get basic attrs 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_statx(). 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_statx() 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); >> + stat->request_mask =3D STATX_BASIC_STATS; >> + return vfs_statx(AT_FDCWD, name, AT_SYMLINK_NOFOLLOW, stat); >> } >> EXPORT_SYMBOL(vfs_lstat); >>=20 >> @@ -141,7 +300,7 @@ static int cp_old_stat(struct kstat *stat, struct = __old_kernel_stat __user * sta >> { >> static int warncount =3D 5; >> struct __old_kernel_stat tmp; >> - >> + >> if (warncount > 0) { >> warncount--; >> printk(KERN_WARNING "VFS: Warning: %s using old stat() = call. Recompile your binary.\n", >> @@ -166,7 +325,7 @@ static int cp_old_stat(struct kstat *stat, struct = __old_kernel_stat __user * sta >> #if BITS_PER_LONG =3D=3D 32 >> if (stat->size > MAX_NON_LFS) >> return -EOVERFLOW; >> -#endif >> +#endif >> tmp.st_size =3D stat->size; >> tmp.st_atime =3D stat->atime.tv_sec; >> tmp.st_mtime =3D stat->mtime.tv_sec; >> @@ -443,6 +602,79 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char = __user *, filename, >> } >> #endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */ >>=20 >> +/* >> + * Set the statx results. >> + */ >> +static long statx_set_result(struct kstat *stat, struct statx __user = *buffer) >> +{ >> + uid_t uid =3D from_kuid_munged(current_user_ns(), stat->uid); >> + gid_t gid =3D from_kgid_munged(current_user_ns(), stat->gid); >> + >> +#define __put_timestamp(kts, uts) ( \ >> + __put_user(kts.tv_sec, uts##_s ) || \ >> + __put_user(kts.tv_nsec, uts##_ns )) >> + >> + if (__put_user(stat->result_mask, &buffer->stx_mask = ) || >> + __put_user(stat->mode, &buffer->stx_mode = ) || >> + __clear_user(&buffer->__spare0, sizeof(buffer->__spare0)) = || >> + __put_user(stat->nlink, &buffer->stx_nlink = ) || >> + __put_user(uid, &buffer->stx_uid = ) || >> + __put_user(gid, &buffer->stx_gid = ) || >> + __put_user(stat->attributes, &buffer->stx_attributes = ) || >> + __put_user(stat->blksize, &buffer->stx_blksize = ) || >> + __put_user(MAJOR(stat->rdev), &buffer->stx_rdev_major = ) || >> + __put_user(MINOR(stat->rdev), &buffer->stx_rdev_minor = ) || >> + __put_user(MAJOR(stat->dev), &buffer->stx_dev_major = ) || >> + __put_user(MINOR(stat->dev), &buffer->stx_dev_minor = ) || >> + __put_timestamp(stat->atime, &buffer->stx_atime = ) || >> + __put_timestamp(stat->btime, &buffer->stx_btime = ) || >> + __put_timestamp(stat->ctime, &buffer->stx_ctime = ) || >> + __put_timestamp(stat->mtime, &buffer->stx_mtime = ) || >> + __put_user(stat->ino, &buffer->stx_ino = ) || >> + __put_user(stat->size, &buffer->stx_size = ) || >> + __put_user(stat->blocks, &buffer->stx_blocks = ) || >> + __put_user(stat->version, &buffer->stx_version = ) || >> + __clear_user(&buffer->__spare1, sizeof(buffer->__spare1))) >> + return -EFAULT; >> + >> + return 0; >> +} >> + >> +/** >> + * sys_statx - System call to get enhanced stats >> + * @dfd: Base directory to pathwalk from *or* fd to stat. >> + * @filename: File to stat *or* NULL. >> + * @flags: AT_* flags to control pathwalk. >> + * @mask: Parts of statx struct actually required. >> + * @buffer: Result buffer. >> + * >> + * Note that if filename is NULL, then it does the equivalent of = fstat() using >> + * dfd to indicate the file of interest. >> + */ >> +SYSCALL_DEFINE5(statx, >> + int, dfd, const char __user *, filename, unsigned, = flags, >> + unsigned int, mask, >> + struct statx __user *, buffer) >> +{ >> + struct kstat stat; >> + int error; >> + >> + if (!access_ok(VERIFY_WRITE, buffer, sizeof(*buffer))) >> + return -EFAULT; >> + >> + memset(&stat, 0, sizeof(stat)); >> + stat.query_flags =3D flags; >> + stat.request_mask =3D mask & STATX_ALL; >> + >> + if (filename) >> + error =3D vfs_statx(dfd, filename, flags, &stat); >> + else >> + error =3D vfs_fstatx(dfd, &stat); >> + if (error) >> + return error; >> + return statx_set_result(&stat, buffer); >> +} >> + >> /* Caller is here responsible for sufficient locking (ie. = inode->i_lock) */ >> void __inode_add_bytes(struct inode *inode, loff_t bytes) >> { >> diff --git a/include/linux/fs.h b/include/linux/fs.h >> index 16d2b6e874d6..f153199566b4 100644 >> --- a/include/linux/fs.h >> +++ b/include/linux/fs.h >> @@ -2916,8 +2916,9 @@ extern const struct inode_operations = page_symlink_inode_operations; >> extern void kfree_link(void *); >> extern int generic_readlink(struct dentry *, char __user *, int); >> extern void generic_fillattr(struct inode *, struct kstat *); >> -int vfs_getattr_nosec(struct path *path, struct kstat *stat); >> +extern int vfs_xgetattr_nosec(struct path *path, struct kstat = *stat); >> extern int vfs_getattr(struct path *, struct kstat *); >> +extern int vfs_xgetattr(struct path *, 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); >> @@ -2935,6 +2936,8 @@ 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 const char *vfs_get_link(struct dentry *, struct delayed_call = *); >> +extern int vfs_xstat(int, const char __user *, int, struct kstat *); >> +extern int vfs_xfstat(unsigned int, struct kstat *); >>=20 >> extern int __generic_block_fiemap(struct inode *inode, >> struct fiemap_extent_info *fieinfo, >> diff --git a/include/linux/stat.h b/include/linux/stat.h >> index 075cb0c7eb2a..cf25bb5cf754 100644 >> --- a/include/linux/stat.h >> +++ b/include/linux/stat.h >> @@ -19,19 +19,26 @@ >> #include >>=20 >> struct kstat { >> - u64 ino; >> - dev_t dev; >> + u32 query_flags; /* Operational flags */ >> +#define KSTAT_QUERY_FLAGS (AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC) >> + u32 request_mask; /* What fields the user asked = for */ >> + u32 result_mask; /* What fields the user got */ >> umode_t mode; >> unsigned int nlink; >> + uint32_t blksize; /* Preferred I/O size */ >> + u64 attributes; >> + u64 ino; >> + dev_t dev; >> + dev_t rdev; >> kuid_t uid; >> kgid_t gid; >> - dev_t rdev; >> loff_t size; >> - struct timespec atime; >> + struct timespec atime; >> struct timespec mtime; >> struct timespec ctime; >> - unsigned long blksize; >> - unsigned long long blocks; >> + struct timespec btime; /* File creation time */ >> + u64 blocks; >> + u64 version; /* Data version */ >> }; >>=20 >> #endif >> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h >> index 91a740f6b884..980c3c9b06f8 100644 >> --- a/include/linux/syscalls.h >> +++ b/include/linux/syscalls.h >> @@ -48,6 +48,7 @@ struct stat; >> struct stat64; >> struct statfs; >> struct statfs64; >> +struct statx; >> struct __sysctl_args; >> struct sysinfo; >> struct timespec; >> @@ -902,5 +903,7 @@ asmlinkage long sys_pkey_mprotect(unsigned long = start, size_t len, >> unsigned long prot, int pkey); >> asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long = init_val); >> asmlinkage long sys_pkey_free(int pkey); >> +asmlinkage long sys_statx(int dfd, const char __user *path, unsigned = flags, >> + unsigned mask, struct statx __user *buffer); >>=20 >> #endif >> diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h >> index beed138bd359..c49cb6edaafa 100644 >> --- a/include/uapi/linux/fcntl.h >> +++ b/include/uapi/linux/fcntl.h >> @@ -62,6 +62,8 @@ >> #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ >> #define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal = automount traversal */ >> #define AT_EMPTY_PATH 0x1000 /* Allow empty relative = pathname */ >> +#define AT_STATX_FORCE_SYNC 0x2000 /* Force the attributes to be = sync'd with the server */ >> +#define AT_STATX_DONT_SYNC 0x4000 /* Don't sync attributes with = the server */ >>=20 >>=20 >> #endif /* _UAPI_LINUX_FCNTL_H */ >> diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h >> index 7fec7e36d921..32ea14c1c960 100644 >> --- a/include/uapi/linux/stat.h >> +++ b/include/uapi/linux/stat.h >> @@ -1,6 +1,7 @@ >> #ifndef _UAPI_LINUX_STAT_H >> #define _UAPI_LINUX_STAT_H >>=20 >> +#include >>=20 >> #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) >>=20 >> @@ -41,5 +42,128 @@ >>=20 >> #endif >>=20 >> +/* >> + * Structures for the extended file attribute retrieval system call >> + * (statx()). >> + * >> + * The caller passes a mask of what they're specifically interested = in as a >> + * parameter to statx(). What statx() actually got will be = indicated in >> + * st_mask upon return. >> + * >> + * For each bit in the mask argument: >> + * >> + * - if the datum is not supported: >> + * >> + * - the bit will be cleared, and >> + * >> + * - the datum will be set to an appropriate fabricated value if = one is >> + * available (eg. CIFS can take a default uid and gid), = otherwise >> + * >> + * - the field will be cleared; >> + * >> + * - otherwise, if explicitly requested: >> + * >> + * - the datum will be synchronised to the server if = AT_STATX_FORCE_SYNC is >> + * set or if the datum is considered out of date, and >> + * >> + * - the field will be filled in and the bit will be set; >> + * >> + * - 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 field and the bit will be cleared before = returning. >> + * >> + * Items in STATX_BASIC_STATS may be marked unavailable on return, = but they >> + * will have values installed for compatibility purposes so that = stat() and >> + * co. can be emulated in userspace. >> + */ >> +struct statx { >> + /* 0x00 */ >> + __u32 stx_mask; /* What results were written [uncond] */ >> + __u32 stx_blksize; /* Preferred general I/O size [uncond] = */ >> + __u64 stx_attributes; /* Flags conveying information about the = file [uncond] */ >> + /* 0x10 */ >> + __u32 stx_nlink; /* Number of hard links */ >> + __u32 stx_uid; /* User ID of owner */ >> + __u32 stx_gid; /* Group ID of owner */ >> + __u16 stx_mode; /* File mode */ >> + __u16 __spare0[1]; >> + /* 0x20 */ >> + __u64 stx_ino; /* Inode number */ >> + __u64 stx_size; /* File size */ >> + __u64 stx_blocks; /* Number of 512-byte blocks allocated = */ >> + __u64 stx_version; /* Data version number */ >> + /* 0x40 */ >> + __s64 stx_atime_s; /* Last access time */ >> + __s64 stx_btime_s; /* File creation time */ >> + __s64 stx_ctime_s; /* Last attribute change time */ >> + __s64 stx_mtime_s; /* Last data modification time */ >> + /* 0x60 */ >> + __s32 stx_atime_ns; /* Last access time (ns part) */ >> + __s32 stx_btime_ns; /* File creation time (ns part) */ >> + __s32 stx_ctime_ns; /* Last attribute change time (ns part) = */ >> + __s32 stx_mtime_ns; /* Last data modification time (ns part) = */ >> + /* 0x70 */ >> + __u32 stx_rdev_major; /* Device ID of special file [if = bdev/cdev] */ >> + __u32 stx_rdev_minor; >> + __u32 stx_dev_major; /* ID of device containing file [uncond] = */ >> + __u32 stx_dev_minor; >> + /* 0x80 */ >> + __u64 __spare1[16]; /* Spare space for future expansion */ >> + /* 0x100 */ >> +}; >> + >> +/* >> + * Flags to be stx_mask >> + * >> + * Query request/result mask for statx() and struct statx::stx_mask. >> + * >> + * These bits should be set in the mask argument of statx() to = request >> + * particular items when calling statx(). >> + */ >> +#define STATX_TYPE 0x00000001U /* Want/got stx_mode & = S_IFMT */ >> +#define STATX_MODE 0x00000002U /* Want/got stx_mode & = ~S_IFMT */ >> +#define STATX_NLINK 0x00000004U /* Want/got stx_nlink */ >> +#define STATX_UID 0x00000008U /* Want/got stx_uid */ >> +#define STATX_GID 0x00000010U /* Want/got stx_gid */ >> +#define STATX_ATIME 0x00000020U /* Want/got stx_atime */ >> +#define STATX_MTIME 0x00000040U /* Want/got stx_mtime */ >> +#define STATX_CTIME 0x00000080U /* Want/got stx_ctime */ >> +#define STATX_INO 0x00000100U /* Want/got stx_ino */ >> +#define STATX_SIZE 0x00000200U /* Want/got stx_size */ >> +#define STATX_BLOCKS 0x00000400U /* Want/got = stx_blocks */ >> +#define STATX_BASIC_STATS 0x000007ffU /* The stuff in the = normal stat struct */ >> +#define STATX_BTIME 0x00000800U /* Want/got stx_btime */ >> +#define STATX_VERSION 0x00001000U /* Want/got = stx_version */ >> +#define STATX_ALL 0x00001fffU /* All currently = supported flags */ >> + >> +/* >> + * Attributes to be found in stx_attributes >> + * >> + * These give information about the features or the state of a file = that might >> + * be of use to ordinary userspace programs such as GUIs or ls = rather than >> + * specialised tools. >> + * >> + * Note that the flags marked [I] correspond to generic FS_IOC_FLAGS >> + * semantically. Where possible, the numerical value is picked to = correspond >> + * also. >> + */ >> +#define STATX_ATTR_COMPRESSED 0x00000004 /* [I] File = is compressed by the fs */ >> +#define STATX_ATTR_IMMUTABLE 0x00000010 /* [I] File = is marked immutable */ >> +#define STATX_ATTR_APPEND 0x00000020 /* [I] File is = append-only */ >> +#define STATX_ATTR_NODUMP 0x00000040 /* [I] File is not to = be dumped */ >> +#define STATX_ATTR_NONUNIX_OWNERSHIP 0x00000100 /* File has = non-Unix ownership details */ >> +#define STATX_ATTR_HAS_ACL 0x00000200 /* File has an ACL */ >> +#define STATX_ATTR_ENCRYPTED 0x00000800 /* [I] File = requires key to decrypt in fs */ >> +#define STATX_ATTR_FS_IOC_FLAGS 0x00000874 /* Attrs = corresponding to FS_*_FL flags */ >> + >> +#define STATX_ATTR_KERNEL_API 0x00001000 /* File is = kernel API (eg: procfs/sysfs) */ >> +#define STATX_ATTR_REMOTE 0x00002000 /* File is remote and = needs network */ >> +#define STATX_ATTR_FABRICATED 0x00004000 /* File was = made up by fs and is transient */ >> +#define STATX_ATTR_AUTOMOUNT 0x00008000 /* Dir: = Automount trigger */ >> +#define STATX_ATTR_UNLISTED_DENTS 0x00010000 /* Dir: Lookup may = find files getdents doesn't */ >> + >>=20 >> #endif /* _UAPI_LINUX_STAT_H */ >> diff --git a/samples/Kconfig b/samples/Kconfig >> index a6d2a43bbf2e..94a7488f14ae 100644 >> --- a/samples/Kconfig >> +++ b/samples/Kconfig >> @@ -105,4 +105,9 @@ config SAMPLE_BLACKFIN_GPTIMERS >> help >> Build samples of blackfin gptimers sample module. >>=20 >> +config SAMPLE_STATX >> + bool "Build example extended-stat using code" >> + help >> + Build example userspace program to use the new extended-stat = syscall. >> + >> endif # SAMPLES >> diff --git a/samples/Makefile b/samples/Makefile >> index e17d66d77f09..8eeb15e13413 100644 >> --- a/samples/Makefile >> +++ b/samples/Makefile >> @@ -2,4 +2,5 @@ >>=20 >> obj-$(CONFIG_SAMPLES) +=3D kobject/ kprobes/ trace_events/ = livepatch/ \ >> hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ = seccomp/ \ >> - configfs/ connector/ v4l/ trace_printk/ = blackfin/ >> + configfs/ connector/ v4l/ trace_printk/ = blackfin/ \ >> + statx/ >> diff --git a/samples/statx/Makefile b/samples/statx/Makefile >> new file mode 100644 >> index 000000000000..1f80a3d8cf45 >> --- /dev/null >> +++ b/samples/statx/Makefile >> @@ -0,0 +1,10 @@ >> +# kbuild trick to avoid linker error. Can be omitted if a module is = built. >> +obj- :=3D dummy.o >> + >> +# List of programs to build >> +hostprogs-$(CONFIG_SAMPLE_STATX) :=3D test-statx >> + >> +# Tell kbuild to always build the programs >> +always :=3D $(hostprogs-y) >> + >> +HOSTCFLAGS_test-statx.o +=3D -I$(objtree)/usr/include >> diff --git a/samples/statx/test-statx.c b/samples/statx/test-statx.c >> new file mode 100644 >> index 000000000000..2419b33fc15d >> --- /dev/null >> +++ b/samples/statx/test-statx.c >> @@ -0,0 +1,248 @@ >> +/* Test the statx() system call >> + * >> + * Copyright (C) 2015 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 >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#define AT_STATX_FORCE_SYNC 0x2000 >> +#define AT_STATX_DONT_SYNC 0x4000 >> + >> +static __attribute__((unused)) >> +ssize_t statx(int dfd, const char *filename, unsigned flags, >> + unsigned int mask, struct statx *buffer) >> +{ >> + return syscall(__NR_statx, dfd, filename, flags, mask, buffer); >> +} >> + >> +static void print_time(const char *field, __s64 tv_sec, __s32 = tv_nsec) >> +{ >> + struct tm tm; >> + time_t tim; >> + char buffer[100]; >> + int len; >> + >> + tim =3D tv_sec; >> + if (!localtime_r(&tim, &tm)) { >> + perror("localtime_r"); >> + exit(1); >> + } >> + len =3D strftime(buffer, 100, "%F %T", &tm); >> + if (len =3D=3D 0) { >> + perror("strftime"); >> + exit(1); >> + } >> + printf("%s", field); >> + fwrite(buffer, 1, len, stdout); >> + printf(".%09u", tv_nsec); >> + len =3D strftime(buffer, 100, "%z", &tm); >> + if (len =3D=3D 0) { >> + perror("strftime2"); >> + exit(1); >> + } >> + fwrite(buffer, 1, len, stdout); >> + printf("\n"); >> +} >> + >> +static void dump_statx(struct statx *stx) >> +{ >> + char buffer[256], ft =3D '?'; >> + >> + printf("results=3D%x\n", stx->stx_mask); >> + >> + printf(" "); >> + if (stx->stx_mask & STATX_SIZE) >> + printf(" Size: %-15llu", (unsigned long = long)stx->stx_size); >> + if (stx->stx_mask & STATX_BLOCKS) >> + printf(" Blocks: %-10llu", (unsigned long = long)stx->stx_blocks); >> + printf(" IO Block: %-6llu ", (unsigned long = long)stx->stx_blksize); >> + if (stx->stx_mask & STATX_MODE) { >> + switch (stx->stx_mode & S_IFMT) { >> + case S_IFIFO: printf(" FIFO\n"); = ft =3D 'p'; break; >> + case S_IFCHR: printf(" character special file\n"); = ft =3D 'c'; break; >> + case S_IFDIR: printf(" directory\n"); = ft =3D 'd'; break; >> + case S_IFBLK: printf(" block special file\n"); = ft =3D 'b'; break; >> + case S_IFREG: printf(" regular file\n"); = ft =3D '-'; break; >> + case S_IFLNK: printf(" symbolic link\n"); = ft =3D 'l'; break; >> + case S_IFSOCK: printf(" socket\n"); = ft =3D 's'; break; >> + default: >> + printf("unknown type (%o)\n", stx->stx_mode & = S_IFMT); >> + break; >> + } >> + } >> + >> + sprintf(buffer, "%02x:%02x", stx->stx_dev_major, = stx->stx_dev_minor); >> + printf("Device: %-15s", buffer); >> + if (stx->stx_mask & STATX_INO) >> + printf(" Inode: %-11llu", (unsigned long long) = stx->stx_ino); >> + if (stx->stx_mask & STATX_SIZE) >> + printf(" Links: %-5u", stx->stx_nlink); >> + switch (stx->stx_mask & STATX_MODE) { >> + case S_IFBLK: >> + case S_IFCHR: >> + printf(" Device type: %u,%u", stx->stx_rdev_major, = stx->stx_rdev_minor); >> + break; >> + } >> + printf("\n"); >> + >> + if (stx->stx_mask & STATX_MODE) >> + printf("Access: (%04o/%c%c%c%c%c%c%c%c%c%c) ", >> + stx->stx_mode & 07777, >> + ft, >> + stx->stx_mode & S_IRUSR ? 'r' : '-', >> + stx->stx_mode & S_IWUSR ? 'w' : '-', >> + stx->stx_mode & S_IXUSR ? 'x' : '-', >> + stx->stx_mode & S_IRGRP ? 'r' : '-', >> + stx->stx_mode & S_IWGRP ? 'w' : '-', >> + stx->stx_mode & S_IXGRP ? 'x' : '-', >> + stx->stx_mode & S_IROTH ? 'r' : '-', >> + stx->stx_mode & S_IWOTH ? 'w' : '-', >> + stx->stx_mode & S_IXOTH ? 'x' : '-'); >> + if (stx->stx_mask & STATX_UID) >> + printf("Uid: %5d ", stx->stx_uid); >> + if (stx->stx_mask & STATX_GID) >> + printf("Gid: %5d\n", stx->stx_gid); >> + >> + if (stx->stx_mask & STATX_ATIME) >> + print_time("Access: ", stx->stx_atime_s, = stx->stx_atime_ns); >> + if (stx->stx_mask & STATX_MTIME) >> + print_time("Modify: ", stx->stx_mtime_s, = stx->stx_mtime_ns); >> + if (stx->stx_mask & STATX_CTIME) >> + print_time("Change: ", stx->stx_ctime_s, = stx->stx_ctime_ns); >> + if (stx->stx_mask & STATX_BTIME) >> + print_time(" Birth: ", stx->stx_btime_s, = stx->stx_btime_ns); >> + >> + if (stx->stx_mask & STATX_VERSION) >> + printf("Data version: %llxh\n", >> + (unsigned long long)stx->stx_version); >> + >> + if (stx->stx_attributes) { >> + unsigned char bits; >> + int loop, byte; >> + >> + static char attr_representation[64 + 1] =3D >> + /* STATX_ATTR_ flags: */ >> + "????????" /* 63-56 */ >> + "????????" /* 55-48 */ >> + "????????" /* 47-40 */ >> + "????????" /* 39-32 */ >> + "????????" /* 31-24 = 0x00000000-ff000000 */ >> + "???????u" /* 23-16 = 0x00000000-00ff0000 */ >> + "mfrke?AU" /* 15- 8 = 0x00000000-0000ff00 */ >> + "?dai?c??" /* 7- 0 = 0x00000000-000000ff */ >> + ; >> + >> + printf("Attributes: %016llx (", stx->stx_attributes); >> + for (byte =3D 64 - 8; byte >=3D 0; byte -=3D 8) { >> + bits =3D stx->stx_attributes >> byte; >> + for (loop =3D 7; loop >=3D 0; loop--) { >> + int bit =3D byte + loop; >> + >> + if (bits & 0x80) >> + putchar(attr_representation[63 - = bit]); >> + else >> + putchar('-'); >> + bits <<=3D 1; >> + } >> + if (byte) >> + putchar(' '); >> + } >> + printf(")\n"); >> + } >> + >> + printf("IO-blocksize: blksize=3D%u\n", stx->stx_blksize); >> +} >> + >> +static void dump_hex(unsigned long long *data, int from, int to) >> +{ >> + unsigned offset, print_offset =3D 1, col =3D 0; >> + >> + from /=3D 8; >> + to =3D (to + 7) / 8; >> + >> + for (offset =3D from; offset < to; offset++) { >> + if (print_offset) { >> + printf("%04x: ", offset * 8); >> + print_offset =3D 0; >> + } >> + printf("%016llx", data[offset]); >> + col++; >> + if ((col & 3) =3D=3D 0) { >> + printf("\n"); >> + print_offset =3D 1; >> + } else { >> + printf(" "); >> + } >> + } >> + >> + if (!print_offset) >> + printf("\n"); >> +} >> + >> +int main(int argc, char **argv) >> +{ >> + struct statx stx; >> + int ret, raw =3D 0, atflag =3D AT_SYMLINK_NOFOLLOW; >> + >> + unsigned int mask =3D STATX_ALL; >> + >> + for (argv++; *argv; argv++) { >> + if (strcmp(*argv, "-F") =3D=3D 0) { >> + atflag |=3D AT_STATX_FORCE_SYNC; >> + continue; >> + } >> + if (strcmp(*argv, "-D") =3D=3D 0) { >> + atflag |=3D AT_STATX_DONT_SYNC; >> + continue; >> + } >> + if (strcmp(*argv, "-L") =3D=3D 0) { >> + atflag &=3D ~AT_SYMLINK_NOFOLLOW; >> + continue; >> + } >> + if (strcmp(*argv, "-O") =3D=3D 0) { >> + mask &=3D ~STATX_BASIC_STATS; >> + continue; >> + } >> + if (strcmp(*argv, "-A") =3D=3D 0) { >> + atflag |=3D AT_NO_AUTOMOUNT; >> + continue; >> + } >> + if (strcmp(*argv, "-R") =3D=3D 0) { >> + raw =3D 1; >> + continue; >> + } >> + >> + memset(&stx, 0xbf, sizeof(stx)); >> + ret =3D statx(AT_FDCWD, *argv, atflag, mask, &stx); >> + printf("statx(%s) =3D %d\n", *argv, ret); >> + if (ret < 0) { >> + perror(*argv); >> + exit(1); >> + } >> + >> + if (raw) >> + dump_hex((unsigned long long *)&stx, 0, = sizeof(stx)); >> + >> + dump_statx(&stx); >> + } >> + return 0; >> +} >>=20 >> -- >> To unsubscribe from this list: send the line "unsubscribe = linux-fsdevel" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html >=20 > -- > Jeff Layton > -- > To unsubscribe from this list: send the line "unsubscribe = linux-fsdevel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Cheers, Andreas --Apple-Mail=_2FC85ADF-D8BB-4DEF-9C80-C77F96F0EEE5 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=signature.asc Content-Type: application/pgp-signature; name=signature.asc Content-Description: Message signed with OpenPGP using GPGMail -----BEGIN PGP SIGNATURE----- Comment: GPGTools - http://gpgtools.org iQIVAwUBWC5oKHKl2rkXzB/gAQgesQ//VRN+5QKsNvZWQXTAHEhHRTrNPIHw+LFB zOAVRRVMbXRjNS4DXhiW2xwjm78fxBE3DuSrCpI+lQ2xfB7mrfenujmoIxNz5Jg8 eSvNrZlJVAcnNNNRyhY9LZkRCOfBGggMsjjBt+xQJXxtpFq8e4LyNiT4xtX6qWab qJDEOAhdqtbl82psxmfIjy7lELQCxk6RH/pz1Oxl1/NDKqneXAVesJglHZYrAKSG iMU5M8H0t2DIMK2ZxVtcIXPLISKLnftO7f7bRdys4QmiGeCZ7yjPPbFRrKsGJU6J na/KDxm6HyYz6xeR8paFYQURv5lE09y9uz0+MfVyO/+0eGkCJ3pYDENaPXMVDx71 mcxCeXx8WfR+yIG9peZ8GCWZrNNBBjUj2wCuTIPRdvgoh47pV+yo9JaS4wUjrLok WQFudB418f9o6DPDT6wbvFCYCgJVRXxGmobHMQJPobOKQRaobvSuwIhQcHOrZbkP /nBllyuuQwYEY23HpqgnIfRtL1fJIzGFiDxmfbgrYSpv7XsRJwuVc8bF2MTXkFT2 96nm4c2mKuUPzoMcbgziTMk1Q4sA+QJUEp2lU1y3of0r6WEbbx4KDyVucPw8ZyF4 XIDOkK9tsdKWaFOQagBFrzJlwuiQdU0Fl8Ho06q/A1aPaonHQi5WtjjUQQCAVl5z wX9VdxjrL9Y= =q04e -----END PGP SIGNATURE----- --Apple-Mail=_2FC85ADF-D8BB-4DEF-9C80-C77F96F0EEE5--