[PATCH 0/6] Extended file stat system call

STALE5219d

5 messages, 3 authors, 2012-04-19 · open the first message on its own page

[PATCH 0/6] Extended file stat system call

From: David Howells <hidden>
Date: 2012-04-19 14:05:58

Implement a pair of new system calls to provide extended and further extensible
stat functions.

The second of the associated patches is the main patch that provides these new
system calls:

	ssize_t ret = xstat(int dfd,
			    const char *filename,
			    unsigned atflag,
			    unsigned mask,
			    struct xstat *buffer);

	ssize_t ret = fxstat(int fd,
			     unsigned atflag,
			     unsigned mask,
			     struct xstat *buffer);

which are more fully documented in the first patch's description.

These new stat functions provide a number of useful features, in summary:

 (1) More information: creation time, inode generation number, data version
     number, flags/attributes.  A subset of these is available through a number
     of filesystems (such as CIFS, NFS, AFS, Ext4 and BTRFS).

 (2) 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.

 (3) Heavyweight stat: Force a netfs to go to the server, even if it thinks its
     cached attributes are up to date.

 (4) 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.

 (5) Make the fields a consistent size on all arches, and make them large.

 (6) Can be extended by using more request flags and appending further data
     after the end of the standard return data.

Note that no lstat() equivalent is required as that can be implemented through
xstat() with atflag == 0.


=======
PATCHES
=======

Patch 1 defines the xstat() and fxstat() system calls.

Patches 2-6 implement extended stat facilities for Ext4, AFS, NFS and CIFS, and
make eCryptFS go to the lower filesystem for such details.


==============
CONSIDERATIONS
==============

Should fxstat() be implemented as xstat() with a NULL filename, using dfd as
fd?

Should the default for a network fs be to do an unconditional (heavyweight)
stat with a flag to suppress going to the server to update the locally held
attributes and flushing pending writebacks?

Should things like the Windows Archive, Hidden and System bits be handled
through IOC flags, perhaps expanded to 64-bits?

Are these things useful to userspace other than Samba and userspace NFS
servers?

Is it useful to pass the volume ID out?  Or is statfs() sufficient for this?

Should I add a sixth argument to xstat(), mark it reserved and require that
must be supplied as 0 to hedge against future use?

Is there anything else I can usefully add at the moment?


==========
TO BE DONE
==========

Autofs, ntfs, btrfs, ...

I should perhaps use u8/u32/u64 rather than uint8/32/64_t.

Handle remote filesystems being offline and indicate this with
XSTAT_INFO_OFFLINE.


=======
TESTING
=======

There's a test program attached to the description for the main patch.  It can
be run as follows:
[root@andromeda tmp]# ./xstat -R /mnt/foo

	xstat(/mnt/foo) = 0
	0000: 000081a40000ffef 0000000000000001 0000020000000000 0000100000080000
	0020: 0000000000000000 0000000600000008 000000004f88499a 0000000136fd9208
	0040: 000000004f88499a 0000000136fd9208 000000004f8849b9 0000000106daf187
	0060: 000000004f8849b9 0000000106daf187 000000000000000c 000000000000000f
	0080: 0000000000000008 00000000484ebbef 0000000000000025 5949ebd4711efd82
	00a0: d3250b5c15d5e380 0000000000000000 0000000000000000 0000000000000000
	00c0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
	00e0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
	results=ffef
	  Size: 15              Blocks: 8          IO Block: 4096    regular file
	Device: 08:06           Inode: 12          Links: 1    
	Access: (0644/-rw-r--r--)  Uid: 0   
	Gid: 0
	Access: 2012-04-13 16:43:22.922587656+0100
	Modify: 2012-04-13 16:43:53.115011975+0100
	Change: 2012-04-13 16:43:53.115011975+0100
	Create: 2012-04-13 16:43:22.922587656+0100
	Inode version: 484ebbefh
	Data version: 25h
	Inode flags: 00080000 (-------- ----e--- -------- --------)
	Information: 00000200 (-------- -------- ------a- --------)
	Volume ID: 82fd1e71d4eb4959-80e3d5155c0b25d3

