From ae50adcb0ac4cde67a7aec8ae67249d1b2be2948 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 16 Feb 2011 10:04:50 -0500 Subject: [PATCH 01/11] /proc/self is never going to be invalidated... Signed-off-by: Al Viro --- fs/proc/base.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 9d096e82b2010..d49c4b5d2c3e9 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2620,35 +2620,6 @@ static const struct pid_entry proc_base_stuff[] = { &proc_self_inode_operations, NULL, {}), }; -/* - * Exceptional case: normally we are not allowed to unhash a busy - * directory. In this case, however, we can do it - no aliasing problems - * due to the way we treat inodes. - */ -static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd) -{ - struct inode *inode; - struct task_struct *task; - - if (nd->flags & LOOKUP_RCU) - return -ECHILD; - - inode = dentry->d_inode; - task = get_proc_task(inode); - if (task) { - put_task_struct(task); - return 1; - } - d_drop(dentry); - return 0; -} - -static const struct dentry_operations proc_base_dentry_operations = -{ - .d_revalidate = proc_base_revalidate, - .d_delete = pid_delete_dentry, -}; - static struct dentry *proc_base_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { @@ -2685,7 +2656,6 @@ static struct dentry *proc_base_instantiate(struct inode *dir, if (p->fop) inode->i_fop = p->fop; ei->op = p->op; - d_set_d_op(dentry, &proc_base_dentry_operations); d_add(dentry, inode); error = NULL; out: From c78f4cc5e7d642c7009089817c12d8984e7ba872 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 16 Feb 2011 10:14:56 -0500 Subject: [PATCH 02/11] reiserfs xattr ->d_revalidate() shouldn't care about RCU ... it returns an error unconditionally Signed-off-by: Al Viro --- fs/reiserfs/xattr.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index 3cfb2e9336442..5c11ca82b7821 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -978,8 +978,6 @@ int reiserfs_permission(struct inode *inode, int mask, unsigned int flags) static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd) { - if (nd->flags & LOOKUP_RCU) - return -ECHILD; return -EPERM; } From 0eb980e31770cfeff6e27760b4692d595b8dbf28 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Mar 2011 03:44:05 -0500 Subject: [PATCH 03/11] ceph: fix d_revalidate oopsen on NFS exports can't blindly check nd->flags in ->d_revalidate() Signed-off-by: Al Viro --- fs/ceph/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 099a58615b90f..ebafa65a29b65 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -993,7 +993,7 @@ static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) { struct inode *dir; - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; dir = dentry->d_parent->d_inode; From 529c5f958f9e60abaa7407986034b17d17536bf2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Mar 2011 03:44:31 -0500 Subject: [PATCH 04/11] fuse: fix d_revalidate oopsen on NFS exports can't blindly check nd->flags in ->d_revalidate() Signed-off-by: Al Viro --- fs/fuse/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 83543b5ff9418..8bd0ef9286c37 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -158,7 +158,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) { struct inode *inode; - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; inode = entry->d_inode; From 53fe924161ff18d24c5c1c256549e9c1b9874827 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Mar 2011 03:44:48 -0500 Subject: [PATCH 05/11] gfs2: fix d_revalidate oopsen on NFS exports can't blindly check nd->flags in ->d_revalidate() Signed-off-by: Al Viro --- fs/gfs2/dentry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index 4a456338b8733..0da8da2c991d3 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c @@ -44,7 +44,7 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) int error; int had_lock = 0; - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; parent = dget_parent(dentry); From 4714e63731a8a641b5e0ed5e2e2191c13bf2d71a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Mar 2011 03:45:07 -0500 Subject: [PATCH 06/11] ocfs2: fix d_revalidate oopsen on NFS exports can't blindly check nd->flags in ->d_revalidate() Signed-off-by: Al Viro --- fs/ocfs2/dcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 6d80ecc7834f5..7eb90403fc8af 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -56,7 +56,7 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry, int ret = 0; /* if all else fails, just return false */ struct ocfs2_super *osb; - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; inode = dentry->d_inode; From 8ce84eeb5b40da21f20174dd25891a8409534237 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Mar 2011 03:45:28 -0500 Subject: [PATCH 07/11] jfs: fix d_revalidate oopsen on NFS exports can't blindly check nd->flags in ->d_revalidate() Signed-off-by: Al Viro --- fs/jfs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 81ead850ddb65..5a2b269428a6d 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1600,7 +1600,7 @@ static int jfs_ci_compare(const struct dentry *parent, static int jfs_ci_revalidate(struct dentry *dentry, struct nameidata *nd) { - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; /* * This is not negative dentry. Always valid. From 9177ada99d5e69fe91950b3ef5c23f2bcd109987 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Mar 2011 03:45:49 -0500 Subject: [PATCH 08/11] fat: fix d_revalidate oopsen on NFS exports can't blindly check nd->flags in ->d_revalidate() Signed-off-by: Al Viro --- fs/fat/namei_vfat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index f88f752babd9c..adae3fb7451aa 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -43,7 +43,7 @@ static int vfat_revalidate_shortname(struct dentry *dentry) static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) { - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; /* This is not negative dentry. Always valid. */ @@ -54,7 +54,7 @@ static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) { - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; /* From 991ac30d8b30ab6051dff5a7b07d84e6f5efa3a6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 10 Mar 2011 11:25:43 +1100 Subject: [PATCH 09/11] sysctl: the include of rcupdate.h is only needed in the kernel Fixes this built error: include/linux/sysctl.h:28: included file 'linux/rcupdate.h' is not exported Signed-off-by: Stephen Rothwell Acked-by: Al Viro Signed-off-by: Al Viro --- include/linux/sysctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index bb7c2b086fa45..11684d9e6bd23 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -25,7 +25,6 @@ #include #include #include -#include struct completion; @@ -931,6 +930,7 @@ enum #ifdef __KERNEL__ #include +#include /* For the /proc/sys support */ struct ctl_table; From 1ca551c6caae7b52178555cdedea6ca26444be46 Mon Sep 17 00:00:00 2001 From: Marco Stornelli Date: Sat, 5 Mar 2011 11:10:19 +0100 Subject: [PATCH 10/11] Check for immutable/append flag in fallocate path In the fallocate path the kernel doesn't check for the immutable/append flag. It's possible to have a race condition in this scenario: an application open a file in read/write and it does something, meanwhile root set the immutable flag on the file, the application at that point can call fallocate with success. In addition, we don't allow to do any unreserve operation on an append only file but only the reserve one. Signed-off-by: Marco Stornelli Signed-off-by: Al Viro --- fs/open.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/open.c b/fs/open.c index 5a2c6ebc22b5d..b47aab39c0572 100644 --- a/fs/open.c +++ b/fs/open.c @@ -233,6 +233,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) if (!(file->f_mode & FMODE_WRITE)) return -EBADF; + + /* It's not possible punch hole on append only file */ + if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode)) + return -EPERM; + + if (IS_IMMUTABLE(inode)) + return -EPERM; + /* * Revalidate the write permissions, in case security policy has * changed since the files were opened. From d891eedbc3b1b0fade8a9ce60cc0eba1cccb59e5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 18 Jan 2011 15:45:09 -0500 Subject: [PATCH 11/11] fs/dcache: allow d_obtain_alias() to return unhashed dentries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch, inodes are not promptly freed on last close of an unlinked file by an nfs client: client$ mount -tnfs4 server:/export/ /mnt/ client$ tail -f /mnt/FOO ... server$ df -i /export server$ rm /export/FOO (^C the tail -f) server$ df -i /export server$ echo 2 >/proc/sys/vm/drop_caches server$ df -i /export the df's will show that the inode is not freed on the filesystem until the last step, when it could have been freed after killing the client's tail -f. On-disk data won't be deallocated either, leading to possible spurious ENOSPC. This occurs because when the client does the close, it arrives in a compound with a putfh and a close, processed like: - putfh: look up the filehandle.  The only alias found for the inode will be DCACHE_UNHASHED alias referenced by the filp this, so it creates a new DCACHE_DISCONECTED dentry and returns that instead. - close: closes the existing filp, which is destroyed immediately by dput() since it's DCACHE_UNHASHED. - end of the compound: release the reference to the current filehandle, and dput() the new DCACHE_DISCONECTED dentry, which gets put on the unused list instead of being destroyed immediately. Nick Piggin suggested fixing this by allowing d_obtain_alias to return the unhashed dentry that is referenced by the filp, instead of making it create a new dentry. Leave __d_find_alias() alone to avoid changing behavior of other callers. Also nfsd doesn't need all the checks of __d_find_alias(); any dentry, hashed or unhashed, disconnected or not, should work. Signed-off-by: J. Bruce Fields Signed-off-by: Al Viro --- fs/dcache.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 2a6bd9a4ae975..611ffe928c03c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1523,6 +1523,28 @@ struct dentry * d_alloc_root(struct inode * root_inode) } EXPORT_SYMBOL(d_alloc_root); +static struct dentry * __d_find_any_alias(struct inode *inode) +{ + struct dentry *alias; + + if (list_empty(&inode->i_dentry)) + return NULL; + alias = list_first_entry(&inode->i_dentry, struct dentry, d_alias); + __dget(alias); + return alias; +} + +static struct dentry * d_find_any_alias(struct inode *inode) +{ + struct dentry *de; + + spin_lock(&inode->i_lock); + de = __d_find_any_alias(inode); + spin_unlock(&inode->i_lock); + return de; +} + + /** * d_obtain_alias - find or allocate a dentry for a given inode * @inode: inode to allocate the dentry for @@ -1552,7 +1574,7 @@ struct dentry *d_obtain_alias(struct inode *inode) if (IS_ERR(inode)) return ERR_CAST(inode); - res = d_find_alias(inode); + res = d_find_any_alias(inode); if (res) goto out_iput; @@ -1565,7 +1587,7 @@ struct dentry *d_obtain_alias(struct inode *inode) spin_lock(&inode->i_lock); - res = __d_find_alias(inode, 0); + res = __d_find_any_alias(inode); if (res) { spin_unlock(&inode->i_lock); dput(tmp);