@@ -670,6 +670,9 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
vnode->fid.vid, vnode->fid.vnode, vnode->flags,
key_serial(key));
+ if (!vnode->volume)
+ goto valid; /* Dynroot */
+
if (unlikely(test_bit(AFS_VNODE_DELETED, &vnode->flags))) {
if (vnode->vfs_inode.i_nlink)
clear_nlink(&vnode->vfs_inode);
@@ -728,10 +731,15 @@ int afs_getattr(struct user_namespace *mnt_userns, const struct path *path,
{
struct inode *inode = d_inode(path->dentry);
struct afs_vnode *vnode = AFS_FS_I(inode);
- int seq = 0;
+ struct afs_file *af = stat->file ? stat->file->private_data : NULL;
+ int ret, seq = 0;
_enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
+ ret = afs_validate(vnode, af ? af->key : NULL);
+ if (ret < 0)
+ return ret;
+
do {
read_seqbegin_or_lock(&vnode->cb_lock, &seq);
generic_fillattr(&init_user_ns, inode, stat);
@@ -303,7 +303,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
* actually call ->getattr, not just read i_ino:
*/
error = vfs_getattr_nosec(&child_path, &stat,
- STATX_INO, AT_STATX_SYNC_AS_STAT);
+ STATX_INO, AT_STATX_SYNC_AS_STAT, NULL);
if (error)
return error;
buffer.ino = stat.ino;
@@ -65,6 +65,7 @@ EXPORT_SYMBOL(generic_fillattr);
* @stat: structure to return attributes in
* @request_mask: STATX_xxx flags indicating what the caller wants
* @query_flags: Query mode (AT_STATX_SYNC_TYPE)
+ * @file: File with credential info or NULL
*
* Get attributes without calling security_inode_getattr.
*
@@ -73,12 +74,14 @@ EXPORT_SYMBOL(generic_fillattr);
* attributes to any user. Any other code probably wants vfs_getattr.
*/
int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
- u32 request_mask, unsigned int query_flags)
+ u32 request_mask, unsigned int query_flags,
+ struct file *file)
{
struct user_namespace *mnt_userns;
struct inode *inode = d_backing_inode(path->dentry);
memset(stat, 0, sizeof(*stat));
+ stat->file = file;
stat->result_mask |= STATX_BASIC_STATS;
query_flags &= AT_STATX_SYNC_TYPE;
@@ -139,7 +142,7 @@ int vfs_getattr(const struct path *path, struct kstat *stat,
retval = security_inode_getattr(path);
if (retval)
return retval;
- return vfs_getattr_nosec(path, stat, request_mask, query_flags);
+ return vfs_getattr_nosec(path, stat, request_mask, query_flags, NULL);
}
EXPORT_SYMBOL(vfs_getattr);
@@ -161,7 +164,11 @@ int vfs_fstat(int fd, struct kstat *stat)
f = fdget_raw(fd);
if (!f.file)
return -EBADF;
- error = vfs_getattr(&f.file->f_path, stat, STATX_BASIC_STATS, 0);
+
+ error = security_inode_getattr(&f.file->f_path);
+ if (!error)
+ error = vfs_getattr_nosec(&f.file->f_path, stat,
+ STATX_BASIC_STATS, 0, f.file);
fdput(f);
return error;
}
@@ -185,7 +192,9 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
struct kstat *stat, u32 request_mask)
{
struct path path;
+ struct fd f;
unsigned lookup_flags = 0;
+ bool put_fd = false;
int error;
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |
@@ -200,17 +209,36 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
lookup_flags |= LOOKUP_EMPTY;
retry:
- error = user_path_at(dfd, filename, lookup_flags, &path);
- if (error)
- goto out;
-
- error = vfs_getattr(&path, stat, request_mask, flags);
- stat->mnt_id = real_mount(path.mnt)->mnt_id;
- stat->result_mask |= STATX_MNT_ID;
- if (path.mnt->mnt_root == path.dentry)
- stat->attributes |= STATX_ATTR_MOUNT_ROOT;
- stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
- path_put(&path);
+ if ((lookup_flags & LOOKUP_EMPTY) &&
+ dfd >= 0 &&
+ filename &&
+ strnlen_user(filename, 2) == 0) {
+ /* Should we use ESTALE retry for direct-fd? */
+ f = fdget_raw(dfd);
+ if (!f.file)
+ return -EBADF;
+ path = f.file->f_path;
+ put_fd = true;
+ } else {
+ f.file = NULL;
+ error = user_path_at(dfd, filename, lookup_flags, &path);
+ if (error)
+ goto out;
+ }
+
+ error = security_inode_getattr(&path);
+ if (!error) {
+ error = vfs_getattr_nosec(&path, stat, request_mask, flags, f.file);
+ stat->mnt_id = real_mount(path.mnt)->mnt_id;
+ stat->result_mask |= STATX_MNT_ID;
+ if (path.mnt->mnt_root == path.dentry)
+ stat->attributes |= STATX_ATTR_MOUNT_ROOT;
+ stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
+ }
+ if (put_fd)
+ fdput(f);
+ else
+ path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
@@ -3312,7 +3312,8 @@ extern int page_symlink(struct inode *inode, const char *symname, int len);
extern const struct inode_operations page_symlink_inode_operations;
extern void kfree_link(void *);
void generic_fillattr(struct user_namespace *, struct inode *, struct kstat *);
-extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int);
+extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int,
+ struct file *);
extern int vfs_getattr(const struct path *, struct kstat *, u32, unsigned int);
void __inode_add_bytes(struct inode *inode, loff_t bytes);
void inode_add_bytes(struct inode *inode, loff_t bytes);
@@ -20,6 +20,7 @@
#include <linux/uidgid.h>
struct kstat {
+ struct file *file; /* File if called from fstat() equivalent or NULL */
u32 result_mask; /* What fields the user got */
umode_t mode;
unsigned int nlink;
read(), write() and ftruncate() all have the file available from which they can extract the information needed to perform authenticated operations to a network filesystem server for that filesystem, but fstat() and statx() do not. This could lead to the situation where a read(), say, on a file descriptor will work, but fstat() will fail because the calling process doesn't intrinsically have the right to do that. Change this by passing the file, if we have it, in struct kstat from which the filesystem can pick it up and use it, similar to the way ftruncate() passes the information in struct iattr. Make use of this in the afs filesystem to pass to validation in case we need to refetch the inode attributes from the server. Signed-off-by: David Howells <dhowells@redhat.com> cc: Alexander Viro <viro@zeniv.linux.org.uk> cc: linux-fsdevel@vger.kernel.org cc: linux-afs@lists.infradead.org --- fs/afs/inode.c | 10 ++++++++- fs/exportfs/expfs.c | 2 - fs/stat.c | 56 ++++++++++++++++++++++++++++++++++++++------------- include/linux/fs.h | 3 +- include/linux/stat.h | 1 5 files changed, 55 insertions(+), 17 deletions(-)