David
---
David Howells (6):
      xstat: eCryptFS: Return extended attributes
      xstat: CIFS: Return extended attributes
      xstat: NFS: Return extended attributes
      xstat: AFS: Return extended attributes
      xstat: Ext4: Return extended attributes
      xstat: Add a pair of system calls to make extended file stats available


 arch/x86/syscalls/syscall_32.tbl |    2 
 arch/x86/syscalls/syscall_64.tbl |    2 
 fs/afs/inode.c                   |   29 ++-
 fs/afs/super.c                   |    7 +
 fs/cifs/cifsfs.h                 |    4 
 fs/cifs/cifsglob.h               |   16 +-
 fs/cifs/dir.c                    |    2 
 fs/cifs/inode.c                  |  120 +++++++++++--
 fs/ecryptfs/inode.c              |   14 +-
 fs/ext4/ext4.h                   |    2 
 fs/ext4/file.c                   |    2 
 fs/ext4/inode.c                  |   32 +++
 fs/ext4/namei.c                  |    2 
 fs/ext4/super.c                  |    1 
 fs/ext4/symlink.c                |    2 
 fs/nfs/inode.c                   |   49 ++++-
 fs/nfs/super.c                   |    1 
 fs/stat.c                        |  350 +++++++++++++++++++++++++++++++++++---
 include/linux/fcntl.h            |    1 
 include/linux/fs.h               |    4 
 include/linux/stat.h             |  126 +++++++++++++-
 include/linux/syscalls.h         |    7 +
 22 files changed, 694 insertions(+), 81 deletions(-)

[PATCH 3/6] xstat: AFS: Return extended attributes

From: David Howells <dhowells@redhat.com>
Date: 2012-04-19 14:06:39

Return extended attributes from the AFS filesystem.  This includes the
following:

 (1) The vnode uniquifier as st_gen.

 (2) The data version number as st_data_version.

 (3) XSTAT_INFO_AUTOMOUNT will be set on automount directories by virtue of
     S_AUTOMOUNT being set on the inode.  These are referrals to other volumes
     or other cells.

 (4) XSTAT_INFO_AUTODIR on a directory that does cell lookup for non-existent
     names and mounts them (typically mounted on /afs with -o autocell).  The
     resulting directories are marked XSTAT_INFO_FABRICATED as they do not
     actually exist in the mounted AFS directory.

 (6) Files, directories and symlinks accessed over AFS are marked
     XSTAT_INFO_REMOTE.

 (7) XSTAT_INFO_NONSYSTEM_OWNERSHIP is set as the UID and GID retrieved from an
     AFS share may not be applicable on the system.

 (8) XSTAT_INFO_HAS_ACL is set as AFS directories have ACLs (the UID and GID
     are only used through the ACLs) and these ACLs apply to the contents of
     the directories.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/inode.c |   29 +++++++++++++++++++++--------
 fs/afs/super.c |    7 +++++++
 2 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index d890ae3..062def2 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -71,9 +71,9 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 	inode->i_uid		= vnode->status.owner;
 	inode->i_gid		= 0;
 	inode->i_size		= vnode->status.size;
-	inode->i_ctime.tv_sec	= vnode->status.mtime_server;
-	inode->i_ctime.tv_nsec	= 0;
-	inode->i_atime		= inode->i_mtime = inode->i_ctime;
+	inode->i_mtime.tv_sec	= vnode->status.mtime_server;
+	inode->i_mtime.tv_nsec	= 0;
+	inode->i_atime		= inode->i_ctime = inode->i_mtime;
 	inode->i_blocks		= 0;
 	inode->i_generation	= vnode->fid.unique;
 	inode->i_version	= vnode->status.data_version;
@@ -374,16 +374,29 @@ 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_ATIME | XSTAT_CTIME | XSTAT_BLOCKS);
+	stat->result_mask |= XSTAT_GEN | XSTAT_VERSION;
+	stat->gen = inode->i_generation;
+	stat->version = inode->i_version;
+
+	if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags))
+		stat->information |= XSTAT_INFO_AUTODIR;
+
+	if (test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(inode)->flags))
+		stat->information |= XSTAT_INFO_FABRICATED;
+	else
+		stat->information |= XSTAT_INFO_REMOTE;
+
+	stat->information |=
+		XSTAT_INFO_NONSYSTEM_OWNERSHIP | XSTAT_INFO_HAS_ACL;
 	return 0;
 }
 
