xref: /openbmc/linux/fs/cachefiles/daemon.c (revision 3bf0b803)
18667d434SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
28667d434SDavid Howells /* Daemon interface
38667d434SDavid Howells  *
48667d434SDavid Howells  * Copyright (C) 2007, 2021 Red Hat, Inc. All Rights Reserved.
58667d434SDavid Howells  * Written by David Howells (dhowells@redhat.com)
68667d434SDavid Howells  */
78667d434SDavid Howells 
88667d434SDavid Howells #include <linux/module.h>
98667d434SDavid Howells #include <linux/init.h>
108667d434SDavid Howells #include <linux/sched.h>
118667d434SDavid Howells #include <linux/completion.h>
128667d434SDavid Howells #include <linux/slab.h>
138667d434SDavid Howells #include <linux/fs.h>
148667d434SDavid Howells #include <linux/file.h>
158667d434SDavid Howells #include <linux/namei.h>
168667d434SDavid Howells #include <linux/poll.h>
178667d434SDavid Howells #include <linux/mount.h>
188667d434SDavid Howells #include <linux/statfs.h>
198667d434SDavid Howells #include <linux/ctype.h>
208667d434SDavid Howells #include <linux/string.h>
218667d434SDavid Howells #include <linux/fs_struct.h>
228667d434SDavid Howells #include "internal.h"
238667d434SDavid Howells 
248667d434SDavid Howells static int cachefiles_daemon_open(struct inode *, struct file *);
258667d434SDavid Howells static int cachefiles_daemon_release(struct inode *, struct file *);
268667d434SDavid Howells static ssize_t cachefiles_daemon_read(struct file *, char __user *, size_t,
278667d434SDavid Howells 				      loff_t *);
288667d434SDavid Howells static ssize_t cachefiles_daemon_write(struct file *, const char __user *,
298667d434SDavid Howells 				       size_t, loff_t *);
308667d434SDavid Howells static __poll_t cachefiles_daemon_poll(struct file *,
318667d434SDavid Howells 					   struct poll_table_struct *);
328667d434SDavid Howells static int cachefiles_daemon_frun(struct cachefiles_cache *, char *);
338667d434SDavid Howells static int cachefiles_daemon_fcull(struct cachefiles_cache *, char *);
348667d434SDavid Howells static int cachefiles_daemon_fstop(struct cachefiles_cache *, char *);
358667d434SDavid Howells static int cachefiles_daemon_brun(struct cachefiles_cache *, char *);
368667d434SDavid Howells static int cachefiles_daemon_bcull(struct cachefiles_cache *, char *);
378667d434SDavid Howells static int cachefiles_daemon_bstop(struct cachefiles_cache *, char *);
388667d434SDavid Howells static int cachefiles_daemon_cull(struct cachefiles_cache *, char *);
398667d434SDavid Howells static int cachefiles_daemon_debug(struct cachefiles_cache *, char *);
408667d434SDavid Howells static int cachefiles_daemon_dir(struct cachefiles_cache *, char *);
418667d434SDavid Howells static int cachefiles_daemon_inuse(struct cachefiles_cache *, char *);
428667d434SDavid Howells static int cachefiles_daemon_secctx(struct cachefiles_cache *, char *);
438667d434SDavid Howells static int cachefiles_daemon_tag(struct cachefiles_cache *, char *);
448667d434SDavid Howells static int cachefiles_daemon_bind(struct cachefiles_cache *, char *);
458667d434SDavid Howells static void cachefiles_daemon_unbind(struct cachefiles_cache *);
468667d434SDavid Howells 
478667d434SDavid Howells static unsigned long cachefiles_open;
488667d434SDavid Howells 
498667d434SDavid Howells const struct file_operations cachefiles_daemon_fops = {
508667d434SDavid Howells 	.owner		= THIS_MODULE,
518667d434SDavid Howells 	.open		= cachefiles_daemon_open,
528667d434SDavid Howells 	.release	= cachefiles_daemon_release,
538667d434SDavid Howells 	.read		= cachefiles_daemon_read,
548667d434SDavid Howells 	.write		= cachefiles_daemon_write,
558667d434SDavid Howells 	.poll		= cachefiles_daemon_poll,
568667d434SDavid Howells 	.llseek		= noop_llseek,
578667d434SDavid Howells };
588667d434SDavid Howells 
598667d434SDavid Howells struct cachefiles_daemon_cmd {
608667d434SDavid Howells 	char name[8];
618667d434SDavid Howells 	int (*handler)(struct cachefiles_cache *cache, char *args);
628667d434SDavid Howells };
638667d434SDavid Howells 
648667d434SDavid Howells static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] = {
658667d434SDavid Howells 	{ "bind",	cachefiles_daemon_bind		},
668667d434SDavid Howells 	{ "brun",	cachefiles_daemon_brun		},
678667d434SDavid Howells 	{ "bcull",	cachefiles_daemon_bcull		},
688667d434SDavid Howells 	{ "bstop",	cachefiles_daemon_bstop		},
698667d434SDavid Howells 	{ "cull",	cachefiles_daemon_cull		},
708667d434SDavid Howells 	{ "debug",	cachefiles_daemon_debug		},
718667d434SDavid Howells 	{ "dir",	cachefiles_daemon_dir		},
728667d434SDavid Howells 	{ "frun",	cachefiles_daemon_frun		},
738667d434SDavid Howells 	{ "fcull",	cachefiles_daemon_fcull		},
748667d434SDavid Howells 	{ "fstop",	cachefiles_daemon_fstop		},
758667d434SDavid Howells 	{ "inuse",	cachefiles_daemon_inuse		},
768667d434SDavid Howells 	{ "secctx",	cachefiles_daemon_secctx	},
778667d434SDavid Howells 	{ "tag",	cachefiles_daemon_tag		},
78c8383054SJeffle Xu #ifdef CONFIG_CACHEFILES_ONDEMAND
79c8383054SJeffle Xu 	{ "copen",	cachefiles_ondemand_copen	},
809f5fa40fSJia Zhu 	{ "restore",	cachefiles_ondemand_restore	},
81c8383054SJeffle Xu #endif
828667d434SDavid Howells 	{ "",		NULL				}
838667d434SDavid Howells };
848667d434SDavid Howells 
858667d434SDavid Howells 
868667d434SDavid Howells /*
878667d434SDavid Howells  * Prepare a cache for caching.
888667d434SDavid Howells  */
cachefiles_daemon_open(struct inode * inode,struct file * file)898667d434SDavid Howells static int cachefiles_daemon_open(struct inode *inode, struct file *file)
908667d434SDavid Howells {
918667d434SDavid Howells 	struct cachefiles_cache *cache;
928667d434SDavid Howells 
938667d434SDavid Howells 	_enter("");
948667d434SDavid Howells 
958667d434SDavid Howells 	/* only the superuser may do this */
968667d434SDavid Howells 	if (!capable(CAP_SYS_ADMIN))
978667d434SDavid Howells 		return -EPERM;
988667d434SDavid Howells 
998667d434SDavid Howells 	/* the cachefiles device may only be open once at a time */
1008667d434SDavid Howells 	if (xchg(&cachefiles_open, 1) == 1)
1018667d434SDavid Howells 		return -EBUSY;
1028667d434SDavid Howells 
1038667d434SDavid Howells 	/* allocate a cache record */
1048667d434SDavid Howells 	cache = kzalloc(sizeof(struct cachefiles_cache), GFP_KERNEL);
1058667d434SDavid Howells 	if (!cache) {
1068667d434SDavid Howells 		cachefiles_open = 0;
1078667d434SDavid Howells 		return -ENOMEM;
1088667d434SDavid Howells 	}
1098667d434SDavid Howells 
1108667d434SDavid Howells 	mutex_init(&cache->daemon_mutex);
1118667d434SDavid Howells 	init_waitqueue_head(&cache->daemon_pollwq);
112fe2140e2SDavid Howells 	INIT_LIST_HEAD(&cache->volumes);
1131f08c925SDavid Howells 	INIT_LIST_HEAD(&cache->object_list);
114fe2140e2SDavid Howells 	spin_lock_init(&cache->object_list_lock);
115d11b0b04SJeffle Xu 	refcount_set(&cache->unbind_pincount, 1);
116c8383054SJeffle Xu 	xa_init_flags(&cache->reqs, XA_FLAGS_ALLOC);
117c8383054SJeffle Xu 	xa_init_flags(&cache->ondemand_ids, XA_FLAGS_ALLOC1);
1188667d434SDavid Howells 
1198667d434SDavid Howells 	/* set default caching limits
1208667d434SDavid Howells 	 * - limit at 1% free space and/or free files
1218667d434SDavid Howells 	 * - cull below 5% free space and/or free files
1228667d434SDavid Howells 	 * - cease culling above 7% free space and/or free files
1238667d434SDavid Howells 	 */
1248667d434SDavid Howells 	cache->frun_percent = 7;
1258667d434SDavid Howells 	cache->fcull_percent = 5;
1268667d434SDavid Howells 	cache->fstop_percent = 1;
1278667d434SDavid Howells 	cache->brun_percent = 7;
1288667d434SDavid Howells 	cache->bcull_percent = 5;
1298667d434SDavid Howells 	cache->bstop_percent = 1;
1308667d434SDavid Howells 
1318667d434SDavid Howells 	file->private_data = cache;
1328667d434SDavid Howells 	cache->cachefilesd = file;
1338667d434SDavid Howells 	return 0;
1348667d434SDavid Howells }
1358667d434SDavid Howells 
cachefiles_flush_reqs(struct cachefiles_cache * cache)1363bf0b803SBaokun Li void cachefiles_flush_reqs(struct cachefiles_cache *cache)
137c8383054SJeffle Xu {
138c8383054SJeffle Xu 	struct xarray *xa = &cache->reqs;
139c8383054SJeffle Xu 	struct cachefiles_req *req;
140c8383054SJeffle Xu 	unsigned long index;
141c8383054SJeffle Xu 
142c8383054SJeffle Xu 	/*
143c8383054SJeffle Xu 	 * Make sure the following two operations won't be reordered.
144c8383054SJeffle Xu 	 *   1) set CACHEFILES_DEAD bit
145c8383054SJeffle Xu 	 *   2) flush requests in the xarray
146c8383054SJeffle Xu 	 * Otherwise the request may be enqueued after xarray has been
147c8383054SJeffle Xu 	 * flushed, leaving the orphan request never being completed.
148c8383054SJeffle Xu 	 *
149c8383054SJeffle Xu 	 * CPU 1			CPU 2
150c8383054SJeffle Xu 	 * =====			=====
151c8383054SJeffle Xu 	 * flush requests in the xarray
152c8383054SJeffle Xu 	 *				test CACHEFILES_DEAD bit
153c8383054SJeffle Xu 	 *				enqueue the request
154c8383054SJeffle Xu 	 * set CACHEFILES_DEAD bit
155c8383054SJeffle Xu 	 */
156c8383054SJeffle Xu 	smp_mb();
157c8383054SJeffle Xu 
158c8383054SJeffle Xu 	xa_lock(xa);
159c8383054SJeffle Xu 	xa_for_each(xa, index, req) {
160c8383054SJeffle Xu 		req->error = -EIO;
161c8383054SJeffle Xu 		complete(&req->done);
16250d0e553SBaokun Li 		__xa_erase(xa, index);
163c8383054SJeffle Xu 	}
164c8383054SJeffle Xu 	xa_unlock(xa);
165c8383054SJeffle Xu 
166c8383054SJeffle Xu 	xa_destroy(&cache->reqs);
167c8383054SJeffle Xu 	xa_destroy(&cache->ondemand_ids);
168c8383054SJeffle Xu }
169c8383054SJeffle Xu 
cachefiles_put_unbind_pincount(struct cachefiles_cache * cache)170d11b0b04SJeffle Xu void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache)
171d11b0b04SJeffle Xu {
172d11b0b04SJeffle Xu 	if (refcount_dec_and_test(&cache->unbind_pincount)) {
173d11b0b04SJeffle Xu 		cachefiles_daemon_unbind(cache);
174d11b0b04SJeffle Xu 		cachefiles_open = 0;
175d11b0b04SJeffle Xu 		kfree(cache);
176d11b0b04SJeffle Xu 	}
177d11b0b04SJeffle Xu }
178d11b0b04SJeffle Xu 
cachefiles_get_unbind_pincount(struct cachefiles_cache * cache)179d11b0b04SJeffle Xu void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache)
180d11b0b04SJeffle Xu {
181d11b0b04SJeffle Xu 	refcount_inc(&cache->unbind_pincount);
182d11b0b04SJeffle Xu }
183d11b0b04SJeffle Xu 
1848667d434SDavid Howells /*
1858667d434SDavid Howells  * Release a cache.
1868667d434SDavid Howells  */
cachefiles_daemon_release(struct inode * inode,struct file * file)1878667d434SDavid Howells static int cachefiles_daemon_release(struct inode *inode, struct file *file)
1888667d434SDavid Howells {
1898667d434SDavid Howells 	struct cachefiles_cache *cache = file->private_data;
1908667d434SDavid Howells 
1918667d434SDavid Howells 	_enter("");
1928667d434SDavid Howells 
1938667d434SDavid Howells 	ASSERT(cache);
1948667d434SDavid Howells 
1958667d434SDavid Howells 	set_bit(CACHEFILES_DEAD, &cache->flags);
1968667d434SDavid Howells 
197c8383054SJeffle Xu 	if (cachefiles_in_ondemand_mode(cache))
198c8383054SJeffle Xu 		cachefiles_flush_reqs(cache);
1998667d434SDavid Howells 
2008667d434SDavid Howells 	/* clean up the control file interface */
2018667d434SDavid Howells 	cache->cachefilesd = NULL;
2028667d434SDavid Howells 	file->private_data = NULL;
2038667d434SDavid Howells 
204d11b0b04SJeffle Xu 	cachefiles_put_unbind_pincount(cache);
2058667d434SDavid Howells 
2068667d434SDavid Howells 	_leave("");
2078667d434SDavid Howells 	return 0;
2088667d434SDavid Howells }
2098667d434SDavid Howells 
cachefiles_do_daemon_read(struct cachefiles_cache * cache,char __user * _buffer,size_t buflen)210c8383054SJeffle Xu static ssize_t cachefiles_do_daemon_read(struct cachefiles_cache *cache,
211c8383054SJeffle Xu 					 char __user *_buffer, size_t buflen)
2128667d434SDavid Howells {
2138667d434SDavid Howells 	unsigned long long b_released;
2148667d434SDavid Howells 	unsigned f_released;
2158667d434SDavid Howells 	char buffer[256];
2168667d434SDavid Howells 	int n;
2178667d434SDavid Howells 
2188667d434SDavid Howells 	/* check how much space the cache has */
2193929eca7SDavid Howells 	cachefiles_has_space(cache, 0, 0, cachefiles_has_space_check);
2208667d434SDavid Howells 
2218667d434SDavid Howells 	/* summarise */
2228667d434SDavid Howells 	f_released = atomic_xchg(&cache->f_released, 0);
2238667d434SDavid Howells 	b_released = atomic_long_xchg(&cache->b_released, 0);
2248667d434SDavid Howells 	clear_bit(CACHEFILES_STATE_CHANGED, &cache->flags);
2258667d434SDavid Howells 
2268667d434SDavid Howells 	n = snprintf(buffer, sizeof(buffer),
2278667d434SDavid Howells 		     "cull=%c"
2288667d434SDavid Howells 		     " frun=%llx"
2298667d434SDavid Howells 		     " fcull=%llx"
2308667d434SDavid Howells 		     " fstop=%llx"
2318667d434SDavid Howells 		     " brun=%llx"
2328667d434SDavid Howells 		     " bcull=%llx"
2338667d434SDavid Howells 		     " bstop=%llx"
2348667d434SDavid Howells 		     " freleased=%x"
2358667d434SDavid Howells 		     " breleased=%llx",
2368667d434SDavid Howells 		     test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0',
2378667d434SDavid Howells 		     (unsigned long long) cache->frun,
2388667d434SDavid Howells 		     (unsigned long long) cache->fcull,
2398667d434SDavid Howells 		     (unsigned long long) cache->fstop,
2408667d434SDavid Howells 		     (unsigned long long) cache->brun,
2418667d434SDavid Howells 		     (unsigned long long) cache->bcull,
2428667d434SDavid Howells 		     (unsigned long long) cache->bstop,
2438667d434SDavid Howells 		     f_released,
2448667d434SDavid Howells 		     b_released);
2458667d434SDavid Howells 
2468667d434SDavid Howells 	if (n > buflen)
2478667d434SDavid Howells 		return -EMSGSIZE;
2488667d434SDavid Howells 
2498667d434SDavid Howells 	if (copy_to_user(_buffer, buffer, n) != 0)
2508667d434SDavid Howells 		return -EFAULT;
2518667d434SDavid Howells 
2528667d434SDavid Howells 	return n;
2538667d434SDavid Howells }
2548667d434SDavid Howells 
2558667d434SDavid Howells /*
256c8383054SJeffle Xu  * Read the cache state.
257c8383054SJeffle Xu  */
cachefiles_daemon_read(struct file * file,char __user * _buffer,size_t buflen,loff_t * pos)258c8383054SJeffle Xu static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
259c8383054SJeffle Xu 				      size_t buflen, loff_t *pos)
260c8383054SJeffle Xu {
261c8383054SJeffle Xu 	struct cachefiles_cache *cache = file->private_data;
262c8383054SJeffle Xu 
263c8383054SJeffle Xu 	//_enter(",,%zu,", buflen);
264c8383054SJeffle Xu 
265c8383054SJeffle Xu 	if (!test_bit(CACHEFILES_READY, &cache->flags))
266c8383054SJeffle Xu 		return 0;
267c8383054SJeffle Xu 
268c8383054SJeffle Xu 	if (cachefiles_in_ondemand_mode(cache))
269c8383054SJeffle Xu 		return cachefiles_ondemand_daemon_read(cache, _buffer, buflen);
270c8383054SJeffle Xu 	else
271c8383054SJeffle Xu 		return cachefiles_do_daemon_read(cache, _buffer, buflen);
272c8383054SJeffle Xu }
273c8383054SJeffle Xu 
274c8383054SJeffle Xu /*
2758667d434SDavid Howells  * Take a command from cachefilesd, parse it and act on it.
2768667d434SDavid Howells  */
cachefiles_daemon_write(struct file * file,const char __user * _data,size_t datalen,loff_t * pos)2778667d434SDavid Howells static ssize_t cachefiles_daemon_write(struct file *file,
2788667d434SDavid Howells 				       const char __user *_data,
2798667d434SDavid Howells 				       size_t datalen,
2808667d434SDavid Howells 				       loff_t *pos)
2818667d434SDavid Howells {
2828667d434SDavid Howells 	const struct cachefiles_daemon_cmd *cmd;
2838667d434SDavid Howells 	struct cachefiles_cache *cache = file->private_data;
2848667d434SDavid Howells 	ssize_t ret;
2858667d434SDavid Howells 	char *data, *args, *cp;
2868667d434SDavid Howells 
2878667d434SDavid Howells 	//_enter(",,%zu,", datalen);
2888667d434SDavid Howells 
2898667d434SDavid Howells 	ASSERT(cache);
2908667d434SDavid Howells 
2918667d434SDavid Howells 	if (test_bit(CACHEFILES_DEAD, &cache->flags))
2928667d434SDavid Howells 		return -EIO;
2938667d434SDavid Howells 
2948667d434SDavid Howells 	if (datalen > PAGE_SIZE - 1)
2958667d434SDavid Howells 		return -EOPNOTSUPP;
2968667d434SDavid Howells 
2978667d434SDavid Howells 	/* drag the command string into the kernel so we can parse it */
2988667d434SDavid Howells 	data = memdup_user_nul(_data, datalen);
2998667d434SDavid Howells 	if (IS_ERR(data))
3008667d434SDavid Howells 		return PTR_ERR(data);
3018667d434SDavid Howells 
3028667d434SDavid Howells 	ret = -EINVAL;
3038667d434SDavid Howells 	if (memchr(data, '\0', datalen))
3048667d434SDavid Howells 		goto error;
3058667d434SDavid Howells 
3068667d434SDavid Howells 	/* strip any newline */
3078667d434SDavid Howells 	cp = memchr(data, '\n', datalen);
3088667d434SDavid Howells 	if (cp) {
3098667d434SDavid Howells 		if (cp == data)
3108667d434SDavid Howells 			goto error;
3118667d434SDavid Howells 
3128667d434SDavid Howells 		*cp = '\0';
3138667d434SDavid Howells 	}
3148667d434SDavid Howells 
3158667d434SDavid Howells 	/* parse the command */
3168667d434SDavid Howells 	ret = -EOPNOTSUPP;
3178667d434SDavid Howells 
3188667d434SDavid Howells 	for (args = data; *args; args++)
3198667d434SDavid Howells 		if (isspace(*args))
3208667d434SDavid Howells 			break;
3218667d434SDavid Howells 	if (*args) {
3228667d434SDavid Howells 		if (args == data)
3238667d434SDavid Howells 			goto error;
3248667d434SDavid Howells 		*args = '\0';
3258667d434SDavid Howells 		args = skip_spaces(++args);
3268667d434SDavid Howells 	}
3278667d434SDavid Howells 
3288667d434SDavid Howells 	/* run the appropriate command handler */
3298667d434SDavid Howells 	for (cmd = cachefiles_daemon_cmds; cmd->name[0]; cmd++)
3308667d434SDavid Howells 		if (strcmp(cmd->name, data) == 0)
3318667d434SDavid Howells 			goto found_command;
3328667d434SDavid Howells 
3338667d434SDavid Howells error:
3348667d434SDavid Howells 	kfree(data);
3358667d434SDavid Howells 	//_leave(" = %zd", ret);
3368667d434SDavid Howells 	return ret;
3378667d434SDavid Howells 
3388667d434SDavid Howells found_command:
3398667d434SDavid Howells 	mutex_lock(&cache->daemon_mutex);
3408667d434SDavid Howells 
3418667d434SDavid Howells 	ret = -EIO;
3428667d434SDavid Howells 	if (!test_bit(CACHEFILES_DEAD, &cache->flags))
3438667d434SDavid Howells 		ret = cmd->handler(cache, args);
3448667d434SDavid Howells 
3458667d434SDavid Howells 	mutex_unlock(&cache->daemon_mutex);
3468667d434SDavid Howells 
3478667d434SDavid Howells 	if (ret == 0)
3488667d434SDavid Howells 		ret = datalen;
3498667d434SDavid Howells 	goto error;
3508667d434SDavid Howells }
3518667d434SDavid Howells 
3528667d434SDavid Howells /*
3538667d434SDavid Howells  * Poll for culling state
3548667d434SDavid Howells  * - use EPOLLOUT to indicate culling state
3558667d434SDavid Howells  */
cachefiles_daemon_poll(struct file * file,struct poll_table_struct * poll)3568667d434SDavid Howells static __poll_t cachefiles_daemon_poll(struct file *file,
3578667d434SDavid Howells 					   struct poll_table_struct *poll)
3588667d434SDavid Howells {
3598667d434SDavid Howells 	struct cachefiles_cache *cache = file->private_data;
3608667d434SDavid Howells 	__poll_t mask;
3618667d434SDavid Howells 
3628667d434SDavid Howells 	poll_wait(file, &cache->daemon_pollwq, poll);
3638667d434SDavid Howells 	mask = 0;
3648667d434SDavid Howells 
365c8383054SJeffle Xu 	if (cachefiles_in_ondemand_mode(cache)) {
366c8383054SJeffle Xu 		if (!xa_empty(&cache->reqs))
367c8383054SJeffle Xu 			mask |= EPOLLIN;
368c8383054SJeffle Xu 	} else {
3698667d434SDavid Howells 		if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
3708667d434SDavid Howells 			mask |= EPOLLIN;
371c8383054SJeffle Xu 	}
3728667d434SDavid Howells 
3738667d434SDavid Howells 	if (test_bit(CACHEFILES_CULLING, &cache->flags))
3748667d434SDavid Howells 		mask |= EPOLLOUT;
3758667d434SDavid Howells 
3768667d434SDavid Howells 	return mask;
3778667d434SDavid Howells }
3788667d434SDavid Howells 
3798667d434SDavid Howells /*
3808667d434SDavid Howells  * Give a range error for cache space constraints
3818667d434SDavid Howells  * - can be tail-called
3828667d434SDavid Howells  */
cachefiles_daemon_range_error(struct cachefiles_cache * cache,char * args)3838667d434SDavid Howells static int cachefiles_daemon_range_error(struct cachefiles_cache *cache,
3848667d434SDavid Howells 					 char *args)
3858667d434SDavid Howells {
3868667d434SDavid Howells 	pr_err("Free space limits must be in range 0%%<=stop<cull<run<100%%\n");
3878667d434SDavid Howells 
3888667d434SDavid Howells 	return -EINVAL;
3898667d434SDavid Howells }
3908667d434SDavid Howells 
3918667d434SDavid Howells /*
3928667d434SDavid Howells  * Set the percentage of files at which to stop culling
3938667d434SDavid Howells  * - command: "frun <N>%"
3948667d434SDavid Howells  */
cachefiles_daemon_frun(struct cachefiles_cache * cache,char * args)3958667d434SDavid Howells static int cachefiles_daemon_frun(struct cachefiles_cache *cache, char *args)
3968667d434SDavid Howells {
3978667d434SDavid Howells 	unsigned long frun;
3988667d434SDavid Howells 
3998667d434SDavid Howells 	_enter(",%s", args);
4008667d434SDavid Howells 
4018667d434SDavid Howells 	if (!*args)
4028667d434SDavid Howells 		return -EINVAL;
4038667d434SDavid Howells 
4048667d434SDavid Howells 	frun = simple_strtoul(args, &args, 10);
4058667d434SDavid Howells 	if (args[0] != '%' || args[1] != '\0')
4068667d434SDavid Howells 		return -EINVAL;
4078667d434SDavid Howells 
4088667d434SDavid Howells 	if (frun <= cache->fcull_percent || frun >= 100)
4098667d434SDavid Howells 		return cachefiles_daemon_range_error(cache, args);
4108667d434SDavid Howells 
4118667d434SDavid Howells 	cache->frun_percent = frun;
4128667d434SDavid Howells 	return 0;
4138667d434SDavid Howells }
4148667d434SDavid Howells 
4158667d434SDavid Howells /*
4168667d434SDavid Howells  * Set the percentage of files at which to start culling
4178667d434SDavid Howells  * - command: "fcull <N>%"
4188667d434SDavid Howells  */
cachefiles_daemon_fcull(struct cachefiles_cache * cache,char * args)4198667d434SDavid Howells static int cachefiles_daemon_fcull(struct cachefiles_cache *cache, char *args)
4208667d434SDavid Howells {
4218667d434SDavid Howells 	unsigned long fcull;
4228667d434SDavid Howells 
4238667d434SDavid Howells 	_enter(",%s", args);
4248667d434SDavid Howells 
4258667d434SDavid Howells 	if (!*args)
4268667d434SDavid Howells 		return -EINVAL;
4278667d434SDavid Howells 
4288667d434SDavid Howells 	fcull = simple_strtoul(args, &args, 10);
4298667d434SDavid Howells 	if (args[0] != '%' || args[1] != '\0')
4308667d434SDavid Howells 		return -EINVAL;
4318667d434SDavid Howells 
4328667d434SDavid Howells 	if (fcull <= cache->fstop_percent || fcull >= cache->frun_percent)
4338667d434SDavid Howells 		return cachefiles_daemon_range_error(cache, args);
4348667d434SDavid Howells 
4358667d434SDavid Howells 	cache->fcull_percent = fcull;
4368667d434SDavid Howells 	return 0;
4378667d434SDavid Howells }
4388667d434SDavid Howells 
4398667d434SDavid Howells /*
4408667d434SDavid Howells  * Set the percentage of files at which to stop allocating
4418667d434SDavid Howells  * - command: "fstop <N>%"
4428667d434SDavid Howells  */
cachefiles_daemon_fstop(struct cachefiles_cache * cache,char * args)4438667d434SDavid Howells static int cachefiles_daemon_fstop(struct cachefiles_cache *cache, char *args)
4448667d434SDavid Howells {
4458667d434SDavid Howells 	unsigned long fstop;
4468667d434SDavid Howells 
4478667d434SDavid Howells 	_enter(",%s", args);
4488667d434SDavid Howells 
4498667d434SDavid Howells 	if (!*args)
4508667d434SDavid Howells 		return -EINVAL;
4518667d434SDavid Howells 
4528667d434SDavid Howells 	fstop = simple_strtoul(args, &args, 10);
4538667d434SDavid Howells 	if (args[0] != '%' || args[1] != '\0')
4548667d434SDavid Howells 		return -EINVAL;
4558667d434SDavid Howells 
4568667d434SDavid Howells 	if (fstop >= cache->fcull_percent)
4578667d434SDavid Howells 		return cachefiles_daemon_range_error(cache, args);
4588667d434SDavid Howells 
4598667d434SDavid Howells 	cache->fstop_percent = fstop;
4608667d434SDavid Howells 	return 0;
4618667d434SDavid Howells }
4628667d434SDavid Howells 
4638667d434SDavid Howells /*
4648667d434SDavid Howells  * Set the percentage of blocks at which to stop culling
4658667d434SDavid Howells  * - command: "brun <N>%"
4668667d434SDavid Howells  */
cachefiles_daemon_brun(struct cachefiles_cache * cache,char * args)4678667d434SDavid Howells static int cachefiles_daemon_brun(struct cachefiles_cache *cache, char *args)
4688667d434SDavid Howells {
4698667d434SDavid Howells 	unsigned long brun;
4708667d434SDavid Howells 
4718667d434SDavid Howells 	_enter(",%s", args);
4728667d434SDavid Howells 
4738667d434SDavid Howells 	if (!*args)
4748667d434SDavid Howells 		return -EINVAL;
4758667d434SDavid Howells 
4768667d434SDavid Howells 	brun = simple_strtoul(args, &args, 10);
4778667d434SDavid Howells 	if (args[0] != '%' || args[1] != '\0')
4788667d434SDavid Howells 		return -EINVAL;
4798667d434SDavid Howells 
4808667d434SDavid Howells 	if (brun <= cache->bcull_percent || brun >= 100)
4818667d434SDavid Howells 		return cachefiles_daemon_range_error(cache, args);
4828667d434SDavid Howells 
4838667d434SDavid Howells 	cache->brun_percent = brun;
4848667d434SDavid Howells 	return 0;
4858667d434SDavid Howells }
4868667d434SDavid Howells 
4878667d434SDavid Howells /*
4888667d434SDavid Howells  * Set the percentage of blocks at which to start culling
4898667d434SDavid Howells  * - command: "bcull <N>%"
4908667d434SDavid Howells  */
cachefiles_daemon_bcull(struct cachefiles_cache * cache,char * args)4918667d434SDavid Howells static int cachefiles_daemon_bcull(struct cachefiles_cache *cache, char *args)
4928667d434SDavid Howells {
4938667d434SDavid Howells 	unsigned long bcull;
4948667d434SDavid Howells 
4958667d434SDavid Howells 	_enter(",%s", args);
4968667d434SDavid Howells 
4978667d434SDavid Howells 	if (!*args)
4988667d434SDavid Howells 		return -EINVAL;
4998667d434SDavid Howells 
5008667d434SDavid Howells 	bcull = simple_strtoul(args, &args, 10);
5018667d434SDavid Howells 	if (args[0] != '%' || args[1] != '\0')
5028667d434SDavid Howells 		return -EINVAL;
5038667d434SDavid Howells 
5048667d434SDavid Howells 	if (bcull <= cache->bstop_percent || bcull >= cache->brun_percent)
5058667d434SDavid Howells 		return cachefiles_daemon_range_error(cache, args);
5068667d434SDavid Howells 
5078667d434SDavid Howells 	cache->bcull_percent = bcull;
5088667d434SDavid Howells 	return 0;
5098667d434SDavid Howells }
5108667d434SDavid Howells 
5118667d434SDavid Howells /*
5128667d434SDavid Howells  * Set the percentage of blocks at which to stop allocating
5138667d434SDavid Howells  * - command: "bstop <N>%"
5148667d434SDavid Howells  */
cachefiles_daemon_bstop(struct cachefiles_cache * cache,char * args)5158667d434SDavid Howells static int cachefiles_daemon_bstop(struct cachefiles_cache *cache, char *args)
5168667d434SDavid Howells {
5178667d434SDavid Howells 	unsigned long bstop;
5188667d434SDavid Howells 
5198667d434SDavid Howells 	_enter(",%s", args);
5208667d434SDavid Howells 
5218667d434SDavid Howells 	if (!*args)
5228667d434SDavid Howells 		return -EINVAL;
5238667d434SDavid Howells 
5248667d434SDavid Howells 	bstop = simple_strtoul(args, &args, 10);
5258667d434SDavid Howells 	if (args[0] != '%' || args[1] != '\0')
5268667d434SDavid Howells 		return -EINVAL;
5278667d434SDavid Howells 
5288667d434SDavid Howells 	if (bstop >= cache->bcull_percent)
5298667d434SDavid Howells 		return cachefiles_daemon_range_error(cache, args);
5308667d434SDavid Howells 
5318667d434SDavid Howells 	cache->bstop_percent = bstop;
5328667d434SDavid Howells 	return 0;
5338667d434SDavid Howells }
5348667d434SDavid Howells 
5358667d434SDavid Howells /*
5368667d434SDavid Howells  * Set the cache directory
5378667d434SDavid Howells  * - command: "dir <name>"
5388667d434SDavid Howells  */
cachefiles_daemon_dir(struct cachefiles_cache * cache,char * args)5398667d434SDavid Howells static int cachefiles_daemon_dir(struct cachefiles_cache *cache, char *args)
5408667d434SDavid Howells {
5418667d434SDavid Howells 	char *dir;
5428667d434SDavid Howells 
5438667d434SDavid Howells 	_enter(",%s", args);
5448667d434SDavid Howells 
5458667d434SDavid Howells 	if (!*args) {
5468667d434SDavid Howells 		pr_err("Empty directory specified\n");
5478667d434SDavid Howells 		return -EINVAL;
5488667d434SDavid Howells 	}
5498667d434SDavid Howells 
5508667d434SDavid Howells 	if (cache->rootdirname) {
5518667d434SDavid Howells 		pr_err("Second cache directory specified\n");
5528667d434SDavid Howells 		return -EEXIST;
5538667d434SDavid Howells 	}
5548667d434SDavid Howells 
5558667d434SDavid Howells 	dir = kstrdup(args, GFP_KERNEL);
5568667d434SDavid Howells 	if (!dir)
5578667d434SDavid Howells 		return -ENOMEM;
5588667d434SDavid Howells 
5598667d434SDavid Howells 	cache->rootdirname = dir;
5608667d434SDavid Howells 	return 0;
5618667d434SDavid Howells }
5628667d434SDavid Howells 
5638667d434SDavid Howells /*
5648667d434SDavid Howells  * Set the cache security context
5658667d434SDavid Howells  * - command: "secctx <ctx>"
5668667d434SDavid Howells  */
cachefiles_daemon_secctx(struct cachefiles_cache * cache,char * args)5678667d434SDavid Howells static int cachefiles_daemon_secctx(struct cachefiles_cache *cache, char *args)
5688667d434SDavid Howells {
5698667d434SDavid Howells 	char *secctx;
5708667d434SDavid Howells 
5718667d434SDavid Howells 	_enter(",%s", args);
5728667d434SDavid Howells 
5738667d434SDavid Howells 	if (!*args) {
5748667d434SDavid Howells 		pr_err("Empty security context specified\n");
5758667d434SDavid Howells 		return -EINVAL;
5768667d434SDavid Howells 	}
5778667d434SDavid Howells 
5788667d434SDavid Howells 	if (cache->secctx) {
5798667d434SDavid Howells 		pr_err("Second security context specified\n");
5808667d434SDavid Howells 		return -EINVAL;
5818667d434SDavid Howells 	}
5828667d434SDavid Howells 
5838667d434SDavid Howells 	secctx = kstrdup(args, GFP_KERNEL);
5848667d434SDavid Howells 	if (!secctx)
5858667d434SDavid Howells 		return -ENOMEM;
5868667d434SDavid Howells 
5878667d434SDavid Howells 	cache->secctx = secctx;
5888667d434SDavid Howells 	return 0;
5898667d434SDavid Howells }
5908667d434SDavid Howells 
5918667d434SDavid Howells /*
5928667d434SDavid Howells  * Set the cache tag
5938667d434SDavid Howells  * - command: "tag <name>"
5948667d434SDavid Howells  */
cachefiles_daemon_tag(struct cachefiles_cache * cache,char * args)5958667d434SDavid Howells static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args)
5968667d434SDavid Howells {
5978667d434SDavid Howells 	char *tag;
5988667d434SDavid Howells 
5998667d434SDavid Howells 	_enter(",%s", args);
6008667d434SDavid Howells 
6018667d434SDavid Howells 	if (!*args) {
6028667d434SDavid Howells 		pr_err("Empty tag specified\n");
6038667d434SDavid Howells 		return -EINVAL;
6048667d434SDavid Howells 	}
6058667d434SDavid Howells 
6068667d434SDavid Howells 	if (cache->tag)
6078667d434SDavid Howells 		return -EEXIST;
6088667d434SDavid Howells 
6098667d434SDavid Howells 	tag = kstrdup(args, GFP_KERNEL);
6108667d434SDavid Howells 	if (!tag)
6118667d434SDavid Howells 		return -ENOMEM;
6128667d434SDavid Howells 
6138667d434SDavid Howells 	cache->tag = tag;
6148667d434SDavid Howells 	return 0;
6158667d434SDavid Howells }
6168667d434SDavid Howells 
6178667d434SDavid Howells /*
6188667d434SDavid Howells  * Request a node in the cache be culled from the current working directory
6198667d434SDavid Howells  * - command: "cull <name>"
6208667d434SDavid Howells  */
cachefiles_daemon_cull(struct cachefiles_cache * cache,char * args)6218667d434SDavid Howells static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args)
6228667d434SDavid Howells {
6238667d434SDavid Howells 	struct path path;
6248667d434SDavid Howells 	const struct cred *saved_cred;
6258667d434SDavid Howells 	int ret;
6268667d434SDavid Howells 
6278667d434SDavid Howells 	_enter(",%s", args);
6288667d434SDavid Howells 
6298667d434SDavid Howells 	if (strchr(args, '/'))
6308667d434SDavid Howells 		goto inval;
6318667d434SDavid Howells 
6328667d434SDavid Howells 	if (!test_bit(CACHEFILES_READY, &cache->flags)) {
6338667d434SDavid Howells 		pr_err("cull applied to unready cache\n");
6348667d434SDavid Howells 		return -EIO;
6358667d434SDavid Howells 	}
6368667d434SDavid Howells 
6378667d434SDavid Howells 	if (test_bit(CACHEFILES_DEAD, &cache->flags)) {
6388667d434SDavid Howells 		pr_err("cull applied to dead cache\n");
6398667d434SDavid Howells 		return -EIO;
6408667d434SDavid Howells 	}
6418667d434SDavid Howells 
6428667d434SDavid Howells 	get_fs_pwd(current->fs, &path);
6438667d434SDavid Howells 
6448667d434SDavid Howells 	if (!d_can_lookup(path.dentry))
6458667d434SDavid Howells 		goto notdir;
6468667d434SDavid Howells 
6478667d434SDavid Howells 	cachefiles_begin_secure(cache, &saved_cred);
64807a90e97SDavid Howells 	ret = cachefiles_cull(cache, path.dentry, args);
6498667d434SDavid Howells 	cachefiles_end_secure(cache, saved_cred);
6508667d434SDavid Howells 
6518667d434SDavid Howells 	path_put(&path);
6528667d434SDavid Howells 	_leave(" = %d", ret);
6538667d434SDavid Howells 	return ret;
6548667d434SDavid Howells 
6558667d434SDavid Howells notdir:
6568667d434SDavid Howells 	path_put(&path);
6578667d434SDavid Howells 	pr_err("cull command requires dirfd to be a directory\n");
6588667d434SDavid Howells 	return -ENOTDIR;
6598667d434SDavid Howells 
6608667d434SDavid Howells inval:
6618667d434SDavid Howells 	pr_err("cull command requires dirfd and filename\n");
6628667d434SDavid Howells 	return -EINVAL;
6638667d434SDavid Howells }
6648667d434SDavid Howells 
6658667d434SDavid Howells /*
6668667d434SDavid Howells  * Set debugging mode
6678667d434SDavid Howells  * - command: "debug <mask>"
6688667d434SDavid Howells  */
cachefiles_daemon_debug(struct cachefiles_cache * cache,char * args)6698667d434SDavid Howells static int cachefiles_daemon_debug(struct cachefiles_cache *cache, char *args)
6708667d434SDavid Howells {
6718667d434SDavid Howells 	unsigned long mask;
6728667d434SDavid Howells 
6738667d434SDavid Howells 	_enter(",%s", args);
6748667d434SDavid Howells 
6758667d434SDavid Howells 	mask = simple_strtoul(args, &args, 0);
6768667d434SDavid Howells 	if (args[0] != '\0')
6778667d434SDavid Howells 		goto inval;
6788667d434SDavid Howells 
6798667d434SDavid Howells 	cachefiles_debug = mask;
6808667d434SDavid Howells 	_leave(" = 0");
6818667d434SDavid Howells 	return 0;
6828667d434SDavid Howells 
6838667d434SDavid Howells inval:
6848667d434SDavid Howells 	pr_err("debug command requires mask\n");
6858667d434SDavid Howells 	return -EINVAL;
6868667d434SDavid Howells }
6878667d434SDavid Howells 
6888667d434SDavid Howells /*
6898667d434SDavid Howells  * Find out whether an object in the current working directory is in use or not
6908667d434SDavid Howells  * - command: "inuse <name>"
6918667d434SDavid Howells  */
cachefiles_daemon_inuse(struct cachefiles_cache * cache,char * args)6928667d434SDavid Howells static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args)
6938667d434SDavid Howells {
6948667d434SDavid Howells 	struct path path;
6958667d434SDavid Howells 	const struct cred *saved_cred;
6968667d434SDavid Howells 	int ret;
6978667d434SDavid Howells 
6988667d434SDavid Howells 	//_enter(",%s", args);
6998667d434SDavid Howells 
7008667d434SDavid Howells 	if (strchr(args, '/'))
7018667d434SDavid Howells 		goto inval;
7028667d434SDavid Howells 
7038667d434SDavid Howells 	if (!test_bit(CACHEFILES_READY, &cache->flags)) {
7048667d434SDavid Howells 		pr_err("inuse applied to unready cache\n");
7058667d434SDavid Howells 		return -EIO;
7068667d434SDavid Howells 	}
7078667d434SDavid Howells 
7088667d434SDavid Howells 	if (test_bit(CACHEFILES_DEAD, &cache->flags)) {
7098667d434SDavid Howells 		pr_err("inuse applied to dead cache\n");
7108667d434SDavid Howells 		return -EIO;
7118667d434SDavid Howells 	}
7128667d434SDavid Howells 
7138667d434SDavid Howells 	get_fs_pwd(current->fs, &path);
7148667d434SDavid Howells 
7158667d434SDavid Howells 	if (!d_can_lookup(path.dentry))
7168667d434SDavid Howells 		goto notdir;
7178667d434SDavid Howells 
7188667d434SDavid Howells 	cachefiles_begin_secure(cache, &saved_cred);
71907a90e97SDavid Howells 	ret = cachefiles_check_in_use(cache, path.dentry, args);
7208667d434SDavid Howells 	cachefiles_end_secure(cache, saved_cred);
7218667d434SDavid Howells 
7228667d434SDavid Howells 	path_put(&path);
7238667d434SDavid Howells 	//_leave(" = %d", ret);
7248667d434SDavid Howells 	return ret;
7258667d434SDavid Howells 
7268667d434SDavid Howells notdir:
7278667d434SDavid Howells 	path_put(&path);
7288667d434SDavid Howells 	pr_err("inuse command requires dirfd to be a directory\n");
7298667d434SDavid Howells 	return -ENOTDIR;
7308667d434SDavid Howells 
7318667d434SDavid Howells inval:
7328667d434SDavid Howells 	pr_err("inuse command requires dirfd and filename\n");
7338667d434SDavid Howells 	return -EINVAL;
7348667d434SDavid Howells }
7358667d434SDavid Howells 
7368667d434SDavid Howells /*
7378667d434SDavid Howells  * Bind a directory as a cache
7388667d434SDavid Howells  */
cachefiles_daemon_bind(struct cachefiles_cache * cache,char * args)7398667d434SDavid Howells static int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args)
7408667d434SDavid Howells {
7418667d434SDavid Howells 	_enter("{%u,%u,%u,%u,%u,%u},%s",
7428667d434SDavid Howells 	       cache->frun_percent,
7438667d434SDavid Howells 	       cache->fcull_percent,
7448667d434SDavid Howells 	       cache->fstop_percent,
7458667d434SDavid Howells 	       cache->brun_percent,
7468667d434SDavid Howells 	       cache->bcull_percent,
7478667d434SDavid Howells 	       cache->bstop_percent,
7488667d434SDavid Howells 	       args);
7498667d434SDavid Howells 
7508667d434SDavid Howells 	if (cache->fstop_percent >= cache->fcull_percent ||
7518667d434SDavid Howells 	    cache->fcull_percent >= cache->frun_percent ||
7528667d434SDavid Howells 	    cache->frun_percent  >= 100)
7538667d434SDavid Howells 		return -ERANGE;
7548667d434SDavid Howells 
7558667d434SDavid Howells 	if (cache->bstop_percent >= cache->bcull_percent ||
7568667d434SDavid Howells 	    cache->bcull_percent >= cache->brun_percent ||
7578667d434SDavid Howells 	    cache->brun_percent  >= 100)
7588667d434SDavid Howells 		return -ERANGE;
7598667d434SDavid Howells 
7608667d434SDavid Howells 	if (!cache->rootdirname) {
7618667d434SDavid Howells 		pr_err("No cache directory specified\n");
7628667d434SDavid Howells 		return -EINVAL;
7638667d434SDavid Howells 	}
7648667d434SDavid Howells 
7658667d434SDavid Howells 	/* Don't permit already bound caches to be re-bound */
7668667d434SDavid Howells 	if (test_bit(CACHEFILES_READY, &cache->flags)) {
7678667d434SDavid Howells 		pr_err("Cache already bound\n");
7688667d434SDavid Howells 		return -EBUSY;
7698667d434SDavid Howells 	}
7708667d434SDavid Howells 
7714e4f1788SJeffle Xu 	if (IS_ENABLED(CONFIG_CACHEFILES_ONDEMAND)) {
7724e4f1788SJeffle Xu 		if (!strcmp(args, "ondemand")) {
7734e4f1788SJeffle Xu 			set_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags);
7744e4f1788SJeffle Xu 		} else if (*args) {
7754e4f1788SJeffle Xu 			pr_err("Invalid argument to the 'bind' command\n");
7764e4f1788SJeffle Xu 			return -EINVAL;
7774e4f1788SJeffle Xu 		}
7784e4f1788SJeffle Xu 	} else if (*args) {
7794e4f1788SJeffle Xu 		pr_err("'bind' command doesn't take an argument\n");
7804e4f1788SJeffle Xu 		return -EINVAL;
7814e4f1788SJeffle Xu 	}
7824e4f1788SJeffle Xu 
783c7ca7315SJeffle Xu 	/* Make sure we have copies of the tag string */
784c7ca7315SJeffle Xu 	if (!cache->tag) {
785c7ca7315SJeffle Xu 		/*
786c7ca7315SJeffle Xu 		 * The tag string is released by the fops->release()
787c7ca7315SJeffle Xu 		 * function, so we don't release it on error here
788c7ca7315SJeffle Xu 		 */
789c7ca7315SJeffle Xu 		cache->tag = kstrdup("CacheFiles", GFP_KERNEL);
790c7ca7315SJeffle Xu 		if (!cache->tag)
791c7ca7315SJeffle Xu 			return -ENOMEM;
792c7ca7315SJeffle Xu 	}
793c7ca7315SJeffle Xu 
794ecd1a5f6SDavid Howells 	return cachefiles_add_cache(cache);
7958667d434SDavid Howells }
7968667d434SDavid Howells 
7978667d434SDavid Howells /*
7988667d434SDavid Howells  * Unbind a cache.
7998667d434SDavid Howells  */
cachefiles_daemon_unbind(struct cachefiles_cache * cache)8008667d434SDavid Howells static void cachefiles_daemon_unbind(struct cachefiles_cache *cache)
8018667d434SDavid Howells {
8028667d434SDavid Howells 	_enter("");
8038667d434SDavid Howells 
804d1065b0aSDavid Howells 	if (test_bit(CACHEFILES_READY, &cache->flags))
805d1065b0aSDavid Howells 		cachefiles_withdraw_cache(cache);
8068667d434SDavid Howells 
807d1065b0aSDavid Howells 	cachefiles_put_directory(cache->graveyard);
808d1065b0aSDavid Howells 	cachefiles_put_directory(cache->store);
8098667d434SDavid Howells 	mntput(cache->mnt);
81038e92161SBaokun Li 	put_cred(cache->cache_cred);
8118667d434SDavid Howells 
8128667d434SDavid Howells 	kfree(cache->rootdirname);
8138667d434SDavid Howells 	kfree(cache->secctx);
8148667d434SDavid Howells 	kfree(cache->tag);
8158667d434SDavid Howells 
8168667d434SDavid Howells 	_leave("");
8178667d434SDavid Howells }
818