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