diff --git a/fs/afs/super.c b/fs/afs/super.c
index f02b31e..1f13b48 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -314,6 +314,13 @@ static int afs_fill_super(struct super_block *sb,
 	sb->s_bdi		= &as->volume->bdi;
 	strlcpy(sb->s_id, as->volume->vlocation->vldb.name, sizeof(sb->s_id));
 
+	/* construct a volume ID from the AFS volume ID and type */
+	sb->s_volume_id[4] = as->volume->type;
+	sb->s_volume_id[3] = as->volume->vid >> 0;
+	sb->s_volume_id[2] = as->volume->vid >> 8;
+	sb->s_volume_id[1] = as->volume->vid >> 16;
+	sb->s_volume_id[0] = as->volume->vid >> 24;
+
 	/* allocate the root inode and dentry */
 	fid.vid		= as->volume->vid;
 	fid.vnode	= 1;

[PATCH 5/6] xstat: CIFS: Return extended attributes

From: David Howells <dhowells@redhat.com>
Date: 2012-04-19 14:07:06

Return extended attributes from the CIFS filesystem.  This includes the
following:

 (1) Return the file creation time as btime.  We assume that the creation time
     won't change over the life of the inode.

 (2) Set XSTAT_INFO_AUTOMOUNT on referral/submount directories.

 (3) Unset XSTAT_INO if we made up the inode number and didn't get it from the
     server.

 (4) Unset XSTAT_[UG]ID if we are either returning values passed to mount
     and/or the server doesn't return them.

 (5) Map various Windows file attributes to FS_xxx_FL flags in st_ioc_flags
     and XSTAT_INFO_xxx flags in st_information, fetching them from the server
     if we don't have them yet or don't have a current copy.

     Possibly things like Hidden, System and Archive should be FS_xxx_FL flags
     rather than XSTAT_INFO_xxx flags and st_ioc_flags should be expanded to
     64 bits.

 (6) Set XSTAT_INFO_REMOTE on all files fetched by CIFS.

 (7) Set XSTAT_INFO_NONSYSTEM_OWNERSHIP on all files as they all have Windows
     ownership details too.

 (8) Set XSTAT_INFO_HAS_ACL if CONFIG_CIFS_ACL=y as Windows ACLs are available
     on the object.

Furthermore, what cifs_getattr() does can be controlled as follows:

 (1) If AT_FORCE_ATTR_SYNC is indicated, or if the inode flags or creation time
     are requested but not yet collected, then the attributes will be reread
     unconditionally.

 (2) If the basic stats are requested or if the inode flags are requested and
     have been collected previously, then the attributes will be reread if out
     of date.

 (3) Otherwise the cached attributes will be used - even if expired - without
     reference to the server.

Note that cifs_revalidate_dentry() will issue an extra operation to get the
FILE_ALL_INFO in addition to the FILE_UNIX_BASIC_INFO if it needs to collect
creation time and attributes on behalf of cifs_getattr().

[NOTE: THIS PATCH IS UNTESTED!]

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/cifs/cifsfs.h   |    4 +-
 fs/cifs/cifsglob.h |   16 +++++--
 fs/cifs/dir.c      |    2 -
 fs/cifs/inode.c    |  120 +++++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 118 insertions(+), 24 deletions(-)
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index d1389bb..021e327 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -56,9 +56,9 @@ extern int cifs_rmdir(struct inode *, struct dentry *);
 extern int cifs_rename(struct inode *, struct dentry *, struct inode *,
 		       struct dentry *);
 extern int cifs_revalidate_file_attr(struct file *filp);
-extern int cifs_revalidate_dentry_attr(struct dentry *);
+extern int cifs_revalidate_dentry_attr(struct dentry *, bool, bool);
 extern int cifs_revalidate_file(struct file *filp);
-extern int cifs_revalidate_dentry(struct dentry *);
+extern int cifs_revalidate_dentry(struct dentry *, bool, bool);
 extern int cifs_invalidate_mapping(struct inode *inode);
 extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 extern int cifs_setattr(struct dentry *, struct iattr *);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 4ff6313..d3567da 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -621,11 +621,15 @@ struct cifsInodeInfo {
 	/* BB add in lists for dirty pages i.e. write caching info for oplock */
 	struct list_head openFileList;
 	__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
-	bool clientCanCacheRead;	/* read oplock */
-	bool clientCanCacheAll;		/* read and writebehind oplock */
-	bool delete_pending;		/* DELETE_ON_CLOSE is set */
-	bool invalid_mapping;		/* pagecache is invalid */
+	bool clientCanCacheRead:1;	/* read oplock */
+	bool clientCanCacheAll:1;	/* read and writebehind oplock */
+	bool delete_pending:1;		/* DELETE_ON_CLOSE is set */
+	bool invalid_mapping:1;		/* pagecache is invalid */
+	bool btime_valid:1;		/* stored creation time is valid */
+	bool uid_faked:1;		/* true if i_uid is faked */
+	bool gid_faked:1;		/* true if i_gid is faked */
 	unsigned long time;		/* jiffies of last update of inode */
+	struct timespec btime;		/* creation time */
 	u64  server_eof;		/* current file size on server -- protected by i_lock */
 	u64  uniqueid;			/* server inode number */
 	u64  createtime;		/* creation time on server */
@@ -833,6 +837,9 @@ struct dfs_info3_param {
 #define CIFS_FATTR_DELETE_PENDING	0x2
 #define CIFS_FATTR_NEED_REVAL		0x4
 #define CIFS_FATTR_INO_COLLISION	0x8
+#define CIFS_FATTR_WINATTRS_VALID	0x10	/* T if cf_btime and cf_cifsattrs valid */
+#define CIFS_FATTR_UID_FAKED		0x20	/* T if cf_uid is faked */
+#define CIFS_FATTR_GID_FAKED		0x40	/* T if cf_gid is faked */
 
 struct cifs_fattr {
 	u32		cf_flags;
@@ -850,6 +857,7 @@ struct cifs_fattr {
 	struct timespec	cf_atime;
 	struct timespec	cf_mtime;
 	struct timespec	cf_ctime;
+	struct timespec	cf_btime;
 };
 
 static inline void free_dfs_info_param(struct dfs_info3_param *param)
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index d172c8e..d9e03ae 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -664,7 +664,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
 		return -ECHILD;
 
 	if (direntry->d_inode) {
-		if (cifs_revalidate_dentry(direntry))
+		if (cifs_revalidate_dentry(direntry, false, false))
 			return 0;
 		else {
 			/*
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 745da3d..662d5ce 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -135,13 +135,21 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
 	set_nlink(inode, fattr->cf_nlink);
 	inode->i_uid = fattr->cf_uid;
 	inode->i_gid = fattr->cf_gid;
+	if (fattr->cf_flags & CIFS_FATTR_UID_FAKED)
+		cifs_i->uid_faked = true;
+	if (fattr->cf_flags & CIFS_FATTR_GID_FAKED)
+		cifs_i->gid_faked = true;
 
 	/* if dynperm is set, don't clobber existing mode */
 	if (inode->i_state & I_NEW ||
 	    !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM))
 		inode->i_mode = fattr->cf_mode;
 
-	cifs_i->cifsAttrs = fattr->cf_cifsattrs;
+	if (fattr->cf_flags & CIFS_FATTR_WINATTRS_VALID) {
+		cifs_i->cifsAttrs = fattr->cf_cifsattrs;
+		cifs_i->btime = fattr->cf_btime;
+		cifs_i->btime_valid = true;
+	}
 
 	if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
 		cifs_i->time = 0;
@@ -248,15 +256,19 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
 		break;
 	}
 
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
+	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) {
 		fattr->cf_uid = cifs_sb->mnt_uid;
-	else
+		fattr->cf_flags |= CIFS_FATTR_UID_FAKED;
+	} else {
 		fattr->cf_uid = le64_to_cpu(info->Uid);
+	}
 
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
+	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) {
 		fattr->cf_gid = cifs_sb->mnt_gid;
-	else
+		fattr->cf_flags |= CIFS_FATTR_GID_FAKED;
+	} else {
 		fattr->cf_gid = le64_to_cpu(info->Gid);
+	}
 
 	fattr->cf_nlink = le64_to_cpu(info->Nlinks);
 }
@@ -283,7 +295,8 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
 	fattr->cf_ctime = CURRENT_TIME;
 	fattr->cf_mtime = CURRENT_TIME;
 	fattr->cf_nlink = 2;
-	fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
+	fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL |
+		CIFS_FATTR_UID_FAKED | CIFS_FATTR_GID_FAKED;
 }
 
 int cifs_get_file_info_unix(struct file *filp)
