xref: /openbmc/linux/fs/cachefiles/namei.c (revision 07a90e97)
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  */
171bd9c4e4SDavid Howells static bool __cachefiles_mark_inode_in_use(struct cachefiles_object *object,
181bd9c4e4SDavid Howells 					   struct dentry *dentry)
191bd9c4e4SDavid Howells {
201bd9c4e4SDavid Howells 	struct inode *inode = d_backing_inode(dentry);
211bd9c4e4SDavid Howells 	bool can_use = false;
221bd9c4e4SDavid Howells 
231bd9c4e4SDavid Howells 	if (!(inode->i_flags & S_KERNEL_FILE)) {
241bd9c4e4SDavid Howells 		inode->i_flags |= S_KERNEL_FILE;
251bd9c4e4SDavid Howells 		trace_cachefiles_mark_active(object, inode);
261bd9c4e4SDavid Howells 		can_use = true;
271bd9c4e4SDavid Howells 	} else {
281bd9c4e4SDavid Howells 		pr_notice("cachefiles: Inode already in use: %pd\n", dentry);
291bd9c4e4SDavid Howells 	}
301bd9c4e4SDavid Howells 
311bd9c4e4SDavid Howells 	return can_use;
321bd9c4e4SDavid Howells }
331bd9c4e4SDavid Howells 
34169379eaSDavid Howells static bool cachefiles_mark_inode_in_use(struct cachefiles_object *object,
35169379eaSDavid Howells 					 struct dentry *dentry)
36169379eaSDavid Howells {
37169379eaSDavid Howells 	struct inode *inode = d_backing_inode(dentry);
38169379eaSDavid Howells 	bool can_use;
39169379eaSDavid Howells 
40169379eaSDavid Howells 	inode_lock(inode);
41169379eaSDavid Howells 	can_use = __cachefiles_mark_inode_in_use(object, dentry);
42169379eaSDavid Howells 	inode_unlock(inode);
43169379eaSDavid Howells 	return can_use;
44169379eaSDavid Howells }
45169379eaSDavid Howells 
461bd9c4e4SDavid Howells /*
471bd9c4e4SDavid Howells  * Unmark a backing inode.  The caller must hold the inode lock.
481bd9c4e4SDavid Howells  */
491bd9c4e4SDavid Howells static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
501bd9c4e4SDavid Howells 					     struct dentry *dentry)
511bd9c4e4SDavid Howells {
521bd9c4e4SDavid Howells 	struct inode *inode = d_backing_inode(dentry);
531bd9c4e4SDavid Howells 
541bd9c4e4SDavid Howells 	inode->i_flags &= ~S_KERNEL_FILE;
551bd9c4e4SDavid Howells 	trace_cachefiles_mark_inactive(object, inode);
561bd9c4e4SDavid Howells }
5732759f7dSDavid Howells 
5832759f7dSDavid Howells /*
59169379eaSDavid Howells  * Unmark a backing inode and tell cachefilesd that there's something that can
60169379eaSDavid Howells  * be culled.
61169379eaSDavid Howells  */
62169379eaSDavid Howells void cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
63169379eaSDavid Howells 				    struct file *file)
64169379eaSDavid Howells {
65169379eaSDavid Howells 	struct cachefiles_cache *cache = object->volume->cache;
66169379eaSDavid Howells 	struct inode *inode = file_inode(file);
67169379eaSDavid Howells 
68169379eaSDavid Howells 	if (inode) {
69169379eaSDavid Howells 		inode_lock(inode);
70169379eaSDavid Howells 		__cachefiles_unmark_inode_in_use(object, file->f_path.dentry);
71169379eaSDavid Howells 		inode_unlock(inode);
72169379eaSDavid Howells 
73169379eaSDavid Howells 		if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
74169379eaSDavid Howells 			atomic_long_add(inode->i_blocks, &cache->b_released);
75169379eaSDavid Howells 			if (atomic_inc_return(&cache->f_released))
76169379eaSDavid Howells 				cachefiles_state_changed(cache);
77169379eaSDavid Howells 		}
78169379eaSDavid Howells 	}
79169379eaSDavid Howells }
80169379eaSDavid Howells 
81169379eaSDavid Howells /*
8232759f7dSDavid Howells  * get a subdirectory
8332759f7dSDavid Howells  */
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);
10432759f7dSDavid Howells 	if (IS_ERR(subdir)) {
10532759f7dSDavid Howells 		trace_cachefiles_vfs_error(NULL, d_backing_inode(dir),
10632759f7dSDavid Howells 					   PTR_ERR(subdir),
10732759f7dSDavid Howells 					   cachefiles_trace_lookup_error);
10832759f7dSDavid Howells 		if (PTR_ERR(subdir) == -ENOMEM)
10932759f7dSDavid Howells 			goto nomem_d_alloc;
11032759f7dSDavid Howells 		goto lookup_error;
11132759f7dSDavid Howells 	}
11232759f7dSDavid Howells 
11332759f7dSDavid Howells 	_debug("subdir -> %pd %s",
11432759f7dSDavid Howells 	       subdir, d_backing_inode(subdir) ? "positive" : "negative");
11532759f7dSDavid Howells 
11632759f7dSDavid Howells 	/* we need to create the subdir if it doesn't exist yet */
11732759f7dSDavid Howells 	if (d_is_negative(subdir)) {
11832759f7dSDavid Howells 		ret = cachefiles_has_space(cache, 1, 0);
11932759f7dSDavid Howells 		if (ret < 0)
12032759f7dSDavid Howells 			goto mkdir_error;
12132759f7dSDavid Howells 
12232759f7dSDavid Howells 		_debug("attempt mkdir");
12332759f7dSDavid Howells 
12432759f7dSDavid Howells 		path.mnt = cache->mnt;
12532759f7dSDavid Howells 		path.dentry = dir;
12632759f7dSDavid Howells 		ret = security_path_mkdir(&path, subdir, 0700);
12732759f7dSDavid Howells 		if (ret < 0)
12832759f7dSDavid Howells 			goto mkdir_error;
12932759f7dSDavid Howells 		ret = cachefiles_inject_write_error();
13032759f7dSDavid Howells 		if (ret == 0)
13132759f7dSDavid Howells 			ret = vfs_mkdir(&init_user_ns, d_inode(dir), subdir, 0700);
13232759f7dSDavid Howells 		if (ret < 0) {
13332759f7dSDavid Howells 			trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
13432759f7dSDavid Howells 						   cachefiles_trace_mkdir_error);
13532759f7dSDavid Howells 			goto mkdir_error;
13632759f7dSDavid Howells 		}
13732759f7dSDavid Howells 
13832759f7dSDavid Howells 		if (unlikely(d_unhashed(subdir))) {
13932759f7dSDavid Howells 			cachefiles_put_directory(subdir);
14032759f7dSDavid Howells 			goto retry;
14132759f7dSDavid Howells 		}
14232759f7dSDavid Howells 		ASSERT(d_backing_inode(subdir));
14332759f7dSDavid Howells 
14432759f7dSDavid Howells 		_debug("mkdir -> %pd{ino=%lu}",
14532759f7dSDavid Howells 		       subdir, d_backing_inode(subdir)->i_ino);
14632759f7dSDavid Howells 		if (_is_new)
14732759f7dSDavid Howells 			*_is_new = true;
14832759f7dSDavid Howells 	}
14932759f7dSDavid Howells 
15032759f7dSDavid Howells 	/* Tell rmdir() it's not allowed to delete the subdir */
15132759f7dSDavid Howells 	inode_lock(d_inode(subdir));
15232759f7dSDavid Howells 	inode_unlock(d_inode(dir));
15332759f7dSDavid Howells 
15432759f7dSDavid Howells 	if (!__cachefiles_mark_inode_in_use(NULL, subdir))
15532759f7dSDavid Howells 		goto mark_error;
15632759f7dSDavid Howells 
15732759f7dSDavid Howells 	inode_unlock(d_inode(subdir));
15832759f7dSDavid Howells 
15932759f7dSDavid Howells 	/* we need to make sure the subdir is a directory */
16032759f7dSDavid Howells 	ASSERT(d_backing_inode(subdir));
16132759f7dSDavid Howells 
16232759f7dSDavid Howells 	if (!d_can_lookup(subdir)) {
16332759f7dSDavid Howells 		pr_err("%s is not a directory\n", dirname);
16432759f7dSDavid Howells 		ret = -EIO;
16532759f7dSDavid Howells 		goto check_error;
16632759f7dSDavid Howells 	}
16732759f7dSDavid Howells 
16832759f7dSDavid Howells 	ret = -EPERM;
16932759f7dSDavid Howells 	if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) ||
17032759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->lookup ||
17132759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->mkdir ||
17232759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->rename ||
17332759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->rmdir ||
17432759f7dSDavid Howells 	    !d_backing_inode(subdir)->i_op->unlink)
17532759f7dSDavid Howells 		goto check_error;
17632759f7dSDavid Howells 
17732759f7dSDavid Howells 	_leave(" = [%lu]", d_backing_inode(subdir)->i_ino);
17832759f7dSDavid Howells 	return subdir;
17932759f7dSDavid Howells 
18032759f7dSDavid Howells check_error:
18132759f7dSDavid Howells 	cachefiles_put_directory(subdir);
18232759f7dSDavid Howells 	_leave(" = %d [check]", ret);
18332759f7dSDavid Howells 	return ERR_PTR(ret);
18432759f7dSDavid Howells 
18532759f7dSDavid Howells mark_error:
18632759f7dSDavid Howells 	inode_unlock(d_inode(subdir));
18732759f7dSDavid Howells 	dput(subdir);
18832759f7dSDavid Howells 	return ERR_PTR(-EBUSY);
18932759f7dSDavid Howells 
19032759f7dSDavid Howells mkdir_error:
19132759f7dSDavid Howells 	inode_unlock(d_inode(dir));
19232759f7dSDavid Howells 	dput(subdir);
19332759f7dSDavid Howells 	pr_err("mkdir %s failed with error %d\n", dirname, ret);
19432759f7dSDavid Howells 	return ERR_PTR(ret);
19532759f7dSDavid Howells 
19632759f7dSDavid Howells lookup_error:
19732759f7dSDavid Howells 	inode_unlock(d_inode(dir));
19832759f7dSDavid Howells 	ret = PTR_ERR(subdir);
19932759f7dSDavid Howells 	pr_err("Lookup %s failed with error %d\n", dirname, ret);
20032759f7dSDavid Howells 	return ERR_PTR(ret);
20132759f7dSDavid Howells 
20232759f7dSDavid Howells nomem_d_alloc:
20332759f7dSDavid Howells 	inode_unlock(d_inode(dir));
20432759f7dSDavid Howells 	_leave(" = -ENOMEM");
20532759f7dSDavid Howells 	return ERR_PTR(-ENOMEM);
20632759f7dSDavid Howells }
20732759f7dSDavid Howells 
20832759f7dSDavid Howells /*
20932759f7dSDavid Howells  * Put a subdirectory.
21032759f7dSDavid Howells  */
21132759f7dSDavid Howells void cachefiles_put_directory(struct dentry *dir)
21232759f7dSDavid Howells {
21332759f7dSDavid Howells 	if (dir) {
21432759f7dSDavid Howells 		inode_lock(dir->d_inode);
21532759f7dSDavid Howells 		__cachefiles_unmark_inode_in_use(NULL, dir);
21632759f7dSDavid Howells 		inode_unlock(dir->d_inode);
21732759f7dSDavid Howells 		dput(dir);
21832759f7dSDavid Howells 	}
21932759f7dSDavid Howells }
220*07a90e97SDavid Howells 
221*07a90e97SDavid Howells /*
222*07a90e97SDavid Howells  * Remove a regular file from the cache.
223*07a90e97SDavid Howells  */
224*07a90e97SDavid Howells static int cachefiles_unlink(struct cachefiles_cache *cache,
225*07a90e97SDavid Howells 			     struct cachefiles_object *object,
226*07a90e97SDavid Howells 			     struct dentry *dir, struct dentry *dentry,
227*07a90e97SDavid Howells 			     enum fscache_why_object_killed why)
228*07a90e97SDavid Howells {
229*07a90e97SDavid Howells 	struct path path = {
230*07a90e97SDavid Howells 		.mnt	= cache->mnt,
231*07a90e97SDavid Howells 		.dentry	= dir,
232*07a90e97SDavid Howells 	};
233*07a90e97SDavid Howells 	int ret;
234*07a90e97SDavid Howells 
235*07a90e97SDavid Howells 	trace_cachefiles_unlink(object, dentry, why);
236*07a90e97SDavid Howells 	ret = security_path_unlink(&path, dentry);
237*07a90e97SDavid Howells 	if (ret < 0) {
238*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Unlink security error");
239*07a90e97SDavid Howells 		return ret;
240*07a90e97SDavid Howells 	}
241*07a90e97SDavid Howells 
242*07a90e97SDavid Howells 	ret = cachefiles_inject_remove_error();
243*07a90e97SDavid Howells 	if (ret == 0) {
244*07a90e97SDavid Howells 		ret = vfs_unlink(&init_user_ns, d_backing_inode(dir), dentry, NULL);
245*07a90e97SDavid Howells 		if (ret == -EIO)
246*07a90e97SDavid Howells 			cachefiles_io_error(cache, "Unlink failed");
247*07a90e97SDavid Howells 	}
248*07a90e97SDavid Howells 	if (ret != 0)
249*07a90e97SDavid Howells 		trace_cachefiles_vfs_error(object, d_backing_inode(dir), ret,
250*07a90e97SDavid Howells 					   cachefiles_trace_unlink_error);
251*07a90e97SDavid Howells 	return ret;
252*07a90e97SDavid Howells }
253*07a90e97SDavid Howells 
254*07a90e97SDavid Howells /*
255*07a90e97SDavid Howells  * Delete an object representation from the cache
256*07a90e97SDavid Howells  * - File backed objects are unlinked
257*07a90e97SDavid Howells  * - Directory backed objects are stuffed into the graveyard for userspace to
258*07a90e97SDavid Howells  *   delete
259*07a90e97SDavid Howells  */
260*07a90e97SDavid Howells int cachefiles_bury_object(struct cachefiles_cache *cache,
261*07a90e97SDavid Howells 			   struct cachefiles_object *object,
262*07a90e97SDavid Howells 			   struct dentry *dir,
263*07a90e97SDavid Howells 			   struct dentry *rep,
264*07a90e97SDavid Howells 			   enum fscache_why_object_killed why)
265*07a90e97SDavid Howells {
266*07a90e97SDavid Howells 	struct dentry *grave, *trap;
267*07a90e97SDavid Howells 	struct path path, path_to_graveyard;
268*07a90e97SDavid Howells 	char nbuffer[8 + 8 + 1];
269*07a90e97SDavid Howells 	int ret;
270*07a90e97SDavid Howells 
271*07a90e97SDavid Howells 	_enter(",'%pd','%pd'", dir, rep);
272*07a90e97SDavid Howells 
273*07a90e97SDavid Howells 	if (rep->d_parent != dir) {
274*07a90e97SDavid Howells 		inode_unlock(d_inode(dir));
275*07a90e97SDavid Howells 		_leave(" = -ESTALE");
276*07a90e97SDavid Howells 		return -ESTALE;
277*07a90e97SDavid Howells 	}
278*07a90e97SDavid Howells 
279*07a90e97SDavid Howells 	/* non-directories can just be unlinked */
280*07a90e97SDavid Howells 	if (!d_is_dir(rep)) {
281*07a90e97SDavid Howells 		dget(rep); /* Stop the dentry being negated if it's only pinned
282*07a90e97SDavid Howells 			    * by a file struct.
283*07a90e97SDavid Howells 			    */
284*07a90e97SDavid Howells 		ret = cachefiles_unlink(cache, object, dir, rep, why);
285*07a90e97SDavid Howells 		dput(rep);
286*07a90e97SDavid Howells 
287*07a90e97SDavid Howells 		inode_unlock(d_inode(dir));
288*07a90e97SDavid Howells 		_leave(" = %d", ret);
289*07a90e97SDavid Howells 		return ret;
290*07a90e97SDavid Howells 	}
291*07a90e97SDavid Howells 
292*07a90e97SDavid Howells 	/* directories have to be moved to the graveyard */
293*07a90e97SDavid Howells 	_debug("move stale object to graveyard");
294*07a90e97SDavid Howells 	inode_unlock(d_inode(dir));
295*07a90e97SDavid Howells 
296*07a90e97SDavid Howells try_again:
297*07a90e97SDavid Howells 	/* first step is to make up a grave dentry in the graveyard */
298*07a90e97SDavid Howells 	sprintf(nbuffer, "%08x%08x",
299*07a90e97SDavid Howells 		(uint32_t) ktime_get_real_seconds(),
300*07a90e97SDavid Howells 		(uint32_t) atomic_inc_return(&cache->gravecounter));
301*07a90e97SDavid Howells 
302*07a90e97SDavid Howells 	/* do the multiway lock magic */
303*07a90e97SDavid Howells 	trap = lock_rename(cache->graveyard, dir);
304*07a90e97SDavid Howells 
305*07a90e97SDavid Howells 	/* do some checks before getting the grave dentry */
306*07a90e97SDavid Howells 	if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) {
307*07a90e97SDavid Howells 		/* the entry was probably culled when we dropped the parent dir
308*07a90e97SDavid Howells 		 * lock */
309*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
310*07a90e97SDavid Howells 		_leave(" = 0 [culled?]");
311*07a90e97SDavid Howells 		return 0;
312*07a90e97SDavid Howells 	}
313*07a90e97SDavid Howells 
314*07a90e97SDavid Howells 	if (!d_can_lookup(cache->graveyard)) {
315*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
316*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Graveyard no longer a directory");
317*07a90e97SDavid Howells 		return -EIO;
318*07a90e97SDavid Howells 	}
319*07a90e97SDavid Howells 
320*07a90e97SDavid Howells 	if (trap == rep) {
321*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
322*07a90e97SDavid Howells 		cachefiles_io_error(cache, "May not make directory loop");
323*07a90e97SDavid Howells 		return -EIO;
324*07a90e97SDavid Howells 	}
325*07a90e97SDavid Howells 
326*07a90e97SDavid Howells 	if (d_mountpoint(rep)) {
327*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
328*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Mountpoint in cache");
329*07a90e97SDavid Howells 		return -EIO;
330*07a90e97SDavid Howells 	}
331*07a90e97SDavid Howells 
332*07a90e97SDavid Howells 	grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer));
333*07a90e97SDavid Howells 	if (IS_ERR(grave)) {
334*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
335*07a90e97SDavid Howells 		trace_cachefiles_vfs_error(object, d_inode(cache->graveyard),
336*07a90e97SDavid Howells 					   PTR_ERR(grave),
337*07a90e97SDavid Howells 					   cachefiles_trace_lookup_error);
338*07a90e97SDavid Howells 
339*07a90e97SDavid Howells 		if (PTR_ERR(grave) == -ENOMEM) {
340*07a90e97SDavid Howells 			_leave(" = -ENOMEM");
341*07a90e97SDavid Howells 			return -ENOMEM;
342*07a90e97SDavid Howells 		}
343*07a90e97SDavid Howells 
344*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave));
345*07a90e97SDavid Howells 		return -EIO;
346*07a90e97SDavid Howells 	}
347*07a90e97SDavid Howells 
348*07a90e97SDavid Howells 	if (d_is_positive(grave)) {
349*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
350*07a90e97SDavid Howells 		dput(grave);
351*07a90e97SDavid Howells 		grave = NULL;
352*07a90e97SDavid Howells 		cond_resched();
353*07a90e97SDavid Howells 		goto try_again;
354*07a90e97SDavid Howells 	}
355*07a90e97SDavid Howells 
356*07a90e97SDavid Howells 	if (d_mountpoint(grave)) {
357*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
358*07a90e97SDavid Howells 		dput(grave);
359*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Mountpoint in graveyard");
360*07a90e97SDavid Howells 		return -EIO;
361*07a90e97SDavid Howells 	}
362*07a90e97SDavid Howells 
363*07a90e97SDavid Howells 	/* target should not be an ancestor of source */
364*07a90e97SDavid Howells 	if (trap == grave) {
365*07a90e97SDavid Howells 		unlock_rename(cache->graveyard, dir);
366*07a90e97SDavid Howells 		dput(grave);
367*07a90e97SDavid Howells 		cachefiles_io_error(cache, "May not make directory loop");
368*07a90e97SDavid Howells 		return -EIO;
369*07a90e97SDavid Howells 	}
370*07a90e97SDavid Howells 
371*07a90e97SDavid Howells 	/* attempt the rename */
372*07a90e97SDavid Howells 	path.mnt = cache->mnt;
373*07a90e97SDavid Howells 	path.dentry = dir;
374*07a90e97SDavid Howells 	path_to_graveyard.mnt = cache->mnt;
375*07a90e97SDavid Howells 	path_to_graveyard.dentry = cache->graveyard;
376*07a90e97SDavid Howells 	ret = security_path_rename(&path, rep, &path_to_graveyard, grave, 0);
377*07a90e97SDavid Howells 	if (ret < 0) {
378*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Rename security error %d", ret);
379*07a90e97SDavid Howells 	} else {
380*07a90e97SDavid Howells 		struct renamedata rd = {
381*07a90e97SDavid Howells 			.old_mnt_userns	= &init_user_ns,
382*07a90e97SDavid Howells 			.old_dir	= d_inode(dir),
383*07a90e97SDavid Howells 			.old_dentry	= rep,
384*07a90e97SDavid Howells 			.new_mnt_userns	= &init_user_ns,
385*07a90e97SDavid Howells 			.new_dir	= d_inode(cache->graveyard),
386*07a90e97SDavid Howells 			.new_dentry	= grave,
387*07a90e97SDavid Howells 		};
388*07a90e97SDavid Howells 		trace_cachefiles_rename(object, rep, grave, why);
389*07a90e97SDavid Howells 		ret = cachefiles_inject_read_error();
390*07a90e97SDavid Howells 		if (ret == 0)
391*07a90e97SDavid Howells 			ret = vfs_rename(&rd);
392*07a90e97SDavid Howells 		if (ret != 0)
393*07a90e97SDavid Howells 			trace_cachefiles_vfs_error(object, d_inode(dir), ret,
394*07a90e97SDavid Howells 						   cachefiles_trace_rename_error);
395*07a90e97SDavid Howells 		if (ret != 0 && ret != -ENOMEM)
396*07a90e97SDavid Howells 			cachefiles_io_error(cache,
397*07a90e97SDavid Howells 					    "Rename failed with error %d", ret);
398*07a90e97SDavid Howells 	}
399*07a90e97SDavid Howells 
400*07a90e97SDavid Howells 	__cachefiles_unmark_inode_in_use(object, rep);
401*07a90e97SDavid Howells 	unlock_rename(cache->graveyard, dir);
402*07a90e97SDavid Howells 	dput(grave);
403*07a90e97SDavid Howells 	_leave(" = 0");
404*07a90e97SDavid Howells 	return 0;
405*07a90e97SDavid Howells }
406*07a90e97SDavid Howells 
407*07a90e97SDavid Howells /*
408*07a90e97SDavid Howells  * Look up an inode to be checked or culled.  Return -EBUSY if the inode is
409*07a90e97SDavid Howells  * marked in use.
410*07a90e97SDavid Howells  */
411*07a90e97SDavid Howells static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *cache,
412*07a90e97SDavid Howells 						 struct dentry *dir,
413*07a90e97SDavid Howells 						 char *filename)
414*07a90e97SDavid Howells {
415*07a90e97SDavid Howells 	struct dentry *victim;
416*07a90e97SDavid Howells 	int ret = -ENOENT;
417*07a90e97SDavid Howells 
418*07a90e97SDavid Howells 	inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
419*07a90e97SDavid Howells 
420*07a90e97SDavid Howells 	victim = lookup_one_len(filename, dir, strlen(filename));
421*07a90e97SDavid Howells 	if (IS_ERR(victim))
422*07a90e97SDavid Howells 		goto lookup_error;
423*07a90e97SDavid Howells 	if (d_is_negative(victim))
424*07a90e97SDavid Howells 		goto lookup_put;
425*07a90e97SDavid Howells 	if (d_inode(victim)->i_flags & S_KERNEL_FILE)
426*07a90e97SDavid Howells 		goto lookup_busy;
427*07a90e97SDavid Howells 	return victim;
428*07a90e97SDavid Howells 
429*07a90e97SDavid Howells lookup_busy:
430*07a90e97SDavid Howells 	ret = -EBUSY;
431*07a90e97SDavid Howells lookup_put:
432*07a90e97SDavid Howells 	inode_unlock(d_inode(dir));
433*07a90e97SDavid Howells 	dput(victim);
434*07a90e97SDavid Howells 	return ERR_PTR(ret);
435*07a90e97SDavid Howells 
436*07a90e97SDavid Howells lookup_error:
437*07a90e97SDavid Howells 	inode_unlock(d_inode(dir));
438*07a90e97SDavid Howells 	ret = PTR_ERR(victim);
439*07a90e97SDavid Howells 	if (ret == -ENOENT)
440*07a90e97SDavid Howells 		return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */
441*07a90e97SDavid Howells 
442*07a90e97SDavid Howells 	if (ret == -EIO) {
443*07a90e97SDavid Howells 		cachefiles_io_error(cache, "Lookup failed");
444*07a90e97SDavid Howells 	} else if (ret != -ENOMEM) {
445*07a90e97SDavid Howells 		pr_err("Internal error: %d\n", ret);
446*07a90e97SDavid Howells 		ret = -EIO;
447*07a90e97SDavid Howells 	}
448*07a90e97SDavid Howells 
449*07a90e97SDavid Howells 	return ERR_PTR(ret);
450*07a90e97SDavid Howells }
451*07a90e97SDavid Howells 
452*07a90e97SDavid Howells /*
453*07a90e97SDavid Howells  * Cull an object if it's not in use
454*07a90e97SDavid Howells  * - called only by cache manager daemon
455*07a90e97SDavid Howells  */
456*07a90e97SDavid Howells int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
457*07a90e97SDavid Howells 		    char *filename)
458*07a90e97SDavid Howells {
459*07a90e97SDavid Howells 	struct dentry *victim;
460*07a90e97SDavid Howells 	struct inode *inode;
461*07a90e97SDavid Howells 	int ret;
462*07a90e97SDavid Howells 
463*07a90e97SDavid Howells 	_enter(",%pd/,%s", dir, filename);
464*07a90e97SDavid Howells 
465*07a90e97SDavid Howells 	victim = cachefiles_lookup_for_cull(cache, dir, filename);
466*07a90e97SDavid Howells 	if (IS_ERR(victim))
467*07a90e97SDavid Howells 		return PTR_ERR(victim);
468*07a90e97SDavid Howells 
469*07a90e97SDavid Howells 	/* check to see if someone is using this object */
470*07a90e97SDavid Howells 	inode = d_inode(victim);
471*07a90e97SDavid Howells 	inode_lock(inode);
472*07a90e97SDavid Howells 	if (inode->i_flags & S_KERNEL_FILE) {
473*07a90e97SDavid Howells 		ret = -EBUSY;
474*07a90e97SDavid Howells 	} else {
475*07a90e97SDavid Howells 		/* Stop the cache from picking it back up */
476*07a90e97SDavid Howells 		inode->i_flags |= S_KERNEL_FILE;
477*07a90e97SDavid Howells 		ret = 0;
478*07a90e97SDavid Howells 	}
479*07a90e97SDavid Howells 	inode_unlock(inode);
480*07a90e97SDavid Howells 	if (ret < 0)
481*07a90e97SDavid Howells 		goto error_unlock;
482*07a90e97SDavid Howells 
483*07a90e97SDavid Howells 	ret = cachefiles_bury_object(cache, NULL, dir, victim,
484*07a90e97SDavid Howells 				     FSCACHE_OBJECT_WAS_CULLED);
485*07a90e97SDavid Howells 	if (ret < 0)
486*07a90e97SDavid Howells 		goto error;
487*07a90e97SDavid Howells 
488*07a90e97SDavid Howells 	dput(victim);
489*07a90e97SDavid Howells 	_leave(" = 0");
490*07a90e97SDavid Howells 	return 0;
491*07a90e97SDavid Howells 
492*07a90e97SDavid Howells error_unlock:
493*07a90e97SDavid Howells 	inode_unlock(d_inode(dir));
494*07a90e97SDavid Howells error:
495*07a90e97SDavid Howells 	dput(victim);
496*07a90e97SDavid Howells 	if (ret == -ENOENT)
497*07a90e97SDavid Howells 		return -ESTALE; /* Probably got retired by the netfs */
498*07a90e97SDavid Howells 
499*07a90e97SDavid Howells 	if (ret != -ENOMEM) {
500*07a90e97SDavid Howells 		pr_err("Internal error: %d\n", ret);
501*07a90e97SDavid Howells 		ret = -EIO;
502*07a90e97SDavid Howells 	}
503*07a90e97SDavid Howells 
504*07a90e97SDavid Howells 	_leave(" = %d", ret);
505*07a90e97SDavid Howells 	return ret;
506*07a90e97SDavid Howells }
507*07a90e97SDavid Howells 
508*07a90e97SDavid Howells /*
509*07a90e97SDavid Howells  * Find out if an object is in use or not
510*07a90e97SDavid Howells  * - called only by cache manager daemon
511*07a90e97SDavid Howells  * - returns -EBUSY or 0 to indicate whether an object is in use or not
512*07a90e97SDavid Howells  */
513*07a90e97SDavid Howells int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir,
514*07a90e97SDavid Howells 			    char *filename)
515*07a90e97SDavid Howells {
516*07a90e97SDavid Howells 	struct dentry *victim;
517*07a90e97SDavid Howells 	int ret = 0;
518*07a90e97SDavid Howells 
519*07a90e97SDavid Howells 	victim = cachefiles_lookup_for_cull(cache, dir, filename);
520*07a90e97SDavid Howells 	if (IS_ERR(victim))
521*07a90e97SDavid Howells 		return PTR_ERR(victim);
522*07a90e97SDavid Howells 
523*07a90e97SDavid Howells 	inode_unlock(d_inode(dir));
524*07a90e97SDavid Howells 	dput(victim);
525*07a90e97SDavid Howells 	return ret;
526*07a90e97SDavid Howells }
527