mirror of https://github.com/torvalds/linux.git
vfs-6.19-rc1.directory.locking
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaSmOZwAKCRCRxhvAZXjc op9tAQCJ//STOkvYHfqgsdRD+cW9MRg/gPzfVZgnV1FTyf8sMgEA0IsY5zCZB9eh 9FdD0E57P8PlWRwWZ+LktnWBzRAUqwI= =MOVR -----END PGP SIGNATURE----- Merge tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull directory locking updates from Christian Brauner: "This contains the work to add centralized APIs for directory locking operations. This series is part of a larger effort to change directory operation locking to allow multiple concurrent operations in a directory. The ultimate goal is to lock the target dentry(s) rather than the whole parent directory. To help with changing the locking protocol, this series centralizes locking and lookup in new helper functions. The helpers establish a pattern where it is the dentry that is being locked and unlocked (currently the lock is held on dentry->d_parent->d_inode, but that can change in the future). This also changes vfs_mkdir() to unlock the parent on failure, as well as dput()ing the dentry. This allows end_creating() to only require the target dentry (which may be IS_ERR() after vfs_mkdir()), not the parent" * tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: nfsd: fix end_creating() conversion VFS: introduce end_creating_keep() VFS: change vfs_mkdir() to unlock on failure. ecryptfs: use new start_creating/start_removing APIs Add start_renaming_two_dentries() VFS/ovl/smb: introduce start_renaming_dentry() VFS/nfsd/ovl: introduce start_renaming() and end_renaming() VFS: add start_creating_killable() and start_removing_killable() VFS: introduce start_removing_dentry() smb/server: use end_removing_noperm for for target of smb2_create_link() VFS: introduce start_creating_noperm() and start_removing_noperm() VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing() VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating() VFS: tidy up do_unlinkat() VFS: introduce start_dirop() and end_dirop() debugfs: rename end_creating() to debugfs_end_creating()
This commit is contained in:
commit
a8058f8442
|
|
@ -1309,3 +1309,16 @@ a different length, use
|
|||
vfs_parse_fs_qstr(fc, key, &QSTR_LEN(value, len))
|
||||
|
||||
instead.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
vfs_mkdir() now returns a dentry - the one returned by ->mkdir(). If
|
||||
that dentry is different from the dentry passed in, including if it is
|
||||
an IS_ERR() dentry pointer, the original dentry is dput().
|
||||
|
||||
When vfs_mkdir() returns an error, and so both dputs() the original
|
||||
dentry and doesn't provide a replacement, it also unlocks the parent.
|
||||
Consequently the return value from vfs_mkdir() can be passed to
|
||||
end_creating() and the parent will be unlocked precisely when necessary.
|
||||
|
|
|
|||
|
|
@ -904,14 +904,9 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
|
|||
struct fscrypt_str name_str = FSTR_INIT((char *)qname->name, qname->len);
|
||||
int ret;
|
||||
|
||||
ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
|
||||
if (ret == -EINTR)
|
||||
return ret;
|
||||
|
||||
dentry = lookup_one(idmap, qname, parent);
|
||||
ret = PTR_ERR(dentry);
|
||||
dentry = start_creating_killable(idmap, parent, qname);
|
||||
if (IS_ERR(dentry))
|
||||
goto out_unlock;
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
ret = btrfs_may_create(idmap, dir, dentry);
|
||||
if (ret)
|
||||
|
|
@ -940,9 +935,7 @@ static noinline int btrfs_mksubvol(struct dentry *parent,
|
|||
out_up_read:
|
||||
up_read(&fs_info->subvol_sem);
|
||||
out_dput:
|
||||
dput(dentry);
|
||||
out_unlock:
|
||||
btrfs_inode_unlock(BTRFS_I(dir), 0);
|
||||
end_creating(dentry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -2417,18 +2410,10 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
|||
goto free_subvol_name;
|
||||
}
|
||||
|
||||
ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
|
||||
if (ret == -EINTR)
|
||||
goto free_subvol_name;
|
||||
dentry = lookup_one(idmap, &QSTR(subvol_name), parent);
|
||||
dentry = start_removing_killable(idmap, parent, &QSTR(subvol_name));
|
||||
if (IS_ERR(dentry)) {
|
||||
ret = PTR_ERR(dentry);
|
||||
goto out_unlock_dir;
|
||||
}
|
||||
|
||||
if (d_really_is_negative(dentry)) {
|
||||
ret = -ENOENT;
|
||||
goto out_dput;
|
||||
goto out_end_removing;
|
||||
}
|
||||
|
||||
inode = d_inode(dentry);
|
||||
|
|
@ -2449,7 +2434,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
|||
*/
|
||||
ret = -EPERM;
|
||||
if (!btrfs_test_opt(fs_info, USER_SUBVOL_RM_ALLOWED))
|
||||
goto out_dput;
|
||||
goto out_end_removing;
|
||||
|
||||
/*
|
||||
* Do not allow deletion if the parent dir is the same
|
||||
|
|
@ -2460,21 +2445,21 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
|||
*/
|
||||
ret = -EINVAL;
|
||||
if (root == dest)
|
||||
goto out_dput;
|
||||
goto out_end_removing;
|
||||
|
||||
ret = inode_permission(idmap, inode, MAY_WRITE | MAY_EXEC);
|
||||
if (ret)
|
||||
goto out_dput;
|
||||
goto out_end_removing;
|
||||
}
|
||||
|
||||
/* check if subvolume may be deleted by a user */
|
||||
ret = btrfs_may_delete(idmap, dir, dentry, 1);
|
||||
if (ret)
|
||||
goto out_dput;
|
||||
goto out_end_removing;
|
||||
|
||||
if (btrfs_ino(BTRFS_I(inode)) != BTRFS_FIRST_FREE_OBJECTID) {
|
||||
ret = -EINVAL;
|
||||
goto out_dput;
|
||||
goto out_end_removing;
|
||||
}
|
||||
|
||||
btrfs_inode_lock(BTRFS_I(inode), 0);
|
||||
|
|
@ -2483,10 +2468,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
|||
if (!ret)
|
||||
d_delete_notify(dir, dentry);
|
||||
|
||||
out_dput:
|
||||
dput(dentry);
|
||||
out_unlock_dir:
|
||||
btrfs_inode_unlock(BTRFS_I(dir), 0);
|
||||
out_end_removing:
|
||||
end_removing(dentry);
|
||||
free_subvol_name:
|
||||
kfree(subvol_name_ptr);
|
||||
free_parent:
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <trace/events/fscache.h>
|
||||
#include "internal.h"
|
||||
|
|
@ -428,10 +429,12 @@ static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie)
|
|||
if (!old_tmpfile) {
|
||||
struct cachefiles_volume *volume = object->volume;
|
||||
struct dentry *fan = volume->fanout[(u8)cookie->key_hash];
|
||||
struct dentry *obj;
|
||||
|
||||
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
|
||||
cachefiles_bury_object(volume->cache, object, fan,
|
||||
old_file->f_path.dentry,
|
||||
obj = start_removing_dentry(fan, old_file->f_path.dentry);
|
||||
if (!IS_ERR(obj))
|
||||
cachefiles_bury_object(volume->cache, object,
|
||||
fan, obj,
|
||||
FSCACHE_OBJECT_INVALIDATED);
|
||||
}
|
||||
fput(old_file);
|
||||
|
|
|
|||
|
|
@ -93,12 +93,11 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
|||
_enter(",,%s", dirname);
|
||||
|
||||
/* search the current directory for the element name */
|
||||
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
|
||||
|
||||
retry:
|
||||
ret = cachefiles_inject_read_error();
|
||||
if (ret == 0)
|
||||
subdir = lookup_one(&nop_mnt_idmap, &QSTR(dirname), dir);
|
||||
subdir = start_creating(&nop_mnt_idmap, dir, &QSTR(dirname));
|
||||
else
|
||||
subdir = ERR_PTR(ret);
|
||||
trace_cachefiles_lookup(NULL, dir, subdir);
|
||||
|
|
@ -129,10 +128,12 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
|||
if (ret < 0)
|
||||
goto mkdir_error;
|
||||
ret = cachefiles_inject_write_error();
|
||||
if (ret == 0)
|
||||
if (ret == 0) {
|
||||
subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
|
||||
else
|
||||
} else {
|
||||
end_creating(subdir);
|
||||
subdir = ERR_PTR(ret);
|
||||
}
|
||||
if (IS_ERR(subdir)) {
|
||||
trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
|
||||
cachefiles_trace_mkdir_error);
|
||||
|
|
@ -141,7 +142,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
|||
trace_cachefiles_mkdir(dir, subdir);
|
||||
|
||||
if (unlikely(d_unhashed(subdir) || d_is_negative(subdir))) {
|
||||
dput(subdir);
|
||||
end_creating(subdir);
|
||||
goto retry;
|
||||
}
|
||||
ASSERT(d_backing_inode(subdir));
|
||||
|
|
@ -154,7 +155,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
|||
|
||||
/* Tell rmdir() it's not allowed to delete the subdir */
|
||||
inode_lock(d_inode(subdir));
|
||||
inode_unlock(d_inode(dir));
|
||||
end_creating_keep(subdir);
|
||||
|
||||
if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) {
|
||||
pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
|
||||
|
|
@ -196,14 +197,11 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
|||
return ERR_PTR(-EBUSY);
|
||||
|
||||
mkdir_error:
|
||||
inode_unlock(d_inode(dir));
|
||||
if (!IS_ERR(subdir))
|
||||
dput(subdir);
|
||||
end_creating(subdir);
|
||||
pr_err("mkdir %s failed with error %d\n", dirname, ret);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
lookup_error:
|
||||
inode_unlock(d_inode(dir));
|
||||
ret = PTR_ERR(subdir);
|
||||
pr_err("Lookup %s failed with error %d\n", dirname, ret);
|
||||
return ERR_PTR(ret);
|
||||
|
|
@ -263,6 +261,8 @@ static int cachefiles_unlink(struct cachefiles_cache *cache,
|
|||
* - File backed objects are unlinked
|
||||
* - Directory backed objects are stuffed into the graveyard for userspace to
|
||||
* delete
|
||||
* On entry dir must be locked. It will be unlocked on exit.
|
||||
* On entry there must be at least 2 refs on rep, one will be dropped on exit.
|
||||
*/
|
||||
int cachefiles_bury_object(struct cachefiles_cache *cache,
|
||||
struct cachefiles_object *object,
|
||||
|
|
@ -278,27 +278,23 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
|
|||
_enter(",'%pd','%pd'", dir, rep);
|
||||
|
||||
if (rep->d_parent != dir) {
|
||||
inode_unlock(d_inode(dir));
|
||||
end_removing(rep);
|
||||
_leave(" = -ESTALE");
|
||||
return -ESTALE;
|
||||
}
|
||||
|
||||
/* non-directories can just be unlinked */
|
||||
if (!d_is_dir(rep)) {
|
||||
dget(rep); /* Stop the dentry being negated if it's only pinned
|
||||
* by a file struct.
|
||||
*/
|
||||
ret = cachefiles_unlink(cache, object, dir, rep, why);
|
||||
dput(rep);
|
||||
end_removing(rep);
|
||||
|
||||
inode_unlock(d_inode(dir));
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* directories have to be moved to the graveyard */
|
||||
_debug("move stale object to graveyard");
|
||||
inode_unlock(d_inode(dir));
|
||||
end_removing(rep);
|
||||
|
||||
try_again:
|
||||
/* first step is to make up a grave dentry in the graveyard */
|
||||
|
|
@ -425,13 +421,12 @@ int cachefiles_delete_object(struct cachefiles_object *object,
|
|||
|
||||
_enter(",OBJ%x{%pD}", object->debug_id, object->file);
|
||||
|
||||
/* Stop the dentry being negated if it's only pinned by a file struct. */
|
||||
dget(dentry);
|
||||
|
||||
inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT);
|
||||
dentry = start_removing_dentry(fan, dentry);
|
||||
if (IS_ERR(dentry))
|
||||
ret = PTR_ERR(dentry);
|
||||
else
|
||||
ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
|
||||
inode_unlock(d_backing_inode(fan));
|
||||
dput(dentry);
|
||||
end_removing(dentry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -644,8 +639,12 @@ bool cachefiles_look_up_object(struct cachefiles_object *object)
|
|||
|
||||
if (!d_is_reg(dentry)) {
|
||||
pr_err("%pd is not a file\n", dentry);
|
||||
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
|
||||
ret = cachefiles_bury_object(volume->cache, object, fan, dentry,
|
||||
struct dentry *de = start_removing_dentry(fan, dentry);
|
||||
if (IS_ERR(de))
|
||||
ret = PTR_ERR(de);
|
||||
else
|
||||
ret = cachefiles_bury_object(volume->cache, object,
|
||||
fan, de,
|
||||
FSCACHE_OBJECT_IS_WEIRD);
|
||||
dput(dentry);
|
||||
if (ret < 0)
|
||||
|
|
@ -679,36 +678,41 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
|
|||
|
||||
_enter(",%pD", object->file);
|
||||
|
||||
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
|
||||
ret = cachefiles_inject_read_error();
|
||||
if (ret == 0)
|
||||
dentry = lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan);
|
||||
dentry = start_creating(&nop_mnt_idmap, fan, &QSTR(object->d_name));
|
||||
else
|
||||
dentry = ERR_PTR(ret);
|
||||
if (IS_ERR(dentry)) {
|
||||
trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
|
||||
cachefiles_trace_lookup_error);
|
||||
_debug("lookup fail %ld", PTR_ERR(dentry));
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!d_is_negative(dentry)) {
|
||||
/*
|
||||
* This loop will only execute more than once if some other thread
|
||||
* races to create the object we are trying to create.
|
||||
*/
|
||||
while (!d_is_negative(dentry)) {
|
||||
ret = cachefiles_unlink(volume->cache, object, fan, dentry,
|
||||
FSCACHE_OBJECT_IS_STALE);
|
||||
if (ret < 0)
|
||||
goto out_dput;
|
||||
goto out_end;
|
||||
|
||||
end_creating(dentry);
|
||||
|
||||
dput(dentry);
|
||||
ret = cachefiles_inject_read_error();
|
||||
if (ret == 0)
|
||||
dentry = lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan);
|
||||
dentry = start_creating(&nop_mnt_idmap, fan,
|
||||
&QSTR(object->d_name));
|
||||
else
|
||||
dentry = ERR_PTR(ret);
|
||||
if (IS_ERR(dentry)) {
|
||||
trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
|
||||
cachefiles_trace_lookup_error);
|
||||
_debug("lookup fail %ld", PTR_ERR(dentry));
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -729,10 +733,9 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
|
|||
success = true;
|
||||
}
|
||||
|
||||
out_dput:
|
||||
dput(dentry);
|
||||
out_unlock:
|
||||
inode_unlock(d_inode(fan));
|
||||
out_end:
|
||||
end_creating(dentry);
|
||||
out:
|
||||
_leave(" = %u", success);
|
||||
return success;
|
||||
}
|
||||
|
|
@ -748,26 +751,20 @@ static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache,
|
|||
struct dentry *victim;
|
||||
int ret = -ENOENT;
|
||||
|
||||
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
|
||||
victim = start_removing(&nop_mnt_idmap, dir, &QSTR(filename));
|
||||
|
||||
victim = lookup_one(&nop_mnt_idmap, &QSTR(filename), dir);
|
||||
if (IS_ERR(victim))
|
||||
goto lookup_error;
|
||||
if (d_is_negative(victim))
|
||||
goto lookup_put;
|
||||
if (d_inode(victim)->i_flags & S_KERNEL_FILE)
|
||||
goto lookup_busy;
|
||||
return victim;
|
||||
|
||||
lookup_busy:
|
||||
ret = -EBUSY;
|
||||
lookup_put:
|
||||
inode_unlock(d_inode(dir));
|
||||
dput(victim);
|
||||
end_removing(victim);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
lookup_error:
|
||||
inode_unlock(d_inode(dir));
|
||||
ret = PTR_ERR(victim);
|
||||
if (ret == -ENOENT)
|
||||
return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */
|
||||
|
|
@ -815,18 +812,17 @@ int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
|
|||
|
||||
ret = cachefiles_bury_object(cache, NULL, dir, victim,
|
||||
FSCACHE_OBJECT_WAS_CULLED);
|
||||
dput(victim);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
fscache_count_culled();
|
||||
dput(victim);
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error_unlock:
|
||||
inode_unlock(d_inode(dir));
|
||||
end_removing(victim);
|
||||
error:
|
||||
dput(victim);
|
||||
if (ret == -ENOENT)
|
||||
return -ESTALE; /* Probably got retired by the netfs */
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/namei.h>
|
||||
#include "internal.h"
|
||||
#include <trace/events/fscache.h>
|
||||
|
||||
|
|
@ -58,8 +59,10 @@ void cachefiles_acquire_volume(struct fscache_volume *vcookie)
|
|||
if (ret < 0) {
|
||||
if (ret != -ESTALE)
|
||||
goto error_dir;
|
||||
inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT);
|
||||
cachefiles_bury_object(cache, NULL, cache->store, vdentry,
|
||||
vdentry = start_removing_dentry(cache->store, vdentry);
|
||||
if (!IS_ERR(vdentry))
|
||||
cachefiles_bury_object(cache, NULL, cache->store,
|
||||
vdentry,
|
||||
FSCACHE_VOLUME_IS_WEIRD);
|
||||
cachefiles_put_directory(volume->dentry);
|
||||
cond_resched();
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ static struct dentry *debugfs_start_creating(const char *name,
|
|||
return dentry;
|
||||
}
|
||||
|
||||
static struct dentry *failed_creating(struct dentry *dentry)
|
||||
static struct dentry *debugfs_failed_creating(struct dentry *dentry)
|
||||
{
|
||||
inode_unlock(d_inode(dentry->d_parent));
|
||||
dput(dentry);
|
||||
|
|
@ -411,7 +411,7 @@ static struct dentry *failed_creating(struct dentry *dentry)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static struct dentry *end_creating(struct dentry *dentry)
|
||||
static struct dentry *debugfs_end_creating(struct dentry *dentry)
|
||||
{
|
||||
inode_unlock(d_inode(dentry->d_parent));
|
||||
return dentry;
|
||||
|
|
@ -435,7 +435,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
|
|||
return dentry;
|
||||
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
|
||||
failed_creating(dentry);
|
||||
debugfs_failed_creating(dentry);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
||||
|
|
@ -443,7 +443,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
|
|||
if (unlikely(!inode)) {
|
||||
pr_err("out of free dentries, can not create file '%s'\n",
|
||||
name);
|
||||
return failed_creating(dentry);
|
||||
return debugfs_failed_creating(dentry);
|
||||
}
|
||||
|
||||
inode->i_mode = mode;
|
||||
|
|
@ -458,7 +458,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
|
|||
|
||||
d_instantiate(dentry, inode);
|
||||
fsnotify_create(d_inode(dentry->d_parent), dentry);
|
||||
return end_creating(dentry);
|
||||
return debugfs_end_creating(dentry);
|
||||
}
|
||||
|
||||
struct dentry *debugfs_create_file_full(const char *name, umode_t mode,
|
||||
|
|
@ -585,7 +585,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
|
|||
return dentry;
|
||||
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
|
||||
failed_creating(dentry);
|
||||
debugfs_failed_creating(dentry);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
||||
|
|
@ -593,7 +593,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
|
|||
if (unlikely(!inode)) {
|
||||
pr_err("out of free dentries, can not create directory '%s'\n",
|
||||
name);
|
||||
return failed_creating(dentry);
|
||||
return debugfs_failed_creating(dentry);
|
||||
}
|
||||
|
||||
inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
|
||||
|
|
@ -605,7 +605,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
|
|||
d_instantiate(dentry, inode);
|
||||
inc_nlink(d_inode(dentry->d_parent));
|
||||
fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
|
||||
return end_creating(dentry);
|
||||
return debugfs_end_creating(dentry);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_dir);
|
||||
|
||||
|
|
@ -632,7 +632,7 @@ struct dentry *debugfs_create_automount(const char *name,
|
|||
return dentry;
|
||||
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
|
||||
failed_creating(dentry);
|
||||
debugfs_failed_creating(dentry);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
||||
|
|
@ -640,7 +640,7 @@ struct dentry *debugfs_create_automount(const char *name,
|
|||
if (unlikely(!inode)) {
|
||||
pr_err("out of free dentries, can not create automount '%s'\n",
|
||||
name);
|
||||
return failed_creating(dentry);
|
||||
return debugfs_failed_creating(dentry);
|
||||
}
|
||||
|
||||
make_empty_dir_inode(inode);
|
||||
|
|
@ -652,7 +652,7 @@ struct dentry *debugfs_create_automount(const char *name,
|
|||
d_instantiate(dentry, inode);
|
||||
inc_nlink(d_inode(dentry->d_parent));
|
||||
fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
|
||||
return end_creating(dentry);
|
||||
return debugfs_end_creating(dentry);
|
||||
}
|
||||
EXPORT_SYMBOL(debugfs_create_automount);
|
||||
|
||||
|
|
@ -699,13 +699,13 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
|
|||
pr_err("out of free dentries, can not create symlink '%s'\n",
|
||||
name);
|
||||
kfree(link);
|
||||
return failed_creating(dentry);
|
||||
return debugfs_failed_creating(dentry);
|
||||
}
|
||||
inode->i_mode = S_IFLNK | S_IRWXUGO;
|
||||
inode->i_op = &debugfs_symlink_inode_operations;
|
||||
inode->i_link = link;
|
||||
d_instantiate(dentry, inode);
|
||||
return end_creating(dentry);
|
||||
return debugfs_end_creating(dentry);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_symlink);
|
||||
|
||||
|
|
@ -842,7 +842,8 @@ int __printf(2, 3) debugfs_change_name(struct dentry *dentry, const char *fmt, .
|
|||
int error = 0;
|
||||
const char *new_name;
|
||||
struct name_snapshot old_name;
|
||||
struct dentry *parent, *target;
|
||||
struct dentry *target;
|
||||
struct renamedata rd = {};
|
||||
struct inode *dir;
|
||||
va_list ap;
|
||||
|
||||
|
|
@ -855,36 +856,31 @@ int __printf(2, 3) debugfs_change_name(struct dentry *dentry, const char *fmt, .
|
|||
if (!new_name)
|
||||
return -ENOMEM;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
dir = d_inode(parent);
|
||||
inode_lock(dir);
|
||||
rd.old_parent = dget_parent(dentry);
|
||||
rd.new_parent = rd.old_parent;
|
||||
rd.flags = RENAME_NOREPLACE;
|
||||
target = lookup_noperm_unlocked(&QSTR(new_name), rd.new_parent);
|
||||
if (IS_ERR(target))
|
||||
return PTR_ERR(target);
|
||||
|
||||
error = start_renaming_two_dentries(&rd, dentry, target);
|
||||
if (error) {
|
||||
if (error == -EEXIST && target == dentry)
|
||||
/* it isn't an error to rename a thing to itself */
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dir = d_inode(rd.old_parent);
|
||||
take_dentry_name_snapshot(&old_name, dentry);
|
||||
|
||||
if (WARN_ON_ONCE(dentry->d_parent != parent)) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(old_name.name.name, new_name) == 0)
|
||||
goto out;
|
||||
target = lookup_noperm(&QSTR(new_name), parent);
|
||||
if (IS_ERR(target)) {
|
||||
error = PTR_ERR(target);
|
||||
goto out;
|
||||
}
|
||||
if (d_really_is_positive(target)) {
|
||||
dput(target);
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
simple_rename_timestamp(dir, dentry, dir, target);
|
||||
d_move(dentry, target);
|
||||
dput(target);
|
||||
simple_rename_timestamp(dir, dentry, dir, rd.new_dentry);
|
||||
d_move(dentry, rd.new_dentry);
|
||||
fsnotify_move(dir, dir, &old_name.name, d_is_dir(dentry), NULL, dentry);
|
||||
out:
|
||||
release_dentry_name_snapshot(&old_name);
|
||||
inode_unlock(dir);
|
||||
dput(parent);
|
||||
end_renaming(&rd);
|
||||
out:
|
||||
dput(rd.old_parent);
|
||||
dput(target);
|
||||
kfree_const(new_name);
|
||||
return error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,18 +24,26 @@
|
|||
#include <linux/unaligned.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
static int lock_parent(struct dentry *dentry,
|
||||
struct dentry **lower_dentry,
|
||||
struct inode **lower_dir)
|
||||
static struct dentry *ecryptfs_start_creating_dentry(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *lower_dir_dentry;
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct dentry *ret;
|
||||
|
||||
lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
|
||||
*lower_dir = d_inode(lower_dir_dentry);
|
||||
*lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
ret = start_creating_dentry(ecryptfs_dentry_to_lower(parent),
|
||||
ecryptfs_dentry_to_lower(dentry));
|
||||
dput(parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inode_lock_nested(*lower_dir, I_MUTEX_PARENT);
|
||||
return (*lower_dentry)->d_parent == lower_dir_dentry ? 0 : -EINVAL;
|
||||
static struct dentry *ecryptfs_start_removing_dentry(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct dentry *ret;
|
||||
|
||||
ret = start_removing_dentry(ecryptfs_dentry_to_lower(parent),
|
||||
ecryptfs_dentry_to_lower(dentry));
|
||||
dput(parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ecryptfs_inode_test(struct inode *inode, void *lower_inode)
|
||||
|
|
@ -141,15 +149,12 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
|
|||
struct inode *lower_dir;
|
||||
int rc;
|
||||
|
||||
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
|
||||
dget(lower_dentry); // don't even try to make the lower negative
|
||||
if (!rc) {
|
||||
if (d_unhashed(lower_dentry))
|
||||
rc = -EINVAL;
|
||||
else
|
||||
rc = vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry,
|
||||
NULL);
|
||||
}
|
||||
lower_dentry = ecryptfs_start_removing_dentry(dentry);
|
||||
if (IS_ERR(lower_dentry))
|
||||
return PTR_ERR(lower_dentry);
|
||||
|
||||
lower_dir = lower_dentry->d_parent->d_inode;
|
||||
rc = vfs_unlink(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
|
||||
goto out_unlock;
|
||||
|
|
@ -158,8 +163,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
|
|||
set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
|
||||
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
|
||||
out_unlock:
|
||||
dput(lower_dentry);
|
||||
inode_unlock(lower_dir);
|
||||
end_removing(lower_dentry);
|
||||
if (!rc)
|
||||
d_drop(dentry);
|
||||
return rc;
|
||||
|
|
@ -186,8 +190,10 @@ ecryptfs_do_create(struct inode *directory_inode,
|
|||
struct inode *lower_dir;
|
||||
struct inode *inode;
|
||||
|
||||
rc = lock_parent(ecryptfs_dentry, &lower_dentry, &lower_dir);
|
||||
if (!rc)
|
||||
lower_dentry = ecryptfs_start_creating_dentry(ecryptfs_dentry);
|
||||
if (IS_ERR(lower_dentry))
|
||||
return ERR_CAST(lower_dentry);
|
||||
lower_dir = lower_dentry->d_parent->d_inode;
|
||||
rc = vfs_create(&nop_mnt_idmap, lower_dentry, mode, NULL);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
|
||||
|
|
@ -204,7 +210,7 @@ ecryptfs_do_create(struct inode *directory_inode,
|
|||
fsstack_copy_attr_times(directory_inode, lower_dir);
|
||||
fsstack_copy_inode_size(directory_inode, lower_dir);
|
||||
out_lock:
|
||||
inode_unlock(lower_dir);
|
||||
end_creating(lower_dentry);
|
||||
return inode;
|
||||
}
|
||||
|
||||
|
|
@ -432,8 +438,10 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
|
|||
|
||||
file_size_save = i_size_read(d_inode(old_dentry));
|
||||
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
|
||||
rc = lock_parent(new_dentry, &lower_new_dentry, &lower_dir);
|
||||
if (!rc)
|
||||
lower_new_dentry = ecryptfs_start_creating_dentry(new_dentry);
|
||||
if (IS_ERR(lower_new_dentry))
|
||||
return PTR_ERR(lower_new_dentry);
|
||||
lower_dir = lower_new_dentry->d_parent->d_inode;
|
||||
rc = vfs_link(lower_old_dentry, &nop_mnt_idmap, lower_dir,
|
||||
lower_new_dentry, NULL);
|
||||
if (rc || d_really_is_negative(lower_new_dentry))
|
||||
|
|
@ -447,7 +455,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
|
|||
ecryptfs_inode_to_lower(d_inode(old_dentry))->i_nlink);
|
||||
i_size_write(d_inode(new_dentry), file_size_save);
|
||||
out_lock:
|
||||
inode_unlock(lower_dir);
|
||||
end_creating(lower_new_dentry);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -467,9 +475,11 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
|
|||
size_t encoded_symlen;
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
|
||||
|
||||
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
|
||||
if (rc)
|
||||
goto out_lock;
|
||||
lower_dentry = ecryptfs_start_creating_dentry(dentry);
|
||||
if (IS_ERR(lower_dentry))
|
||||
return PTR_ERR(lower_dentry);
|
||||
lower_dir = lower_dentry->d_parent->d_inode;
|
||||
|
||||
mount_crypt_stat = &ecryptfs_superblock_to_private(
|
||||
dir->i_sb)->mount_crypt_stat;
|
||||
rc = ecryptfs_encrypt_and_encode_filename(&encoded_symname,
|
||||
|
|
@ -489,7 +499,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
|
|||
fsstack_copy_attr_times(dir, lower_dir);
|
||||
fsstack_copy_inode_size(dir, lower_dir);
|
||||
out_lock:
|
||||
inode_unlock(lower_dir);
|
||||
end_creating(lower_dentry);
|
||||
if (d_really_is_negative(dentry))
|
||||
d_drop(dentry);
|
||||
return rc;
|
||||
|
|
@ -500,12 +510,14 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
|||
{
|
||||
int rc;
|
||||
struct dentry *lower_dentry;
|
||||
struct dentry *lower_dir_dentry;
|
||||
struct inode *lower_dir;
|
||||
|
||||
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
lower_dentry = ecryptfs_start_creating_dentry(dentry);
|
||||
if (IS_ERR(lower_dentry))
|
||||
return lower_dentry;
|
||||
lower_dir_dentry = dget(lower_dentry->d_parent);
|
||||
lower_dir = lower_dir_dentry->d_inode;
|
||||
lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
|
||||
lower_dentry, mode, NULL);
|
||||
rc = PTR_ERR(lower_dentry);
|
||||
|
|
@ -521,7 +533,7 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
|||
fsstack_copy_inode_size(dir, lower_dir);
|
||||
set_nlink(dir, lower_dir->i_nlink);
|
||||
out:
|
||||
inode_unlock(lower_dir);
|
||||
end_creating(lower_dentry);
|
||||
if (d_really_is_negative(dentry))
|
||||
d_drop(dentry);
|
||||
return ERR_PTR(rc);
|
||||
|
|
@ -533,21 +545,18 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
struct inode *lower_dir;
|
||||
int rc;
|
||||
|
||||
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
|
||||
dget(lower_dentry); // don't even try to make the lower negative
|
||||
if (!rc) {
|
||||
if (d_unhashed(lower_dentry))
|
||||
rc = -EINVAL;
|
||||
else
|
||||
lower_dentry = ecryptfs_start_removing_dentry(dentry);
|
||||
if (IS_ERR(lower_dentry))
|
||||
return PTR_ERR(lower_dentry);
|
||||
lower_dir = lower_dentry->d_parent->d_inode;
|
||||
|
||||
rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);
|
||||
}
|
||||
if (!rc) {
|
||||
clear_nlink(d_inode(dentry));
|
||||
fsstack_copy_attr_times(dir, lower_dir);
|
||||
set_nlink(dir, lower_dir->i_nlink);
|
||||
}
|
||||
dput(lower_dentry);
|
||||
inode_unlock(lower_dir);
|
||||
end_removing(lower_dentry);
|
||||
if (!rc)
|
||||
d_drop(dentry);
|
||||
return rc;
|
||||
|
|
@ -561,10 +570,12 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
|||
struct dentry *lower_dentry;
|
||||
struct inode *lower_dir;
|
||||
|
||||
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
|
||||
if (!rc)
|
||||
rc = vfs_mknod(&nop_mnt_idmap, lower_dir,
|
||||
lower_dentry, mode, dev, NULL);
|
||||
lower_dentry = ecryptfs_start_creating_dentry(dentry);
|
||||
if (IS_ERR(lower_dentry))
|
||||
return PTR_ERR(lower_dentry);
|
||||
lower_dir = lower_dentry->d_parent->d_inode;
|
||||
|
||||
rc = vfs_mknod(&nop_mnt_idmap, lower_dir, lower_dentry, mode, dev, NULL);
|
||||
if (rc || d_really_is_negative(lower_dentry))
|
||||
goto out;
|
||||
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
|
||||
|
|
@ -573,7 +584,7 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
|||
fsstack_copy_attr_times(dir, lower_dir);
|
||||
fsstack_copy_inode_size(dir, lower_dir);
|
||||
out:
|
||||
inode_unlock(lower_dir);
|
||||
end_removing(lower_dentry);
|
||||
if (d_really_is_negative(dentry))
|
||||
d_drop(dentry);
|
||||
return rc;
|
||||
|
|
@ -589,7 +600,6 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
|||
struct dentry *lower_new_dentry;
|
||||
struct dentry *lower_old_dir_dentry;
|
||||
struct dentry *lower_new_dir_dentry;
|
||||
struct dentry *trap;
|
||||
struct inode *target_inode;
|
||||
struct renamedata rd = {};
|
||||
|
||||
|
|
@ -604,31 +614,13 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
|||
|
||||
target_inode = d_inode(new_dentry);
|
||||
|
||||
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||
if (IS_ERR(trap))
|
||||
return PTR_ERR(trap);
|
||||
dget(lower_new_dentry);
|
||||
rc = -EINVAL;
|
||||
if (lower_old_dentry->d_parent != lower_old_dir_dentry)
|
||||
goto out_lock;
|
||||
if (lower_new_dentry->d_parent != lower_new_dir_dentry)
|
||||
goto out_lock;
|
||||
if (d_unhashed(lower_old_dentry) || d_unhashed(lower_new_dentry))
|
||||
goto out_lock;
|
||||
/* source should not be ancestor of target */
|
||||
if (trap == lower_old_dentry)
|
||||
goto out_lock;
|
||||
/* target should not be ancestor of source */
|
||||
if (trap == lower_new_dentry) {
|
||||
rc = -ENOTEMPTY;
|
||||
goto out_lock;
|
||||
}
|
||||
|
||||
rd.mnt_idmap = &nop_mnt_idmap;
|
||||
rd.old_parent = lower_old_dir_dentry;
|
||||
rd.old_dentry = lower_old_dentry;
|
||||
rd.new_parent = lower_new_dir_dentry;
|
||||
rd.new_dentry = lower_new_dentry;
|
||||
rc = start_renaming_two_dentries(&rd, lower_old_dentry, lower_new_dentry);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = vfs_rename(&rd);
|
||||
if (rc)
|
||||
goto out_lock;
|
||||
|
|
@ -639,8 +631,7 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
|||
if (new_dir != old_dir)
|
||||
fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry));
|
||||
out_lock:
|
||||
dput(lower_new_dentry);
|
||||
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||
end_renaming(&rd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1397,27 +1397,25 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
|
|||
if (!parent)
|
||||
return -ENOENT;
|
||||
|
||||
inode_lock_nested(parent, I_MUTEX_PARENT);
|
||||
if (!S_ISDIR(parent->i_mode))
|
||||
goto unlock;
|
||||
goto put_parent;
|
||||
|
||||
err = -ENOENT;
|
||||
dir = d_find_alias(parent);
|
||||
if (!dir)
|
||||
goto unlock;
|
||||
goto put_parent;
|
||||
|
||||
name->hash = full_name_hash(dir, name->name, name->len);
|
||||
entry = d_lookup(dir, name);
|
||||
entry = start_removing_noperm(dir, name);
|
||||
dput(dir);
|
||||
if (!entry)
|
||||
goto unlock;
|
||||
if (IS_ERR(entry))
|
||||
goto put_parent;
|
||||
|
||||
fuse_dir_changed(parent);
|
||||
if (!(flags & FUSE_EXPIRE_ONLY))
|
||||
d_invalidate(entry);
|
||||
fuse_invalidate_entry_cache(entry);
|
||||
|
||||
if (child_nodeid != 0 && d_really_is_positive(entry)) {
|
||||
if (child_nodeid != 0) {
|
||||
inode_lock(d_inode(entry));
|
||||
if (get_node_id(d_inode(entry)) != child_nodeid) {
|
||||
err = -ENOENT;
|
||||
|
|
@ -1445,10 +1443,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
|
|||
} else {
|
||||
err = 0;
|
||||
}
|
||||
dput(entry);
|
||||
|
||||
unlock:
|
||||
inode_unlock(parent);
|
||||
end_removing(entry);
|
||||
put_parent:
|
||||
iput(parent);
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ int vfs_tmpfile(struct mnt_idmap *idmap,
|
|||
const struct path *parentpath,
|
||||
struct file *file, umode_t mode);
|
||||
struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
|
||||
struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
|
||||
unsigned int lookup_flags);
|
||||
int lookup_noperm_common(struct qstr *qname, struct dentry *base);
|
||||
|
||||
/*
|
||||
* namespace.c
|
||||
|
|
|
|||
36
fs/libfs.c
36
fs/libfs.c
|
|
@ -2290,27 +2290,25 @@ void stashed_dentry_prune(struct dentry *dentry)
|
|||
cmpxchg(stashed, dentry, NULL);
|
||||
}
|
||||
|
||||
/* parent must be held exclusive */
|
||||
/**
|
||||
* simple_start_creating - prepare to create a given name
|
||||
* @parent: directory in which to prepare to create the name
|
||||
* @name: the name to be created
|
||||
*
|
||||
* Required lock is taken and a lookup in performed prior to creating an
|
||||
* object in a directory. No permission checking is performed.
|
||||
*
|
||||
* Returns: a negative dentry on which vfs_create() or similar may
|
||||
* be attempted, or an error.
|
||||
*/
|
||||
struct dentry *simple_start_creating(struct dentry *parent, const char *name)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct inode *dir = d_inode(parent);
|
||||
struct qstr qname = QSTR(name);
|
||||
int err;
|
||||
|
||||
inode_lock(dir);
|
||||
if (unlikely(IS_DEADDIR(dir))) {
|
||||
inode_unlock(dir);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
dentry = lookup_noperm(&QSTR(name), parent);
|
||||
if (IS_ERR(dentry)) {
|
||||
inode_unlock(dir);
|
||||
return dentry;
|
||||
}
|
||||
if (dentry->d_inode) {
|
||||
dput(dentry);
|
||||
inode_unlock(dir);
|
||||
return ERR_PTR(-EEXIST);
|
||||
}
|
||||
return dentry;
|
||||
err = lookup_noperm_common(&qname, parent);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return start_dirop(parent, &qname, LOOKUP_CREATE | LOOKUP_EXCL);
|
||||
}
|
||||
EXPORT_SYMBOL(simple_start_creating);
|
||||
|
|
|
|||
735
fs/namei.c
735
fs/namei.c
|
|
@ -2835,6 +2835,62 @@ static int filename_parentat(int dfd, struct filename *name,
|
|||
return __filename_parentat(dfd, name, flags, parent, last, type, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* start_dirop - begin a create or remove dirop, performing locking and lookup
|
||||
* @parent: the dentry of the parent in which the operation will occur
|
||||
* @name: a qstr holding the name within that parent
|
||||
* @lookup_flags: intent and other lookup flags.
|
||||
*
|
||||
* The lookup is performed and necessary locks are taken so that, on success,
|
||||
* the returned dentry can be operated on safely.
|
||||
* The qstr must already have the hash value calculated.
|
||||
*
|
||||
* Returns: a locked dentry, or an error.
|
||||
*
|
||||
*/
|
||||
static struct dentry *__start_dirop(struct dentry *parent, struct qstr *name,
|
||||
unsigned int lookup_flags,
|
||||
unsigned int state)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct inode *dir = d_inode(parent);
|
||||
|
||||
if (state == TASK_KILLABLE) {
|
||||
int ret = down_write_killable_nested(&dir->i_rwsem,
|
||||
I_MUTEX_PARENT);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
} else {
|
||||
inode_lock_nested(dir, I_MUTEX_PARENT);
|
||||
}
|
||||
dentry = lookup_one_qstr_excl(name, parent, lookup_flags);
|
||||
if (IS_ERR(dentry))
|
||||
inode_unlock(dir);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
|
||||
unsigned int lookup_flags)
|
||||
{
|
||||
return __start_dirop(parent, name, lookup_flags, TASK_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* end_dirop - signal completion of a dirop
|
||||
* @de: the dentry which was returned by start_dirop or similar.
|
||||
*
|
||||
* If the de is an error, nothing happens. Otherwise any lock taken to
|
||||
* protect the dentry is dropped and the dentry itself is release (dput()).
|
||||
*/
|
||||
void end_dirop(struct dentry *de)
|
||||
{
|
||||
if (!IS_ERR(de)) {
|
||||
inode_unlock(de->d_parent->d_inode);
|
||||
dput(de);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(end_dirop);
|
||||
|
||||
/* does lookup, returns the object with parent locked */
|
||||
static struct dentry *__start_removing_path(int dfd, struct filename *name,
|
||||
struct path *path)
|
||||
|
|
@ -2851,10 +2907,9 @@ static struct dentry *__start_removing_path(int dfd, struct filename *name,
|
|||
return ERR_PTR(-EINVAL);
|
||||
/* don't fail immediately if it's r/o, at least try to report other errors */
|
||||
error = mnt_want_write(parent_path.mnt);
|
||||
inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
|
||||
d = lookup_one_qstr_excl(&last, parent_path.dentry, 0);
|
||||
d = start_dirop(parent_path.dentry, &last, 0);
|
||||
if (IS_ERR(d))
|
||||
goto unlock;
|
||||
goto drop;
|
||||
if (error)
|
||||
goto fail;
|
||||
path->dentry = no_free_ptr(parent_path.dentry);
|
||||
|
|
@ -2862,10 +2917,9 @@ static struct dentry *__start_removing_path(int dfd, struct filename *name,
|
|||
return d;
|
||||
|
||||
fail:
|
||||
dput(d);
|
||||
end_dirop(d);
|
||||
d = ERR_PTR(error);
|
||||
unlock:
|
||||
inode_unlock(parent_path.dentry->d_inode);
|
||||
drop:
|
||||
if (!error)
|
||||
mnt_drop_write(parent_path.mnt);
|
||||
return d;
|
||||
|
|
@ -2980,7 +3034,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
|
|||
}
|
||||
EXPORT_SYMBOL(vfs_path_lookup);
|
||||
|
||||
static int lookup_noperm_common(struct qstr *qname, struct dentry *base)
|
||||
int lookup_noperm_common(struct qstr *qname, struct dentry *base)
|
||||
{
|
||||
const char *name = qname->name;
|
||||
u32 len = qname->len;
|
||||
|
|
@ -3251,6 +3305,234 @@ struct dentry *lookup_noperm_positive_unlocked(struct qstr *name,
|
|||
}
|
||||
EXPORT_SYMBOL(lookup_noperm_positive_unlocked);
|
||||
|
||||
/**
|
||||
* start_creating - prepare to create a given name with permission checking
|
||||
* @idmap: idmap of the mount
|
||||
* @parent: directory in which to prepare to create the name
|
||||
* @name: the name to be created
|
||||
*
|
||||
* Locks are taken and a lookup is performed prior to creating
|
||||
* an object in a directory. Permission checking (MAY_EXEC) is performed
|
||||
* against @idmap.
|
||||
*
|
||||
* If the name already exists, a positive dentry is returned, so
|
||||
* behaviour is similar to O_CREAT without O_EXCL, which doesn't fail
|
||||
* with -EEXIST.
|
||||
*
|
||||
* Returns: a negative or positive dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = lookup_one_common(idmap, name, parent);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return start_dirop(parent, name, LOOKUP_CREATE);
|
||||
}
|
||||
EXPORT_SYMBOL(start_creating);
|
||||
|
||||
/**
|
||||
* start_removing - prepare to remove a given name with permission checking
|
||||
* @idmap: idmap of the mount
|
||||
* @parent: directory in which to find the name
|
||||
* @name: the name to be removed
|
||||
*
|
||||
* Locks are taken and a lookup in performed prior to removing
|
||||
* an object from a directory. Permission checking (MAY_EXEC) is performed
|
||||
* against @idmap.
|
||||
*
|
||||
* If the name doesn't exist, an error is returned.
|
||||
*
|
||||
* end_removing() should be called when removal is complete, or aborted.
|
||||
*
|
||||
* Returns: a positive dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = lookup_one_common(idmap, name, parent);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return start_dirop(parent, name, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(start_removing);
|
||||
|
||||
/**
|
||||
* start_creating_killable - prepare to create a given name with permission checking
|
||||
* @idmap: idmap of the mount
|
||||
* @parent: directory in which to prepare to create the name
|
||||
* @name: the name to be created
|
||||
*
|
||||
* Locks are taken and a lookup in performed prior to creating
|
||||
* an object in a directory. Permission checking (MAY_EXEC) is performed
|
||||
* against @idmap.
|
||||
*
|
||||
* If the name already exists, a positive dentry is returned.
|
||||
*
|
||||
* If a signal is received or was already pending, the function aborts
|
||||
* with -EINTR;
|
||||
*
|
||||
* Returns: a negative or positive dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_creating_killable(struct mnt_idmap *idmap,
|
||||
struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = lookup_one_common(idmap, name, parent);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return __start_dirop(parent, name, LOOKUP_CREATE, TASK_KILLABLE);
|
||||
}
|
||||
EXPORT_SYMBOL(start_creating_killable);
|
||||
|
||||
/**
|
||||
* start_removing_killable - prepare to remove a given name with permission checking
|
||||
* @idmap: idmap of the mount
|
||||
* @parent: directory in which to find the name
|
||||
* @name: the name to be removed
|
||||
*
|
||||
* Locks are taken and a lookup in performed prior to removing
|
||||
* an object from a directory. Permission checking (MAY_EXEC) is performed
|
||||
* against @idmap.
|
||||
*
|
||||
* If the name doesn't exist, an error is returned.
|
||||
*
|
||||
* end_removing() should be called when removal is complete, or aborted.
|
||||
*
|
||||
* If a signal is received or was already pending, the function aborts
|
||||
* with -EINTR;
|
||||
*
|
||||
* Returns: a positive dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_removing_killable(struct mnt_idmap *idmap,
|
||||
struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = lookup_one_common(idmap, name, parent);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return __start_dirop(parent, name, 0, TASK_KILLABLE);
|
||||
}
|
||||
EXPORT_SYMBOL(start_removing_killable);
|
||||
|
||||
/**
|
||||
* start_creating_noperm - prepare to create a given name without permission checking
|
||||
* @parent: directory in which to prepare to create the name
|
||||
* @name: the name to be created
|
||||
*
|
||||
* Locks are taken and a lookup in performed prior to creating
|
||||
* an object in a directory.
|
||||
*
|
||||
* If the name already exists, a positive dentry is returned.
|
||||
*
|
||||
* Returns: a negative or positive dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_creating_noperm(struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = lookup_noperm_common(name, parent);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return start_dirop(parent, name, LOOKUP_CREATE);
|
||||
}
|
||||
EXPORT_SYMBOL(start_creating_noperm);
|
||||
|
||||
/**
|
||||
* start_removing_noperm - prepare to remove a given name without permission checking
|
||||
* @parent: directory in which to find the name
|
||||
* @name: the name to be removed
|
||||
*
|
||||
* Locks are taken and a lookup in performed prior to removing
|
||||
* an object from a directory.
|
||||
*
|
||||
* If the name doesn't exist, an error is returned.
|
||||
*
|
||||
* end_removing() should be called when removal is complete, or aborted.
|
||||
*
|
||||
* Returns: a positive dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_removing_noperm(struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = lookup_noperm_common(name, parent);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return start_dirop(parent, name, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(start_removing_noperm);
|
||||
|
||||
/**
|
||||
* start_creating_dentry - prepare to create a given dentry
|
||||
* @parent: directory from which dentry should be removed
|
||||
* @child: the dentry to be removed
|
||||
*
|
||||
* A lock is taken to protect the dentry again other dirops and
|
||||
* the validity of the dentry is checked: correct parent and still hashed.
|
||||
*
|
||||
* If the dentry is valid and negative a reference is taken and
|
||||
* returned. If not an error is returned.
|
||||
*
|
||||
* end_creating() should be called when creation is complete, or aborted.
|
||||
*
|
||||
* Returns: the valid dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_creating_dentry(struct dentry *parent,
|
||||
struct dentry *child)
|
||||
{
|
||||
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
|
||||
if (unlikely(IS_DEADDIR(parent->d_inode) ||
|
||||
child->d_parent != parent ||
|
||||
d_unhashed(child))) {
|
||||
inode_unlock(parent->d_inode);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
if (d_is_positive(child)) {
|
||||
inode_unlock(parent->d_inode);
|
||||
return ERR_PTR(-EEXIST);
|
||||
}
|
||||
return dget(child);
|
||||
}
|
||||
EXPORT_SYMBOL(start_creating_dentry);
|
||||
|
||||
/**
|
||||
* start_removing_dentry - prepare to remove a given dentry
|
||||
* @parent: directory from which dentry should be removed
|
||||
* @child: the dentry to be removed
|
||||
*
|
||||
* A lock is taken to protect the dentry again other dirops and
|
||||
* the validity of the dentry is checked: correct parent and still hashed.
|
||||
*
|
||||
* If the dentry is valid and positive, a reference is taken and
|
||||
* returned. If not an error is returned.
|
||||
*
|
||||
* end_removing() should be called when removal is complete, or aborted.
|
||||
*
|
||||
* Returns: the valid dentry, or an error.
|
||||
*/
|
||||
struct dentry *start_removing_dentry(struct dentry *parent,
|
||||
struct dentry *child)
|
||||
{
|
||||
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
|
||||
if (unlikely(IS_DEADDIR(parent->d_inode) ||
|
||||
child->d_parent != parent ||
|
||||
d_unhashed(child))) {
|
||||
inode_unlock(parent->d_inode);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
if (d_is_negative(child)) {
|
||||
inode_unlock(parent->d_inode);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
return dget(child);
|
||||
}
|
||||
EXPORT_SYMBOL(start_removing_dentry);
|
||||
|
||||
#ifdef CONFIG_UNIX98_PTYS
|
||||
int path_pts(struct path *path)
|
||||
{
|
||||
|
|
@ -3488,6 +3770,290 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
|
|||
}
|
||||
EXPORT_SYMBOL(unlock_rename);
|
||||
|
||||
/**
|
||||
* __start_renaming - lookup and lock names for rename
|
||||
* @rd: rename data containing parents and flags, and
|
||||
* for receiving found dentries
|
||||
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
|
||||
* LOOKUP_NO_SYMLINKS etc).
|
||||
* @old_last: name of object in @rd.old_parent
|
||||
* @new_last: name of object in @rd.new_parent
|
||||
*
|
||||
* Look up two names and ensure locks are in place for
|
||||
* rename.
|
||||
*
|
||||
* On success the found dentries are stored in @rd.old_dentry,
|
||||
* @rd.new_dentry and an extra ref is taken on @rd.old_parent.
|
||||
* These references and the lock are dropped by end_renaming().
|
||||
*
|
||||
* The passed in qstrs must have the hash calculated, and no permission
|
||||
* checking is performed.
|
||||
*
|
||||
* Returns: zero or an error.
|
||||
*/
|
||||
static int
|
||||
__start_renaming(struct renamedata *rd, int lookup_flags,
|
||||
struct qstr *old_last, struct qstr *new_last)
|
||||
{
|
||||
struct dentry *trap;
|
||||
struct dentry *d1, *d2;
|
||||
int target_flags = LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
|
||||
int err;
|
||||
|
||||
if (rd->flags & RENAME_EXCHANGE)
|
||||
target_flags = 0;
|
||||
if (rd->flags & RENAME_NOREPLACE)
|
||||
target_flags |= LOOKUP_EXCL;
|
||||
|
||||
trap = lock_rename(rd->old_parent, rd->new_parent);
|
||||
if (IS_ERR(trap))
|
||||
return PTR_ERR(trap);
|
||||
|
||||
d1 = lookup_one_qstr_excl(old_last, rd->old_parent,
|
||||
lookup_flags);
|
||||
err = PTR_ERR(d1);
|
||||
if (IS_ERR(d1))
|
||||
goto out_unlock;
|
||||
|
||||
d2 = lookup_one_qstr_excl(new_last, rd->new_parent,
|
||||
lookup_flags | target_flags);
|
||||
err = PTR_ERR(d2);
|
||||
if (IS_ERR(d2))
|
||||
goto out_dput_d1;
|
||||
|
||||
if (d1 == trap) {
|
||||
/* source is an ancestor of target */
|
||||
err = -EINVAL;
|
||||
goto out_dput_d2;
|
||||
}
|
||||
|
||||
if (d2 == trap) {
|
||||
/* target is an ancestor of source */
|
||||
if (rd->flags & RENAME_EXCHANGE)
|
||||
err = -EINVAL;
|
||||
else
|
||||
err = -ENOTEMPTY;
|
||||
goto out_dput_d2;
|
||||
}
|
||||
|
||||
rd->old_dentry = d1;
|
||||
rd->new_dentry = d2;
|
||||
dget(rd->old_parent);
|
||||
return 0;
|
||||
|
||||
out_dput_d2:
|
||||
dput(d2);
|
||||
out_dput_d1:
|
||||
dput(d1);
|
||||
out_unlock:
|
||||
unlock_rename(rd->old_parent, rd->new_parent);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* start_renaming - lookup and lock names for rename with permission checking
|
||||
* @rd: rename data containing parents and flags, and
|
||||
* for receiving found dentries
|
||||
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
|
||||
* LOOKUP_NO_SYMLINKS etc).
|
||||
* @old_last: name of object in @rd.old_parent
|
||||
* @new_last: name of object in @rd.new_parent
|
||||
*
|
||||
* Look up two names and ensure locks are in place for
|
||||
* rename.
|
||||
*
|
||||
* On success the found dentries are stored in @rd.old_dentry,
|
||||
* @rd.new_dentry. Also the refcount on @rd->old_parent is increased.
|
||||
* These references and the lock are dropped by end_renaming().
|
||||
*
|
||||
* The passed in qstrs need not have the hash calculated, and basic
|
||||
* eXecute permission checking is performed against @rd.mnt_idmap.
|
||||
*
|
||||
* Returns: zero or an error.
|
||||
*/
|
||||
int start_renaming(struct renamedata *rd, int lookup_flags,
|
||||
struct qstr *old_last, struct qstr *new_last)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = lookup_one_common(rd->mnt_idmap, old_last, rd->old_parent);
|
||||
if (err)
|
||||
return err;
|
||||
err = lookup_one_common(rd->mnt_idmap, new_last, rd->new_parent);
|
||||
if (err)
|
||||
return err;
|
||||
return __start_renaming(rd, lookup_flags, old_last, new_last);
|
||||
}
|
||||
EXPORT_SYMBOL(start_renaming);
|
||||
|
||||
static int
|
||||
__start_renaming_dentry(struct renamedata *rd, int lookup_flags,
|
||||
struct dentry *old_dentry, struct qstr *new_last)
|
||||
{
|
||||
struct dentry *trap;
|
||||
struct dentry *d2;
|
||||
int target_flags = LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
|
||||
int err;
|
||||
|
||||
if (rd->flags & RENAME_EXCHANGE)
|
||||
target_flags = 0;
|
||||
if (rd->flags & RENAME_NOREPLACE)
|
||||
target_flags |= LOOKUP_EXCL;
|
||||
|
||||
/* Already have the dentry - need to be sure to lock the correct parent */
|
||||
trap = lock_rename_child(old_dentry, rd->new_parent);
|
||||
if (IS_ERR(trap))
|
||||
return PTR_ERR(trap);
|
||||
if (d_unhashed(old_dentry) ||
|
||||
(rd->old_parent && rd->old_parent != old_dentry->d_parent)) {
|
||||
/* dentry was removed, or moved and explicit parent requested */
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
d2 = lookup_one_qstr_excl(new_last, rd->new_parent,
|
||||
lookup_flags | target_flags);
|
||||
err = PTR_ERR(d2);
|
||||
if (IS_ERR(d2))
|
||||
goto out_unlock;
|
||||
|
||||
if (old_dentry == trap) {
|
||||
/* source is an ancestor of target */
|
||||
err = -EINVAL;
|
||||
goto out_dput_d2;
|
||||
}
|
||||
|
||||
if (d2 == trap) {
|
||||
/* target is an ancestor of source */
|
||||
if (rd->flags & RENAME_EXCHANGE)
|
||||
err = -EINVAL;
|
||||
else
|
||||
err = -ENOTEMPTY;
|
||||
goto out_dput_d2;
|
||||
}
|
||||
|
||||
rd->old_dentry = dget(old_dentry);
|
||||
rd->new_dentry = d2;
|
||||
rd->old_parent = dget(old_dentry->d_parent);
|
||||
return 0;
|
||||
|
||||
out_dput_d2:
|
||||
dput(d2);
|
||||
out_unlock:
|
||||
unlock_rename(old_dentry->d_parent, rd->new_parent);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* start_renaming_dentry - lookup and lock name for rename with permission checking
|
||||
* @rd: rename data containing parents and flags, and
|
||||
* for receiving found dentries
|
||||
* @lookup_flags: extra flags to pass to ->lookup (e.g. LOOKUP_REVAL,
|
||||
* LOOKUP_NO_SYMLINKS etc).
|
||||
* @old_dentry: dentry of name to move
|
||||
* @new_last: name of target in @rd.new_parent
|
||||
*
|
||||
* Look up target name and ensure locks are in place for
|
||||
* rename.
|
||||
*
|
||||
* On success the found dentry is stored in @rd.new_dentry and
|
||||
* @rd.old_parent is confirmed to be the parent of @old_dentry. If it
|
||||
* was originally %NULL, it is set. In either case a reference is taken
|
||||
* so that end_renaming() can have a stable reference to unlock.
|
||||
*
|
||||
* References and the lock can be dropped with end_renaming()
|
||||
*
|
||||
* The passed in qstr need not have the hash calculated, and basic
|
||||
* eXecute permission checking is performed against @rd.mnt_idmap.
|
||||
*
|
||||
* Returns: zero or an error.
|
||||
*/
|
||||
int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
|
||||
struct dentry *old_dentry, struct qstr *new_last)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = lookup_one_common(rd->mnt_idmap, new_last, rd->new_parent);
|
||||
if (err)
|
||||
return err;
|
||||
return __start_renaming_dentry(rd, lookup_flags, old_dentry, new_last);
|
||||
}
|
||||
EXPORT_SYMBOL(start_renaming_dentry);
|
||||
|
||||
/**
|
||||
* start_renaming_two_dentries - Lock to dentries in given parents for rename
|
||||
* @rd: rename data containing parent
|
||||
* @old_dentry: dentry of name to move
|
||||
* @new_dentry: dentry to move to
|
||||
*
|
||||
* Ensure locks are in place for rename and check parentage is still correct.
|
||||
*
|
||||
* On success the two dentries are stored in @rd.old_dentry and
|
||||
* @rd.new_dentry and @rd.old_parent and @rd.new_parent are confirmed to
|
||||
* be the parents of the dentries.
|
||||
*
|
||||
* References and the lock can be dropped with end_renaming()
|
||||
*
|
||||
* Returns: zero or an error.
|
||||
*/
|
||||
int
|
||||
start_renaming_two_dentries(struct renamedata *rd,
|
||||
struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
{
|
||||
struct dentry *trap;
|
||||
int err;
|
||||
|
||||
/* Already have the dentry - need to be sure to lock the correct parent */
|
||||
trap = lock_rename_child(old_dentry, rd->new_parent);
|
||||
if (IS_ERR(trap))
|
||||
return PTR_ERR(trap);
|
||||
err = -EINVAL;
|
||||
if (d_unhashed(old_dentry) ||
|
||||
(rd->old_parent && rd->old_parent != old_dentry->d_parent))
|
||||
/* old_dentry was removed, or moved and explicit parent requested */
|
||||
goto out_unlock;
|
||||
if (d_unhashed(new_dentry) ||
|
||||
rd->new_parent != new_dentry->d_parent)
|
||||
/* new_dentry was removed or moved */
|
||||
goto out_unlock;
|
||||
|
||||
if (old_dentry == trap)
|
||||
/* source is an ancestor of target */
|
||||
goto out_unlock;
|
||||
|
||||
if (new_dentry == trap) {
|
||||
/* target is an ancestor of source */
|
||||
if (rd->flags & RENAME_EXCHANGE)
|
||||
err = -EINVAL;
|
||||
else
|
||||
err = -ENOTEMPTY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = -EEXIST;
|
||||
if (d_is_positive(new_dentry) && (rd->flags & RENAME_NOREPLACE))
|
||||
goto out_unlock;
|
||||
|
||||
rd->old_dentry = dget(old_dentry);
|
||||
rd->new_dentry = dget(new_dentry);
|
||||
rd->old_parent = dget(old_dentry->d_parent);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
unlock_rename(old_dentry->d_parent, rd->new_parent);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(start_renaming_two_dentries);
|
||||
|
||||
void end_renaming(struct renamedata *rd)
|
||||
{
|
||||
unlock_rename(rd->old_parent, rd->new_parent);
|
||||
dput(rd->old_dentry);
|
||||
dput(rd->new_dentry);
|
||||
dput(rd->old_parent);
|
||||
}
|
||||
EXPORT_SYMBOL(end_renaming);
|
||||
|
||||
/**
|
||||
* vfs_prepare_mode - prepare the mode to be used for a new inode
|
||||
* @idmap: idmap of the mount the inode was found from
|
||||
|
|
@ -4310,21 +4876,18 @@ static struct dentry *filename_create(int dfd, struct filename *name,
|
|||
*/
|
||||
if (last.name[last.len] && !want_dir)
|
||||
create_flags &= ~LOOKUP_CREATE;
|
||||
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
|
||||
dentry = lookup_one_qstr_excl(&last, path->dentry,
|
||||
reval_flag | create_flags);
|
||||
dentry = start_dirop(path->dentry, &last, reval_flag | create_flags);
|
||||
if (IS_ERR(dentry))
|
||||
goto unlock;
|
||||
goto out_drop_write;
|
||||
|
||||
if (unlikely(error))
|
||||
goto fail;
|
||||
|
||||
return dentry;
|
||||
fail:
|
||||
dput(dentry);
|
||||
end_dirop(dentry);
|
||||
dentry = ERR_PTR(error);
|
||||
unlock:
|
||||
inode_unlock(path->dentry->d_inode);
|
||||
out_drop_write:
|
||||
if (!error)
|
||||
mnt_drop_write(path->mnt);
|
||||
out:
|
||||
|
|
@ -4343,11 +4906,20 @@ struct dentry *start_creating_path(int dfd, const char *pathname,
|
|||
}
|
||||
EXPORT_SYMBOL(start_creating_path);
|
||||
|
||||
/**
|
||||
* end_creating_path - finish a code section started by start_creating_path()
|
||||
* @path: the path instantiated by start_creating_path()
|
||||
* @dentry: the dentry returned by start_creating_path()
|
||||
*
|
||||
* end_creating_path() will unlock and locks taken by start_creating_path()
|
||||
* and drop an references that were taken. It should only be called
|
||||
* if start_creating_path() returned a non-error.
|
||||
* If vfs_mkdir() was called and it returned an error, that error *should*
|
||||
* be passed to end_creating_path() together with the path.
|
||||
*/
|
||||
void end_creating_path(const struct path *path, struct dentry *dentry)
|
||||
{
|
||||
if (!IS_ERR(dentry))
|
||||
dput(dentry);
|
||||
inode_unlock(path->dentry->d_inode);
|
||||
end_creating(dentry);
|
||||
mnt_drop_write(path->mnt);
|
||||
path_put(path);
|
||||
}
|
||||
|
|
@ -4567,7 +5139,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
|||
return dentry;
|
||||
|
||||
err:
|
||||
dput(dentry);
|
||||
end_creating(dentry);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_mkdir);
|
||||
|
|
@ -4709,8 +5281,7 @@ int do_rmdir(int dfd, struct filename *name)
|
|||
if (error)
|
||||
goto exit2;
|
||||
|
||||
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
|
||||
dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
|
||||
dentry = start_dirop(path.dentry, &last, lookup_flags);
|
||||
error = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry))
|
||||
goto exit3;
|
||||
|
|
@ -4720,9 +5291,8 @@ int do_rmdir(int dfd, struct filename *name)
|
|||
error = vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode,
|
||||
dentry, &delegated_inode);
|
||||
exit4:
|
||||
dput(dentry);
|
||||
end_dirop(dentry);
|
||||
exit3:
|
||||
inode_unlock(path.dentry->d_inode);
|
||||
mnt_drop_write(path.mnt);
|
||||
exit2:
|
||||
path_put(&path);
|
||||
|
|
@ -4831,67 +5401,62 @@ int do_unlinkat(int dfd, struct filename *name)
|
|||
struct path path;
|
||||
struct qstr last;
|
||||
int type;
|
||||
struct inode *inode = NULL;
|
||||
struct inode *inode;
|
||||
struct delegated_inode delegated_inode = { };
|
||||
unsigned int lookup_flags = 0;
|
||||
retry:
|
||||
error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
|
||||
if (error)
|
||||
goto exit1;
|
||||
goto exit_putname;
|
||||
|
||||
error = -EISDIR;
|
||||
if (type != LAST_NORM)
|
||||
goto exit2;
|
||||
goto exit_path_put;
|
||||
|
||||
error = mnt_want_write(path.mnt);
|
||||
if (error)
|
||||
goto exit2;
|
||||
goto exit_path_put;
|
||||
retry_deleg:
|
||||
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
|
||||
dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
|
||||
dentry = start_dirop(path.dentry, &last, lookup_flags);
|
||||
error = PTR_ERR(dentry);
|
||||
if (!IS_ERR(dentry)) {
|
||||
if (IS_ERR(dentry))
|
||||
goto exit_drop_write;
|
||||
|
||||
/* Why not before? Because we want correct error value */
|
||||
if (last.name[last.len])
|
||||
goto slashes;
|
||||
if (unlikely(last.name[last.len])) {
|
||||
if (d_is_dir(dentry))
|
||||
error = -EISDIR;
|
||||
else
|
||||
error = -ENOTDIR;
|
||||
end_dirop(dentry);
|
||||
goto exit_drop_write;
|
||||
}
|
||||
inode = dentry->d_inode;
|
||||
ihold(inode);
|
||||
error = security_path_unlink(&path, dentry);
|
||||
if (error)
|
||||
goto exit3;
|
||||
goto exit_end_dirop;
|
||||
error = vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode,
|
||||
dentry, &delegated_inode);
|
||||
exit3:
|
||||
dput(dentry);
|
||||
}
|
||||
inode_unlock(path.dentry->d_inode);
|
||||
if (inode)
|
||||
exit_end_dirop:
|
||||
end_dirop(dentry);
|
||||
iput(inode); /* truncate the inode here */
|
||||
inode = NULL;
|
||||
if (is_delegated(&delegated_inode)) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
if (!error)
|
||||
goto retry_deleg;
|
||||
}
|
||||
exit_drop_write:
|
||||
mnt_drop_write(path.mnt);
|
||||
exit2:
|
||||
exit_path_put:
|
||||
path_put(&path);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
inode = NULL;
|
||||
goto retry;
|
||||
}
|
||||
exit1:
|
||||
exit_putname:
|
||||
putname(name);
|
||||
return error;
|
||||
|
||||
slashes:
|
||||
if (d_is_dir(dentry))
|
||||
error = -EISDIR;
|
||||
else
|
||||
error = -ENOTDIR;
|
||||
goto exit3;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag)
|
||||
|
|
@ -5404,14 +5969,11 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
|||
struct filename *to, unsigned int flags)
|
||||
{
|
||||
struct renamedata rd;
|
||||
struct dentry *old_dentry, *new_dentry;
|
||||
struct dentry *trap;
|
||||
struct path old_path, new_path;
|
||||
struct qstr old_last, new_last;
|
||||
int old_type, new_type;
|
||||
struct delegated_inode delegated_inode = { };
|
||||
unsigned int lookup_flags = 0, target_flags =
|
||||
LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
|
||||
unsigned int lookup_flags = 0;
|
||||
bool should_retry = false;
|
||||
int error = -EINVAL;
|
||||
|
||||
|
|
@ -5422,11 +5984,6 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
|||
(flags & RENAME_EXCHANGE))
|
||||
goto put_names;
|
||||
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
target_flags = 0;
|
||||
if (flags & RENAME_NOREPLACE)
|
||||
target_flags |= LOOKUP_EXCL;
|
||||
|
||||
retry:
|
||||
error = filename_parentat(olddfd, from, lookup_flags, &old_path,
|
||||
&old_last, &old_type);
|
||||
|
|
@ -5456,66 +6013,40 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
|||
goto exit2;
|
||||
|
||||
retry_deleg:
|
||||
trap = lock_rename(new_path.dentry, old_path.dentry);
|
||||
if (IS_ERR(trap)) {
|
||||
error = PTR_ERR(trap);
|
||||
goto exit_lock_rename;
|
||||
}
|
||||
rd.old_parent = old_path.dentry;
|
||||
rd.mnt_idmap = mnt_idmap(old_path.mnt);
|
||||
rd.new_parent = new_path.dentry;
|
||||
rd.delegated_inode = &delegated_inode;
|
||||
rd.flags = flags;
|
||||
|
||||
error = __start_renaming(&rd, lookup_flags, &old_last, &new_last);
|
||||
if (error)
|
||||
goto exit_lock_rename;
|
||||
|
||||
old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry,
|
||||
lookup_flags);
|
||||
error = PTR_ERR(old_dentry);
|
||||
if (IS_ERR(old_dentry))
|
||||
goto exit3;
|
||||
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
|
||||
lookup_flags | target_flags);
|
||||
error = PTR_ERR(new_dentry);
|
||||
if (IS_ERR(new_dentry))
|
||||
goto exit4;
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
if (!d_is_dir(new_dentry)) {
|
||||
if (!d_is_dir(rd.new_dentry)) {
|
||||
error = -ENOTDIR;
|
||||
if (new_last.name[new_last.len])
|
||||
goto exit5;
|
||||
goto exit_unlock;
|
||||
}
|
||||
}
|
||||
/* unless the source is a directory trailing slashes give -ENOTDIR */
|
||||
if (!d_is_dir(old_dentry)) {
|
||||
if (!d_is_dir(rd.old_dentry)) {
|
||||
error = -ENOTDIR;
|
||||
if (old_last.name[old_last.len])
|
||||
goto exit5;
|
||||
goto exit_unlock;
|
||||
if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len])
|
||||
goto exit5;
|
||||
goto exit_unlock;
|
||||
}
|
||||
/* source should not be ancestor of target */
|
||||
error = -EINVAL;
|
||||
if (old_dentry == trap)
|
||||
goto exit5;
|
||||
/* target should not be an ancestor of source */
|
||||
if (!(flags & RENAME_EXCHANGE))
|
||||
error = -ENOTEMPTY;
|
||||
if (new_dentry == trap)
|
||||
goto exit5;
|
||||
|
||||
error = security_path_rename(&old_path, old_dentry,
|
||||
&new_path, new_dentry, flags);
|
||||
error = security_path_rename(&old_path, rd.old_dentry,
|
||||
&new_path, rd.new_dentry, flags);
|
||||
if (error)
|
||||
goto exit5;
|
||||
goto exit_unlock;
|
||||
|
||||
rd.old_parent = old_path.dentry;
|
||||
rd.old_dentry = old_dentry;
|
||||
rd.mnt_idmap = mnt_idmap(old_path.mnt);
|
||||
rd.new_parent = new_path.dentry;
|
||||
rd.new_dentry = new_dentry;
|
||||
rd.delegated_inode = &delegated_inode;
|
||||
rd.flags = flags;
|
||||
error = vfs_rename(&rd);
|
||||
exit5:
|
||||
dput(new_dentry);
|
||||
exit4:
|
||||
dput(old_dentry);
|
||||
exit3:
|
||||
unlock_rename(new_path.dentry, old_path.dentry);
|
||||
exit_unlock:
|
||||
end_renaming(&rd);
|
||||
exit_lock_rename:
|
||||
if (is_delegated(&delegated_inode)) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
|
|
|
|||
|
|
@ -281,14 +281,11 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
if (host_err)
|
||||
return nfserrno(host_err);
|
||||
|
||||
inode_lock_nested(inode, I_MUTEX_PARENT);
|
||||
|
||||
child = lookup_one(&nop_mnt_idmap,
|
||||
&QSTR_LEN(argp->name, argp->len),
|
||||
parent);
|
||||
child = start_creating(&nop_mnt_idmap, parent,
|
||||
&QSTR_LEN(argp->name, argp->len));
|
||||
if (IS_ERR(child)) {
|
||||
status = nfserrno(PTR_ERR(child));
|
||||
goto out;
|
||||
goto out_write;
|
||||
}
|
||||
|
||||
if (d_really_is_negative(child)) {
|
||||
|
|
@ -367,9 +364,8 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
|
||||
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
if (child && !IS_ERR(child))
|
||||
dput(child);
|
||||
end_creating(child);
|
||||
out_write:
|
||||
fh_drop_write(fhp);
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,14 +264,11 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
if (is_create_with_attrs(open))
|
||||
nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
|
||||
|
||||
inode_lock_nested(inode, I_MUTEX_PARENT);
|
||||
|
||||
child = lookup_one(&nop_mnt_idmap,
|
||||
&QSTR_LEN(open->op_fname, open->op_fnamelen),
|
||||
parent);
|
||||
child = start_creating(&nop_mnt_idmap, parent,
|
||||
&QSTR_LEN(open->op_fname, open->op_fnamelen));
|
||||
if (IS_ERR(child)) {
|
||||
status = nfserrno(PTR_ERR(child));
|
||||
goto out;
|
||||
goto out_write;
|
||||
}
|
||||
|
||||
if (d_really_is_negative(child)) {
|
||||
|
|
@ -379,10 +376,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
if (attrs.na_aclerr)
|
||||
open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
end_creating(child);
|
||||
nfsd_attrs_free(&attrs);
|
||||
if (child && !IS_ERR(child))
|
||||
dput(child);
|
||||
out_write:
|
||||
fh_drop_write(fhp);
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,13 +195,11 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
|
|||
goto out_creds;
|
||||
|
||||
dir = nn->rec_file->f_path.dentry;
|
||||
/* lock the parent */
|
||||
inode_lock(d_inode(dir));
|
||||
|
||||
dentry = lookup_one(&nop_mnt_idmap, &QSTR(dname), dir);
|
||||
dentry = start_creating(&nop_mnt_idmap, dir, &QSTR(dname));
|
||||
if (IS_ERR(dentry)) {
|
||||
status = PTR_ERR(dentry);
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
if (d_really_is_positive(dentry))
|
||||
/*
|
||||
|
|
@ -212,15 +210,13 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
|
|||
* In the 4.0 case, we should never get here; but we may
|
||||
* as well be forgiving and just succeed silently.
|
||||
*/
|
||||
goto out_put;
|
||||
goto out_end;
|
||||
dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, 0700, NULL);
|
||||
if (IS_ERR(dentry))
|
||||
status = PTR_ERR(dentry);
|
||||
out_put:
|
||||
if (!status)
|
||||
dput(dentry);
|
||||
out_unlock:
|
||||
inode_unlock(d_inode(dir));
|
||||
out_end:
|
||||
end_creating(dentry);
|
||||
out:
|
||||
if (status == 0) {
|
||||
if (nn->in_grace)
|
||||
__nfsd4_create_reclaim_record_grace(clp, dname,
|
||||
|
|
@ -328,20 +324,12 @@ nfsd4_unlink_clid_dir(char *name, struct nfsd_net *nn)
|
|||
dprintk("NFSD: nfsd4_unlink_clid_dir. name %s\n", name);
|
||||
|
||||
dir = nn->rec_file->f_path.dentry;
|
||||
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
|
||||
dentry = lookup_one(&nop_mnt_idmap, &QSTR(name), dir);
|
||||
if (IS_ERR(dentry)) {
|
||||
status = PTR_ERR(dentry);
|
||||
goto out_unlock;
|
||||
}
|
||||
status = -ENOENT;
|
||||
if (d_really_is_negative(dentry))
|
||||
goto out;
|
||||
dentry = start_removing(&nop_mnt_idmap, dir, &QSTR(name));
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry, NULL);
|
||||
out:
|
||||
dput(dentry);
|
||||
out_unlock:
|
||||
inode_unlock(d_inode(dir));
|
||||
end_removing(dentry);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,18 +306,16 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
|||
goto done;
|
||||
}
|
||||
|
||||
inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT);
|
||||
dchild = lookup_one(&nop_mnt_idmap, &QSTR_LEN(argp->name, argp->len),
|
||||
dirfhp->fh_dentry);
|
||||
dchild = start_creating(&nop_mnt_idmap, dirfhp->fh_dentry,
|
||||
&QSTR_LEN(argp->name, argp->len));
|
||||
if (IS_ERR(dchild)) {
|
||||
resp->status = nfserrno(PTR_ERR(dchild));
|
||||
goto out_unlock;
|
||||
goto out_write;
|
||||
}
|
||||
fh_init(newfhp, NFS_FHSIZE);
|
||||
resp->status = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
|
||||
if (!resp->status && d_really_is_negative(dchild))
|
||||
resp->status = nfserr_noent;
|
||||
dput(dchild);
|
||||
if (resp->status) {
|
||||
if (resp->status != nfserr_noent)
|
||||
goto out_unlock;
|
||||
|
|
@ -409,6 +407,9 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
|||
/* File doesn't exist. Create it and set attrs */
|
||||
resp->status = nfsd_create_locked(rqstp, dirfhp, &attrs, type,
|
||||
rdev, newfhp);
|
||||
/* nfsd_create_locked() unlocked the parent */
|
||||
dput(dchild);
|
||||
goto out_write;
|
||||
} else if (type == S_IFREG) {
|
||||
dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
|
||||
argp->name, attr->ia_valid, (long) attr->ia_size);
|
||||
|
|
@ -423,7 +424,8 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
|||
}
|
||||
|
||||
out_unlock:
|
||||
inode_unlock(dirfhp->fh_dentry->d_inode);
|
||||
end_creating(dchild);
|
||||
out_write:
|
||||
fh_drop_write(dirfhp);
|
||||
done:
|
||||
fh_put(dirfhp);
|
||||
|
|
|
|||
155
fs/nfsd/vfs.c
155
fs/nfsd/vfs.c
|
|
@ -1522,7 +1522,7 @@ nfsd_check_ignore_resizing(struct iattr *iap)
|
|||
iap->ia_valid &= ~ATTR_SIZE;
|
||||
}
|
||||
|
||||
/* The parent directory should already be locked: */
|
||||
/* The parent directory should already be locked - we will unlock */
|
||||
__be32
|
||||
nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct nfsd_attrs *attrs,
|
||||
|
|
@ -1587,8 +1587,9 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
err = nfsd_create_setattr(rqstp, fhp, resfhp, attrs);
|
||||
|
||||
out:
|
||||
if (!IS_ERR(dchild))
|
||||
dput(dchild);
|
||||
if (!err)
|
||||
fh_fill_post_attrs(fhp);
|
||||
end_creating(dchild);
|
||||
return err;
|
||||
|
||||
out_nfserr:
|
||||
|
|
@ -1626,28 +1627,24 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
if (host_err)
|
||||
return nfserrno(host_err);
|
||||
|
||||
inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT);
|
||||
dchild = lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry);
|
||||
dchild = start_creating(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen));
|
||||
host_err = PTR_ERR(dchild);
|
||||
if (IS_ERR(dchild)) {
|
||||
err = nfserrno(host_err);
|
||||
goto out_unlock;
|
||||
}
|
||||
if (IS_ERR(dchild))
|
||||
return nfserrno(host_err);
|
||||
|
||||
err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
|
||||
/*
|
||||
* We unconditionally drop our ref to dchild as fh_compose will have
|
||||
* already grabbed its own ref for it.
|
||||
*/
|
||||
dput(dchild);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
err = fh_fill_pre_attrs(fhp);
|
||||
if (err != nfs_ok)
|
||||
goto out_unlock;
|
||||
err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
|
||||
fh_fill_post_attrs(fhp);
|
||||
/* nfsd_create_locked() unlocked the parent */
|
||||
dput(dchild);
|
||||
return err;
|
||||
|
||||
out_unlock:
|
||||
inode_unlock(dentry->d_inode);
|
||||
end_creating(dchild);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1733,11 +1730,9 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
}
|
||||
|
||||
dentry = fhp->fh_dentry;
|
||||
inode_lock_nested(dentry->d_inode, I_MUTEX_PARENT);
|
||||
dnew = lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry);
|
||||
dnew = start_creating(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen));
|
||||
if (IS_ERR(dnew)) {
|
||||
err = nfserrno(PTR_ERR(dnew));
|
||||
inode_unlock(dentry->d_inode);
|
||||
goto out_drop_write;
|
||||
}
|
||||
err = fh_fill_pre_attrs(fhp);
|
||||
|
|
@ -1750,11 +1745,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
nfsd_create_setattr(rqstp, fhp, resfhp, attrs);
|
||||
fh_fill_post_attrs(fhp);
|
||||
out_unlock:
|
||||
inode_unlock(dentry->d_inode);
|
||||
end_creating(dnew);
|
||||
if (!err)
|
||||
err = nfserrno(commit_metadata(fhp));
|
||||
dput(dnew);
|
||||
if (err==0) err = cerr;
|
||||
if (!err)
|
||||
err = cerr;
|
||||
out_drop_write:
|
||||
fh_drop_write(fhp);
|
||||
out:
|
||||
|
|
@ -1809,32 +1804,31 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
|||
|
||||
ddir = ffhp->fh_dentry;
|
||||
dirp = d_inode(ddir);
|
||||
inode_lock_nested(dirp, I_MUTEX_PARENT);
|
||||
dnew = start_creating(&nop_mnt_idmap, ddir, &QSTR_LEN(name, len));
|
||||
|
||||
dnew = lookup_one(&nop_mnt_idmap, &QSTR_LEN(name, len), ddir);
|
||||
if (IS_ERR(dnew)) {
|
||||
host_err = PTR_ERR(dnew);
|
||||
goto out_unlock;
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
dold = tfhp->fh_dentry;
|
||||
|
||||
err = nfserr_noent;
|
||||
if (d_really_is_negative(dold))
|
||||
goto out_dput;
|
||||
goto out_unlock;
|
||||
err = fh_fill_pre_attrs(ffhp);
|
||||
if (err != nfs_ok)
|
||||
goto out_dput;
|
||||
goto out_unlock;
|
||||
host_err = vfs_link(dold, &nop_mnt_idmap, dirp, dnew, NULL);
|
||||
fh_fill_post_attrs(ffhp);
|
||||
inode_unlock(dirp);
|
||||
out_unlock:
|
||||
end_creating(dnew);
|
||||
if (!host_err) {
|
||||
host_err = commit_metadata(ffhp);
|
||||
if (!host_err)
|
||||
host_err = commit_metadata(tfhp);
|
||||
}
|
||||
|
||||
dput(dnew);
|
||||
out_drop_write:
|
||||
fh_drop_write(tfhp);
|
||||
if (host_err == -EBUSY) {
|
||||
|
|
@ -1849,12 +1843,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
|||
}
|
||||
out:
|
||||
return err != nfs_ok ? err : nfserrno(host_err);
|
||||
|
||||
out_dput:
|
||||
dput(dnew);
|
||||
out_unlock:
|
||||
inode_unlock(dirp);
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1895,11 +1883,12 @@ __be32
|
|||
nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
||||
struct svc_fh *tfhp, char *tname, int tlen)
|
||||
{
|
||||
struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap;
|
||||
struct dentry *fdentry, *tdentry;
|
||||
int type = S_IFDIR;
|
||||
struct renamedata rd = {};
|
||||
__be32 err;
|
||||
int host_err;
|
||||
bool close_cached = false;
|
||||
struct dentry *close_cached;
|
||||
|
||||
trace_nfsd_vfs_rename(rqstp, ffhp, tfhp, fname, flen, tname, tlen);
|
||||
|
||||
|
|
@ -1925,15 +1914,22 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
|||
goto out;
|
||||
|
||||
retry:
|
||||
close_cached = NULL;
|
||||
host_err = fh_want_write(ffhp);
|
||||
if (host_err) {
|
||||
err = nfserrno(host_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
trap = lock_rename(tdentry, fdentry);
|
||||
if (IS_ERR(trap)) {
|
||||
err = nfserr_xdev;
|
||||
rd.mnt_idmap = &nop_mnt_idmap;
|
||||
rd.old_parent = fdentry;
|
||||
rd.new_parent = tdentry;
|
||||
|
||||
host_err = start_renaming(&rd, 0, &QSTR_LEN(fname, flen),
|
||||
&QSTR_LEN(tname, tlen));
|
||||
|
||||
if (host_err) {
|
||||
err = nfserrno(host_err);
|
||||
goto out_want_write;
|
||||
}
|
||||
err = fh_fill_pre_attrs(ffhp);
|
||||
|
|
@ -1943,48 +1939,23 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
|||
if (err != nfs_ok)
|
||||
goto out_unlock;
|
||||
|
||||
odentry = lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), fdentry);
|
||||
host_err = PTR_ERR(odentry);
|
||||
if (IS_ERR(odentry))
|
||||
goto out_nfserr;
|
||||
type = d_inode(rd.old_dentry)->i_mode & S_IFMT;
|
||||
|
||||
host_err = -ENOENT;
|
||||
if (d_really_is_negative(odentry))
|
||||
goto out_dput_old;
|
||||
host_err = -EINVAL;
|
||||
if (odentry == trap)
|
||||
goto out_dput_old;
|
||||
type = d_inode(odentry)->i_mode & S_IFMT;
|
||||
if (d_inode(rd.new_dentry))
|
||||
type = d_inode(rd.new_dentry)->i_mode & S_IFMT;
|
||||
|
||||
ndentry = lookup_one(&nop_mnt_idmap, &QSTR_LEN(tname, tlen), tdentry);
|
||||
host_err = PTR_ERR(ndentry);
|
||||
if (IS_ERR(ndentry))
|
||||
goto out_dput_old;
|
||||
if (d_inode(ndentry))
|
||||
type = d_inode(ndentry)->i_mode & S_IFMT;
|
||||
host_err = -ENOTEMPTY;
|
||||
if (ndentry == trap)
|
||||
goto out_dput_new;
|
||||
|
||||
if ((ndentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) &&
|
||||
nfsd_has_cached_files(ndentry)) {
|
||||
close_cached = true;
|
||||
goto out_dput_old;
|
||||
if ((rd.new_dentry->d_sb->s_export_op->flags & EXPORT_OP_CLOSE_BEFORE_UNLINK) &&
|
||||
nfsd_has_cached_files(rd.new_dentry)) {
|
||||
close_cached = dget(rd.new_dentry);
|
||||
goto out_unlock;
|
||||
} else {
|
||||
struct renamedata rd = {
|
||||
.mnt_idmap = &nop_mnt_idmap,
|
||||
.old_parent = fdentry,
|
||||
.old_dentry = odentry,
|
||||
.new_parent = tdentry,
|
||||
.new_dentry = ndentry,
|
||||
};
|
||||
int retries;
|
||||
|
||||
for (retries = 1;;) {
|
||||
host_err = vfs_rename(&rd);
|
||||
if (host_err != -EAGAIN || !retries--)
|
||||
break;
|
||||
if (!nfsd_wait_for_delegreturn(rqstp, d_inode(odentry)))
|
||||
if (!nfsd_wait_for_delegreturn(rqstp, d_inode(rd.old_dentry)))
|
||||
break;
|
||||
}
|
||||
if (!host_err) {
|
||||
|
|
@ -1993,11 +1964,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
|||
host_err = commit_metadata(ffhp);
|
||||
}
|
||||
}
|
||||
out_dput_new:
|
||||
dput(ndentry);
|
||||
out_dput_old:
|
||||
dput(odentry);
|
||||
out_nfserr:
|
||||
if (host_err == -EBUSY) {
|
||||
/*
|
||||
* See RFC 8881 Section 18.26.4 para 1-3: NFSv4 RENAME
|
||||
|
|
@ -2016,7 +1982,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
|||
fh_fill_post_attrs(tfhp);
|
||||
}
|
||||
out_unlock:
|
||||
unlock_rename(tdentry, fdentry);
|
||||
end_renaming(&rd);
|
||||
out_want_write:
|
||||
fh_drop_write(ffhp);
|
||||
|
||||
|
|
@ -2027,9 +1993,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
|||
* until this point and then reattempt the whole shebang.
|
||||
*/
|
||||
if (close_cached) {
|
||||
close_cached = false;
|
||||
nfsd_close_cached_files(ndentry);
|
||||
dput(ndentry);
|
||||
nfsd_close_cached_files(close_cached);
|
||||
dput(close_cached);
|
||||
goto retry;
|
||||
}
|
||||
out:
|
||||
|
|
@ -2054,7 +2019,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
|||
{
|
||||
struct dentry *dentry, *rdentry;
|
||||
struct inode *dirp;
|
||||
struct inode *rinode;
|
||||
struct inode *rinode = NULL;
|
||||
__be32 err;
|
||||
int host_err;
|
||||
|
||||
|
|
@ -2073,24 +2038,21 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
|||
|
||||
dentry = fhp->fh_dentry;
|
||||
dirp = d_inode(dentry);
|
||||
inode_lock_nested(dirp, I_MUTEX_PARENT);
|
||||
|
||||
rdentry = lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry);
|
||||
rdentry = start_removing(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen));
|
||||
|
||||
host_err = PTR_ERR(rdentry);
|
||||
if (IS_ERR(rdentry))
|
||||
goto out_unlock;
|
||||
goto out_drop_write;
|
||||
|
||||
if (d_really_is_negative(rdentry)) {
|
||||
dput(rdentry);
|
||||
host_err = -ENOENT;
|
||||
goto out_unlock;
|
||||
}
|
||||
rinode = d_inode(rdentry);
|
||||
err = fh_fill_pre_attrs(fhp);
|
||||
if (err != nfs_ok)
|
||||
goto out_unlock;
|
||||
|
||||
rinode = d_inode(rdentry);
|
||||
/* Prevent truncation until after locks dropped */
|
||||
ihold(rinode);
|
||||
|
||||
if (!type)
|
||||
type = d_inode(rdentry)->i_mode & S_IFMT;
|
||||
|
||||
|
|
@ -2112,10 +2074,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
|||
}
|
||||
fh_fill_post_attrs(fhp);
|
||||
|
||||
inode_unlock(dirp);
|
||||
if (!host_err)
|
||||
out_unlock:
|
||||
end_removing(rdentry);
|
||||
if (!err && !host_err)
|
||||
host_err = commit_metadata(fhp);
|
||||
dput(rdentry);
|
||||
iput(rinode); /* truncate the inode here */
|
||||
|
||||
out_drop_write:
|
||||
|
|
@ -2133,9 +2095,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
|||
}
|
||||
out:
|
||||
return err != nfs_ok ? err : nfserrno(host_err);
|
||||
out_unlock:
|
||||
inode_unlock(dirp);
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -523,8 +523,8 @@ static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
|
|||
{
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct dentry *indexdir = ovl_indexdir(dentry->d_sb);
|
||||
struct dentry *index = NULL;
|
||||
struct dentry *temp = NULL;
|
||||
struct renamedata rd = {};
|
||||
struct qstr name = { };
|
||||
int err;
|
||||
|
||||
|
|
@ -556,17 +556,15 @@ static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ovl_parent_lock(indexdir, temp);
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = indexdir;
|
||||
rd.new_parent = indexdir;
|
||||
err = start_renaming_dentry(&rd, 0, temp, &name);
|
||||
if (err)
|
||||
goto out;
|
||||
index = ovl_lookup_upper(ofs, name.name, indexdir, name.len);
|
||||
if (IS_ERR(index)) {
|
||||
err = PTR_ERR(index);
|
||||
} else {
|
||||
err = ovl_do_rename(ofs, indexdir, temp, indexdir, index, 0);
|
||||
dput(index);
|
||||
}
|
||||
ovl_parent_unlock(indexdir);
|
||||
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
out:
|
||||
if (err)
|
||||
ovl_cleanup(ofs, indexdir, temp);
|
||||
|
|
@ -613,9 +611,9 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
inode_lock_nested(udir, I_MUTEX_PARENT);
|
||||
upper = ovl_lookup_upper(ofs, c->dentry->d_name.name, upperdir,
|
||||
c->dentry->d_name.len);
|
||||
upper = ovl_start_creating_upper(ofs, upperdir,
|
||||
&QSTR_LEN(c->dentry->d_name.name,
|
||||
c->dentry->d_name.len));
|
||||
err = PTR_ERR(upper);
|
||||
if (!IS_ERR(upper)) {
|
||||
err = ovl_do_link(ofs, ovl_dentry_upper(c->dentry), udir, upper);
|
||||
|
|
@ -626,9 +624,8 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
|||
ovl_dentry_set_upper_alias(c->dentry);
|
||||
ovl_dentry_update_reval(c->dentry, upper);
|
||||
}
|
||||
dput(upper);
|
||||
end_creating(upper);
|
||||
}
|
||||
inode_unlock(udir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
|
@ -764,7 +761,8 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
|||
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
|
||||
struct inode *inode;
|
||||
struct path path = { .mnt = ovl_upper_mnt(ofs) };
|
||||
struct dentry *temp, *upper, *trap;
|
||||
struct renamedata rd = {};
|
||||
struct dentry *temp;
|
||||
struct ovl_cu_creds cc;
|
||||
int err;
|
||||
struct ovl_cattr cattr = {
|
||||
|
|
@ -808,29 +806,24 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
|||
* ovl_copy_up_data(), so lock workdir and destdir and make sure that
|
||||
* temp wasn't moved before copy up completion or cleanup.
|
||||
*/
|
||||
trap = lock_rename(c->workdir, c->destdir);
|
||||
if (trap || temp->d_parent != c->workdir) {
|
||||
/* temp or workdir moved underneath us? abort without cleanup */
|
||||
dput(temp);
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = c->workdir;
|
||||
rd.new_parent = c->destdir;
|
||||
rd.flags = 0;
|
||||
err = start_renaming_dentry(&rd, 0, temp,
|
||||
&QSTR_LEN(c->destname.name, c->destname.len));
|
||||
if (err) {
|
||||
/* temp or workdir moved underneath us? map to -EIO */
|
||||
err = -EIO;
|
||||
if (!IS_ERR(trap))
|
||||
unlock_rename(c->workdir, c->destdir);
|
||||
goto out;
|
||||
}
|
||||
if (err)
|
||||
goto cleanup_unlocked;
|
||||
|
||||
err = ovl_copy_up_metadata(c, temp);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
if (!err)
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
|
||||
upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
|
||||
c->destname.len);
|
||||
err = PTR_ERR(upper);
|
||||
if (IS_ERR(upper))
|
||||
goto cleanup;
|
||||
|
||||
err = ovl_do_rename(ofs, c->workdir, temp, c->destdir, upper, 0);
|
||||
unlock_rename(c->workdir, c->destdir);
|
||||
dput(upper);
|
||||
if (err)
|
||||
goto cleanup_unlocked;
|
||||
|
||||
|
|
@ -851,8 +844,6 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
|||
|
||||
return err;
|
||||
|
||||
cleanup:
|
||||
unlock_rename(c->workdir, c->destdir);
|
||||
cleanup_unlocked:
|
||||
ovl_cleanup(ofs, c->workdir, temp);
|
||||
dput(temp);
|
||||
|
|
@ -894,16 +885,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
inode_lock_nested(udir, I_MUTEX_PARENT);
|
||||
|
||||
upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
|
||||
c->destname.len);
|
||||
upper = ovl_start_creating_upper(ofs, c->destdir,
|
||||
&QSTR_LEN(c->destname.name,
|
||||
c->destname.len));
|
||||
err = PTR_ERR(upper);
|
||||
if (!IS_ERR(upper)) {
|
||||
err = ovl_do_link(ofs, temp, udir, upper);
|
||||
dput(upper);
|
||||
end_creating(upper);
|
||||
}
|
||||
inode_unlock(udir);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -47,79 +47,70 @@ static int ovl_cleanup_locked(struct ovl_fs *ofs, struct inode *wdir,
|
|||
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir,
|
||||
struct dentry *wdentry)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ovl_parent_lock(workdir, wdentry);
|
||||
if (err)
|
||||
return err;
|
||||
wdentry = start_removing_dentry(workdir, wdentry);
|
||||
if (IS_ERR(wdentry))
|
||||
return PTR_ERR(wdentry);
|
||||
|
||||
ovl_cleanup_locked(ofs, workdir->d_inode, wdentry);
|
||||
ovl_parent_unlock(workdir);
|
||||
end_removing(wdentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir)
|
||||
void ovl_tempname(char name[OVL_TEMPNAME_SIZE])
|
||||
{
|
||||
struct dentry *temp;
|
||||
char name[20];
|
||||
static atomic_t temp_id = ATOMIC_INIT(0);
|
||||
|
||||
/* counter is allowed to wrap, since temp dentries are ephemeral */
|
||||
snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
|
||||
|
||||
temp = ovl_lookup_upper(ofs, name, workdir, strlen(name));
|
||||
if (!IS_ERR(temp) && temp->d_inode) {
|
||||
pr_err("workdir/%s already exists\n", name);
|
||||
dput(temp);
|
||||
temp = ERR_PTR(-EIO);
|
||||
snprintf(name, OVL_TEMPNAME_SIZE, "#%x", atomic_inc_return(&temp_id));
|
||||
}
|
||||
|
||||
return temp;
|
||||
static struct dentry *ovl_start_creating_temp(struct ovl_fs *ofs,
|
||||
struct dentry *workdir)
|
||||
{
|
||||
char name[OVL_TEMPNAME_SIZE];
|
||||
|
||||
ovl_tempname(name);
|
||||
return start_creating(ovl_upper_mnt_idmap(ofs), workdir,
|
||||
&QSTR(name));
|
||||
}
|
||||
|
||||
static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
|
||||
{
|
||||
int err;
|
||||
struct dentry *whiteout;
|
||||
struct dentry *whiteout, *link;
|
||||
struct dentry *workdir = ofs->workdir;
|
||||
struct inode *wdir = workdir->d_inode;
|
||||
|
||||
guard(mutex)(&ofs->whiteout_lock);
|
||||
|
||||
if (!ofs->whiteout) {
|
||||
inode_lock_nested(wdir, I_MUTEX_PARENT);
|
||||
whiteout = ovl_lookup_temp(ofs, workdir);
|
||||
if (!IS_ERR(whiteout)) {
|
||||
err = ovl_do_whiteout(ofs, wdir, whiteout);
|
||||
if (err) {
|
||||
dput(whiteout);
|
||||
whiteout = ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
inode_unlock(wdir);
|
||||
whiteout = ovl_start_creating_temp(ofs, workdir);
|
||||
if (IS_ERR(whiteout))
|
||||
return whiteout;
|
||||
ofs->whiteout = whiteout;
|
||||
err = ovl_do_whiteout(ofs, wdir, whiteout);
|
||||
if (!err)
|
||||
ofs->whiteout = dget(whiteout);
|
||||
end_creating(whiteout);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (!ofs->no_shared_whiteout) {
|
||||
inode_lock_nested(wdir, I_MUTEX_PARENT);
|
||||
whiteout = ovl_lookup_temp(ofs, workdir);
|
||||
if (!IS_ERR(whiteout)) {
|
||||
err = ovl_do_link(ofs, ofs->whiteout, wdir, whiteout);
|
||||
if (err) {
|
||||
dput(whiteout);
|
||||
whiteout = ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
inode_unlock(wdir);
|
||||
if (!IS_ERR(whiteout))
|
||||
return whiteout;
|
||||
if (PTR_ERR(whiteout) != -EMLINK) {
|
||||
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%lu)\n",
|
||||
link = ovl_start_creating_temp(ofs, workdir);
|
||||
if (IS_ERR(link))
|
||||
return link;
|
||||
err = ovl_do_link(ofs, ofs->whiteout, wdir, link);
|
||||
if (!err)
|
||||
whiteout = dget(link);
|
||||
end_creating(link);
|
||||
if (!err)
|
||||
return whiteout;;
|
||||
|
||||
if (err != -EMLINK) {
|
||||
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%u)\n",
|
||||
ofs->whiteout->d_inode->i_nlink,
|
||||
PTR_ERR(whiteout));
|
||||
err);
|
||||
ofs->no_shared_whiteout = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -132,6 +123,7 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *dir,
|
|||
struct dentry *dentry)
|
||||
{
|
||||
struct dentry *whiteout;
|
||||
struct renamedata rd = {};
|
||||
int err;
|
||||
int flags = 0;
|
||||
|
||||
|
|
@ -143,10 +135,14 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *dir,
|
|||
if (d_is_dir(dentry))
|
||||
flags = RENAME_EXCHANGE;
|
||||
|
||||
err = ovl_lock_rename_workdir(ofs->workdir, whiteout, dir, dentry);
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = ofs->workdir;
|
||||
rd.new_parent = dir;
|
||||
rd.flags = flags;
|
||||
err = start_renaming_two_dentries(&rd, whiteout, dentry);
|
||||
if (!err) {
|
||||
err = ovl_do_rename(ofs, ofs->workdir, whiteout, dir, dentry, flags);
|
||||
unlock_rename(ofs->workdir, dir);
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
}
|
||||
if (err)
|
||||
goto kill_whiteout;
|
||||
|
|
@ -191,7 +187,7 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
|
|||
if (!err && ofs->casefold != ovl_dentry_casefolded(newdentry)) {
|
||||
pr_warn_ratelimited("wrong inherited casefold (%pd2)\n",
|
||||
newdentry);
|
||||
dput(newdentry);
|
||||
end_creating(newdentry);
|
||||
err = -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
|
@ -241,8 +237,7 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
|
|||
}
|
||||
out:
|
||||
if (err) {
|
||||
if (!IS_ERR(newdentry))
|
||||
dput(newdentry);
|
||||
end_creating(newdentry);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
return newdentry;
|
||||
|
|
@ -252,11 +247,11 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
|
|||
struct ovl_cattr *attr)
|
||||
{
|
||||
struct dentry *ret;
|
||||
inode_lock_nested(workdir->d_inode, I_MUTEX_PARENT);
|
||||
ret = ovl_create_real(ofs, workdir,
|
||||
ovl_lookup_temp(ofs, workdir), attr);
|
||||
inode_unlock(workdir->d_inode);
|
||||
ret = ovl_start_creating_temp(ofs, workdir);
|
||||
if (IS_ERR(ret))
|
||||
return ret;
|
||||
ret = ovl_create_real(ofs, workdir, ret, attr);
|
||||
return end_creating_keep(ret);
|
||||
}
|
||||
|
||||
static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
|
||||
|
|
@ -354,18 +349,19 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
|
|||
{
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
||||
struct inode *udir = upperdir->d_inode;
|
||||
struct dentry *newdentry;
|
||||
int err;
|
||||
|
||||
inode_lock_nested(udir, I_MUTEX_PARENT);
|
||||
newdentry = ovl_create_real(ofs, upperdir,
|
||||
ovl_lookup_upper(ofs, dentry->d_name.name,
|
||||
upperdir, dentry->d_name.len),
|
||||
attr);
|
||||
inode_unlock(udir);
|
||||
newdentry = ovl_start_creating_upper(ofs, upperdir,
|
||||
&QSTR_LEN(dentry->d_name.name,
|
||||
dentry->d_name.len));
|
||||
if (IS_ERR(newdentry))
|
||||
return PTR_ERR(newdentry);
|
||||
newdentry = ovl_create_real(ofs, upperdir, newdentry, attr);
|
||||
if (IS_ERR(newdentry))
|
||||
return PTR_ERR(newdentry);
|
||||
|
||||
end_creating_keep(newdentry);
|
||||
|
||||
if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) &&
|
||||
!ovl_allow_offline_changes(ofs)) {
|
||||
|
|
@ -391,6 +387,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct dentry *workdir = ovl_workdir(dentry);
|
||||
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
||||
struct renamedata rd = {};
|
||||
struct path upperpath;
|
||||
struct dentry *upper;
|
||||
struct dentry *opaquedir;
|
||||
|
|
@ -416,7 +413,11 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|||
if (IS_ERR(opaquedir))
|
||||
goto out;
|
||||
|
||||
err = ovl_lock_rename_workdir(workdir, opaquedir, upperdir, upper);
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = workdir;
|
||||
rd.new_parent = upperdir;
|
||||
rd.flags = RENAME_EXCHANGE;
|
||||
err = start_renaming_two_dentries(&rd, opaquedir, upper);
|
||||
if (err)
|
||||
goto out_cleanup_unlocked;
|
||||
|
||||
|
|
@ -434,8 +435,8 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|||
if (err)
|
||||
goto out_cleanup;
|
||||
|
||||
err = ovl_do_rename(ofs, workdir, opaquedir, upperdir, upper, RENAME_EXCHANGE);
|
||||
unlock_rename(workdir, upperdir);
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
if (err)
|
||||
goto out_cleanup_unlocked;
|
||||
|
||||
|
|
@ -448,7 +449,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|||
return opaquedir;
|
||||
|
||||
out_cleanup:
|
||||
unlock_rename(workdir, upperdir);
|
||||
end_renaming(&rd);
|
||||
out_cleanup_unlocked:
|
||||
ovl_cleanup(ofs, workdir, opaquedir);
|
||||
dput(opaquedir);
|
||||
|
|
@ -471,6 +472,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
|||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct dentry *workdir = ovl_workdir(dentry);
|
||||
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
||||
struct renamedata rd = {};
|
||||
struct dentry *upper;
|
||||
struct dentry *newdentry;
|
||||
int err;
|
||||
|
|
@ -502,7 +504,11 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
|||
if (IS_ERR(newdentry))
|
||||
goto out_dput;
|
||||
|
||||
err = ovl_lock_rename_workdir(workdir, newdentry, upperdir, upper);
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = workdir;
|
||||
rd.new_parent = upperdir;
|
||||
rd.flags = 0;
|
||||
err = start_renaming_two_dentries(&rd, newdentry, upper);
|
||||
if (err)
|
||||
goto out_cleanup_unlocked;
|
||||
|
||||
|
|
@ -539,16 +545,16 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
|||
if (err)
|
||||
goto out_cleanup;
|
||||
|
||||
err = ovl_do_rename(ofs, workdir, newdentry, upperdir, upper,
|
||||
RENAME_EXCHANGE);
|
||||
unlock_rename(workdir, upperdir);
|
||||
rd.flags = RENAME_EXCHANGE;
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
if (err)
|
||||
goto out_cleanup_unlocked;
|
||||
|
||||
ovl_cleanup(ofs, workdir, upper);
|
||||
} else {
|
||||
err = ovl_do_rename(ofs, workdir, newdentry, upperdir, upper, 0);
|
||||
unlock_rename(workdir, upperdir);
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
if (err)
|
||||
goto out_cleanup_unlocked;
|
||||
}
|
||||
|
|
@ -568,7 +574,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
|||
return err;
|
||||
|
||||
out_cleanup:
|
||||
unlock_rename(workdir, upperdir);
|
||||
end_renaming(&rd);
|
||||
out_cleanup_unlocked:
|
||||
ovl_cleanup(ofs, workdir, newdentry);
|
||||
dput(newdentry);
|
||||
|
|
@ -850,17 +856,17 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
|
|||
goto out;
|
||||
}
|
||||
|
||||
inode_lock_nested(dir, I_MUTEX_PARENT);
|
||||
upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
|
||||
dentry->d_name.len);
|
||||
upper = ovl_start_removing_upper(ofs, upperdir,
|
||||
&QSTR_LEN(dentry->d_name.name,
|
||||
dentry->d_name.len));
|
||||
err = PTR_ERR(upper);
|
||||
if (IS_ERR(upper))
|
||||
goto out_unlock;
|
||||
goto out_dput;
|
||||
|
||||
err = -ESTALE;
|
||||
if ((opaquedir && upper != opaquedir) ||
|
||||
(!opaquedir && !ovl_matches_upper(dentry, upper)))
|
||||
goto out_dput_upper;
|
||||
goto out_unlock;
|
||||
|
||||
if (is_dir)
|
||||
err = ovl_do_rmdir(ofs, dir, upper);
|
||||
|
|
@ -876,10 +882,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
|
|||
*/
|
||||
if (!err)
|
||||
d_drop(dentry);
|
||||
out_dput_upper:
|
||||
dput(upper);
|
||||
out_unlock:
|
||||
inode_unlock(dir);
|
||||
end_removing(upper);
|
||||
out_dput:
|
||||
dput(opaquedir);
|
||||
out:
|
||||
return err;
|
||||
|
|
@ -1111,9 +1116,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
|||
int err;
|
||||
struct dentry *old_upperdir;
|
||||
struct dentry *new_upperdir;
|
||||
struct dentry *olddentry = NULL;
|
||||
struct dentry *newdentry = NULL;
|
||||
struct dentry *trap, *de;
|
||||
struct renamedata rd = {};
|
||||
bool old_opaque;
|
||||
bool new_opaque;
|
||||
bool cleanup_whiteout = false;
|
||||
|
|
@ -1123,6 +1126,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
|||
bool new_is_dir = d_is_dir(new);
|
||||
bool samedir = olddir == newdir;
|
||||
struct dentry *opaquedir = NULL;
|
||||
struct dentry *whiteout = NULL;
|
||||
const struct cred *old_cred = NULL;
|
||||
struct ovl_fs *ofs = OVL_FS(old->d_sb);
|
||||
LIST_HEAD(list);
|
||||
|
|
@ -1220,45 +1224,37 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
|||
}
|
||||
}
|
||||
|
||||
trap = lock_rename(new_upperdir, old_upperdir);
|
||||
if (IS_ERR(trap)) {
|
||||
err = PTR_ERR(trap);
|
||||
goto out_revert_creds;
|
||||
}
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = old_upperdir;
|
||||
rd.new_parent = new_upperdir;
|
||||
rd.flags = flags;
|
||||
|
||||
de = ovl_lookup_upper(ofs, old->d_name.name, old_upperdir,
|
||||
old->d_name.len);
|
||||
err = PTR_ERR(de);
|
||||
if (IS_ERR(de))
|
||||
goto out_unlock;
|
||||
olddentry = de;
|
||||
err = start_renaming(&rd, 0,
|
||||
&QSTR_LEN(old->d_name.name, old->d_name.len),
|
||||
&QSTR_LEN(new->d_name.name, new->d_name.len));
|
||||
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
|
||||
err = -ESTALE;
|
||||
if (!ovl_matches_upper(old, olddentry))
|
||||
if (!ovl_matches_upper(old, rd.old_dentry))
|
||||
goto out_unlock;
|
||||
|
||||
de = ovl_lookup_upper(ofs, new->d_name.name, new_upperdir,
|
||||
new->d_name.len);
|
||||
err = PTR_ERR(de);
|
||||
if (IS_ERR(de))
|
||||
goto out_unlock;
|
||||
newdentry = de;
|
||||
|
||||
old_opaque = ovl_dentry_is_opaque(old);
|
||||
new_opaque = ovl_dentry_is_opaque(new);
|
||||
|
||||
err = -ESTALE;
|
||||
if (d_inode(new) && ovl_dentry_upper(new)) {
|
||||
if (opaquedir) {
|
||||
if (newdentry != opaquedir)
|
||||
if (rd.new_dentry != opaquedir)
|
||||
goto out_unlock;
|
||||
} else {
|
||||
if (!ovl_matches_upper(new, newdentry))
|
||||
if (!ovl_matches_upper(new, rd.new_dentry))
|
||||
goto out_unlock;
|
||||
}
|
||||
} else {
|
||||
if (!d_is_negative(newdentry)) {
|
||||
if (!new_opaque || !ovl_upper_is_whiteout(ofs, newdentry))
|
||||
if (!d_is_negative(rd.new_dentry)) {
|
||||
if (!new_opaque || !ovl_upper_is_whiteout(ofs, rd.new_dentry))
|
||||
goto out_unlock;
|
||||
} else {
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
|
|
@ -1266,19 +1262,14 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
|||
}
|
||||
}
|
||||
|
||||
if (olddentry == trap)
|
||||
goto out_unlock;
|
||||
if (newdentry == trap)
|
||||
goto out_unlock;
|
||||
|
||||
if (olddentry->d_inode == newdentry->d_inode)
|
||||
if (rd.old_dentry->d_inode == rd.new_dentry->d_inode)
|
||||
goto out_unlock;
|
||||
|
||||
err = 0;
|
||||
if (ovl_type_merge_or_lower(old))
|
||||
err = ovl_set_redirect(old, samedir);
|
||||
else if (is_dir && !old_opaque && ovl_type_merge(new->d_parent))
|
||||
err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
|
||||
err = ovl_set_opaque_xerr(old, rd.old_dentry, -EXDEV);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -1286,18 +1277,24 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
|||
err = ovl_set_redirect(new, samedir);
|
||||
else if (!overwrite && new_is_dir && !new_opaque &&
|
||||
ovl_type_merge(old->d_parent))
|
||||
err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
|
||||
err = ovl_set_opaque_xerr(new, rd.new_dentry, -EXDEV);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
err = ovl_do_rename(ofs, old_upperdir, olddentry,
|
||||
new_upperdir, newdentry, flags);
|
||||
unlock_rename(new_upperdir, old_upperdir);
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
|
||||
if (!err && cleanup_whiteout)
|
||||
whiteout = dget(rd.new_dentry);
|
||||
|
||||
end_renaming(&rd);
|
||||
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
|
||||
if (cleanup_whiteout)
|
||||
ovl_cleanup(ofs, old_upperdir, newdentry);
|
||||
if (whiteout) {
|
||||
ovl_cleanup(ofs, old_upperdir, whiteout);
|
||||
dput(whiteout);
|
||||
}
|
||||
|
||||
if (overwrite && d_inode(new)) {
|
||||
if (new_is_dir)
|
||||
|
|
@ -1323,14 +1320,12 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
|||
else
|
||||
ovl_drop_write(old);
|
||||
out:
|
||||
dput(newdentry);
|
||||
dput(olddentry);
|
||||
dput(opaquedir);
|
||||
ovl_cache_free(&list);
|
||||
return err;
|
||||
|
||||
out_unlock:
|
||||
unlock_rename(new_upperdir, old_upperdir);
|
||||
end_renaming(&rd);
|
||||
goto out_revert_creds;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -355,11 +355,24 @@ static inline int ovl_do_remove_acl(struct ovl_fs *ofs, struct dentry *dentry,
|
|||
return vfs_remove_acl(ovl_upper_mnt_idmap(ofs), dentry, acl_name);
|
||||
}
|
||||
|
||||
static inline int ovl_do_rename_rd(struct renamedata *rd)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_debug("rename(%pd2, %pd2, 0x%x)\n", rd->old_dentry, rd->new_dentry,
|
||||
rd->flags);
|
||||
err = vfs_rename(rd);
|
||||
if (err) {
|
||||
pr_debug("...rename(%pd2, %pd2, ...) = %i\n",
|
||||
rd->old_dentry, rd->new_dentry, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int ovl_do_rename(struct ovl_fs *ofs, struct dentry *olddir,
|
||||
struct dentry *olddentry, struct dentry *newdir,
|
||||
struct dentry *newdentry, unsigned int flags)
|
||||
{
|
||||
int err;
|
||||
struct renamedata rd = {
|
||||
.mnt_idmap = ovl_upper_mnt_idmap(ofs),
|
||||
.old_parent = olddir,
|
||||
|
|
@ -369,13 +382,7 @@ static inline int ovl_do_rename(struct ovl_fs *ofs, struct dentry *olddir,
|
|||
.flags = flags,
|
||||
};
|
||||
|
||||
pr_debug("rename(%pd2, %pd2, 0x%x)\n", olddentry, newdentry, flags);
|
||||
err = vfs_rename(&rd);
|
||||
if (err) {
|
||||
pr_debug("...rename(%pd2, %pd2, ...) = %i\n",
|
||||
olddentry, newdentry, err);
|
||||
}
|
||||
return err;
|
||||
return ovl_do_rename_rd(&rd);
|
||||
}
|
||||
|
||||
static inline int ovl_do_whiteout(struct ovl_fs *ofs,
|
||||
|
|
@ -415,6 +422,22 @@ static inline struct dentry *ovl_lookup_upper_unlocked(struct ovl_fs *ofs,
|
|||
&QSTR_LEN(name, len), base);
|
||||
}
|
||||
|
||||
static inline struct dentry *ovl_start_creating_upper(struct ovl_fs *ofs,
|
||||
struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
return start_creating(ovl_upper_mnt_idmap(ofs),
|
||||
parent, name);
|
||||
}
|
||||
|
||||
static inline struct dentry *ovl_start_removing_upper(struct ovl_fs *ofs,
|
||||
struct dentry *parent,
|
||||
struct qstr *name)
|
||||
{
|
||||
return start_removing(ovl_upper_mnt_idmap(ofs),
|
||||
parent, name);
|
||||
}
|
||||
|
||||
static inline bool ovl_open_flags_need_copy_up(int flags)
|
||||
{
|
||||
if (!flags)
|
||||
|
|
@ -424,11 +447,6 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
|
|||
}
|
||||
|
||||
/* util.c */
|
||||
int ovl_parent_lock(struct dentry *parent, struct dentry *child);
|
||||
static inline void ovl_parent_unlock(struct dentry *parent)
|
||||
{
|
||||
inode_unlock(parent->d_inode);
|
||||
}
|
||||
int ovl_get_write_access(struct dentry *dentry);
|
||||
void ovl_put_write_access(struct dentry *dentry);
|
||||
void ovl_start_write(struct dentry *dentry);
|
||||
|
|
@ -865,7 +883,8 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs,
|
|||
struct dentry *parent, struct dentry *newdentry,
|
||||
struct ovl_cattr *attr);
|
||||
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *dentry);
|
||||
struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir);
|
||||
#define OVL_TEMPNAME_SIZE 20
|
||||
void ovl_tempname(char name[OVL_TEMPNAME_SIZE]);
|
||||
struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
|
||||
struct ovl_cattr *attr);
|
||||
|
||||
|
|
|
|||
|
|
@ -1242,11 +1242,11 @@ int ovl_workdir_cleanup(struct ovl_fs *ofs, struct dentry *parent,
|
|||
if (!d_is_dir(dentry) || level > 1)
|
||||
return ovl_cleanup(ofs, parent, dentry);
|
||||
|
||||
err = ovl_parent_lock(parent, dentry);
|
||||
if (err)
|
||||
return err;
|
||||
dentry = start_removing_dentry(parent, dentry);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
err = ovl_do_rmdir(ofs, parent->d_inode, dentry);
|
||||
ovl_parent_unlock(parent);
|
||||
end_removing(dentry);
|
||||
if (err) {
|
||||
struct path path = { .mnt = mnt, .dentry = dentry };
|
||||
|
||||
|
|
|
|||
|
|
@ -310,8 +310,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
|
|||
bool retried = false;
|
||||
|
||||
retry:
|
||||
inode_lock_nested(dir, I_MUTEX_PARENT);
|
||||
work = ovl_lookup_upper(ofs, name, ofs->workbasedir, strlen(name));
|
||||
work = ovl_start_creating_upper(ofs, ofs->workbasedir, &QSTR(name));
|
||||
|
||||
if (!IS_ERR(work)) {
|
||||
struct iattr attr = {
|
||||
|
|
@ -320,14 +319,12 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
|
|||
};
|
||||
|
||||
if (work->d_inode) {
|
||||
err = -EEXIST;
|
||||
inode_unlock(dir);
|
||||
if (retried)
|
||||
goto out_dput;
|
||||
|
||||
end_creating_keep(work);
|
||||
if (persist)
|
||||
return work;
|
||||
|
||||
err = -EEXIST;
|
||||
if (retried)
|
||||
goto out_dput;
|
||||
retried = true;
|
||||
err = ovl_workdir_cleanup(ofs, ofs->workbasedir, mnt, work, 0);
|
||||
dput(work);
|
||||
|
|
@ -338,7 +335,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
|
|||
}
|
||||
|
||||
work = ovl_do_mkdir(ofs, dir, work, attr.ia_mode);
|
||||
inode_unlock(dir);
|
||||
end_creating_keep(work);
|
||||
err = PTR_ERR(work);
|
||||
if (IS_ERR(work))
|
||||
goto out_err;
|
||||
|
|
@ -376,7 +373,6 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
|
|||
if (err)
|
||||
goto out_dput;
|
||||
} else {
|
||||
inode_unlock(dir);
|
||||
err = PTR_ERR(work);
|
||||
goto out_err;
|
||||
}
|
||||
|
|
@ -567,9 +563,10 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
|
|||
{
|
||||
struct dentry *workdir = ofs->workdir;
|
||||
struct dentry *temp;
|
||||
struct dentry *dest;
|
||||
struct dentry *whiteout;
|
||||
struct name_snapshot name;
|
||||
struct renamedata rd = {};
|
||||
char name2[OVL_TEMPNAME_SIZE];
|
||||
int err;
|
||||
|
||||
temp = ovl_create_temp(ofs, workdir, OVL_CATTR(S_IFREG | 0));
|
||||
|
|
@ -577,23 +574,21 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
|
|||
if (IS_ERR(temp))
|
||||
return err;
|
||||
|
||||
err = ovl_parent_lock(workdir, temp);
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = workdir;
|
||||
rd.new_parent = workdir;
|
||||
rd.flags = RENAME_WHITEOUT;
|
||||
ovl_tempname(name2);
|
||||
err = start_renaming_dentry(&rd, 0, temp, &QSTR(name2));
|
||||
if (err) {
|
||||
dput(temp);
|
||||
return err;
|
||||
}
|
||||
dest = ovl_lookup_temp(ofs, workdir);
|
||||
err = PTR_ERR(dest);
|
||||
if (IS_ERR(dest)) {
|
||||
dput(temp);
|
||||
ovl_parent_unlock(workdir);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Name is inline and stable - using snapshot as a copy helper */
|
||||
take_dentry_name_snapshot(&name, temp);
|
||||
err = ovl_do_rename(ofs, workdir, temp, workdir, dest, RENAME_WHITEOUT);
|
||||
ovl_parent_unlock(workdir);
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
end_renaming(&rd);
|
||||
if (err) {
|
||||
if (err == -EINVAL)
|
||||
err = 0;
|
||||
|
|
@ -617,7 +612,6 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
|
|||
ovl_cleanup(ofs, workdir, temp);
|
||||
release_dentry_name_snapshot(&name);
|
||||
dput(temp);
|
||||
dput(dest);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -626,14 +620,15 @@ static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
|
|||
struct dentry *parent,
|
||||
const char *name, umode_t mode)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
struct dentry *child;
|
||||
|
||||
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
|
||||
child = ovl_lookup_upper(ofs, name, parent, len);
|
||||
if (!IS_ERR(child) && !child->d_inode)
|
||||
child = ovl_create_real(ofs, parent, child, OVL_CATTR(mode));
|
||||
inode_unlock(parent->d_inode);
|
||||
child = ovl_start_creating_upper(ofs, parent, &QSTR(name));
|
||||
if (!IS_ERR(child)) {
|
||||
if (!child->d_inode)
|
||||
child = ovl_create_real(ofs, parent, child,
|
||||
OVL_CATTR(mode));
|
||||
end_creating_keep(child);
|
||||
}
|
||||
dput(parent);
|
||||
|
||||
return child;
|
||||
|
|
|
|||
|
|
@ -1548,14 +1548,3 @@ void ovl_copyattr(struct inode *inode)
|
|||
i_size_write(inode, i_size_read(realinode));
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
int ovl_parent_lock(struct dentry *parent, struct dentry *child)
|
||||
{
|
||||
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
|
||||
if (!child ||
|
||||
(!d_unhashed(child) && child->d_parent == parent))
|
||||
return 0;
|
||||
|
||||
inode_unlock(parent->d_inode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6092,7 +6092,7 @@ static int smb2_create_link(struct ksmbd_work *work,
|
|||
}
|
||||
|
||||
ksmbd_debug(SMB, "target name is %s\n", target_name);
|
||||
rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
|
||||
rc = ksmbd_vfs_kern_path_start_removing(work, link_name, LOOKUP_NO_SYMLINKS,
|
||||
&path, 0);
|
||||
if (rc) {
|
||||
if (rc != -ENOENT)
|
||||
|
|
@ -6111,7 +6111,7 @@ static int smb2_create_link(struct ksmbd_work *work,
|
|||
ksmbd_debug(SMB, "link already exists\n");
|
||||
goto out;
|
||||
}
|
||||
ksmbd_vfs_kern_path_unlock(&path);
|
||||
ksmbd_vfs_kern_path_end_removing(&path);
|
||||
}
|
||||
rc = ksmbd_vfs_link(work, target_name, link_name);
|
||||
if (rc)
|
||||
|
|
|
|||
|
|
@ -49,27 +49,9 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
|
|||
i_uid_write(inode, i_uid_read(parent_inode));
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
|
||||
* @parent: parent dentry
|
||||
* @child: child dentry
|
||||
*
|
||||
* Returns: %0 on success, %-ENOENT if the parent dentry is not stable
|
||||
*/
|
||||
int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
|
||||
{
|
||||
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
|
||||
if (child->d_parent != parent) {
|
||||
inode_unlock(d_inode(parent));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf,
|
||||
char *pathname, unsigned int flags,
|
||||
struct path *path, bool do_lock)
|
||||
struct path *path, bool for_remove)
|
||||
{
|
||||
struct qstr last;
|
||||
struct filename *filename __free(putname) = NULL;
|
||||
|
|
@ -99,22 +81,20 @@ static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (do_lock) {
|
||||
if (for_remove) {
|
||||
err = mnt_want_write(path->mnt);
|
||||
if (err) {
|
||||
path_put(path);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
|
||||
d = lookup_one_qstr_excl(&last, path->dentry, 0);
|
||||
d = start_removing_noperm(path->dentry, &last);
|
||||
|
||||
if (!IS_ERR(d)) {
|
||||
dput(path->dentry);
|
||||
path->dentry = d;
|
||||
return 0;
|
||||
}
|
||||
inode_unlock(path->dentry->d_inode);
|
||||
mnt_drop_write(path->mnt);
|
||||
path_put(path);
|
||||
return -ENOENT;
|
||||
|
|
@ -680,7 +660,6 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
|
|||
int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
|
||||
char *newname, int flags)
|
||||
{
|
||||
struct dentry *old_parent, *new_dentry, *trap;
|
||||
struct dentry *old_child = old_path->dentry;
|
||||
struct path new_path;
|
||||
struct qstr new_last;
|
||||
|
|
@ -690,7 +669,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
|
|||
struct ksmbd_file *parent_fp;
|
||||
int new_type;
|
||||
int err, lookup_flags = LOOKUP_NO_SYMLINKS;
|
||||
int target_lookup_flags = LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
|
||||
|
||||
if (ksmbd_override_fsids(work))
|
||||
return -ENOMEM;
|
||||
|
|
@ -701,14 +679,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
|
|||
goto revert_fsids;
|
||||
}
|
||||
|
||||
/*
|
||||
* explicitly handle file overwrite case, for compatibility with
|
||||
* filesystems that may not support rename flags (e.g: fuse)
|
||||
*/
|
||||
if (flags & RENAME_NOREPLACE)
|
||||
target_lookup_flags |= LOOKUP_EXCL;
|
||||
flags &= ~(RENAME_NOREPLACE);
|
||||
|
||||
retry:
|
||||
err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH,
|
||||
&new_path, &new_last, &new_type,
|
||||
|
|
@ -725,17 +695,14 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
|
|||
if (err)
|
||||
goto out2;
|
||||
|
||||
trap = lock_rename_child(old_child, new_path.dentry);
|
||||
if (IS_ERR(trap)) {
|
||||
err = PTR_ERR(trap);
|
||||
rd.mnt_idmap = mnt_idmap(old_path->mnt);
|
||||
rd.old_parent = NULL;
|
||||
rd.new_parent = new_path.dentry;
|
||||
rd.flags = flags;
|
||||
rd.delegated_inode = NULL,
|
||||
err = start_renaming_dentry(&rd, lookup_flags, old_child, &new_last);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
old_parent = dget(old_child->d_parent);
|
||||
if (d_unhashed(old_child)) {
|
||||
err = -EINVAL;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent);
|
||||
if (parent_fp) {
|
||||
|
|
@ -748,44 +715,17 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
|
|||
ksmbd_fd_put(work, parent_fp);
|
||||
}
|
||||
|
||||
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
|
||||
lookup_flags | target_lookup_flags);
|
||||
if (IS_ERR(new_dentry)) {
|
||||
err = PTR_ERR(new_dentry);
|
||||
if (d_is_symlink(rd.new_dentry)) {
|
||||
err = -EACCES;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
if (d_is_symlink(new_dentry)) {
|
||||
err = -EACCES;
|
||||
goto out4;
|
||||
}
|
||||
|
||||
if (old_child == trap) {
|
||||
err = -EINVAL;
|
||||
goto out4;
|
||||
}
|
||||
|
||||
if (new_dentry == trap) {
|
||||
err = -ENOTEMPTY;
|
||||
goto out4;
|
||||
}
|
||||
|
||||
rd.mnt_idmap = mnt_idmap(old_path->mnt),
|
||||
rd.old_parent = old_parent,
|
||||
rd.old_dentry = old_child,
|
||||
rd.new_parent = new_path.dentry,
|
||||
rd.new_dentry = new_dentry,
|
||||
rd.flags = flags,
|
||||
rd.delegated_inode = NULL,
|
||||
err = vfs_rename(&rd);
|
||||
if (err)
|
||||
ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
|
||||
|
||||
out4:
|
||||
dput(new_dentry);
|
||||
out3:
|
||||
dput(old_parent);
|
||||
unlock_rename(old_parent, new_path.dentry);
|
||||
end_renaming(&rd);
|
||||
out_drop_write:
|
||||
mnt_drop_write(old_path->mnt);
|
||||
out2:
|
||||
|
|
@ -1083,18 +1023,17 @@ int ksmbd_vfs_unlink(struct file *filp)
|
|||
return err;
|
||||
|
||||
dir = dget_parent(dentry);
|
||||
err = ksmbd_vfs_lock_parent(dir, dentry);
|
||||
if (err)
|
||||
dentry = start_removing_dentry(dir, dentry);
|
||||
err = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry))
|
||||
goto out;
|
||||
dget(dentry);
|
||||
|
||||
if (S_ISDIR(d_inode(dentry)->i_mode))
|
||||
err = vfs_rmdir(idmap, d_inode(dir), dentry, NULL);
|
||||
else
|
||||
err = vfs_unlink(idmap, d_inode(dir), dentry, NULL);
|
||||
|
||||
dput(dentry);
|
||||
inode_unlock(d_inode(dir));
|
||||
end_removing(dentry);
|
||||
if (err)
|
||||
ksmbd_debug(VFS, "failed to delete, err %d\n", err);
|
||||
out:
|
||||
|
|
@ -1206,7 +1145,7 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
|
|||
static
|
||||
int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
|
||||
unsigned int flags,
|
||||
struct path *path, bool caseless, bool do_lock)
|
||||
struct path *path, bool caseless, bool for_remove)
|
||||
{
|
||||
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
|
||||
struct path parent_path;
|
||||
|
|
@ -1214,7 +1153,7 @@ int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
|
|||
int err;
|
||||
|
||||
retry:
|
||||
err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, do_lock);
|
||||
err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, for_remove);
|
||||
if (!err || !caseless)
|
||||
return err;
|
||||
|
||||
|
|
@ -1285,7 +1224,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
|
|||
}
|
||||
|
||||
/**
|
||||
* ksmbd_vfs_kern_path_locked() - lookup a file and get path info
|
||||
* ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal
|
||||
* @work: work
|
||||
* @filepath: file path that is relative to share
|
||||
* @flags: lookup flags
|
||||
|
|
@ -1297,7 +1236,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
|
|||
* filesystem will have been gained.
|
||||
* Return: 0 on if file was found, otherwise error
|
||||
*/
|
||||
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *filepath,
|
||||
int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *filepath,
|
||||
unsigned int flags,
|
||||
struct path *path, bool caseless)
|
||||
{
|
||||
|
|
@ -1305,12 +1244,11 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *filepath,
|
|||
caseless, true);
|
||||
}
|
||||
|
||||
void ksmbd_vfs_kern_path_unlock(const struct path *path)
|
||||
void ksmbd_vfs_kern_path_end_removing(const struct path *path)
|
||||
{
|
||||
/* While lock is still held, ->d_parent is safe */
|
||||
inode_unlock(d_inode(path->dentry->d_parent));
|
||||
end_removing(path->dentry);
|
||||
mnt_drop_write(path->mnt);
|
||||
path_put(path);
|
||||
mntput(path->mnt);
|
||||
}
|
||||
|
||||
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
|
||||
|
|
|
|||
|
|
@ -120,10 +120,10 @@ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
|
|||
int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
|
||||
unsigned int flags,
|
||||
struct path *path, bool caseless);
|
||||
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
|
||||
int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *name,
|
||||
unsigned int flags,
|
||||
struct path *path, bool caseless);
|
||||
void ksmbd_vfs_kern_path_unlock(const struct path *path);
|
||||
void ksmbd_vfs_kern_path_end_removing(const struct path *path);
|
||||
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
|
||||
const char *name,
|
||||
unsigned int flags,
|
||||
|
|
|
|||
|
|
@ -152,11 +152,10 @@ xrep_orphanage_create(
|
|||
}
|
||||
|
||||
/* Try to find the orphanage directory. */
|
||||
inode_lock_nested(root_inode, I_MUTEX_PARENT);
|
||||
orphanage_dentry = lookup_noperm(&QSTR(ORPHANAGE), root_dentry);
|
||||
orphanage_dentry = start_creating_noperm(root_dentry, &QSTR(ORPHANAGE));
|
||||
if (IS_ERR(orphanage_dentry)) {
|
||||
error = PTR_ERR(orphanage_dentry);
|
||||
goto out_unlock_root;
|
||||
goto out_dput_root;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -170,7 +169,7 @@ xrep_orphanage_create(
|
|||
orphanage_dentry, 0750, NULL);
|
||||
error = PTR_ERR(orphanage_dentry);
|
||||
if (IS_ERR(orphanage_dentry))
|
||||
goto out_unlock_root;
|
||||
goto out_dput_orphanage;
|
||||
}
|
||||
|
||||
/* Not a directory? Bail out. */
|
||||
|
|
@ -200,9 +199,7 @@ xrep_orphanage_create(
|
|||
sc->orphanage_ilock_flags = 0;
|
||||
|
||||
out_dput_orphanage:
|
||||
dput(orphanage_dentry);
|
||||
out_unlock_root:
|
||||
inode_unlock(VFS_I(sc->mp->m_rootip));
|
||||
end_creating(orphanage_dentry);
|
||||
out_dput_root:
|
||||
dput(root_dentry);
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -3175,6 +3175,8 @@ extern void iterate_supers_type(struct file_system_type *,
|
|||
void filesystems_freeze(bool freeze_all);
|
||||
void filesystems_thaw(void);
|
||||
|
||||
void end_dirop(struct dentry *de);
|
||||
|
||||
extern int dcache_dir_open(struct inode *, struct file *);
|
||||
extern int dcache_dir_close(struct inode *, struct file *);
|
||||
extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
|
||||
|
|
|
|||
|
|
@ -89,6 +89,81 @@ struct dentry *lookup_one_positive_killable(struct mnt_idmap *idmap,
|
|||
struct qstr *name,
|
||||
struct dentry *base);
|
||||
|
||||
struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
|
||||
struct qstr *name);
|
||||
struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
|
||||
struct qstr *name);
|
||||
struct dentry *start_creating_killable(struct mnt_idmap *idmap,
|
||||
struct dentry *parent,
|
||||
struct qstr *name);
|
||||
struct dentry *start_removing_killable(struct mnt_idmap *idmap,
|
||||
struct dentry *parent,
|
||||
struct qstr *name);
|
||||
struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *name);
|
||||
struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *name);
|
||||
struct dentry *start_creating_dentry(struct dentry *parent,
|
||||
struct dentry *child);
|
||||
struct dentry *start_removing_dentry(struct dentry *parent,
|
||||
struct dentry *child);
|
||||
|
||||
/* end_creating - finish action started with start_creating
|
||||
* @child: dentry returned by start_creating() or vfs_mkdir()
|
||||
*
|
||||
* Unlock and release the child. This can be called after
|
||||
* start_creating() whether that function succeeded or not,
|
||||
* but it is not needed on failure.
|
||||
*
|
||||
* If vfs_mkdir() was called then the value returned from that function
|
||||
* should be given for @child rather than the original dentry, as vfs_mkdir()
|
||||
* may have provided a new dentry.
|
||||
*
|
||||
*
|
||||
* If vfs_mkdir() was not called, then @child will be a valid dentry and
|
||||
* @parent will be ignored.
|
||||
*/
|
||||
static inline void end_creating(struct dentry *child)
|
||||
{
|
||||
end_dirop(child);
|
||||
}
|
||||
|
||||
/* end_creating_keep - finish action started with start_creating() and return result
|
||||
* @child: dentry returned by start_creating() or vfs_mkdir()
|
||||
*
|
||||
* Unlock and return the child. This can be called after
|
||||
* start_creating() whether that function succeeded or not,
|
||||
* but it is not needed on failure.
|
||||
*
|
||||
* If vfs_mkdir() was called then the value returned from that function
|
||||
* should be given for @child rather than the original dentry, as vfs_mkdir()
|
||||
* may have provided a new dentry.
|
||||
*
|
||||
* Returns: @child, which may be a dentry or an error.
|
||||
*
|
||||
*/
|
||||
static inline struct dentry *end_creating_keep(struct dentry *child)
|
||||
{
|
||||
if (!IS_ERR(child))
|
||||
dget(child);
|
||||
end_dirop(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* end_removing - finish action started with start_removing
|
||||
* @child: dentry returned by start_removing()
|
||||
* @parent: dentry given to start_removing()
|
||||
*
|
||||
* Unlock and release the child.
|
||||
*
|
||||
* This is identical to end_dirop(). It can be passed the result of
|
||||
* start_removing() whether that was successful or not, but it not needed
|
||||
* if start_removing() failed.
|
||||
*/
|
||||
static inline void end_removing(struct dentry *child)
|
||||
{
|
||||
end_dirop(child);
|
||||
}
|
||||
|
||||
extern int follow_down_one(struct path *);
|
||||
extern int follow_down(struct path *path, unsigned int flags);
|
||||
extern int follow_up(struct path *);
|
||||
|
|
@ -96,6 +171,13 @@ extern int follow_up(struct path *);
|
|||
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
|
||||
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
|
||||
extern void unlock_rename(struct dentry *, struct dentry *);
|
||||
int start_renaming(struct renamedata *rd, int lookup_flags,
|
||||
struct qstr *old_last, struct qstr *new_last);
|
||||
int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
|
||||
struct dentry *old_dentry, struct qstr *new_last);
|
||||
int start_renaming_two_dentries(struct renamedata *rd,
|
||||
struct dentry *old_dentry, struct dentry *new_dentry);
|
||||
void end_renaming(struct renamedata *rd);
|
||||
|
||||
/**
|
||||
* mode_strip_umask - handle vfs umask stripping
|
||||
|
|
|
|||
28
ipc/mqueue.c
28
ipc/mqueue.c
|
|
@ -913,13 +913,12 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
|
|||
goto out_putname;
|
||||
|
||||
ro = mnt_want_write(mnt); /* we'll drop it in any case */
|
||||
inode_lock(d_inode(root));
|
||||
path.dentry = lookup_noperm(&QSTR(name->name), root);
|
||||
path.dentry = start_creating_noperm(root, &QSTR(name->name));
|
||||
if (IS_ERR(path.dentry)) {
|
||||
error = PTR_ERR(path.dentry);
|
||||
goto out_putfd;
|
||||
}
|
||||
path.mnt = mntget(mnt);
|
||||
path.mnt = mnt;
|
||||
error = prepare_open(path.dentry, oflag, ro, mode, name, attr);
|
||||
if (!error) {
|
||||
struct file *file = dentry_open(&path, oflag, current_cred());
|
||||
|
|
@ -928,13 +927,12 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
|
|||
else
|
||||
error = PTR_ERR(file);
|
||||
}
|
||||
path_put(&path);
|
||||
out_putfd:
|
||||
if (error) {
|
||||
put_unused_fd(fd);
|
||||
fd = error;
|
||||
}
|
||||
inode_unlock(d_inode(root));
|
||||
end_creating(path.dentry);
|
||||
if (!ro)
|
||||
mnt_drop_write(mnt);
|
||||
out_putname:
|
||||
|
|
@ -957,7 +955,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
|||
int err;
|
||||
struct filename *name;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode = NULL;
|
||||
struct inode *inode;
|
||||
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
|
||||
struct vfsmount *mnt = ipc_ns->mq_mnt;
|
||||
|
||||
|
|
@ -969,26 +967,20 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
|||
err = mnt_want_write(mnt);
|
||||
if (err)
|
||||
goto out_name;
|
||||
inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT);
|
||||
dentry = lookup_noperm(&QSTR(name->name), mnt->mnt_root);
|
||||
dentry = start_removing_noperm(mnt->mnt_root, &QSTR(name->name));
|
||||
if (IS_ERR(dentry)) {
|
||||
err = PTR_ERR(dentry);
|
||||
goto out_unlock;
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
inode = d_inode(dentry);
|
||||
if (!inode) {
|
||||
err = -ENOENT;
|
||||
} else {
|
||||
ihold(inode);
|
||||
err = vfs_unlink(&nop_mnt_idmap, d_inode(dentry->d_parent),
|
||||
err = vfs_unlink(&nop_mnt_idmap, d_inode(mnt->mnt_root),
|
||||
dentry, NULL);
|
||||
}
|
||||
dput(dentry);
|
||||
|
||||
out_unlock:
|
||||
inode_unlock(d_inode(mnt->mnt_root));
|
||||
end_removing(dentry);
|
||||
iput(inode);
|
||||
|
||||
out_drop_write:
|
||||
mnt_drop_write(mnt);
|
||||
out_name:
|
||||
putname(name);
|
||||
|
|
|
|||
|
|
@ -355,17 +355,17 @@ static void aafs_remove(struct dentry *dentry)
|
|||
if (!dentry || IS_ERR(dentry))
|
||||
return;
|
||||
|
||||
/* ->d_parent is stable as rename is not supported */
|
||||
dir = d_inode(dentry->d_parent);
|
||||
inode_lock(dir);
|
||||
if (simple_positive(dentry)) {
|
||||
dentry = start_removing_dentry(dentry->d_parent, dentry);
|
||||
if (!IS_ERR(dentry) && simple_positive(dentry)) {
|
||||
if (d_is_dir(dentry))
|
||||
simple_rmdir(dir, dentry);
|
||||
else
|
||||
simple_unlink(dir, dentry);
|
||||
d_delete(dentry);
|
||||
dput(dentry);
|
||||
}
|
||||
inode_unlock(dir);
|
||||
end_removing(dentry);
|
||||
simple_release_fs(&aafs_mnt, &aafs_count);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -506,6 +506,7 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
|
|||
{
|
||||
int ret = 0;
|
||||
struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir;
|
||||
struct renamedata rd = {};
|
||||
unsigned int bool_num = 0;
|
||||
char **bool_names = NULL;
|
||||
int *bool_values = NULL;
|
||||
|
|
@ -539,9 +540,14 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
lock_rename(tmp_parent, fsi->sb->s_root);
|
||||
rd.old_parent = tmp_parent;
|
||||
rd.new_parent = fsi->sb->s_root;
|
||||
|
||||
/* booleans */
|
||||
ret = start_renaming_two_dentries(&rd, tmp_bool_dir, fsi->bool_dir);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
d_exchange(tmp_bool_dir, fsi->bool_dir);
|
||||
|
||||
swap(fsi->bool_num, bool_num);
|
||||
|
|
@ -549,12 +555,17 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
|
|||
swap(fsi->bool_pending_values, bool_values);
|
||||
|
||||
fsi->bool_dir = tmp_bool_dir;
|
||||
end_renaming(&rd);
|
||||
|
||||
/* classes */
|
||||
ret = start_renaming_two_dentries(&rd, tmp_class_dir, fsi->class_dir);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
d_exchange(tmp_class_dir, fsi->class_dir);
|
||||
fsi->class_dir = tmp_class_dir;
|
||||
|
||||
unlock_rename(tmp_parent, fsi->sb->s_root);
|
||||
end_renaming(&rd);
|
||||
|
||||
out:
|
||||
sel_remove_old_bool_data(bool_num, bool_names, bool_values);
|
||||
|
|
|
|||
Loading…
Reference in New Issue