@@ -510,6 +523,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
 	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 
 	memset(fattr, 0, sizeof(*fattr));
+	fattr->cf_flags = CIFS_FATTR_WINATTRS_VALID;
 	fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
 	if (info->DeletePending)
 		fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
@@ -521,6 +535,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
 
 	fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
 	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
+	fattr->cf_btime = cifs_NTtimeToUnix(info->CreationTime);
 
 	if (adjust_tz) {
 		fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
@@ -1724,7 +1739,8 @@ int cifs_revalidate_file_attr(struct file *filp)
 	return rc;
 }
 
-int cifs_revalidate_dentry_attr(struct dentry *dentry)
+int cifs_revalidate_dentry_attr(struct dentry *dentry,
+				bool want_extra_bits, bool force)
 {
 	int xid;
 	int rc = 0;
@@ -1735,7 +1751,7 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
 	if (inode == NULL)
 		return -ENOENT;
 
-	if (!cifs_inode_needs_reval(inode))
+	if (!force && !cifs_inode_needs_reval(inode))
 		return rc;
 
 	xid = GetXid();
@@ -1752,9 +1768,12 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
 		 "%ld jiffies %ld", full_path, inode, inode->i_count.counter,
 		 dentry, dentry->d_time, jiffies);
 
-	if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
+	if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) {
 		rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
-	else
+		if (rc != 0)
+			goto out;
+	}
+	if (!cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext || want_extra_bits)
 		rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
 					 xid, NULL);
 
