]>
Commit | Line | Data |
---|---|---|
9ae326a6 DH |
1 | /* Bind and unbind a cache from the filesystem backing it |
2 | * | |
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
4 | * Written by David Howells ([email protected]) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public Licence | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the Licence, or (at your option) any later version. | |
10 | */ | |
11 | ||
12 | #include <linux/module.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/completion.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/fs.h> | |
18 | #include <linux/file.h> | |
19 | #include <linux/namei.h> | |
20 | #include <linux/mount.h> | |
21 | #include <linux/statfs.h> | |
22 | #include <linux/ctype.h> | |
5d6c3191 | 23 | #include <linux/xattr.h> |
9ae326a6 DH |
24 | #include "internal.h" |
25 | ||
26 | static int cachefiles_daemon_add_cache(struct cachefiles_cache *caches); | |
27 | ||
28 | /* | |
29 | * bind a directory as a cache | |
30 | */ | |
31 | int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args) | |
32 | { | |
33 | _enter("{%u,%u,%u,%u,%u,%u},%s", | |
34 | cache->frun_percent, | |
35 | cache->fcull_percent, | |
36 | cache->fstop_percent, | |
37 | cache->brun_percent, | |
38 | cache->bcull_percent, | |
39 | cache->bstop_percent, | |
40 | args); | |
41 | ||
42 | /* start by checking things over */ | |
43 | ASSERT(cache->fstop_percent >= 0 && | |
44 | cache->fstop_percent < cache->fcull_percent && | |
45 | cache->fcull_percent < cache->frun_percent && | |
46 | cache->frun_percent < 100); | |
47 | ||
48 | ASSERT(cache->bstop_percent >= 0 && | |
49 | cache->bstop_percent < cache->bcull_percent && | |
50 | cache->bcull_percent < cache->brun_percent && | |
51 | cache->brun_percent < 100); | |
52 | ||
53 | if (*args) { | |
6ff66ac7 | 54 | pr_err("'bind' command doesn't take an argument\n"); |
9ae326a6 DH |
55 | return -EINVAL; |
56 | } | |
57 | ||
58 | if (!cache->rootdirname) { | |
6ff66ac7 | 59 | pr_err("No cache directory specified\n"); |
9ae326a6 DH |
60 | return -EINVAL; |
61 | } | |
62 | ||
63 | /* don't permit already bound caches to be re-bound */ | |
64 | if (test_bit(CACHEFILES_READY, &cache->flags)) { | |
6ff66ac7 | 65 | pr_err("Cache already bound\n"); |
9ae326a6 DH |
66 | return -EBUSY; |
67 | } | |
68 | ||
69 | /* make sure we have copies of the tag and dirname strings */ | |
70 | if (!cache->tag) { | |
71 | /* the tag string is released by the fops->release() | |
72 | * function, so we don't release it on error here */ | |
73 | cache->tag = kstrdup("CacheFiles", GFP_KERNEL); | |
74 | if (!cache->tag) | |
75 | return -ENOMEM; | |
76 | } | |
77 | ||
78 | /* add the cache */ | |
79 | return cachefiles_daemon_add_cache(cache); | |
80 | } | |
81 | ||
82 | /* | |
83 | * add a cache | |
84 | */ | |
85 | static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache) | |
86 | { | |
87 | struct cachefiles_object *fsdef; | |
b0446be4 | 88 | struct path path; |
9ae326a6 DH |
89 | struct kstatfs stats; |
90 | struct dentry *graveyard, *cachedir, *root; | |
91 | const struct cred *saved_cred; | |
92 | int ret; | |
93 | ||
94 | _enter(""); | |
95 | ||
96 | /* we want to work under the module's security ID */ | |
97 | ret = cachefiles_get_security_ID(cache); | |
98 | if (ret < 0) | |
99 | return ret; | |
100 | ||
101 | cachefiles_begin_secure(cache, &saved_cred); | |
102 | ||
103 | /* allocate the root index object */ | |
104 | ret = -ENOMEM; | |
105 | ||
106 | fsdef = kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL); | |
107 | if (!fsdef) | |
108 | goto error_root_object; | |
109 | ||
110 | ASSERTCMP(fsdef->backer, ==, NULL); | |
111 | ||
112 | atomic_set(&fsdef->usage, 1); | |
113 | fsdef->type = FSCACHE_COOKIE_TYPE_INDEX; | |
114 | ||
115 | _debug("- fsdef %p", fsdef); | |
116 | ||
117 | /* look up the directory at the root of the cache */ | |
b0446be4 | 118 | ret = kern_path(cache->rootdirname, LOOKUP_DIRECTORY, &path); |
9ae326a6 DH |
119 | if (ret < 0) |
120 | goto error_open_root; | |
121 | ||
b0446be4 AV |
122 | cache->mnt = path.mnt; |
123 | root = path.dentry; | |
9ae326a6 DH |
124 | |
125 | /* check parameters */ | |
126 | ret = -EOPNOTSUPP; | |
466b77bc DH |
127 | if (d_is_negative(root) || |
128 | !d_backing_inode(root)->i_op->lookup || | |
129 | !d_backing_inode(root)->i_op->mkdir || | |
5d6c3191 | 130 | !(d_backing_inode(root)->i_opflags & IOP_XATTR) || |
9ae326a6 DH |
131 | !root->d_sb->s_op->statfs || |
132 | !root->d_sb->s_op->sync_fs) | |
133 | goto error_unsupported; | |
134 | ||
135 | ret = -EROFS; | |
bc98a42c | 136 | if (sb_rdonly(root->d_sb)) |
9ae326a6 DH |
137 | goto error_unsupported; |
138 | ||
139 | /* determine the security of the on-disk cache as this governs | |
140 | * security ID of files we create */ | |
141 | ret = cachefiles_determine_cache_security(cache, root, &saved_cred); | |
142 | if (ret < 0) | |
143 | goto error_unsupported; | |
144 | ||
145 | /* get the cache size and blocksize */ | |
ebabe9a9 | 146 | ret = vfs_statfs(&path, &stats); |
9ae326a6 DH |
147 | if (ret < 0) |
148 | goto error_unsupported; | |
149 | ||
150 | ret = -ERANGE; | |
151 | if (stats.f_bsize <= 0) | |
152 | goto error_unsupported; | |
153 | ||
154 | ret = -EOPNOTSUPP; | |
155 | if (stats.f_bsize > PAGE_SIZE) | |
156 | goto error_unsupported; | |
157 | ||
158 | cache->bsize = stats.f_bsize; | |
159 | cache->bshift = 0; | |
160 | if (stats.f_bsize < PAGE_SIZE) | |
161 | cache->bshift = PAGE_SHIFT - ilog2(stats.f_bsize); | |
162 | ||
163 | _debug("blksize %u (shift %u)", | |
164 | cache->bsize, cache->bshift); | |
165 | ||
166 | _debug("size %llu, avail %llu", | |
167 | (unsigned long long) stats.f_blocks, | |
168 | (unsigned long long) stats.f_bavail); | |
169 | ||
170 | /* set up caching limits */ | |
171 | do_div(stats.f_files, 100); | |
172 | cache->fstop = stats.f_files * cache->fstop_percent; | |
173 | cache->fcull = stats.f_files * cache->fcull_percent; | |
174 | cache->frun = stats.f_files * cache->frun_percent; | |
175 | ||
176 | _debug("limits {%llu,%llu,%llu} files", | |
177 | (unsigned long long) cache->frun, | |
178 | (unsigned long long) cache->fcull, | |
179 | (unsigned long long) cache->fstop); | |
180 | ||
181 | stats.f_blocks >>= cache->bshift; | |
182 | do_div(stats.f_blocks, 100); | |
183 | cache->bstop = stats.f_blocks * cache->bstop_percent; | |
184 | cache->bcull = stats.f_blocks * cache->bcull_percent; | |
185 | cache->brun = stats.f_blocks * cache->brun_percent; | |
186 | ||
187 | _debug("limits {%llu,%llu,%llu} blocks", | |
188 | (unsigned long long) cache->brun, | |
189 | (unsigned long long) cache->bcull, | |
190 | (unsigned long long) cache->bstop); | |
191 | ||
192 | /* get the cache directory and check its type */ | |
193 | cachedir = cachefiles_get_directory(cache, root, "cache"); | |
194 | if (IS_ERR(cachedir)) { | |
195 | ret = PTR_ERR(cachedir); | |
196 | goto error_unsupported; | |
197 | } | |
198 | ||
199 | fsdef->dentry = cachedir; | |
200 | fsdef->fscache.cookie = NULL; | |
201 | ||
202 | ret = cachefiles_check_object_type(fsdef); | |
203 | if (ret < 0) | |
204 | goto error_unsupported; | |
205 | ||
206 | /* get the graveyard directory */ | |
207 | graveyard = cachefiles_get_directory(cache, root, "graveyard"); | |
208 | if (IS_ERR(graveyard)) { | |
209 | ret = PTR_ERR(graveyard); | |
210 | goto error_unsupported; | |
211 | } | |
212 | ||
213 | cache->graveyard = graveyard; | |
214 | ||
215 | /* publish the cache */ | |
216 | fscache_init_cache(&cache->cache, | |
217 | &cachefiles_cache_ops, | |
218 | "%s", | |
219 | fsdef->dentry->d_sb->s_id); | |
220 | ||
f29507ce KKM |
221 | fscache_object_init(&fsdef->fscache, &fscache_fsdef_index, |
222 | &cache->cache); | |
9ae326a6 DH |
223 | |
224 | ret = fscache_add_cache(&cache->cache, &fsdef->fscache, cache->tag); | |
225 | if (ret < 0) | |
226 | goto error_add_cache; | |
227 | ||
228 | /* done */ | |
229 | set_bit(CACHEFILES_READY, &cache->flags); | |
230 | dput(root); | |
231 | ||
0227d6ab | 232 | pr_info("File cache on %s registered\n", cache->cache.identifier); |
9ae326a6 DH |
233 | |
234 | /* check how much space the cache has */ | |
235 | cachefiles_has_space(cache, 0, 0); | |
236 | cachefiles_end_secure(cache, saved_cred); | |
237 | return 0; | |
238 | ||
239 | error_add_cache: | |
240 | dput(cache->graveyard); | |
241 | cache->graveyard = NULL; | |
242 | error_unsupported: | |
243 | mntput(cache->mnt); | |
244 | cache->mnt = NULL; | |
245 | dput(fsdef->dentry); | |
246 | fsdef->dentry = NULL; | |
247 | dput(root); | |
248 | error_open_root: | |
249 | kmem_cache_free(cachefiles_object_jar, fsdef); | |
250 | error_root_object: | |
251 | cachefiles_end_secure(cache, saved_cred); | |
6ff66ac7 | 252 | pr_err("Failed to register: %d\n", ret); |
9ae326a6 DH |
253 | return ret; |
254 | } | |
255 | ||
256 | /* | |
257 | * unbind a cache on fd release | |
258 | */ | |
259 | void cachefiles_daemon_unbind(struct cachefiles_cache *cache) | |
260 | { | |
261 | _enter(""); | |
262 | ||
263 | if (test_bit(CACHEFILES_READY, &cache->flags)) { | |
0227d6ab | 264 | pr_info("File cache on %s unregistering\n", |
4e1eb883 | 265 | cache->cache.identifier); |
9ae326a6 DH |
266 | |
267 | fscache_withdraw_cache(&cache->cache); | |
268 | } | |
269 | ||
270 | dput(cache->graveyard); | |
271 | mntput(cache->mnt); | |
272 | ||
273 | kfree(cache->rootdirname); | |
274 | kfree(cache->secctx); | |
275 | kfree(cache->tag); | |
276 | ||
277 | _leave(""); | |
278 | } |