diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-13 23:56:27 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-13 23:56:27 (GMT) |
commit | 2821fe6b00a1e902fd399bb4b7e40bc3041f4d44 (patch) | |
tree | ec95a7f7e79c26abd48c82c238cdec5ac8a74d60 | |
parent | f47671e2d861a2093179cd64dda22016664b2015 (diff) | |
parent | 441a9d0e1e827e6433e3487145fbb0c5513301e2 (diff) | |
download | linux-2821fe6b00a1e902fd399bb4b7e40bc3041f4d44.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull VFS fixes from Al Viro:
"Several fixes, mostly for regressions in the last pile. Howeover,
prepend_path() forgetting to reininitalize dentry/vfsmount is in 3.12
as well and qib_fs had been leaking all along..."
The unpaired RCU lock issue was also independently reported by Dave
Jones with his fuzzer tool..
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
qib_fs: fix (some) dcache abuses
prepend_path() needs to reinitialize dentry/vfsmount/mnt on restarts
fix unpaired rcu lock in prepend_path()
locks: missing unlock on error in generic_add_lease()
aio: checking for NULL instead of IS_ERR
-rw-r--r-- | drivers/infiniband/hw/qib/qib_fs.c | 7 | ||||
-rw-r--r-- | fs/aio.c | 4 | ||||
-rw-r--r-- | fs/dcache.c | 13 | ||||
-rw-r--r-- | fs/locks.c | 1 |
4 files changed, 18 insertions, 7 deletions
diff --git a/drivers/infiniband/hw/qib/qib_fs.c b/drivers/infiniband/hw/qib/qib_fs.c index f247fc6e..c61e2a9 100644 --- a/drivers/infiniband/hw/qib/qib_fs.c +++ b/drivers/infiniband/hw/qib/qib_fs.c @@ -456,13 +456,13 @@ static int remove_file(struct dentry *parent, char *name) spin_lock(&tmp->d_lock); if (!(d_unhashed(tmp) && tmp->d_inode)) { - dget_dlock(tmp); __d_drop(tmp); spin_unlock(&tmp->d_lock); simple_unlink(parent->d_inode, tmp); } else { spin_unlock(&tmp->d_lock); } + dput(tmp); ret = 0; bail: @@ -491,6 +491,7 @@ static int remove_device_files(struct super_block *sb, goto bail; } + mutex_lock(&dir->d_inode->i_mutex); remove_file(dir, "counters"); remove_file(dir, "counter_names"); remove_file(dir, "portcounter_names"); @@ -505,8 +506,10 @@ static int remove_device_files(struct super_block *sb, } } remove_file(dir, "flash"); - d_delete(dir); + mutex_unlock(&dir->d_inode->i_mutex); ret = simple_rmdir(root->d_inode, dir); + d_delete(dir); + dput(dir); bail: mutex_unlock(&root->d_inode->i_mutex); @@ -163,8 +163,8 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages) struct file *file; struct path path; struct inode *inode = alloc_anon_inode(aio_mnt->mnt_sb); - if (!inode) - return ERR_PTR(-ENOMEM); + if (IS_ERR(inode)) + return ERR_CAST(inode); inode->i_mapping->a_ops = &aio_ctx_aops; inode->i_mapping->private_data = ctx; diff --git a/fs/dcache.c b/fs/dcache.c index 1f24cd6..a9dd384 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2912,9 +2912,9 @@ static int prepend_path(const struct path *path, const struct path *root, char **buffer, int *buflen) { - struct dentry *dentry = path->dentry; - struct vfsmount *vfsmnt = path->mnt; - struct mount *mnt = real_mount(vfsmnt); + struct dentry *dentry; + struct vfsmount *vfsmnt; + struct mount *mnt; int error = 0; unsigned seq, m_seq = 0; char *bptr; @@ -2924,10 +2924,14 @@ static int prepend_path(const struct path *path, restart_mnt: read_seqbegin_or_lock(&mount_lock, &m_seq); seq = 0; + rcu_read_lock(); restart: bptr = *buffer; blen = *buflen; error = 0; + dentry = path->dentry; + vfsmnt = path->mnt; + mnt = real_mount(vfsmnt); read_seqbegin_or_lock(&rename_lock, &seq); while (dentry != root->dentry || vfsmnt != root->mnt) { struct dentry * parent; @@ -2971,6 +2975,9 @@ restart: goto restart; } done_seqretry(&rename_lock, seq); + + if (!(m_seq & 1)) + rcu_read_unlock(); if (need_seqretry(&mount_lock, m_seq)) { m_seq = 1; goto restart_mnt; @@ -1494,6 +1494,7 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp if (is_deleg && arg == F_WRLCK) { /* Write delegations are not currently supported: */ + mutex_unlock(&inode->i_mutex); WARN_ON_ONCE(1); return -EINVAL; } |