@@ -1779,12 +1798,13 @@ int cifs_revalidate_file(struct file *filp)
 }
 
 /* revalidate a dentry's inode attributes */
-int cifs_revalidate_dentry(struct dentry *dentry)
+int cifs_revalidate_dentry(struct dentry *dentry,
+			   bool want_extra_bits, bool force)
 {
 	int rc;
 	struct inode *inode = dentry->d_inode;
 
-	rc = cifs_revalidate_dentry_attr(dentry);
+	rc = cifs_revalidate_dentry_attr(dentry, want_extra_bits, force);
 	if (rc)
 		return rc;
 
@@ -1796,11 +1816,30 @@ int cifs_revalidate_dentry(struct dentry *dentry)
 int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		 struct kstat *stat)
 {
+	struct cifsInodeInfo *cifs_i = CIFS_I(dentry->d_inode);
 	struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
 	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 	struct inode *inode = dentry->d_inode;
+	unsigned force = stat->query_flags & AT_FORCE_ATTR_SYNC;
+	bool want_extra_bits = false;
+	u32 info, ioc = 0;
+	u32 attrs;
 	int rc;
 
+	if (cifs_i->uid_faked)
+		stat->request_mask &= ~XSTAT_UID;
+	if (cifs_i->gid_faked)
+		stat->request_mask &= ~XSTAT_GID;
+
+	if (stat->request_mask & XSTAT_BTIME && !cifs_i->btime_valid) {
+		want_extra_bits = true;
+		force = true;
+	}
+	if (stat->request_mask & XSTAT_IOC_FLAGS) {
+		want_extra_bits = true;
+		force = true;
+	}
+
 	/*
 	 * We need to be sure that all dirty pages are written and the server
 	 * has actual ctime, mtime and file length.
@@ -1814,13 +1853,14 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		}
 	}
 
-	rc = cifs_revalidate_dentry_attr(dentry);
-	if (rc)
-		return rc;
+	if (force || stat->request_mask & XSTAT_BASIC_STATS) {
+		rc = cifs_revalidate_dentry(dentry, want_extra_bits, force);
+		if (rc)
+			return rc;
+	}
 
 	generic_fillattr(inode, stat);
 	stat->blksize = CIFS_MAX_MSGSIZE;
-	stat->ino = CIFS_I(inode)->uniqueid;
 
 	/*
 	 * If on a multiuser mount without unix extensions, and the admin hasn't
@@ -1834,7 +1874,53 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID))
 			stat->gid = current_fsgid();
 	}
-	return rc;
+
+	info = XSTAT_INFO_REMOTE | XSTAT_INFO_NONSYSTEM_OWNERSHIP;
+#ifdef CONFIG_CIFS_ACL
+	info |= XSTAT_INFO_HAS_ACL;
+#endif
+
+	if (cifs_i->btime_valid) {
+		stat->btime = cifs_i->btime;
+		stat->result_mask |= XSTAT_BTIME;
+	}
+
+	/* We don't promise an inode number if we made one up */
+	stat->ino = cifs_i->uniqueid;
+	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
+		stat->result_mask &= ~XSTAT_INO;
+
+	/*
+	 * If on a multiuser mount without unix extensions, and the admin
+	 * hasn't overridden them, set the ownership to the fsuid/fsgid of the
+	 * current process.
+	 */
+	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) &&
+	    !tcon->unix_ext) {
+		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID))
+			stat->uid = current_fsuid();
+		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID))
+			stat->gid = current_fsgid();
+	}
+	if (cifs_i->uid_faked)
+		stat->result_mask &= ~XSTAT_UID;
+	if (cifs_i->gid_faked)
+		stat->result_mask &= ~XSTAT_GID;
+
+	attrs = cifs_i->cifsAttrs;
+	if (attrs & ATTR_HIDDEN)	info |= XSTAT_INFO_HIDDEN;
+	if (attrs & ATTR_SYSTEM)	info |= XSTAT_INFO_SYSTEM;
+	if (attrs & ATTR_ARCHIVE)	info |= XSTAT_INFO_ARCHIVE;
+	if (attrs & ATTR_TEMPORARY)	info |= XSTAT_INFO_TEMPORARY;
+	if (attrs & ATTR_REPARSE)	info |= XSTAT_INFO_REPARSE_POINT;
+	if (attrs & ATTR_OFFLINE)	info |= XSTAT_INFO_OFFLINE;
+	if (attrs & ATTR_ENCRYPTED)	info |= XSTAT_INFO_ENCRYPTED;
+	stat->information |= info;
+
+	if (attrs & ATTR_READONLY)	ioc |= FS_IMMUTABLE_FL;
+	if (attrs & ATTR_COMPRESSED)	ioc |= FS_COMPR_FL;
+	stat->ioc_flags |= ioc;
+	return 0;
 }
 
 static int cifs_truncate_page(struct address_space *mapping, loff_t from)

