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