diff options
Diffstat (limited to 'fs/cachefiles/bind.c')
-rw-r--r-- | fs/cachefiles/bind.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/fs/cachefiles/bind.c b/fs/cachefiles/bind.c new file mode 100644 index 000000000000..3797e0077b35 --- /dev/null +++ b/fs/cachefiles/bind.c | |||
@@ -0,0 +1,286 @@ | |||
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 (dhowells@redhat.com) | ||
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> | ||
23 | #include "internal.h" | ||
24 | |||
25 | static int cachefiles_daemon_add_cache(struct cachefiles_cache *caches); | ||
26 | |||
27 | /* | ||
28 | * bind a directory as a cache | ||
29 | */ | ||
30 | int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args) | ||
31 | { | ||
32 | _enter("{%u,%u,%u,%u,%u,%u},%s", | ||
33 | cache->frun_percent, | ||
34 | cache->fcull_percent, | ||
35 | cache->fstop_percent, | ||
36 | cache->brun_percent, | ||
37 | cache->bcull_percent, | ||
38 | cache->bstop_percent, | ||
39 | args); | ||
40 | |||
41 | /* start by checking things over */ | ||
42 | ASSERT(cache->fstop_percent >= 0 && | ||
43 | cache->fstop_percent < cache->fcull_percent && | ||
44 | cache->fcull_percent < cache->frun_percent && | ||
45 | cache->frun_percent < 100); | ||
46 | |||
47 | ASSERT(cache->bstop_percent >= 0 && | ||
48 | cache->bstop_percent < cache->bcull_percent && | ||
49 | cache->bcull_percent < cache->brun_percent && | ||
50 | cache->brun_percent < 100); | ||
51 | |||
52 | if (*args) { | ||
53 | kerror("'bind' command doesn't take an argument"); | ||
54 | return -EINVAL; | ||
55 | } | ||
56 | |||
57 | if (!cache->rootdirname) { | ||
58 | kerror("No cache directory specified"); | ||
59 | return -EINVAL; | ||
60 | } | ||
61 | |||
62 | /* don't permit already bound caches to be re-bound */ | ||
63 | if (test_bit(CACHEFILES_READY, &cache->flags)) { | ||
64 | kerror("Cache already bound"); | ||
65 | return -EBUSY; | ||
66 | } | ||
67 | |||
68 | /* make sure we have copies of the tag and dirname strings */ | ||
69 | if (!cache->tag) { | ||
70 | /* the tag string is released by the fops->release() | ||
71 | * function, so we don't release it on error here */ | ||
72 | cache->tag = kstrdup("CacheFiles", GFP_KERNEL); | ||
73 | if (!cache->tag) | ||
74 | return -ENOMEM; | ||
75 | } | ||
76 | |||
77 | /* add the cache */ | ||
78 | return cachefiles_daemon_add_cache(cache); | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * add a cache | ||
83 | */ | ||
84 | static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache) | ||
85 | { | ||
86 | struct cachefiles_object *fsdef; | ||
87 | struct nameidata nd; | ||
88 | struct kstatfs stats; | ||
89 | struct dentry *graveyard, *cachedir, *root; | ||
90 | const struct cred *saved_cred; | ||
91 | int ret; | ||
92 | |||
93 | _enter(""); | ||
94 | |||
95 | /* we want to work under the module's security ID */ | ||
96 | ret = cachefiles_get_security_ID(cache); | ||
97 | if (ret < 0) | ||
98 | return ret; | ||
99 | |||
100 | cachefiles_begin_secure(cache, &saved_cred); | ||
101 | |||
102 | /* allocate the root index object */ | ||
103 | ret = -ENOMEM; | ||
104 | |||
105 | fsdef = kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL); | ||
106 | if (!fsdef) | ||
107 | goto error_root_object; | ||
108 | |||
109 | ASSERTCMP(fsdef->backer, ==, NULL); | ||
110 | |||
111 | atomic_set(&fsdef->usage, 1); | ||
112 | fsdef->type = FSCACHE_COOKIE_TYPE_INDEX; | ||
113 | |||
114 | _debug("- fsdef %p", fsdef); | ||
115 | |||
116 | /* look up the directory at the root of the cache */ | ||
117 | memset(&nd, 0, sizeof(nd)); | ||
118 | |||
119 | ret = path_lookup(cache->rootdirname, LOOKUP_DIRECTORY, &nd); | ||
120 | if (ret < 0) | ||
121 | goto error_open_root; | ||
122 | |||
123 | cache->mnt = mntget(nd.path.mnt); | ||
124 | root = dget(nd.path.dentry); | ||
125 | path_put(&nd.path); | ||
126 | |||
127 | /* check parameters */ | ||
128 | ret = -EOPNOTSUPP; | ||
129 | if (!root->d_inode || | ||
130 | !root->d_inode->i_op || | ||
131 | !root->d_inode->i_op->lookup || | ||
132 | !root->d_inode->i_op->mkdir || | ||
133 | !root->d_inode->i_op->setxattr || | ||
134 | !root->d_inode->i_op->getxattr || | ||
135 | !root->d_sb || | ||
136 | !root->d_sb->s_op || | ||
137 | !root->d_sb->s_op->statfs || | ||
138 | !root->d_sb->s_op->sync_fs) | ||
139 | goto error_unsupported; | ||
140 | |||
141 | ret = -EROFS; | ||
142 | if (root->d_sb->s_flags & MS_RDONLY) | ||
143 | goto error_unsupported; | ||
144 | |||
145 | /* determine the security of the on-disk cache as this governs | ||
146 | * security ID of files we create */ | ||
147 | ret = cachefiles_determine_cache_security(cache, root, &saved_cred); | ||
148 | if (ret < 0) | ||
149 | goto error_unsupported; | ||
150 | |||
151 | /* get the cache size and blocksize */ | ||
152 | ret = vfs_statfs(root, &stats); | ||
153 | if (ret < 0) | ||
154 | goto error_unsupported; | ||
155 | |||
156 | ret = -ERANGE; | ||
157 | if (stats.f_bsize <= 0) | ||
158 | goto error_unsupported; | ||
159 | |||
160 | ret = -EOPNOTSUPP; | ||
161 | if (stats.f_bsize > PAGE_SIZE) | ||
162 | goto error_unsupported; | ||
163 | |||
164 | cache->bsize = stats.f_bsize; | ||
165 | cache->bshift = 0; | ||
166 | if (stats.f_bsize < PAGE_SIZE) | ||
167 | cache->bshift = PAGE_SHIFT - ilog2(stats.f_bsize); | ||
168 | |||
169 | _debug("blksize %u (shift %u)", | ||
170 | cache->bsize, cache->bshift); | ||
171 | |||
172 | _debug("size %llu, avail %llu", | ||
173 | (unsigned long long) stats.f_blocks, | ||
174 | (unsigned long long) stats.f_bavail); | ||
175 | |||
176 | /* set up caching limits */ | ||
177 | do_div(stats.f_files, 100); | ||
178 | cache->fstop = stats.f_files * cache->fstop_percent; | ||
179 | cache->fcull = stats.f_files * cache->fcull_percent; | ||
180 | cache->frun = stats.f_files * cache->frun_percent; | ||
181 | |||
182 | _debug("limits {%llu,%llu,%llu} files", | ||
183 | (unsigned long long) cache->frun, | ||
184 | (unsigned long long) cache->fcull, | ||
185 | (unsigned long long) cache->fstop); | ||
186 | |||
187 | stats.f_blocks >>= cache->bshift; | ||
188 | do_div(stats.f_blocks, 100); | ||
189 | cache->bstop = stats.f_blocks * cache->bstop_percent; | ||
190 | cache->bcull = stats.f_blocks * cache->bcull_percent; | ||
191 | cache->brun = stats.f_blocks * cache->brun_percent; | ||
192 | |||
193 | _debug("limits {%llu,%llu,%llu} blocks", | ||
194 | (unsigned long long) cache->brun, | ||
195 | (unsigned long long) cache->bcull, | ||
196 | (unsigned long long) cache->bstop); | ||
197 | |||
198 | /* get the cache directory and check its type */ | ||
199 | cachedir = cachefiles_get_directory(cache, root, "cache"); | ||
200 | if (IS_ERR(cachedir)) { | ||
201 | ret = PTR_ERR(cachedir); | ||
202 | goto error_unsupported; | ||
203 | } | ||
204 | |||
205 | fsdef->dentry = cachedir; | ||
206 | fsdef->fscache.cookie = NULL; | ||
207 | |||
208 | ret = cachefiles_check_object_type(fsdef); | ||
209 | if (ret < 0) | ||
210 | goto error_unsupported; | ||
211 | |||
212 | /* get the graveyard directory */ | ||
213 | graveyard = cachefiles_get_directory(cache, root, "graveyard"); | ||
214 | if (IS_ERR(graveyard)) { | ||
215 | ret = PTR_ERR(graveyard); | ||
216 | goto error_unsupported; | ||
217 | } | ||
218 | |||
219 | cache->graveyard = graveyard; | ||
220 | |||
221 | /* publish the cache */ | ||
222 | fscache_init_cache(&cache->cache, | ||
223 | &cachefiles_cache_ops, | ||
224 | "%s", | ||
225 | fsdef->dentry->d_sb->s_id); | ||
226 | |||
227 | fscache_object_init(&fsdef->fscache, NULL, &cache->cache); | ||
228 | |||
229 | ret = fscache_add_cache(&cache->cache, &fsdef->fscache, cache->tag); | ||
230 | if (ret < 0) | ||
231 | goto error_add_cache; | ||
232 | |||
233 | /* done */ | ||
234 | set_bit(CACHEFILES_READY, &cache->flags); | ||
235 | dput(root); | ||
236 | |||
237 | printk(KERN_INFO "CacheFiles:" | ||
238 | " File cache on %s registered\n", | ||
239 | cache->cache.identifier); | ||
240 | |||
241 | /* check how much space the cache has */ | ||
242 | cachefiles_has_space(cache, 0, 0); | ||
243 | cachefiles_end_secure(cache, saved_cred); | ||
244 | return 0; | ||
245 | |||
246 | error_add_cache: | ||
247 | dput(cache->graveyard); | ||
248 | cache->graveyard = NULL; | ||
249 | error_unsupported: | ||
250 | mntput(cache->mnt); | ||
251 | cache->mnt = NULL; | ||
252 | dput(fsdef->dentry); | ||
253 | fsdef->dentry = NULL; | ||
254 | dput(root); | ||
255 | error_open_root: | ||
256 | kmem_cache_free(cachefiles_object_jar, fsdef); | ||
257 | error_root_object: | ||
258 | cachefiles_end_secure(cache, saved_cred); | ||
259 | kerror("Failed to register: %d", ret); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * unbind a cache on fd release | ||
265 | */ | ||
266 | void cachefiles_daemon_unbind(struct cachefiles_cache *cache) | ||
267 | { | ||
268 | _enter(""); | ||
269 | |||
270 | if (test_bit(CACHEFILES_READY, &cache->flags)) { | ||
271 | printk(KERN_INFO "CacheFiles:" | ||
272 | " File cache on %s unregistering\n", | ||
273 | cache->cache.identifier); | ||
274 | |||
275 | fscache_withdraw_cache(&cache->cache); | ||
276 | } | ||
277 | |||
278 | dput(cache->graveyard); | ||
279 | mntput(cache->mnt); | ||
280 | |||
281 | kfree(cache->rootdirname); | ||
282 | kfree(cache->secctx); | ||
283 | kfree(cache->tag); | ||
284 | |||
285 | _leave(""); | ||
286 | } | ||