[PATCH 4/6] xstat: NFS: Return extended attributes

From: David Howells <dhowells@redhat.com>
Date: 2012-04-19 14:07:40

Return extended attributes from the NFS filesystem.  This includes the
following:

 (1) The change attribute as st_data_version if NFSv4.

 (2) XSTAT_INFO_AUTOMOUNT and XSTAT_INFO_FABRICATED are set on referral or
     submount directories that are automounted upon.  NFS shows one directory
     with a different FSID, but the local filesystem has two: the mountpoint
     directory and the root of the filesystem mounted upon it.

 (3) XSTAT_INFO_REMOTE is set on files acquired over NFS.

Furthermore, what nfs_getattr() does can be controlled as follows:

 (1) If AT_FORCE_ATTR_SYNC is indicated, or mtime, ctime or data_version (NFSv4
     only) are requested then the outstanding writes will be written to the
     server first.

 (2) The inode's attributes may be synchronised with the server:

     (a) If AT_FORCE_ATTR_SYNC is indicated or if atime is requested (and atime
     	 updating is not suppressed by a mount flag) then the attributes will
     	 be reread unconditionally.

     (b) If the data version or any of basic stats are requested then the
     	 attributes will be reread if the cached attributes have expired.

     (c) Otherwise the cached attributes will be used - even if expired -
     	 without reference to the server.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/nfs/inode.c |   49 +++++++++++++++++++++++++++++++++++++------------
 fs/nfs/super.c |    1 +
 2 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index e8bbfa5..460fcf3 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -509,11 +509,18 @@ 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_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_MTIME | XSTAT_CTIME | XSTAT_VERSION))) &&
