xref: /linux/fs/cachefiles/namei.c (revision a8b00268)
11bd9c4e4SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
21bd9c4e4SDavid Howells /* CacheFiles path walking and related routines
31bd9c4e4SDavid Howells  *
41bd9c4e4SDavid Howells  * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
51bd9c4e4SDavid Howells  * Written by David Howells (dhowells@redhat.com)
61bd9c4e4SDavid Howells  */
71bd9c4e4SDavid Howells 
81bd9c4e4SDavid Howells #include <linux/fs.h>
932759f7dSDavid Howells #include <linux/namei.h>
101bd9c4e4SDavid Howells #include "internal.h"
111bd9c4e4SDavid Howells 
121bd9c4e4SDavid Howells /*
131bd9c4e4SDavid Howells  * Mark the backing file as being a cache file if it's not already in use.  The
141bd9c4e4SDavid Howells  * mark tells the culling request command that it's not allowed to cull the
151bd9c4e4SDavid Howells  * file or directory.  The caller must hold the inode lock.
161bd9c4e4SDavid Howells  */
__cachefiles_mark_inode_in_use(struct cachefiles_object * object,struct inode * inode)171bd9c4e4SDavid Howells static bool __cachefiles_mark_inode_in_use(struct cachefiles_object *object,
1808d7a6fbSMiklos Szeredi 					   struct inode *inode)
191bd9c4e4SDavid Howells {
201bd9c4e4SDavid Howells 	bool can_use = false;
211bd9c4e4SDavid Howells 
221bd9c4e4SDavid Howells 	if (!(inode->i_flags & S_KERNEL_FILE)) {
231bd9c4e4SDavid Howells 		inode->i_flags |= S_KERNEL_FILE;
241bd9c4e4SDavid Howells 		trace_cachefiles_mark_active(object, inode);
251bd9c4e4SDavid Howells 		can_use = true;
261bd9c4e4SDavid Howells 	} else {
27b64a3314SDavid Howells 		trace_cachefiles_mark_failed(object, inode);
281bd9c4e4SDavid Howells 	}
291bd9c4e4SDavid Howells 
301bd9c4e4SDavid Howells 	return can_use;
311bd9c4e4SDavid Howells }
321bd9c4e4SDavid Howells 
cachefiles_mark_inode_in_use(struct cachefiles_object * object,struct inode * inode)33169379eaSDavid Howells static bool cachefiles_mark_inode_in_use(struct cachefiles_object *object,
3408d7a6fbSMiklos Szeredi 					 struct inode *inode)
35169379eaSDavid Howells {
36169379eaSDavid Howells 	bool can_use;
37169379eaSDavid Howells 
38169379eaSDavid Howells 	inode_lock(inode);
3908d7a6fbSMiklos Szeredi 	can_use = __cachefiles_mark_inode_in_use(object, inode);
40169379eaSDavid Howells 	inode_unlock(inode);
41169379eaSDavid Howells 	return can_use;
42169379eaSDavid Howells }
43169379eaSDavid Howells 
441bd9c4e4SDavid Howells /*
451bd9c4e4SDavid Howells  * Unmark a backing inode.  The caller must hold the inode lock.
461bd9c4e4SDavid Howells  */
__cachefiles_unmark_inode_in_use(struct cachefiles_object * object,struct inode * inode)471bd9c4e4SDavid Howells static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
4808d7a6fbSMiklos Szeredi 					     struct inode *inode)
491bd9c4e4SDavid Howells {
501bd9c4e4SDavid Howells 	inode->i_flags &= ~S_KERNEL_FILE;
511bd9c4e4SDavid Howells 	trace_cachefiles_mark_inactive(object, inode);
521bd9c4e4SDavid Howells }
5332759f7dSDavid Howells 
cachefiles_do_unmark_inode_in_use(struct cachefiles_object * object,struct inode * inode)54ea5dc046SJeffle Xu static void cachefiles_do_unmark_inode_in_use(struct cachefiles_object *object,
5508d7a6fbSMiklos Szeredi 					      struct inode *inode)
56ea5dc046SJeffle Xu {
57ea5dc046SJeffle Xu 	inode_lock(inode);
5808d7a6fbSMiklos Szeredi 	__cachefiles_unmark_inode_in_use(object, inode);
59ea5dc046SJeffle Xu 	inode_unlock(inode);
60ea5dc046SJeffle Xu }
61ea5dc046SJeffle Xu 
6232759f7dSDavid Howells /*
63169379eaSDavid Howells  * Unmark a backing inode and tell cachefilesd that there's something that can
64169379eaSDavid Howells  * be culled.
65169379eaSDavid Howells  */
cachefiles_unmark_inode_in_use(struct cachefiles_object * object,struct file * file)66169379eaSDavid Howells void cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
67169379eaSDavid Howells 				    struct file *file)
68169379eaSDavid Howells {
69169379eaSDavid Howells 	struct cachefiles_cache *cache = object->volume->cache;
70169379eaSDavid Howells 	struct inode *inode = file_inode(file);
71169379eaSDavid Howells 
7208d7a6fbSMiklos Szeredi 	cachefiles_do_unmark_inode_in_use(object, inode);
73169379eaSDavid Howells 
74169379eaSDavid Howells 	if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
75169379eaSDavid Howells 		atomic_long_add(inode->i_blocks, &cache->b_released);
76169379eaSDavid Howells 		if (atomic_inc_return(&cache->f_released))
77169379eaSDavid Howells 			cachefiles_state_changed(cache);
78169379eaSDavid Howells 	}
79169379eaSDavid Howells }
80169379eaSDavid Howells 
81169379eaSDavid Howells /*
8232759f7dSDavid Howells  * get a subdirectory
8332759f7dSDavid Howells  */
cachefiles_get_directory(struct cachefiles_cache * cache,struct dentry * dir,const char * dirname,bool * _is_new)8432759f7dSDavid Howells struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
8532759f7dSDavid Howells 					struct dentry *dir,
8632759f7dSDavid Howells 					const char *dirname,
8732759f7dSDavid Howells 					bool *_is_new)
8832759f7dSDavid Howells {
8932759f7dSDavid Howells 	struct dentry *subdir;
9032759f7dSDavid Howells 	struct path path;
9132759f7dSDavid Howells 	int ret;
9232759f7dSDavid Howells 
9332759f7dSDavid Howells 	_enter(",,%s", dirname);
9432759f7dSDavid Howells 
9532759f7dSDavid Howells 	/* search the current directory for the element name */
9632759f7dSDavid Howells 	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
9732759f7dSDavid Howells 
9832759f7dSDavid Howells retry:
9932759f7dSDavid Howells 	ret = cachefiles_inject_read_error();
10032759f7dSDavid Howells 	if (ret == 0)
10132759f7dSDavid Howells 		subdir = lookup_one_len(dirname, dir, strlen(dirname));
10232759f7dSDavid Howells 	else
10332759f7dSDavid Howells 		subdir = ERR_PTR(ret);
1048c39b8bcSDavid Howells 	trace_cachefiles_lookup(NULL, dir, subdir);
10532759f7dSDavid Howells 	if (IS_ERR(subdir)) {
10632759f7dSDavid Howells 		trace_cachefiles_vfs_error(NULL, d_backing_inode(dir),
10732759f7dSDavid Howells 					   PTR_ERR(subdir),
10832759f7dSDavid Howells 					   cachefiles_trace_lookup_error);
10932759f7dSDavid Howells 		if (PTR_ERR(subdir) == -ENOMEM)
11032759f7dSDavid Howells 			goto nomem_d_alloc;
11132759f7dSDavid Howells 		goto lookup_error;
11232759f7dSDavid Howells 	}
11332759f7dSDavid Howells 
11432759f7dSDavid Howells 	_debug("subdir -> %pd %s",
11532759f7dSDavid Howells 	       subdir, d_backing_inode(subdir) ? "positive" : "negative");
11632759f7dSDavid Howells 
11732759f7dSDavid Howells 	/* we need to create the subdir if it doesn't exist yet */
11832759f7dSDavid Howells 	if (d_is_negative(subdir)) {
1193929eca7SDavid Howells 		ret = cachefiles_has_space(cache, 1, 0,
1203929eca7SDavid Howells 					   cachefiles_has_space_for_create);
12132759f7dSDavid Howells 		if (ret < 0)
12232759f7dSDavid Howells 			goto mkdir_error;
12332759f7dSDavid Howells 
12432759f7dSDavid Howells 		_debug("attempt mkdir");
12532759f7dSDavid Howells 
12632759f7dSDavid Howells 		path.mnt = cache->mnt;
12732759f7dSDavid Howells 		path.dentry = dir;
12832759f7dSDavid Howells 		ret = security_path_mkdir(&path, subdir, 0700);
12932759f7dSDavid Howells 		if (ret < 0)
13032759f7dSDavid Howells 			goto mkdir_error;
13132759f7dSDavid Howells 		ret = cachefiles_inject_write_error();
13232759f7dSDavid Howells 		if (ret == 0)
133abf08576SChristian Brauner 			ret = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
13432759f7dSDavid Howells 		if (ret < 0) {
13532759f7dSDavid Howells 			trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
13632759f7dSDavid Howells 						   cachefiles_trace_mkdir_error);
13732759f7dSDavid Howells 			goto mkdir_error;
13832759f7dSDavid Howells 		}
1398c39b8bcSDavid Howells 		trace_cachefiles_mkdir(dir, subdir);
14032759f7dSDavid Howells 
14132759f7dSDavid Howells 		if (unlikely(d_unhashed(subdir))) {
14232759f7dSDavid Howells 			cachefiles_put_directory(subdir);
14332759f7dSDavid Howells 			goto retry;
14432759f7dSDavid Howells 		}
14532759f7dSDavid Howells 		ASSERT(d_backing_inode(subdir));
14632759f7dSDavid Howells 
14732759f7dSDavid Howells 		_debug("mkdir -> %pd{ino=%lu}",
14832759f7dSDavid Howells 		       subdir, d_backing_inode(subdir)->i_ino);
14932759f7dSDavid Howells 		if (_is_new)
15032759f7dSDavid Howells 			*_is_new = true;
15132759f7dSDavid Howells 	}
15232759f7dSDavid Howells 
15332759f7dSDavid Howells 	/* Tell rmdir() it's not allowed to delete the subdir */
15432759f7dSDavid Howells 	inode_lock(d_inode(subdir));
15532759f7dSDavid Howells 	inode_unlock(d_inode(dir));
15632759f7dSDavid Howells 
15708d7a6fbSMiklos Szeredi 	if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) {
15808d7a6fbSMiklos Szeredi 		pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
15908d7a6fbSMiklos Szeredi 			  subdir, d_inode(subdir)->i_ino);
16032759f7dSDavid Howells 		goto mark_error;
16108d7a6fbSMiklos Szeredi 	}
16232759f7dSDavid Howells 
16332759f7dSDavid Howells 	inode_unlock(d_inode(subdir));
16432759f7dSDavid Howells 
16532759f7dSDavid Howells 	/* we need to make sure the subdir is a directory */
16632759f7dSDavid Howells 	ASSERT(d_backing_inode(subdir));
16732759f7dSDavid Howells 
16832759f7dSDavid Howells 	if (!d_can_lookup(subdir)) {
16932759f7dSDavid Howells 		pr_err("%s is not a directory\n", dirname);
17032759f7dSDavid Howells 		ret = -EIO;
17132759f7dSDavid Howells 		goto check_error;
17232759f7dSDavid Howells 	}
17332759f7dSDavid Howells 
17432759f7dSDavid Howells 	ret = -EPERM;
17532759f7dSDavid Howells 	if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) ||
17632759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->lookup ||
17732759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->mkdir ||
17832759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->rename ||
17932759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->rmdir ||
18032759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->unlink)
18132759f7dSDavid Howells 		goto check_error;
18232759f7dSDavid Howells 
18332759f7dSDavid Howells 	_leave(" = [%lu]", d_backing_inode(subdir)->i_ino);
18432759f7dSDavid Howells 	return subdir;
18532759f7dSDavid Howells 
18632759f7dSDavid Howells check_error:
18732759f7dSDavid Howells 	cachefiles_put_directory(subdir);
18832759f7dSDavid Howells 	_leave(" = %d [check]", ret);
18932759f7dSDavid Howells 	return ERR_PTR(ret);
19032759f7dSDavid Howells 
19132759f7dSDavid Howells mark_error:
19232759f7dSDavid Howells 	inode_unlock(d_inode(subdir));
19332759f7dSDavid Howells 	dput(subdir);
19432759f7dSDavid Howells 	return ERR_PTR(-EBUSY);
19532759f7dSDavid Howells 
19632759f7dSDavid Howells mkdir_error:
19732759f7dSDavid Howells 	inode_unlock(d_inode(dir));
19832759f7dSDavid Howells 	dput(subdir);
19932759f7dSDavid Howells 	pr_err("mkdir %s failed with error %d\n", dirname, ret);
20032759f7dSDavid Howells 	return ERR_PTR(ret);
20132759f7dSDavid Howells 
20232759f7dSDavid Howells lookup_error:
20332759f7dSDavid Howells 	inode_unlock(d_inode(dir));
20432759f7dSDavid Howells 	ret = PTR_ERR(subdir);
20532759f7dSDavid Howells 	pr_err("Lookup %s failed with error %d\n", dirname, ret);
20632759f7dSDavid Howells 	return ERR_PTR(ret);
20732759f7dSDavid Howells 
20832759f7dSDavid Howells nomem_d_alloc:
20932759f7dSDavid Howells 	inode_unlock(d_inode(dir));
21032759f7dSDavid Howells 	_leave(" = -ENOMEM");
21132759f7dSDavid Howells 	return ERR_PTR(-ENOMEM);
21232759f7dSDavid Howells }
21332759f7dSDavid Howells 
21432759f7dSDavid Howells /*
21532759f7dSDavid Howells  * Put a subdirectory.
21632759f7dSDavid Howells  */
cachefiles_put_directory(struct dentry * dir)21732759f7dSDavid Howells void cachefiles_put_directory(struct dentry *dir)
21832759f7dSDavid Howells {
21932759f7dSDavid Howells 	if (dir) {
22008d7a6fbSMiklos Szeredi 		cachefiles_do_unmark_inode_in_use(NULL, d_inode(dir));
22132759f7dSDavid Howells 		dput(dir);
22232759f7dSDavid Howells 	}
22332759f7dSDavid Howells }
22407a90e97SDavid Howells 
22507a90e97SDavid Howells /*
22607a90e97SDavid Howells  * Remove a regular file from the cache.
22707a90e97SDavid Howells  */
cachefiles_unlink(struct cachefiles_cache * cache,struct cachefiles_object * object,struct dentry * dir,struct dentry * dentry,enum fscache_why_object_killed why)22807a90e97SDavid Howells static int cachefiles_unlink(struct cachefiles_cache *cache,
22907a90e97SDavid Howells 			     struct cachefiles_object *object,
23007a90e97SDavid Howells 			     struct dentry *dir, struct dentry *dentry,
23107a90e97SDavid Howells 			     enum fscache_why_object_killed why)
23207a90e97SDavid Howells {
23307a90e97SDavid Howells 	struct path path = {
23407a90e97SDavid Howells 		.mnt	= cache->mnt,
23507a90e97SDavid Howells 		.dentry	= dir,
23607a90e97SDavid Howells 	};
23707a90e97SDavid Howells 	int ret;
23807a90e97SDavid Howells 
2398c39b8bcSDavid Howells 	trace_cachefiles_unlink(object, d_inode(dentry)->i_ino, why);
24007a90e97SDavid Howells 	ret = security_path_unlink(&path, dentry);
24107a90e97SDavid Howells 	if (ret < 0) {
24207a90e97SDavid Howells 		cachefiles_io_error(cache, "Unlink security error");
24307a90e97SDavid Howells 		return ret;
24407a90e97SDavid Howells 	}
24507a90e97SDavid Howells 
24607a90e97SDavid Howells 	ret = cachefiles_inject_remove_error();
24707a90e97SDavid Howells 	if (ret == 0) {
248abf08576SChristian Brauner 		ret = vfs_unlink(&nop_mnt_idmap, d_backing_inode(dir), dentry, NULL);
24907a90e97SDavid Howells 		if (ret == -EIO)
25007a90e97SDavid Howells 			cachefiles_io_error(cache, "Unlink failed");
25107a90e97SDavid Howells 	}
25207a90e97SDavid Howells 	if (ret != 0)
25307a90e97SDavid Howells 		trace_cachefiles_vfs_error(object, d_backing_inode(dir), ret,
25407a90e97SDavid Howells 					   cachefiles_trace_unlink_error);
25507a90e97SDavid Howells 	return ret;
25607a90e97SDavid Howells }
25707a90e97SDavid Howells 
25807a90e97SDavid Howells /*
25907a90e97SDavid Howells  * Delete an object representation from the cache
26007a90e97SDavid Howells  * - File backed objects are unlinked
26107a90e97SDavid Howells  * - Directory backed objects are stuffed into the graveyard for userspace to
26207a90e97SDavid Howells  *   delete
26307a90e97SDavid Howells  */
cachefiles_bury_object(struct cachefiles_cache * cache,struct cachefiles_object * object,struct dentry * dir,struct dentry * rep,enum fscache_why_object_killed why)26407a90e97SDavid Howells int cachefiles_bury_object(struct cachefiles_cache *cache,
26507a90e97SDavid Howells 			   struct cachefiles_object *object,
26607a90e97SDavid Howells 			   struct dentry *dir,
26707a90e97SDavid Howells 			   struct dentry *rep,
26807a90e97SDavid Howells 			   enum fscache_why_object_killed why)
26907a90e97SDavid Howells {
27007a90e97SDavid Howells 	struct dentry *grave, *trap;
27107a90e97SDavid Howells 	struct path path, path_to_graveyard;
27207a90e97SDavid Howells 	char nbuffer[8 + 8 + 1];
27307a90e97SDavid Howells 	int ret;
27407a90e97SDavid Howells 
27507a90e97SDavid Howells 	_enter(",'%pd','%pd'", dir, rep);
27607a90e97SDavid Howells 
27707a90e97SDavid Howells 	if (rep->d_parent != dir) {
27807a90e97SDavid Howells 		inode_unlock(d_inode(dir));
27907a90e97SDavid Howells 		_leave(" = -ESTALE");
28007a90e97SDavid Howells 		return -ESTALE;
28107a90e97SDavid Howells 	}
28207a90e97SDavid Howells 
28307a90e97SDavid Howells 	/* non-directories can just be unlinked */
28407a90e97SDavid Howells 	if (!d_is_dir(rep)) {
28507a90e97SDavid Howells 		dget(rep); /* Stop the dentry being negated if it's only pinned
28607a90e97SDavid Howells 			    * by a file struct.
28707a90e97SDavid Howells 			    */
28807a90e97SDavid Howells 		ret = cachefiles_unlink(cache, object, dir, rep, why);
28907a90e97SDavid Howells 		dput(rep);
29007a90e97SDavid Howells 
29107a90e97SDavid Howells 		inode_unlock(d_inode(dir));
29207a90e97SDavid Howells 		_leave(" = %d", ret);
29307a90e97SDavid Howells 		return ret;
29407a90e97SDavid Howells 	}
29507a90e97SDavid Howells 
29607a90e97SDavid Howells 	/* directories have to be moved to the graveyard */
29707a90e97SDavid Howells 	_debug("move stale object to graveyard");
29807a90e97SDavid Howells 	inode_unlock(d_inode(dir));
29907a90e97SDavid Howells 
30007a90e97SDavid Howells try_again:
30107a90e97SDavid Howells 	/* first step is to make up a grave dentry in the graveyard */
30207a90e97SDavid Howells 	sprintf(nbuffer, "%08x%08x",
30307a90e97SDavid Howells 		(uint32_t) ktime_get_real_seconds(),
30407a90e97SDavid Howells 		(uint32_t) atomic_inc_return(&cache->gravecounter));
30507a90e97SDavid Howells 
30607a90e97SDavid Howells 	/* do the multiway lock magic */
30707a90e97SDavid Howells 	trap = lock_rename(cache->graveyard, dir);
308*a8b00268SAl Viro 	if (IS_ERR(trap))
309*a8b00268SAl Viro 		return PTR_ERR(trap);
31007a90e97SDavid Howells 
31107a90e97SDavid Howells 	/* do some checks before getting the grave dentry */
31207a90e97SDavid Howells 	if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) {
31307a90e97SDavid Howells 		/* the entry was probably culled when we dropped the parent dir
31407a90e97SDavid Howells 		 * lock */
31507a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
31607a90e97SDavid Howells 		_leave(" = 0 [culled?]");
31707a90e97SDavid Howells 		return 0;
31807a90e97SDavid Howells 	}
31907a90e97SDavid Howells 
32007a90e97SDavid Howells 	if (!d_can_lookup(cache->graveyard)) {
32107a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
32207a90e97SDavid Howells 		cachefiles_io_error(cache, "Graveyard no longer a directory");
32307a90e97SDavid Howells 		return -EIO;
32407a90e97SDavid Howells 	}
32507a90e97SDavid Howells 
32607a90e97SDavid Howells 	if (trap == rep) {
32707a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
32807a90e97SDavid Howells 		cachefiles_io_error(cache, "May not make directory loop");
32907a90e97SDavid Howells 		return -EIO;
33007a90e97SDavid Howells 	}
33107a90e97SDavid Howells 
33207a90e97SDavid Howells 	if (d_mountpoint(rep)) {
33307a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
33407a90e97SDavid Howells 		cachefiles_io_error(cache, "Mountpoint in cache");
33507a90e97SDavid Howells 		return -EIO;
33607a90e97SDavid Howells 	}
33707a90e97SDavid Howells 
33807a90e97SDavid Howells 	grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer));
33907a90e97SDavid Howells 	if (IS_ERR(grave)) {
34007a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
34107a90e97SDavid Howells 		trace_cachefiles_vfs_error(object, d_inode(cache->graveyard),
34207a90e97SDavid Howells 					   PTR_ERR(grave),
34307a90e97SDavid Howells 					   cachefiles_trace_lookup_error);
34407a90e97SDavid Howells 
34507a90e97SDavid Howells 		if (PTR_ERR(grave) == -ENOMEM) {
34607a90e97SDavid Howells 			_leave(" = -ENOMEM");
34707a90e97SDavid Howells 			return -ENOMEM;
34807a90e97SDavid Howells 		}
34907a90e97SDavid Howells 
35007a90e97SDavid Howells 		cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave));
35107a90e97SDavid Howells 		return -EIO;
35207a90e97SDavid Howells 	}
35307a90e97SDavid Howells 
35407a90e97SDavid Howells 	if (d_is_positive(grave)) {
35507a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
35607a90e97SDavid Howells 		dput(grave);
35707a90e97SDavid Howells 		grave = NULL;
35807a90e97SDavid Howells 		cond_resched();
35907a90e97SDavid Howells 		goto try_again;
36007a90e97SDavid Howells 	}
36107a90e97SDavid Howells 
36207a90e97SDavid Howells 	if (d_mountpoint(grave)) {
36307a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
36407a90e97SDavid Howells 		dput(grave);
36507a90e97SDavid Howells 		cachefiles_io_error(cache, "Mountpoint in graveyard");
36607a90e97SDavid Howells 		return -EIO;
36707a90e97SDavid Howells 	}
36807a90e97SDavid Howells 
36907a90e97SDavid Howells 	/* target should not be an ancestor of source */
37007a90e97SDavid Howells 	if (trap == grave) {
37107a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
37207a90e97SDavid Howells 		dput(grave);
37307a90e97SDavid Howells 		cachefiles_io_error(cache, "May not make directory loop");
37407a90e97SDavid Howells 		return -EIO;
37507a90e97SDavid Howells 	}
37607a90e97SDavid Howells 
37707a90e97SDavid Howells 	/* attempt the rename */
37807a90e97SDavid Howells 	path.mnt = cache->mnt;
37907a90e97SDavid Howells 	path.dentry = dir;
38007a90e97SDavid Howells 	path_to_graveyard.mnt = cache->mnt;
38107a90e97SDavid Howells 	path_to_graveyard.dentry = cache->graveyard;
38207a90e97SDavid Howells 	ret = security_path_rename(&path, rep, &path_to_graveyard, grave, 0);
38307a90e97SDavid Howells 	if (ret < 0) {
38407a90e97SDavid Howells 		cachefiles_io_error(cache, "Rename security error %d", ret);
38507a90e97SDavid Howells 	} else {
38607a90e97SDavid Howells 		struct renamedata rd = {
387abf08576SChristian Brauner 			.old_mnt_idmap	= &nop_mnt_idmap,
38807a90e97SDavid Howells 			.old_dir	= d_inode(dir),
38907a90e97SDavid Howells 			.old_dentry	= rep,
390abf08576SChristian Brauner 			.new_mnt_idmap	= &nop_mnt_idmap,
39107a90e97SDavid Howells 			.new_dir	= d_inode(cache->graveyard),
39207a90e97SDavid Howells 			.new_dentry	= grave,
39307a90e97SDavid Howells 		};
3948c39b8bcSDavid Howells 		trace_cachefiles_rename(object, d_inode(rep)->i_ino, why);
39507a90e97SDavid Howells 		ret = cachefiles_inject_read_error();
39607a90e97SDavid Howells 		if (ret == 0)
39707a90e97SDavid Howells 			ret = vfs_rename(&rd);
39807a90e97SDavid Howells 		if (ret != 0)
39907a90e97SDavid Howells 			trace_cachefiles_vfs_error(object, d_inode(dir), ret,
40007a90e97SDavid Howells 						   cachefiles_trace_rename_error);
40107a90e97SDavid Howells 		if (ret != 0 && ret != -ENOMEM)
40207a90e97SDavid Howells 			cachefiles_io_error(cache,
40307a90e97SDavid Howells 					    "Rename failed with error %d", ret);
40407a90e97SDavid Howells 	}
40507a90e97SDavid Howells 
40608d7a6fbSMiklos Szeredi 	__cachefiles_unmark_inode_in_use(object, d_inode(rep));
40707a90e97SDavid Howells 	unlock_rename(cache->graveyard, dir);
40807a90e97SDavid Howells 	dput(grave);
40907a90e97SDavid Howells 	_leave(" = 0");
41007a90e97SDavid Howells 	return 0;
41107a90e97SDavid Howells }
41207a90e97SDavid Howells 
41307a90e97SDavid Howells /*
4141f08c925SDavid Howells  * Delete a cache file.
4151f08c925SDavid Howells  */
cachefiles_delete_object(struct cachefiles_object * object,enum fscache_why_object_killed why)4161f08c925SDavid Howells int cachefiles_delete_object(struct cachefiles_object *object,
4171f08c925SDavid Howells 			     enum fscache_why_object_killed why)
4181f08c925SDavid Howells {
4191f08c925SDavid Howells 	struct cachefiles_volume *volume = object->volume;
4201f08c925SDavid Howells 	struct dentry *dentry = object->file->f_path.dentry;
4211f08c925SDavid Howells 	struct dentry *fan = volume->fanout[(u8)object->cookie->key_hash];
4221f08c925SDavid Howells 	int ret;
4231f08c925SDavid Howells 
4241f08c925SDavid Howells 	_enter(",OBJ%x{%pD}", object->debug_id, object->file);
4251f08c925SDavid Howells 
4261f08c925SDavid Howells 	/* Stop the dentry being negated if it's only pinned by a file struct. */
4271f08c925SDavid Howells 	dget(dentry);
4281f08c925SDavid Howells 
4291f08c925SDavid Howells 	inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT);
4301f08c925SDavid Howells 	ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
4311f08c925SDavid Howells 	inode_unlock(d_backing_inode(fan));
4321f08c925SDavid Howells 	dput(dentry);
4331f08c925SDavid Howells 	return ret;
4341f08c925SDavid Howells }
4351f08c925SDavid Howells 
4361f08c925SDavid Howells /*
4371f08c925SDavid Howells  * Create a temporary file and leave it unattached and un-xattr'd until the
4381f08c925SDavid Howells  * time comes to discard the object from memory.
4391f08c925SDavid Howells  */
cachefiles_create_tmpfile(struct cachefiles_object * object)4401f08c925SDavid Howells struct file *cachefiles_create_tmpfile(struct cachefiles_object *object)
4411f08c925SDavid Howells {
4421f08c925SDavid Howells 	struct cachefiles_volume *volume = object->volume;
4431f08c925SDavid Howells 	struct cachefiles_cache *cache = volume->cache;
4441f08c925SDavid Howells 	const struct cred *saved_cred;
4451f08c925SDavid Howells 	struct dentry *fan = volume->fanout[(u8)object->cookie->key_hash];
4461f08c925SDavid Howells 	struct file *file;
44724a81759SMiklos Szeredi 	const struct path parentpath = { .mnt = cache->mnt, .dentry = fan };
448c8383054SJeffle Xu 	uint64_t ni_size;
4491f08c925SDavid Howells 	long ret;
4501f08c925SDavid Howells 
4511f08c925SDavid Howells 
4521f08c925SDavid Howells 	cachefiles_begin_secure(cache, &saved_cred);
4531f08c925SDavid Howells 
4541f08c925SDavid Howells 	ret = cachefiles_inject_write_error();
45538017d44SMiklos Szeredi 	if (ret == 0) {
4561f2300a7SLinus Torvalds 		file = kernel_tmpfile_open(&nop_mnt_idmap, &parentpath,
45779aa2849SDavid Howells 					   S_IFREG | 0600,
45824a81759SMiklos Szeredi 					   O_RDWR | O_LARGEFILE | O_DIRECT,
45924a81759SMiklos Szeredi 					   cache->cache_cred);
46024a81759SMiklos Szeredi 		ret = PTR_ERR_OR_ZERO(file);
46138017d44SMiklos Szeredi 	}
46238017d44SMiklos Szeredi 	if (ret) {
46338017d44SMiklos Szeredi 		trace_cachefiles_vfs_error(object, d_inode(fan), ret,
4641f08c925SDavid Howells 					   cachefiles_trace_tmpfile_error);
46538017d44SMiklos Szeredi 		if (ret == -EIO)
4661f08c925SDavid Howells 			cachefiles_io_error_obj(object, "Failed to create tmpfile");
46738017d44SMiklos Szeredi 		goto err;
4681f08c925SDavid Howells 	}
4691f08c925SDavid Howells 
47024a81759SMiklos Szeredi 	trace_cachefiles_tmpfile(object, file_inode(file));
4711f08c925SDavid Howells 
47208d7a6fbSMiklos Szeredi 	/* This is a newly created file with no other possible user */
47324a81759SMiklos Szeredi 	if (!cachefiles_mark_inode_in_use(object, file_inode(file)))
47408d7a6fbSMiklos Szeredi 		WARN_ON(1);
4751f08c925SDavid Howells 
476c8383054SJeffle Xu 	ret = cachefiles_ondemand_init_object(object);
47738017d44SMiklos Szeredi 	if (ret < 0)
47838017d44SMiklos Szeredi 		goto err_unuse;
479c8383054SJeffle Xu 
480c8383054SJeffle Xu 	ni_size = object->cookie->object_size;
481c8383054SJeffle Xu 	ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE);
482c8383054SJeffle Xu 
4831f08c925SDavid Howells 	if (ni_size > 0) {
48424a81759SMiklos Szeredi 		trace_cachefiles_trunc(object, file_inode(file), 0, ni_size,
4851f08c925SDavid Howells 				       cachefiles_trunc_expand_tmpfile);
4861f08c925SDavid Howells 		ret = cachefiles_inject_write_error();
4871f08c925SDavid Howells 		if (ret == 0)
48824a81759SMiklos Szeredi 			ret = vfs_truncate(&file->f_path, ni_size);
4891f08c925SDavid Howells 		if (ret < 0) {
4901f08c925SDavid Howells 			trace_cachefiles_vfs_error(
49124a81759SMiklos Szeredi 				object, file_inode(file), ret,
4921f08c925SDavid Howells 				cachefiles_trace_trunc_error);
49338017d44SMiklos Szeredi 			goto err_unuse;
4941f08c925SDavid Howells 		}
4951f08c925SDavid Howells 	}
4961f08c925SDavid Howells 
49738017d44SMiklos Szeredi 	ret = -EINVAL;
4981f08c925SDavid Howells 	if (unlikely(!file->f_op->read_iter) ||
4991f08c925SDavid Howells 	    unlikely(!file->f_op->write_iter)) {
5001f08c925SDavid Howells 		fput(file);
5011f08c925SDavid Howells 		pr_notice("Cache does not support read_iter and write_iter\n");
50238017d44SMiklos Szeredi 		goto err_unuse;
5031f08c925SDavid Howells 	}
5041f08c925SDavid Howells out:
5051f08c925SDavid Howells 	cachefiles_end_secure(cache, saved_cred);
5061f08c925SDavid Howells 	return file;
50738017d44SMiklos Szeredi 
50838017d44SMiklos Szeredi err_unuse:
50924a81759SMiklos Szeredi 	cachefiles_do_unmark_inode_in_use(object, file_inode(file));
51024a81759SMiklos Szeredi 	fput(file);
51138017d44SMiklos Szeredi err:
51238017d44SMiklos Szeredi 	file = ERR_PTR(ret);
51338017d44SMiklos Szeredi 	goto out;
5141f08c925SDavid Howells }
5151f08c925SDavid Howells 
5161f08c925SDavid Howells /*
5171f08c925SDavid Howells  * Create a new file.
5181f08c925SDavid Howells  */
cachefiles_create_file(struct cachefiles_object * object)5191f08c925SDavid Howells static bool cachefiles_create_file(struct cachefiles_object *object)
5201f08c925SDavid Howells {
5211f08c925SDavid Howells 	struct file *file;
5221f08c925SDavid Howells 	int ret;
5231f08c925SDavid Howells 
5243929eca7SDavid Howells 	ret = cachefiles_has_space(object->volume->cache, 1, 0,
5253929eca7SDavid Howells 				   cachefiles_has_space_for_create);
5261f08c925SDavid Howells 	if (ret < 0)
5271f08c925SDavid Howells 		return false;
5281f08c925SDavid Howells 
5291f08c925SDavid Howells 	file = cachefiles_create_tmpfile(object);
5301f08c925SDavid Howells 	if (IS_ERR(file))
5311f08c925SDavid Howells 		return false;
5321f08c925SDavid Howells 
5331f08c925SDavid Howells 	set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags);
5341f08c925SDavid Howells 	set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags);
5351f08c925SDavid Howells 	_debug("create -> %pD{ino=%lu}", file, file_inode(file)->i_ino);
5361f08c925SDavid Howells 	object->file = file;
5371f08c925SDavid Howells 	return true;
5381f08c925SDavid Howells }
5391f08c925SDavid Howells 
5401f08c925SDavid Howells /*
5411f08c925SDavid Howells  * Open an existing file, checking its attributes and replacing it if it is
5421f08c925SDavid Howells  * stale.
5431f08c925SDavid Howells  */
cachefiles_open_file(struct cachefiles_object * object,struct dentry * dentry)5441f08c925SDavid Howells static bool cachefiles_open_file(struct cachefiles_object *object,
5451f08c925SDavid Howells 				 struct dentry *dentry)
5461f08c925SDavid Howells {
5471f08c925SDavid Howells 	struct cachefiles_cache *cache = object->volume->cache;
5481f08c925SDavid Howells 	struct file *file;
5491f08c925SDavid Howells 	struct path path;
5501f08c925SDavid Howells 	int ret;
5511f08c925SDavid Howells 
5521f08c925SDavid Howells 	_enter("%pd", dentry);
5531f08c925SDavid Howells 
55408d7a6fbSMiklos Szeredi 	if (!cachefiles_mark_inode_in_use(object, d_inode(dentry))) {
55508d7a6fbSMiklos Szeredi 		pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
55608d7a6fbSMiklos Szeredi 			  dentry, d_inode(dentry)->i_ino);
5571f08c925SDavid Howells 		return false;
55808d7a6fbSMiklos Szeredi 	}
5591f08c925SDavid Howells 
5601f08c925SDavid Howells 	/* We need to open a file interface onto a data file now as we can't do
5611f08c925SDavid Howells 	 * it on demand because writeback called from do_exit() sees
5621f08c925SDavid Howells 	 * current->fs == NULL - which breaks d_path() called from ext4 open.
5631f08c925SDavid Howells 	 */
5641f08c925SDavid Howells 	path.mnt = cache->mnt;
5651f08c925SDavid Howells 	path.dentry = dentry;
566cbb0b9d4SAmir Goldstein 	file = kernel_file_open(&path, O_RDWR | O_LARGEFILE | O_DIRECT,
5671f08c925SDavid Howells 				d_backing_inode(dentry), cache->cache_cred);
5681f08c925SDavid Howells 	if (IS_ERR(file)) {
5691f08c925SDavid Howells 		trace_cachefiles_vfs_error(object, d_backing_inode(dentry),
5701f08c925SDavid Howells 					   PTR_ERR(file),
5711f08c925SDavid Howells 					   cachefiles_trace_open_error);
5721f08c925SDavid Howells 		goto error;
5731f08c925SDavid Howells 	}
5741f08c925SDavid Howells 
5751f08c925SDavid Howells 	if (unlikely(!file->f_op->read_iter) ||
5761f08c925SDavid Howells 	    unlikely(!file->f_op->write_iter)) {
5771f08c925SDavid Howells 		pr_notice("Cache does not support read_iter and write_iter\n");
5781f08c925SDavid Howells 		goto error_fput;
5791f08c925SDavid Howells 	}
5801f08c925SDavid Howells 	_debug("file -> %pd positive", dentry);
5811f08c925SDavid Howells 
582c8383054SJeffle Xu 	ret = cachefiles_ondemand_init_object(object);
583c8383054SJeffle Xu 	if (ret < 0)
584c8383054SJeffle Xu 		goto error_fput;
585c8383054SJeffle Xu 
5861f08c925SDavid Howells 	ret = cachefiles_check_auxdata(object, file);
5871f08c925SDavid Howells 	if (ret < 0)
5881f08c925SDavid Howells 		goto check_failed;
5891f08c925SDavid Howells 
590b4fa966fSDavid Howells 	clear_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &object->cookie->flags);
591b4fa966fSDavid Howells 
5921f08c925SDavid Howells 	object->file = file;
5931f08c925SDavid Howells 
5941f08c925SDavid Howells 	/* Always update the atime on an object we've just looked up (this is
5951f08c925SDavid Howells 	 * used to keep track of culling, and atimes are only updated by read,
5961f08c925SDavid Howells 	 * write and readdir but not lookup or open).
5971f08c925SDavid Howells 	 */
5981f08c925SDavid Howells 	touch_atime(&file->f_path);
5991f08c925SDavid Howells 	dput(dentry);
6001f08c925SDavid Howells 	return true;
6011f08c925SDavid Howells 
6021f08c925SDavid Howells check_failed:
6031f08c925SDavid Howells 	fscache_cookie_lookup_negative(object->cookie);
6041f08c925SDavid Howells 	cachefiles_unmark_inode_in_use(object, file);
6051f08c925SDavid Howells 	fput(file);
6061f08c925SDavid Howells 	dput(dentry);
607ea5dc046SJeffle Xu 	if (ret == -ESTALE)
6081f08c925SDavid Howells 		return cachefiles_create_file(object);
609ea5dc046SJeffle Xu 	return false;
610ea5dc046SJeffle Xu 
6111f08c925SDavid Howells error_fput:
6121f08c925SDavid Howells 	fput(file);
6131f08c925SDavid Howells error:
61408d7a6fbSMiklos Szeredi 	cachefiles_do_unmark_inode_in_use(object, d_inode(dentry));
6151f08c925SDavid Howells 	dput(dentry);
6161f08c925SDavid Howells 	return false;
6171f08c925SDavid Howells }
6181f08c925SDavid Howells 
6191f08c925SDavid Howells /*
6201f08c925SDavid Howells  * walk from the parent object to the child object through the backing
6211f08c925SDavid Howells  * filesystem, creating directories as we go
6221f08c925SDavid Howells  */
cachefiles_look_up_object(struct cachefiles_object * object)6231f08c925SDavid Howells bool cachefiles_look_up_object(struct cachefiles_object *object)
6241f08c925SDavid Howells {
6251f08c925SDavid Howells 	struct cachefiles_volume *volume = object->volume;
6261f08c925SDavid Howells 	struct dentry *dentry, *fan = volume->fanout[(u8)object->cookie->key_hash];
6271f08c925SDavid Howells 	int ret;
6281f08c925SDavid Howells 
6291f08c925SDavid Howells 	_enter("OBJ%x,%s,", object->debug_id, object->d_name);
6301f08c925SDavid Howells 
6311f08c925SDavid Howells 	/* Look up path "cache/vol/fanout/file". */
6321f08c925SDavid Howells 	ret = cachefiles_inject_read_error();
6331f08c925SDavid Howells 	if (ret == 0)
6341f08c925SDavid Howells 		dentry = lookup_positive_unlocked(object->d_name, fan,
6351f08c925SDavid Howells 						  object->d_name_len);
6361f08c925SDavid Howells 	else
6371f08c925SDavid Howells 		dentry = ERR_PTR(ret);
6388c39b8bcSDavid Howells 	trace_cachefiles_lookup(object, fan, dentry);
6391f08c925SDavid Howells 	if (IS_ERR(dentry)) {
6401f08c925SDavid Howells 		if (dentry == ERR_PTR(-ENOENT))
6411f08c925SDavid Howells 			goto new_file;
6421f08c925SDavid Howells 		if (dentry == ERR_PTR(-EIO))
6431f08c925SDavid Howells 			cachefiles_io_error_obj(object, "Lookup failed");
6441f08c925SDavid Howells 		return false;
6451f08c925SDavid Howells 	}
6461f08c925SDavid Howells 
6471f08c925SDavid Howells 	if (!d_is_reg(dentry)) {
6481f08c925SDavid Howells 		pr_err("%pd is not a file\n", dentry);
6491f08c925SDavid Howells 		inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
6501f08c925SDavid Howells 		ret = cachefiles_bury_object(volume->cache, object, fan, dentry,
6511f08c925SDavid Howells 					     FSCACHE_OBJECT_IS_WEIRD);
6521f08c925SDavid Howells 		dput(dentry);
6531f08c925SDavid Howells 		if (ret < 0)
6541f08c925SDavid Howells 			return false;
6551f08c925SDavid Howells 		goto new_file;
6561f08c925SDavid Howells 	}
6571f08c925SDavid Howells 
6581f08c925SDavid Howells 	if (!cachefiles_open_file(object, dentry))
6591f08c925SDavid Howells 		return false;
6601f08c925SDavid Howells 
6611f08c925SDavid Howells 	_leave(" = t [%lu]", file_inode(object->file)->i_ino);
6621f08c925SDavid Howells 	return true;
6631f08c925SDavid Howells 
6641f08c925SDavid Howells new_file:
6651f08c925SDavid Howells 	fscache_cookie_lookup_negative(object->cookie);
6661f08c925SDavid Howells 	return cachefiles_create_file(object);
6671f08c925SDavid Howells }
6681f08c925SDavid Howells 
6691f08c925SDavid Howells /*
6701f08c925SDavid Howells  * Attempt to link a temporary file into its rightful place in the cache.
6711f08c925SDavid Howells  */
cachefiles_commit_tmpfile(struct cachefiles_cache * cache,struct cachefiles_object * object)6721f08c925SDavid Howells bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
6731f08c925SDavid Howells 			       struct cachefiles_object *object)
6741f08c925SDavid Howells {
6751f08c925SDavid Howells 	struct cachefiles_volume *volume = object->volume;
6761f08c925SDavid Howells 	struct dentry *dentry, *fan = volume->fanout[(u8)object->cookie->key_hash];
6771f08c925SDavid Howells 	bool success = false;
6781f08c925SDavid Howells 	int ret;
6791f08c925SDavid Howells 
6801f08c925SDavid Howells 	_enter(",%pD", object->file);
6811f08c925SDavid Howells 
6821f08c925SDavid Howells 	inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
6831f08c925SDavid Howells 	ret = cachefiles_inject_read_error();
6841f08c925SDavid Howells 	if (ret == 0)
6851f08c925SDavid Howells 		dentry = lookup_one_len(object->d_name, fan, object->d_name_len);
6861f08c925SDavid Howells 	else
6871f08c925SDavid Howells 		dentry = ERR_PTR(ret);
6881f08c925SDavid Howells 	if (IS_ERR(dentry)) {
6891f08c925SDavid Howells 		trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
6901f08c925SDavid Howells 					   cachefiles_trace_lookup_error);
6911f08c925SDavid Howells 		_debug("lookup fail %ld", PTR_ERR(dentry));
6921f08c925SDavid Howells 		goto out_unlock;
6931f08c925SDavid Howells 	}
6941f08c925SDavid Howells 
6951f08c925SDavid Howells 	if (!d_is_negative(dentry)) {
6961f08c925SDavid Howells 		if (d_backing_inode(dentry) == file_inode(object->file)) {
6971f08c925SDavid Howells 			success = true;
6981f08c925SDavid Howells 			goto out_dput;
6991f08c925SDavid Howells 		}
7001f08c925SDavid Howells 
7011f08c925SDavid Howells 		ret = cachefiles_unlink(volume->cache, object, fan, dentry,
7021f08c925SDavid Howells 					FSCACHE_OBJECT_IS_STALE);
7031f08c925SDavid Howells 		if (ret < 0)
7041f08c925SDavid Howells 			goto out_dput;
7051f08c925SDavid Howells 
7061f08c925SDavid Howells 		dput(dentry);
7071f08c925SDavid Howells 		ret = cachefiles_inject_read_error();
7081f08c925SDavid Howells 		if (ret == 0)
7091f08c925SDavid Howells 			dentry = lookup_one_len(object->d_name, fan, object->d_name_len);
7101f08c925SDavid Howells 		else
7111f08c925SDavid Howells 			dentry = ERR_PTR(ret);
7121f08c925SDavid Howells 		if (IS_ERR(dentry)) {
7131f08c925SDavid Howells 			trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
7141f08c925SDavid Howells 						   cachefiles_trace_lookup_error);
7151f08c925SDavid Howells 			_debug("lookup fail %ld", PTR_ERR(dentry));
7161f08c925SDavid Howells 			goto out_unlock;
7171f08c925SDavid Howells 		}
7181f08c925SDavid Howells 	}
7191f08c925SDavid Howells 
7201f08c925SDavid Howells 	ret = cachefiles_inject_read_error();
7211f08c925SDavid Howells 	if (ret == 0)
722abf08576SChristian Brauner 		ret = vfs_link(object->file->f_path.dentry, &nop_mnt_idmap,
7231f08c925SDavid Howells 			       d_inode(fan), dentry, NULL);
7241f08c925SDavid Howells 	if (ret < 0) {
7251f08c925SDavid Howells 		trace_cachefiles_vfs_error(object, d_inode(fan), ret,
7261f08c925SDavid Howells 					   cachefiles_trace_link_error);
7271f08c925SDavid Howells 		_debug("link fail %d", ret);
7281f08c925SDavid Howells 	} else {
7291f08c925SDavid Howells 		trace_cachefiles_link(object, file_inode(object->file));
7301f08c925SDavid Howells 		spin_lock(&object->lock);
7311f08c925SDavid Howells 		/* TODO: Do we want to switch the file pointer to the new dentry? */
7321f08c925SDavid Howells 		clear_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags);
7331f08c925SDavid Howells 		spin_unlock(&object->lock);
7341f08c925SDavid Howells 		success = true;
7351f08c925SDavid Howells 	}
7361f08c925SDavid Howells 
7371f08c925SDavid Howells out_dput:
7381f08c925SDavid Howells 	dput(dentry);
7391f08c925SDavid Howells out_unlock:
7401f08c925SDavid Howells 	inode_unlock(d_inode(fan));
7411f08c925SDavid Howells 	_leave(" = %u", success);
7421f08c925SDavid Howells 	return success;
7431f08c925SDavid Howells }
7441f08c925SDavid Howells 
7451f08c925SDavid Howells /*
74607a90e97SDavid Howells  * Look up an inode to be checked or culled.  Return -EBUSY if the inode is
74707a90e97SDavid Howells  * marked in use.
74807a90e97SDavid Howells  */
cachefiles_lookup_for_cull(struct cachefiles_cache * cache,struct dentry * dir,char * filename)74907a90e97SDavid Howells static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache,
75007a90e97SDavid Howells 						 struct dentry *dir,
75107a90e97SDavid Howells 						 char *filename)
75207a90e97SDavid Howells {
75307a90e97SDavid Howells 	struct dentry *victim;
75407a90e97SDavid Howells 	int ret = -ENOENT;
75507a90e97SDavid Howells 
75607a90e97SDavid Howells 	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
75707a90e97SDavid Howells 
75807a90e97SDavid Howells 	victim = lookup_one_len(filename, dir, strlen(filename));
75907a90e97SDavid Howells 	if (IS_ERR(victim))
76007a90e97SDavid Howells 		goto lookup_error;
76107a90e97SDavid Howells 	if (d_is_negative(victim))
76207a90e97SDavid Howells 		goto lookup_put;
76307a90e97SDavid Howells 	if (d_inode(victim)->i_flags & S_KERNEL_FILE)
76407a90e97SDavid Howells 		goto lookup_busy;
76507a90e97SDavid Howells 	return victim;
76607a90e97SDavid Howells 
76707a90e97SDavid Howells lookup_busy:
76807a90e97SDavid Howells 	ret = -EBUSY;
76907a90e97SDavid Howells lookup_put:
77007a90e97SDavid Howells 	inode_unlock(d_inode(dir));
77107a90e97SDavid Howells 	dput(victim);
77207a90e97SDavid Howells 	return ERR_PTR(ret);
77307a90e97SDavid Howells 
77407a90e97SDavid Howells lookup_error:
77507a90e97SDavid Howells 	inode_unlock(d_inode(dir));
77607a90e97SDavid Howells 	ret = PTR_ERR(victim);
77707a90e97SDavid Howells 	if (ret == -ENOENT)
77807a90e97SDavid Howells 		return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */
77907a90e97SDavid Howells 
78007a90e97SDavid Howells 	if (ret == -EIO) {
78107a90e97SDavid Howells 		cachefiles_io_error(cache, "Lookup failed");
78207a90e97SDavid Howells 	} else if (ret != -ENOMEM) {
78307a90e97SDavid Howells 		pr_err("Internal error: %d\n", ret);
78407a90e97SDavid Howells 		ret = -EIO;
78507a90e97SDavid Howells 	}
78607a90e97SDavid Howells 
78707a90e97SDavid Howells 	return ERR_PTR(ret);
78807a90e97SDavid Howells }
78907a90e97SDavid Howells 
79007a90e97SDavid Howells /*
79107a90e97SDavid Howells  * Cull an object if it's not in use
79207a90e97SDavid Howells  * - called only by cache manager daemon
79307a90e97SDavid Howells  */
cachefiles_cull(struct cachefiles_cache * cache,struct dentry * dir,char * filename)79407a90e97SDavid Howells int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
79507a90e97SDavid Howells 		    char *filename)
79607a90e97SDavid Howells {
79707a90e97SDavid Howells 	struct dentry *victim;
79807a90e97SDavid Howells 	struct inode *inode;
79907a90e97SDavid Howells 	int ret;
80007a90e97SDavid Howells 
80107a90e97SDavid Howells 	_enter(",%pd/,%s", dir, filename);
80207a90e97SDavid Howells 
80307a90e97SDavid Howells 	victim = cachefiles_lookup_for_cull(cache, dir, filename);
80407a90e97SDavid Howells 	if (IS_ERR(victim))
80507a90e97SDavid Howells 		return PTR_ERR(victim);
80607a90e97SDavid Howells 
80707a90e97SDavid Howells 	/* check to see if someone is using this object */
80807a90e97SDavid Howells 	inode = d_inode(victim);
80907a90e97SDavid Howells 	inode_lock(inode);
81007a90e97SDavid Howells 	if (inode->i_flags & S_KERNEL_FILE) {
81107a90e97SDavid Howells 		ret = -EBUSY;
81207a90e97SDavid Howells 	} else {
81307a90e97SDavid Howells 		/* Stop the cache from picking it back up */
81407a90e97SDavid Howells 		inode->i_flags |= S_KERNEL_FILE;
81507a90e97SDavid Howells 		ret = 0;
81607a90e97SDavid Howells 	}
81707a90e97SDavid Howells 	inode_unlock(inode);
81807a90e97SDavid Howells 	if (ret < 0)
81907a90e97SDavid Howells 		goto error_unlock;
82007a90e97SDavid Howells 
82107a90e97SDavid Howells 	ret = cachefiles_bury_object(cache, NULL, dir, victim,
82207a90e97SDavid Howells 				     FSCACHE_OBJECT_WAS_CULLED);
82307a90e97SDavid Howells 	if (ret < 0)
82407a90e97SDavid Howells 		goto error;
82507a90e97SDavid Howells 
8269f08ebc3SDavid Howells 	fscache_count_culled();
82707a90e97SDavid Howells 	dput(victim);
82807a90e97SDavid Howells 	_leave(" = 0");
82907a90e97SDavid Howells 	return 0;
83007a90e97SDavid Howells 
83107a90e97SDavid Howells error_unlock:
83207a90e97SDavid Howells 	inode_unlock(d_inode(dir));
83307a90e97SDavid Howells error:
83407a90e97SDavid Howells 	dput(victim);
83507a90e97SDavid Howells 	if (ret == -ENOENT)
83607a90e97SDavid Howells 		return -ESTALE; /* Probably got retired by the netfs */
83707a90e97SDavid Howells 
83807a90e97SDavid Howells 	if (ret != -ENOMEM) {
83907a90e97SDavid Howells 		pr_err("Internal error: %d\n", ret);
84007a90e97SDavid Howells 		ret = -EIO;
84107a90e97SDavid Howells 	}
84207a90e97SDavid Howells 
84307a90e97SDavid Howells 	_leave(" = %d", ret);
84407a90e97SDavid Howells 	return ret;
84507a90e97SDavid Howells }
84607a90e97SDavid Howells 
84707a90e97SDavid Howells /*
84807a90e97SDavid Howells  * Find out if an object is in use or not
84907a90e97SDavid Howells  * - called only by cache manager daemon
85007a90e97SDavid Howells  * - returns -EBUSY or 0 to indicate whether an object is in use or not
85107a90e97SDavid Howells  */
cachefiles_check_in_use(struct cachefiles_cache * cache,struct dentry * dir,char * filename)85207a90e97SDavid Howells int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir,
85307a90e97SDavid Howells 			    char *filename)
85407a90e97SDavid Howells {
85507a90e97SDavid Howells 	struct dentry *victim;
85607a90e97SDavid Howells 	int ret = 0;
85707a90e97SDavid Howells 
85807a90e97SDavid Howells 	victim = cachefiles_lookup_for_cull(cache, dir, filename);
85907a90e97SDavid Howells 	if (IS_ERR(victim))
86007a90e97SDavid Howells 		return PTR_ERR(victim);
86107a90e97SDavid Howells 
86207a90e97SDavid Howells 	inode_unlock(d_inode(dir));
86307a90e97SDavid Howells 	dput(victim);
86407a90e97SDavid Howells 	return ret;
86507a90e97SDavid Howells }
866