diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-07 08:53:50 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-07 08:53:50 -0700 |
commit | 70f2ae1f002b0ed4b4382210df8e4b6e54079012 (patch) | |
tree | 113839bf2f25f75f7ca53595149ebe9679aee674 /fs/inode.c | |
parent | da315f6e03988a7127680bbc26e1028991b899b8 (diff) | |
parent | 01b39dcc95680b04c7af5de7f39f577e9c4865e3 (diff) |
Merge tag 'ovl-fixes-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi:
"This contains a fix for the vfs_mkdir() issue discovered by Al, as
well as other fixes and cleanups"
* tag 'ovl-fixes-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
ovl: use inode_insert5() to hash a newly created inode
ovl: Pass argument to ovl_get_inode() in a structure
vfs: factor out inode_insert5()
ovl: clean up copy-up error paths
ovl: return EIO on internal error
ovl: make ovl_create_real() cope with vfs_mkdir() safely
ovl: create helper ovl_create_temp()
ovl: return dentry from ovl_create_real()
ovl: struct cattr cleanups
ovl: strip debug argument from ovl_do_ helpers
ovl: remove WARN_ON() real inode attributes mismatch
ovl: Kconfig documentation fixes
ovl: update documentation for unionmount-testsuite
Diffstat (limited to 'fs/inode.c')
-rw-r--r-- | fs/inode.c | 164 |
1 files changed, 75 insertions, 89 deletions
diff --git a/fs/inode.c b/fs/inode.c index 3b55391072f3..0df41bb77e0f 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1004,6 +1004,70 @@ void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2) EXPORT_SYMBOL(unlock_two_nondirectories); /** + * inode_insert5 - obtain an inode from a mounted file system + * @inode: pre-allocated inode to use for insert to cache + * @hashval: hash value (usually inode number) to get + * @test: callback used for comparisons between inodes + * @set: callback used to initialize a new struct inode + * @data: opaque data pointer to pass to @test and @set + * + * Search for the inode specified by @hashval and @data in the inode cache, + * and if present it is return it with an increased reference count. This is + * a variant of iget5_locked() for callers that don't want to fail on memory + * allocation of inode. + * + * If the inode is not in cache, insert the pre-allocated inode to cache and + * return it locked, hashed, and with the I_NEW flag set. The file system gets + * to fill it in before unlocking it via unlock_new_inode(). + * + * Note both @test and @set are called with the inode_hash_lock held, so can't + * sleep. + */ +struct inode *inode_insert5(struct inode *inode, unsigned long hashval, + int (*test)(struct inode *, void *), + int (*set)(struct inode *, void *), void *data) +{ + struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval); + struct inode *old; + +again: + spin_lock(&inode_hash_lock); + old = find_inode(inode->i_sb, head, test, data); + if (unlikely(old)) { + /* + * Uhhuh, somebody else created the same inode under us. + * Use the old inode instead of the preallocated one. + */ + spin_unlock(&inode_hash_lock); + wait_on_inode(old); + if (unlikely(inode_unhashed(old))) { + iput(old); + goto again; + } + return old; + } + + if (set && unlikely(set(inode, data))) { + inode = NULL; + goto unlock; + } + + /* + * Return the locked inode with I_NEW set, the + * caller is responsible for filling in the contents + */ + spin_lock(&inode->i_lock); + inode->i_state |= I_NEW; + hlist_add_head(&inode->i_hash, head); + spin_unlock(&inode->i_lock); +unlock: + spin_unlock(&inode_hash_lock); + + return inode; +} +EXPORT_SYMBOL(inode_insert5); + +/** * iget5_locked - obtain an inode from a mounted file system * @sb: super block of file system * @hashval: hash value (usually inode number) to get @@ -1027,66 +1091,18 @@ struct inode *iget5_locked(struct super_block *sb, unsigned long hashval, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *data) { - struct hlist_head *head = inode_hashtable + hash(sb, hashval); - struct inode *inode; -again: - spin_lock(&inode_hash_lock); - inode = find_inode(sb, head, test, data); - spin_unlock(&inode_hash_lock); + struct inode *inode = ilookup5(sb, hashval, test, data); - if (inode) { - wait_on_inode(inode); - if (unlikely(inode_unhashed(inode))) { - iput(inode); - goto again; - } - return inode; - } + if (!inode) { + struct inode *new = new_inode(sb); - inode = alloc_inode(sb); - if (inode) { - struct inode *old; - - spin_lock(&inode_hash_lock); - /* We released the lock, so.. */ - old = find_inode(sb, head, test, data); - if (!old) { - if (set(inode, data)) - goto set_failed; - - spin_lock(&inode->i_lock); - inode->i_state = I_NEW; - hlist_add_head(&inode->i_hash, head); - spin_unlock(&inode->i_lock); - inode_sb_list_add(inode); - spin_unlock(&inode_hash_lock); - - /* Return the locked inode with I_NEW set, the - * caller is responsible for filling in the contents - */ - return inode; - } - - /* - * Uhhuh, somebody else created the same inode under - * us. Use the old inode instead of the one we just - * allocated. - */ - spin_unlock(&inode_hash_lock); - destroy_inode(inode); - inode = old; - wait_on_inode(inode); - if (unlikely(inode_unhashed(inode))) { - iput(inode); - goto again; + if (new) { + inode = inode_insert5(new, hashval, test, set, data); + if (unlikely(inode != new)) + iput(new); } } return inode; - -set_failed: - spin_unlock(&inode_hash_lock); - destroy_inode(inode); - return NULL; } EXPORT_SYMBOL(iget5_locked); @@ -1427,43 +1443,13 @@ EXPORT_SYMBOL(insert_inode_locked); int insert_inode_locked4(struct inode *inode, unsigned long hashval, int (*test)(struct inode *, void *), void *data) { - struct super_block *sb = inode->i_sb; - struct hlist_head *head = inode_hashtable + hash(sb, hashval); + struct inode *old = inode_insert5(inode, hashval, test, NULL, data); - while (1) { - struct inode *old = NULL; - - spin_lock(&inode_hash_lock); - hlist_for_each_entry(old, head, i_hash) { - if (old->i_sb != sb) - continue; - if (!test(old, data)) - continue; - spin_lock(&old->i_lock); - if (old->i_state & (I_FREEING|I_WILL_FREE)) { - spin_unlock(&old->i_lock); - continue; - } - break; - } - if (likely(!old)) { - spin_lock(&inode->i_lock); - inode->i_state |= I_NEW; - hlist_add_head(&inode->i_hash, head); - spin_unlock(&inode->i_lock); - spin_unlock(&inode_hash_lock); - return 0; - } - __iget(old); - spin_unlock(&old->i_lock); - spin_unlock(&inode_hash_lock); - wait_on_inode(old); - if (unlikely(!inode_unhashed(old))) { - iput(old); - return -EBUSY; - } + if (old != inode) { iput(old); + return -EBUSY; } + return 0; } EXPORT_SYMBOL(insert_inode_locked4); |