+	    S_ISREG(inode->i_mode)) {
 		err = filemap_write_and_wait(inode->i_mapping);
 		if (err)
 			goto out;
@@ -528,18 +535,36 @@ 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 (mnt->mnt_flags & MNT_NOATIME ||
+	    (mnt->mnt_flags & MNT_NODIRATIME && S_ISDIR(inode->i_mode))) {
+		stat->ioc_flags |= FS_NOATIME_FL;
+		need_atime = 0;
+	} else if (!(stat->request_mask & XSTAT_ATIME)) {
 		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_BASIC_STATS | XSTAT_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_VERSION) {
+		stat->version = inode->i_version;
+		stat->result_mask |= XSTAT_VERSION;
+	}
+
+	if (IS_AUTOMOUNT(inode))
+		stat->information |= XSTAT_INFO_FABRICATED;
+
+	stat->information |= XSTAT_INFO_REMOTE;
+
 out:
 	return err;
 }
@@ -852,7 +877,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/nfs/super.c b/fs/nfs/super.c
index 37412f7..faa652c 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2222,6 +2222,7 @@ static int nfs_set_super(struct super_block *s, void *data)
 	ret = set_anon_super(s, server);
 	if (ret == 0)
 		server->s_dev = s->s_dev;
+	memcpy(&s->s_volume_id, &server->fsid, sizeof(s->s_volume_id));
 	return ret;
 }
 

Re: [PATCH 0/6] Extended file stat system call

From: Andreas Dilger <hidden>
Date: 2012-04-19 23:25:59

On 2012-04-19, at 8:05 AM, David Howells wrote:
Implement a pair of new system calls to provide extended and further
extensible stat functions.
Hallelujah for this.  I've been waiting/wanting something like this
for ages already.  Now if only we can get this landed before it
degrades into the mess it did last time.
Should fxstat() be implemented as xstat() with a NULL filename,
using dfd as fd?
I'm personally inclined toward fewer syscalls, especially since
the fstatxat()->statxat() mapping (if I can be so bold as to
prefer the names used later in this thread) is IMHO clear and
unambiguous, and avoids several thin wrappers in the kernel.
Should the default for a network fs be to do an unconditional
(heavyweight) stat with a flag to suppress going to the server
to update the locally held attributes and flushing pending writebacks?
NOOOooo!  If application writers are going to use this, they should
request the information needed, and no more.  Make no assumptions
about what information is easy or hard for a filesystem to return,
since the overhead can vary wildly depending on the implementation.

Something like "ls --color" (no -l or -s) always stats the file just
to get the mode bits to color executable files differently.  Having
to return other information that isn't totally free almost ruins the
benefit of adding a new syscall in the first place.
Should things like the Windows Archive, Hidden and System bits be
handled through IOC flags, perhaps expanded to 64-bits?
I'm definitely in favour of a 64-bit IOC flags value, since they are
getting close to running out already.  As to whether those other bits
should be merged into the IOC flags, I'm mostly indifferent, but I
lean toward including them since they are definitely related.

I wouldn't object to 64-bit UID/GID values or split 32-bit low/hi UID
and GID values, since NFSv4 and Kerberos realms will likely need this
at some point as well.  That said, if the API is extensible, it would
be just as easy to add the low/hi split values when they are needed
in the future.
Are these things useful to userspace other than Samba and userspace
NFS servers?
Definitely yes.  The GNU fileutils can use a lot of this, since they
are VERY stat() heavy for things like checking st_dev and st_ino
changes during directory traversal, but don't need any of the other
info.
Is it useful to pass the volume ID out?  Or is statfs() sufficient
for this?
Can't hurt, IMHO.  It is a better (more persistent) identifier than
st_dev, and if it is free, or explicitly requested by the application
(Samba, Ganesha, etc) it can be very useful.

Cheers, Andreas




Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help