cachefiles: Implement a function to get/create a directory in the cache
Implement a function to get/create structural directories in the cache. This is used for setting up a cache and creating volume substructures. The directory in memory are marked with the S_KERNEL_FILE inode flag whilst they're in use to tell rmdir to reject attempts to remove them. Changes ======= ver #3: - Return an indication as to whether the directory was freshly created. Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819631182.215744.3322471539523262619.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906933130.143852.962088616746509062.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967141952.1823006.7832985646370603833.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021542169.640689.18266858945694357839.stgit@warthog.procyon.org.uk/ # v4
This commit is contained in:
parent
1bd9c4e4f0
commit
32759f7d7a
|
@ -125,6 +125,15 @@ static inline int cachefiles_inject_remove_error(void)
|
|||
return cachefiles_error_injection_state & 2 ? -EIO : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* namei.c
|
||||
*/
|
||||
extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
||||
struct dentry *dir,
|
||||
const char *name,
|
||||
bool *_is_new);
|
||||
extern void cachefiles_put_directory(struct dentry *dir);
|
||||
|
||||
/*
|
||||
* security.c
|
||||
*/
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
|
@ -41,3 +42,143 @@ static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
|
|||
inode->i_flags &= ~S_KERNEL_FILE;
|
||||
trace_cachefiles_mark_inactive(object, inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* get a subdirectory
|
||||
*/
|
||||
struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
||||
struct dentry *dir,
|
||||
const char *dirname,
|
||||
bool *_is_new)
|
||||
{
|
||||
struct dentry *subdir;
|
||||
struct path path;
|
||||
int ret;
|
||||
|
||||
_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_len(dirname, dir, strlen(dirname));
|
||||
else
|
||||
subdir = ERR_PTR(ret);
|
||||
if (IS_ERR(subdir)) {
|
||||
trace_cachefiles_vfs_error(NULL, d_backing_inode(dir),
|
||||
PTR_ERR(subdir),
|
||||
cachefiles_trace_lookup_error);
|
||||
if (PTR_ERR(subdir) == -ENOMEM)
|
||||
goto nomem_d_alloc;
|
||||
goto lookup_error;
|
||||
}
|
||||
|
||||
_debug("subdir -> %pd %s",
|
||||
subdir, d_backing_inode(subdir) ? "positive" : "negative");
|
||||
|
||||
/* we need to create the subdir if it doesn't exist yet */
|
||||
if (d_is_negative(subdir)) {
|
||||
ret = cachefiles_has_space(cache, 1, 0);
|
||||
if (ret < 0)
|
||||
goto mkdir_error;
|
||||
|
||||
_debug("attempt mkdir");
|
||||
|
||||
path.mnt = cache->mnt;
|
||||
path.dentry = dir;
|
||||
ret = security_path_mkdir(&path, subdir, 0700);
|
||||
if (ret < 0)
|
||||
goto mkdir_error;
|
||||
ret = cachefiles_inject_write_error();
|
||||
if (ret == 0)
|
||||
ret = vfs_mkdir(&init_user_ns, d_inode(dir), subdir, 0700);
|
||||
if (ret < 0) {
|
||||
trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
|
||||
cachefiles_trace_mkdir_error);
|
||||
goto mkdir_error;
|
||||
}
|
||||
|
||||
if (unlikely(d_unhashed(subdir))) {
|
||||
cachefiles_put_directory(subdir);
|
||||
goto retry;
|
||||
}
|
||||
ASSERT(d_backing_inode(subdir));
|
||||
|
||||
_debug("mkdir -> %pd{ino=%lu}",
|
||||
subdir, d_backing_inode(subdir)->i_ino);
|
||||
if (_is_new)
|
||||
*_is_new = true;
|
||||
}
|
||||
|
||||
/* Tell rmdir() it's not allowed to delete the subdir */
|
||||
inode_lock(d_inode(subdir));
|
||||
inode_unlock(d_inode(dir));
|
||||
|
||||
if (!__cachefiles_mark_inode_in_use(NULL, subdir))
|
||||
goto mark_error;
|
||||
|
||||
inode_unlock(d_inode(subdir));
|
||||
|
||||
/* we need to make sure the subdir is a directory */
|
||||
ASSERT(d_backing_inode(subdir));
|
||||
|
||||
if (!d_can_lookup(subdir)) {
|
||||
pr_err("%s is not a directory\n", dirname);
|
||||
ret = -EIO;
|
||||
goto check_error;
|
||||
}
|
||||
|
||||
ret = -EPERM;
|
||||
if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) ||
|
||||
!d_backing_inode(subdir)->i_op->lookup ||
|
||||
!d_backing_inode(subdir)->i_op->mkdir ||
|
||||
!d_backing_inode(subdir)->i_op->rename ||
|
||||
!d_backing_inode(subdir)->i_op->rmdir ||
|
||||
!d_backing_inode(subdir)->i_op->unlink)
|
||||
goto check_error;
|
||||
|
||||
_leave(" = [%lu]", d_backing_inode(subdir)->i_ino);
|
||||
return subdir;
|
||||
|
||||
check_error:
|
||||
cachefiles_put_directory(subdir);
|
||||
_leave(" = %d [check]", ret);
|
||||
return ERR_PTR(ret);
|
||||
|
||||
mark_error:
|
||||
inode_unlock(d_inode(subdir));
|
||||
dput(subdir);
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
mkdir_error:
|
||||
inode_unlock(d_inode(dir));
|
||||
dput(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);
|
||||
|
||||
nomem_d_alloc:
|
||||
inode_unlock(d_inode(dir));
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a subdirectory.
|
||||
*/
|
||||
void cachefiles_put_directory(struct dentry *dir)
|
||||
{
|
||||
if (dir) {
|
||||
inode_lock(dir->d_inode);
|
||||
__cachefiles_unmark_inode_in_use(NULL, dir);
|
||||
inode_unlock(dir->d_inode);
|
||||
dput(dir);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue