diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 1 | ||||
-rw-r--r-- | fs/Makefile | 1 | ||||
-rw-r--r-- | fs/cachefiles/Kconfig | 39 | ||||
-rw-r--r-- | fs/cachefiles/Makefile | 18 | ||||
-rw-r--r-- | fs/cachefiles/bind.c | 286 | ||||
-rw-r--r-- | fs/cachefiles/daemon.c | 755 | ||||
-rw-r--r-- | fs/cachefiles/interface.c | 449 | ||||
-rw-r--r-- | fs/cachefiles/internal.h | 360 | ||||
-rw-r--r-- | fs/cachefiles/key.c | 159 | ||||
-rw-r--r-- | fs/cachefiles/main.c | 106 | ||||
-rw-r--r-- | fs/cachefiles/namei.c | 771 | ||||
-rw-r--r-- | fs/cachefiles/proc.c | 134 | ||||
-rw-r--r-- | fs/cachefiles/rdwr.c | 879 | ||||
-rw-r--r-- | fs/cachefiles/security.c | 116 | ||||
-rw-r--r-- | fs/cachefiles/xattr.c | 291 |
15 files changed, 4365 insertions, 0 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index 3942df6ad04f..c0022b1d5877 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
@@ -69,6 +69,7 @@ config GENERIC_ACL | |||
69 | menu "Caches" | 69 | menu "Caches" |
70 | 70 | ||
71 | source "fs/fscache/Kconfig" | 71 | source "fs/fscache/Kconfig" |
72 | source "fs/cachefiles/Kconfig" | ||
72 | 73 | ||
73 | endmenu | 74 | endmenu |
74 | 75 | ||
diff --git a/fs/Makefile b/fs/Makefile index ff5a10d898c6..055d5237b109 100644 --- a/fs/Makefile +++ b/fs/Makefile | |||
@@ -117,6 +117,7 @@ obj-$(CONFIG_AFS_FS) += afs/ | |||
117 | obj-$(CONFIG_BEFS_FS) += befs/ | 117 | obj-$(CONFIG_BEFS_FS) += befs/ |
118 | obj-$(CONFIG_HOSTFS) += hostfs/ | 118 | obj-$(CONFIG_HOSTFS) += hostfs/ |
119 | obj-$(CONFIG_HPPFS) += hppfs/ | 119 | obj-$(CONFIG_HPPFS) += hppfs/ |
120 | obj-$(CONFIG_CACHEFILES) += cachefiles/ | ||
120 | obj-$(CONFIG_DEBUG_FS) += debugfs/ | 121 | obj-$(CONFIG_DEBUG_FS) += debugfs/ |
121 | obj-$(CONFIG_OCFS2_FS) += ocfs2/ | 122 | obj-$(CONFIG_OCFS2_FS) += ocfs2/ |
122 | obj-$(CONFIG_BTRFS_FS) += btrfs/ | 123 | obj-$(CONFIG_BTRFS_FS) += btrfs/ |
diff --git a/fs/cachefiles/Kconfig b/fs/cachefiles/Kconfig new file mode 100644 index 000000000000..80e9c6167f0b --- /dev/null +++ b/fs/cachefiles/Kconfig | |||
@@ -0,0 +1,39 @@ | |||
1 | |||
2 | config CACHEFILES | ||
3 | tristate "Filesystem caching on files" | ||
4 | depends on FSCACHE && BLOCK | ||
5 | help | ||
6 | This permits use of a mounted filesystem as a cache for other | ||
7 | filesystems - primarily networking filesystems - thus allowing fast | ||
8 | local disk to enhance the speed of slower devices. | ||
9 | |||
10 | See Documentation/filesystems/caching/cachefiles.txt for more | ||
11 | information. | ||
12 | |||
13 | config CACHEFILES_DEBUG | ||
14 | bool "Debug CacheFiles" | ||
15 | depends on CACHEFILES | ||
16 | help | ||
17 | This permits debugging to be dynamically enabled in the filesystem | ||
18 | caching on files module. If this is set, the debugging output may be | ||
19 | enabled by setting bits in /sys/modules/cachefiles/parameter/debug or | ||
20 | by including a debugging specifier in /etc/cachefilesd.conf. | ||
21 | |||
22 | config CACHEFILES_HISTOGRAM | ||
23 | bool "Gather latency information on CacheFiles" | ||
24 | depends on CACHEFILES && PROC_FS | ||
25 | help | ||
26 | |||
27 | This option causes latency information to be gathered on CacheFiles | ||
28 | operation and exported through file: | ||
29 | |||
30 | /proc/fs/cachefiles/histogram | ||
31 | |||
32 | The generation of this histogram adds a certain amount of overhead to | ||
33 | execution as there are a number of points at which data is gathered, | ||
34 | and on a multi-CPU system these may be on cachelines that keep | ||
35 | bouncing between CPUs. On the other hand, the histogram may be | ||
36 | useful for debugging purposes. Saying 'N' here is recommended. | ||
37 | |||
38 | See Documentation/filesystems/caching/cachefiles.txt for more | ||
39 | information. | ||
diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile new file mode 100644 index 000000000000..32cbab0ffce3 --- /dev/null +++ b/fs/cachefiles/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | # | ||
2 | # Makefile for caching in a mounted filesystem | ||
3 | # | ||
4 | |||
5 | cachefiles-y := \ | ||
6 | bind.o \ | ||
7 | daemon.o \ | ||
8 | interface.o \ | ||
9 | key.o \ | ||
10 | main.o \ | ||
11 | namei.o \ | ||
12 | rdwr.o \ | ||
13 | security.o \ | ||
14 | xattr.o | ||
15 | |||
16 | cachefiles-$(CONFIG_CACHEFILES_HISTOGRAM) += proc.o | ||
17 | |||
18 | obj-$(CONFIG_CACHEFILES) := cachefiles.o | ||
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 | } | ||
diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c new file mode 100644 index 000000000000..4618516dd994 --- /dev/null +++ b/fs/cachefiles/daemon.c | |||
@@ -0,0 +1,755 @@ | |||
1 | /* Daemon interface | ||
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/poll.h> | ||
21 | #include <linux/mount.h> | ||
22 | #include <linux/statfs.h> | ||
23 | #include <linux/ctype.h> | ||
24 | #include <linux/fs_struct.h> | ||
25 | #include "internal.h" | ||
26 | |||
27 | static int cachefiles_daemon_open(struct inode *, struct file *); | ||
28 | static int cachefiles_daemon_release(struct inode *, struct file *); | ||
29 | static ssize_t cachefiles_daemon_read(struct file *, char __user *, size_t, | ||
30 | loff_t *); | ||
31 | static ssize_t cachefiles_daemon_write(struct file *, const char __user *, | ||
32 | size_t, loff_t *); | ||
33 | static unsigned int cachefiles_daemon_poll(struct file *, | ||
34 | struct poll_table_struct *); | ||
35 | static int cachefiles_daemon_frun(struct cachefiles_cache *, char *); | ||
36 | static int cachefiles_daemon_fcull(struct cachefiles_cache *, char *); | ||
37 | static int cachefiles_daemon_fstop(struct cachefiles_cache *, char *); | ||
38 | static int cachefiles_daemon_brun(struct cachefiles_cache *, char *); | ||
39 | static int cachefiles_daemon_bcull(struct cachefiles_cache *, char *); | ||
40 | static int cachefiles_daemon_bstop(struct cachefiles_cache *, char *); | ||
41 | static int cachefiles_daemon_cull(struct cachefiles_cache *, char *); | ||
42 | static int cachefiles_daemon_debug(struct cachefiles_cache *, char *); | ||
43 | static int cachefiles_daemon_dir(struct cachefiles_cache *, char *); | ||
44 | static int cachefiles_daemon_inuse(struct cachefiles_cache *, char *); | ||
45 | static int cachefiles_daemon_secctx(struct cachefiles_cache *, char *); | ||
46 | static int cachefiles_daemon_tag(struct cachefiles_cache *, char *); | ||
47 | |||
48 | static unsigned long cachefiles_open; | ||
49 | |||
50 | const struct file_operations cachefiles_daemon_fops = { | ||
51 | .owner = THIS_MODULE, | ||
52 | .open = cachefiles_daemon_open, | ||
53 | .release = cachefiles_daemon_release, | ||
54 | .read = cachefiles_daemon_read, | ||
55 | .write = cachefiles_daemon_write, | ||
56 | .poll = cachefiles_daemon_poll, | ||
57 | }; | ||
58 | |||
59 | struct cachefiles_daemon_cmd { | ||
60 | char name[8]; | ||
61 | int (*handler)(struct cachefiles_cache *cache, char *args); | ||
62 | }; | ||
63 | |||
64 | static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] = { | ||
65 | { "bind", cachefiles_daemon_bind }, | ||
66 | { "brun", cachefiles_daemon_brun }, | ||
67 | { "bcull", cachefiles_daemon_bcull }, | ||
68 | { "bstop", cachefiles_daemon_bstop }, | ||
69 | { "cull", cachefiles_daemon_cull }, | ||
70 | { "debug", cachefiles_daemon_debug }, | ||
71 | { "dir", cachefiles_daemon_dir }, | ||
72 | { "frun", cachefiles_daemon_frun }, | ||
73 | { "fcull", cachefiles_daemon_fcull }, | ||
74 | { "fstop", cachefiles_daemon_fstop }, | ||
75 | { "inuse", cachefiles_daemon_inuse }, | ||
76 | { "secctx", cachefiles_daemon_secctx }, | ||
77 | { "tag", cachefiles_daemon_tag }, | ||
78 | { "", NULL } | ||
79 | }; | ||
80 | |||
81 | |||
82 | /* | ||
83 | * do various checks | ||
84 | */ | ||
85 | static int cachefiles_daemon_open(struct inode *inode, struct file *file) | ||
86 | { | ||
87 | struct cachefiles_cache *cache; | ||
88 | |||
89 | _enter(""); | ||
90 | |||
91 | /* only the superuser may do this */ | ||
92 | if (!capable(CAP_SYS_ADMIN)) | ||
93 | return -EPERM; | ||
94 | |||
95 | /* the cachefiles device may only be open once at a time */ | ||
96 | if (xchg(&cachefiles_open, 1) == 1) | ||
97 | return -EBUSY; | ||
98 | |||
99 | /* allocate a cache record */ | ||
100 | cache = kzalloc(sizeof(struct cachefiles_cache), GFP_KERNEL); | ||
101 | if (!cache) { | ||
102 | cachefiles_open = 0; | ||
103 | return -ENOMEM; | ||
104 | } | ||
105 | |||
106 | mutex_init(&cache->daemon_mutex); | ||
107 | cache->active_nodes = RB_ROOT; | ||
108 | rwlock_init(&cache->active_lock); | ||
109 | init_waitqueue_head(&cache->daemon_pollwq); | ||
110 | |||
111 | /* set default caching limits | ||
112 | * - limit at 1% free space and/or free files | ||
113 | * - cull below 5% free space and/or free files | ||
114 | * - cease culling above 7% free space and/or free files | ||
115 | */ | ||
116 | cache->frun_percent = 7; | ||
117 | cache->fcull_percent = 5; | ||
118 | cache->fstop_percent = 1; | ||
119 | cache->brun_percent = 7; | ||
120 | cache->bcull_percent = 5; | ||
121 | cache->bstop_percent = 1; | ||
122 | |||
123 | file->private_data = cache; | ||
124 | cache->cachefilesd = file; | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * release a cache | ||
130 | */ | ||
131 | static int cachefiles_daemon_release(struct inode *inode, struct file *file) | ||
132 | { | ||
133 | struct cachefiles_cache *cache = file->private_data; | ||
134 | |||
135 | _enter(""); | ||
136 | |||
137 | ASSERT(cache); | ||
138 | |||
139 | set_bit(CACHEFILES_DEAD, &cache->flags); | ||
140 | |||
141 | cachefiles_daemon_unbind(cache); | ||
142 | |||
143 | ASSERT(!cache->active_nodes.rb_node); | ||
144 | |||
145 | /* clean up the control file interface */ | ||
146 | cache->cachefilesd = NULL; | ||
147 | file->private_data = NULL; | ||
148 | cachefiles_open = 0; | ||
149 | |||
150 | kfree(cache); | ||
151 | |||
152 | _leave(""); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * read the cache state | ||
158 | */ | ||
159 | static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer, | ||
160 | size_t buflen, loff_t *pos) | ||
161 | { | ||
162 | struct cachefiles_cache *cache = file->private_data; | ||
163 | char buffer[256]; | ||
164 | int n; | ||
165 | |||
166 | //_enter(",,%zu,", buflen); | ||
167 | |||
168 | if (!test_bit(CACHEFILES_READY, &cache->flags)) | ||
169 | return 0; | ||
170 | |||
171 | /* check how much space the cache has */ | ||
172 | cachefiles_has_space(cache, 0, 0); | ||
173 | |||
174 | /* summarise */ | ||
175 | clear_bit(CACHEFILES_STATE_CHANGED, &cache->flags); | ||
176 | |||
177 | n = snprintf(buffer, sizeof(buffer), | ||
178 | "cull=%c" | ||
179 | " frun=%llx" | ||
180 | " fcull=%llx" | ||
181 | " fstop=%llx" | ||
182 | " brun=%llx" | ||
183 | " bcull=%llx" | ||
184 | " bstop=%llx", | ||
185 | test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0', | ||
186 | (unsigned long long) cache->frun, | ||
187 | (unsigned long long) cache->fcull, | ||
188 | (unsigned long long) cache->fstop, | ||
189 | (unsigned long long) cache->brun, | ||
190 | (unsigned long long) cache->bcull, | ||
191 | (unsigned long long) cache->bstop | ||
192 | ); | ||
193 | |||
194 | if (n > buflen) | ||
195 | return -EMSGSIZE; | ||
196 | |||
197 | if (copy_to_user(_buffer, buffer, n) != 0) | ||
198 | return -EFAULT; | ||
199 | |||
200 | return n; | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | * command the cache | ||
205 | */ | ||
206 | static ssize_t cachefiles_daemon_write(struct file *file, | ||
207 | const char __user *_data, | ||
208 | size_t datalen, | ||
209 | loff_t *pos) | ||
210 | { | ||
211 | const struct cachefiles_daemon_cmd *cmd; | ||
212 | struct cachefiles_cache *cache = file->private_data; | ||
213 | ssize_t ret; | ||
214 | char *data, *args, *cp; | ||
215 | |||
216 | //_enter(",,%zu,", datalen); | ||
217 | |||
218 | ASSERT(cache); | ||
219 | |||
220 | if (test_bit(CACHEFILES_DEAD, &cache->flags)) | ||
221 | return -EIO; | ||
222 | |||
223 | if (datalen < 0 || datalen > PAGE_SIZE - 1) | ||
224 | return -EOPNOTSUPP; | ||
225 | |||
226 | /* drag the command string into the kernel so we can parse it */ | ||
227 | data = kmalloc(datalen + 1, GFP_KERNEL); | ||
228 | if (!data) | ||
229 | return -ENOMEM; | ||
230 | |||
231 | ret = -EFAULT; | ||
232 | if (copy_from_user(data, _data, datalen) != 0) | ||
233 | goto error; | ||
234 | |||
235 | data[datalen] = '\0'; | ||
236 | |||
237 | ret = -EINVAL; | ||
238 | if (memchr(data, '\0', datalen)) | ||
239 | goto error; | ||
240 | |||
241 | /* strip any newline */ | ||
242 | cp = memchr(data, '\n', datalen); | ||
243 | if (cp) { | ||
244 | if (cp == data) | ||
245 | goto error; | ||
246 | |||
247 | *cp = '\0'; | ||
248 | } | ||
249 | |||
250 | /* parse the command */ | ||
251 | ret = -EOPNOTSUPP; | ||
252 | |||
253 | for (args = data; *args; args++) | ||
254 | if (isspace(*args)) | ||
255 | break; | ||
256 | if (*args) { | ||
257 | if (args == data) | ||
258 | goto error; | ||
259 | *args = '\0'; | ||
260 | for (args++; isspace(*args); args++) | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | /* run the appropriate command handler */ | ||
265 | for (cmd = cachefiles_daemon_cmds; cmd->name[0]; cmd++) | ||
266 | if (strcmp(cmd->name, data) == 0) | ||
267 | goto found_command; | ||
268 | |||
269 | error: | ||
270 | kfree(data); | ||
271 | //_leave(" = %zd", ret); | ||
272 | return ret; | ||
273 | |||
274 | found_command: | ||
275 | mutex_lock(&cache->daemon_mutex); | ||
276 | |||
277 | ret = -EIO; | ||
278 | if (!test_bit(CACHEFILES_DEAD, &cache->flags)) | ||
279 | ret = cmd->handler(cache, args); | ||
280 | |||
281 | mutex_unlock(&cache->daemon_mutex); | ||
282 | |||
283 | if (ret == 0) | ||
284 | ret = datalen; | ||
285 | goto error; | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * poll for culling state | ||
290 | * - use POLLOUT to indicate culling state | ||
291 | */ | ||
292 | static unsigned int cachefiles_daemon_poll(struct file *file, | ||
293 | struct poll_table_struct *poll) | ||
294 | { | ||
295 | struct cachefiles_cache *cache = file->private_data; | ||
296 | unsigned int mask; | ||
297 | |||
298 | poll_wait(file, &cache->daemon_pollwq, poll); | ||
299 | mask = 0; | ||
300 | |||
301 | if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags)) | ||
302 | mask |= POLLIN; | ||
303 | |||
304 | if (test_bit(CACHEFILES_CULLING, &cache->flags)) | ||
305 | mask |= POLLOUT; | ||
306 | |||
307 | return mask; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * give a range error for cache space constraints | ||
312 | * - can be tail-called | ||
313 | */ | ||
314 | static int cachefiles_daemon_range_error(struct cachefiles_cache *cache, | ||
315 | char *args) | ||
316 | { | ||
317 | kerror("Free space limits must be in range" | ||
318 | " 0%%<=stop<cull<run<100%%"); | ||
319 | |||
320 | return -EINVAL; | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * set the percentage of files at which to stop culling | ||
325 | * - command: "frun <N>%" | ||
326 | */ | ||
327 | static int cachefiles_daemon_frun(struct cachefiles_cache *cache, char *args) | ||
328 | { | ||
329 | unsigned long frun; | ||
330 | |||
331 | _enter(",%s", args); | ||
332 | |||
333 | if (!*args) | ||
334 | return -EINVAL; | ||
335 | |||
336 | frun = simple_strtoul(args, &args, 10); | ||
337 | if (args[0] != '%' || args[1] != '\0') | ||
338 | return -EINVAL; | ||
339 | |||
340 | if (frun <= cache->fcull_percent || frun >= 100) | ||
341 | return cachefiles_daemon_range_error(cache, args); | ||
342 | |||
343 | cache->frun_percent = frun; | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * set the percentage of files at which to start culling | ||
349 | * - command: "fcull <N>%" | ||
350 | */ | ||
351 | static int cachefiles_daemon_fcull(struct cachefiles_cache *cache, char *args) | ||
352 | { | ||
353 | unsigned long fcull; | ||
354 | |||
355 | _enter(",%s", args); | ||
356 | |||
357 | if (!*args) | ||
358 | return -EINVAL; | ||
359 | |||
360 | fcull = simple_strtoul(args, &args, 10); | ||
361 | if (args[0] != '%' || args[1] != '\0') | ||
362 | return -EINVAL; | ||
363 | |||
364 | if (fcull <= cache->fstop_percent || fcull >= cache->frun_percent) | ||
365 | return cachefiles_daemon_range_error(cache, args); | ||
366 | |||
367 | cache->fcull_percent = fcull; | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * set the percentage of files at which to stop allocating | ||
373 | * - command: "fstop <N>%" | ||
374 | */ | ||
375 | static int cachefiles_daemon_fstop(struct cachefiles_cache *cache, char *args) | ||
376 | { | ||
377 | unsigned long fstop; | ||
378 | |||
379 | _enter(",%s", args); | ||
380 | |||
381 | if (!*args) | ||
382 | return -EINVAL; | ||
383 | |||
384 | fstop = simple_strtoul(args, &args, 10); | ||
385 | if (args[0] != '%' || args[1] != '\0') | ||
386 | return -EINVAL; | ||
387 | |||
388 | if (fstop < 0 || fstop >= cache->fcull_percent) | ||
389 | return cachefiles_daemon_range_error(cache, args); | ||
390 | |||
391 | cache->fstop_percent = fstop; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | /* | ||
396 | * set the percentage of blocks at which to stop culling | ||
397 | * - command: "brun <N>%" | ||
398 | */ | ||
399 | static int cachefiles_daemon_brun(struct cachefiles_cache *cache, char *args) | ||
400 | { | ||
401 | unsigned long brun; | ||
402 | |||
403 | _enter(",%s", args); | ||
404 | |||
405 | if (!*args) | ||
406 | return -EINVAL; | ||
407 | |||
408 | brun = simple_strtoul(args, &args, 10); | ||
409 | if (args[0] != '%' || args[1] != '\0') | ||
410 | return -EINVAL; | ||
411 | |||
412 | if (brun <= cache->bcull_percent || brun >= 100) | ||
413 | return cachefiles_daemon_range_error(cache, args); | ||
414 | |||
415 | cache->brun_percent = brun; | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | /* | ||
420 | * set the percentage of blocks at which to start culling | ||
421 | * - command: "bcull <N>%" | ||
422 | */ | ||
423 | static int cachefiles_daemon_bcull(struct cachefiles_cache *cache, char *args) | ||
424 | { | ||
425 | unsigned long bcull; | ||
426 | |||
427 | _enter(",%s", args); | ||
428 | |||
429 | if (!*args) | ||
430 | return -EINVAL; | ||
431 | |||
432 | bcull = simple_strtoul(args, &args, 10); | ||
433 | if (args[0] != '%' || args[1] != '\0') | ||
434 | return -EINVAL; | ||
435 | |||
436 | if (bcull <= cache->bstop_percent || bcull >= cache->brun_percent) | ||
437 | return cachefiles_daemon_range_error(cache, args); | ||
438 | |||
439 | cache->bcull_percent = bcull; | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * set the percentage of blocks at which to stop allocating | ||
445 | * - command: "bstop <N>%" | ||
446 | */ | ||
447 | static int cachefiles_daemon_bstop(struct cachefiles_cache *cache, char *args) | ||
448 | { | ||
449 | unsigned long bstop; | ||
450 | |||
451 | _enter(",%s", args); | ||
452 | |||
453 | if (!*args) | ||
454 | return -EINVAL; | ||
455 | |||
456 | bstop = simple_strtoul(args, &args, 10); | ||
457 | if (args[0] != '%' || args[1] != '\0') | ||
458 | return -EINVAL; | ||
459 | |||
460 | if (bstop < 0 || bstop >= cache->bcull_percent) | ||
461 | return cachefiles_daemon_range_error(cache, args); | ||
462 | |||
463 | cache->bstop_percent = bstop; | ||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * set the cache directory | ||
469 | * - command: "dir <name>" | ||
470 | */ | ||
471 | static int cachefiles_daemon_dir(struct cachefiles_cache *cache, char *args) | ||
472 | { | ||
473 | char *dir; | ||
474 | |||
475 | _enter(",%s", args); | ||
476 | |||
477 | if (!*args) { | ||
478 | kerror("Empty directory specified"); | ||
479 | return -EINVAL; | ||
480 | } | ||
481 | |||
482 | if (cache->rootdirname) { | ||
483 | kerror("Second cache directory specified"); | ||
484 | return -EEXIST; | ||
485 | } | ||
486 | |||
487 | dir = kstrdup(args, GFP_KERNEL); | ||
488 | if (!dir) | ||
489 | return -ENOMEM; | ||
490 | |||
491 | cache->rootdirname = dir; | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * set the cache security context | ||
497 | * - command: "secctx <ctx>" | ||
498 | */ | ||
499 | static int cachefiles_daemon_secctx(struct cachefiles_cache *cache, char *args) | ||
500 | { | ||
501 | char *secctx; | ||
502 | |||
503 | _enter(",%s", args); | ||
504 | |||
505 | if (!*args) { | ||
506 | kerror("Empty security context specified"); | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | |||
510 | if (cache->secctx) { | ||
511 | kerror("Second security context specified"); | ||
512 | return -EINVAL; | ||
513 | } | ||
514 | |||
515 | secctx = kstrdup(args, GFP_KERNEL); | ||
516 | if (!secctx) | ||
517 | return -ENOMEM; | ||
518 | |||
519 | cache->secctx = secctx; | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | /* | ||
524 | * set the cache tag | ||
525 | * - command: "tag <name>" | ||
526 | */ | ||
527 | static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args) | ||
528 | { | ||
529 | char *tag; | ||
530 | |||
531 | _enter(",%s", args); | ||
532 | |||
533 | if (!*args) { | ||
534 | kerror("Empty tag specified"); | ||
535 | return -EINVAL; | ||
536 | } | ||
537 | |||
538 | if (cache->tag) | ||
539 | return -EEXIST; | ||
540 | |||
541 | tag = kstrdup(args, GFP_KERNEL); | ||
542 | if (!tag) | ||
543 | return -ENOMEM; | ||
544 | |||
545 | cache->tag = tag; | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | /* | ||
550 | * request a node in the cache be culled from the current working directory | ||
551 | * - command: "cull <name>" | ||
552 | */ | ||
553 | static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) | ||
554 | { | ||
555 | struct fs_struct *fs; | ||
556 | struct dentry *dir; | ||
557 | const struct cred *saved_cred; | ||
558 | int ret; | ||
559 | |||
560 | _enter(",%s", args); | ||
561 | |||
562 | if (strchr(args, '/')) | ||
563 | goto inval; | ||
564 | |||
565 | if (!test_bit(CACHEFILES_READY, &cache->flags)) { | ||
566 | kerror("cull applied to unready cache"); | ||
567 | return -EIO; | ||
568 | } | ||
569 | |||
570 | if (test_bit(CACHEFILES_DEAD, &cache->flags)) { | ||
571 | kerror("cull applied to dead cache"); | ||
572 | return -EIO; | ||
573 | } | ||
574 | |||
575 | /* extract the directory dentry from the cwd */ | ||
576 | fs = current->fs; | ||
577 | read_lock(&fs->lock); | ||
578 | dir = dget(fs->pwd.dentry); | ||
579 | read_unlock(&fs->lock); | ||
580 | |||
581 | if (!S_ISDIR(dir->d_inode->i_mode)) | ||
582 | goto notdir; | ||
583 | |||
584 | cachefiles_begin_secure(cache, &saved_cred); | ||
585 | ret = cachefiles_cull(cache, dir, args); | ||
586 | cachefiles_end_secure(cache, saved_cred); | ||
587 | |||
588 | dput(dir); | ||
589 | _leave(" = %d", ret); | ||
590 | return ret; | ||
591 | |||
592 | notdir: | ||
593 | dput(dir); | ||
594 | kerror("cull command requires dirfd to be a directory"); | ||
595 | return -ENOTDIR; | ||
596 | |||
597 | inval: | ||
598 | kerror("cull command requires dirfd and filename"); | ||
599 | return -EINVAL; | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * set debugging mode | ||
604 | * - command: "debug <mask>" | ||
605 | */ | ||
606 | static int cachefiles_daemon_debug(struct cachefiles_cache *cache, char *args) | ||
607 | { | ||
608 | unsigned long mask; | ||
609 | |||
610 | _enter(",%s", args); | ||
611 | |||
612 | mask = simple_strtoul(args, &args, 0); | ||
613 | if (args[0] != '\0') | ||
614 | goto inval; | ||
615 | |||
616 | cachefiles_debug = mask; | ||
617 | _leave(" = 0"); | ||
618 | return 0; | ||
619 | |||
620 | inval: | ||
621 | kerror("debug command requires mask"); | ||
622 | return -EINVAL; | ||
623 | } | ||
624 | |||
625 | /* | ||
626 | * find out whether an object in the current working directory is in use or not | ||
627 | * - command: "inuse <name>" | ||
628 | */ | ||
629 | static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) | ||
630 | { | ||
631 | struct fs_struct *fs; | ||
632 | struct dentry *dir; | ||
633 | const struct cred *saved_cred; | ||
634 | int ret; | ||
635 | |||
636 | //_enter(",%s", args); | ||
637 | |||
638 | if (strchr(args, '/')) | ||
639 | goto inval; | ||
640 | |||
641 | if (!test_bit(CACHEFILES_READY, &cache->flags)) { | ||
642 | kerror("inuse applied to unready cache"); | ||
643 | return -EIO; | ||
644 | } | ||
645 | |||
646 | if (test_bit(CACHEFILES_DEAD, &cache->flags)) { | ||
647 | kerror("inuse applied to dead cache"); | ||
648 | return -EIO; | ||
649 | } | ||
650 | |||
651 | /* extract the directory dentry from the cwd */ | ||
652 | fs = current->fs; | ||
653 | read_lock(&fs->lock); | ||
654 | dir = dget(fs->pwd.dentry); | ||
655 | read_unlock(&fs->lock); | ||
656 | |||
657 | if (!S_ISDIR(dir->d_inode->i_mode)) | ||
658 | goto notdir; | ||
659 | |||
660 | cachefiles_begin_secure(cache, &saved_cred); | ||
661 | ret = cachefiles_check_in_use(cache, dir, args); | ||
662 | cachefiles_end_secure(cache, saved_cred); | ||
663 | |||
664 | dput(dir); | ||
665 | //_leave(" = %d", ret); | ||
666 | return ret; | ||
667 | |||
668 | notdir: | ||
669 | dput(dir); | ||
670 | kerror("inuse command requires dirfd to be a directory"); | ||
671 | return -ENOTDIR; | ||
672 | |||
673 | inval: | ||
674 | kerror("inuse command requires dirfd and filename"); | ||
675 | return -EINVAL; | ||
676 | } | ||
677 | |||
678 | /* | ||
679 | * see if we have space for a number of pages and/or a number of files in the | ||
680 | * cache | ||
681 | */ | ||
682 | int cachefiles_has_space(struct cachefiles_cache *cache, | ||
683 | unsigned fnr, unsigned bnr) | ||
684 | { | ||
685 | struct kstatfs stats; | ||
686 | int ret; | ||
687 | |||
688 | //_enter("{%llu,%llu,%llu,%llu,%llu,%llu},%u,%u", | ||
689 | // (unsigned long long) cache->frun, | ||
690 | // (unsigned long long) cache->fcull, | ||
691 | // (unsigned long long) cache->fstop, | ||
692 | // (unsigned long long) cache->brun, | ||
693 | // (unsigned long long) cache->bcull, | ||
694 | // (unsigned long long) cache->bstop, | ||
695 | // fnr, bnr); | ||
696 | |||
697 | /* find out how many pages of blockdev are available */ | ||
698 | memset(&stats, 0, sizeof(stats)); | ||
699 | |||
700 | ret = vfs_statfs(cache->mnt->mnt_root, &stats); | ||
701 | if (ret < 0) { | ||
702 | if (ret == -EIO) | ||
703 | cachefiles_io_error(cache, "statfs failed"); | ||
704 | _leave(" = %d", ret); | ||
705 | return ret; | ||
706 | } | ||
707 | |||
708 | stats.f_bavail >>= cache->bshift; | ||
709 | |||
710 | //_debug("avail %llu,%llu", | ||
711 | // (unsigned long long) stats.f_ffree, | ||
712 | // (unsigned long long) stats.f_bavail); | ||
713 | |||
714 | /* see if there is sufficient space */ | ||
715 | if (stats.f_ffree > fnr) | ||
716 | stats.f_ffree -= fnr; | ||
717 | else | ||
718 | stats.f_ffree = 0; | ||
719 | |||
720 | if (stats.f_bavail > bnr) | ||
721 | stats.f_bavail -= bnr; | ||
722 | else | ||
723 | stats.f_bavail = 0; | ||
724 | |||
725 | ret = -ENOBUFS; | ||
726 | if (stats.f_ffree < cache->fstop || | ||
727 | stats.f_bavail < cache->bstop) | ||
728 | goto begin_cull; | ||
729 | |||
730 | ret = 0; | ||
731 | if (stats.f_ffree < cache->fcull || | ||
732 | stats.f_bavail < cache->bcull) | ||
733 | goto begin_cull; | ||
734 | |||
735 | if (test_bit(CACHEFILES_CULLING, &cache->flags) && | ||
736 | stats.f_ffree >= cache->frun && | ||
737 | stats.f_bavail >= cache->brun && | ||
738 | test_and_clear_bit(CACHEFILES_CULLING, &cache->flags) | ||
739 | ) { | ||
740 | _debug("cease culling"); | ||
741 | cachefiles_state_changed(cache); | ||
742 | } | ||
743 | |||
744 | //_leave(" = 0"); | ||
745 | return 0; | ||
746 | |||
747 | begin_cull: | ||
748 | if (!test_and_set_bit(CACHEFILES_CULLING, &cache->flags)) { | ||
749 | _debug("### CULL CACHE ###"); | ||
750 | cachefiles_state_changed(cache); | ||
751 | } | ||
752 | |||
753 | _leave(" = %d", ret); | ||
754 | return ret; | ||
755 | } | ||
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c new file mode 100644 index 000000000000..1e962348d111 --- /dev/null +++ b/fs/cachefiles/interface.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* FS-Cache interface to CacheFiles | ||
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/mount.h> | ||
13 | #include <linux/buffer_head.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | #define list_to_page(head) (list_entry((head)->prev, struct page, lru)) | ||
17 | |||
18 | struct cachefiles_lookup_data { | ||
19 | struct cachefiles_xattr *auxdata; /* auxiliary data */ | ||
20 | char *key; /* key path */ | ||
21 | }; | ||
22 | |||
23 | static int cachefiles_attr_changed(struct fscache_object *_object); | ||
24 | |||
25 | /* | ||
26 | * allocate an object record for a cookie lookup and prepare the lookup data | ||
27 | */ | ||
28 | static struct fscache_object *cachefiles_alloc_object( | ||
29 | struct fscache_cache *_cache, | ||
30 | struct fscache_cookie *cookie) | ||
31 | { | ||
32 | struct cachefiles_lookup_data *lookup_data; | ||
33 | struct cachefiles_object *object; | ||
34 | struct cachefiles_cache *cache; | ||
35 | struct cachefiles_xattr *auxdata; | ||
36 | unsigned keylen, auxlen; | ||
37 | void *buffer; | ||
38 | char *key; | ||
39 | |||
40 | cache = container_of(_cache, struct cachefiles_cache, cache); | ||
41 | |||
42 | _enter("{%s},%p,", cache->cache.identifier, cookie); | ||
43 | |||
44 | lookup_data = kmalloc(sizeof(*lookup_data), GFP_KERNEL); | ||
45 | if (!lookup_data) | ||
46 | goto nomem_lookup_data; | ||
47 | |||
48 | /* create a new object record and a temporary leaf image */ | ||
49 | object = kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL); | ||
50 | if (!object) | ||
51 | goto nomem_object; | ||
52 | |||
53 | ASSERTCMP(object->backer, ==, NULL); | ||
54 | |||
55 | BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); | ||
56 | atomic_set(&object->usage, 1); | ||
57 | |||
58 | fscache_object_init(&object->fscache, cookie, &cache->cache); | ||
59 | |||
60 | object->type = cookie->def->type; | ||
61 | |||
62 | /* get hold of the raw key | ||
63 | * - stick the length on the front and leave space on the back for the | ||
64 | * encoder | ||
65 | */ | ||
66 | buffer = kmalloc((2 + 512) + 3, GFP_KERNEL); | ||
67 | if (!buffer) | ||
68 | goto nomem_buffer; | ||
69 | |||
70 | keylen = cookie->def->get_key(cookie->netfs_data, buffer + 2, 512); | ||
71 | ASSERTCMP(keylen, <, 512); | ||
72 | |||
73 | *(uint16_t *)buffer = keylen; | ||
74 | ((char *)buffer)[keylen + 2] = 0; | ||
75 | ((char *)buffer)[keylen + 3] = 0; | ||
76 | ((char *)buffer)[keylen + 4] = 0; | ||
77 | |||
78 | /* turn the raw key into something that can work with as a filename */ | ||
79 | key = cachefiles_cook_key(buffer, keylen + 2, object->type); | ||
80 | if (!key) | ||
81 | goto nomem_key; | ||
82 | |||
83 | /* get hold of the auxiliary data and prepend the object type */ | ||
84 | auxdata = buffer; | ||
85 | auxlen = 0; | ||
86 | if (cookie->def->get_aux) { | ||
87 | auxlen = cookie->def->get_aux(cookie->netfs_data, | ||
88 | auxdata->data, 511); | ||
89 | ASSERTCMP(auxlen, <, 511); | ||
90 | } | ||
91 | |||
92 | auxdata->len = auxlen + 1; | ||
93 | auxdata->type = cookie->def->type; | ||
94 | |||
95 | lookup_data->auxdata = auxdata; | ||
96 | lookup_data->key = key; | ||
97 | object->lookup_data = lookup_data; | ||
98 | |||
99 | _leave(" = %p [%p]", &object->fscache, lookup_data); | ||
100 | return &object->fscache; | ||
101 | |||
102 | nomem_key: | ||
103 | kfree(buffer); | ||
104 | nomem_buffer: | ||
105 | BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); | ||
106 | kmem_cache_free(cachefiles_object_jar, object); | ||
107 | fscache_object_destroyed(&cache->cache); | ||
108 | nomem_object: | ||
109 | kfree(lookup_data); | ||
110 | nomem_lookup_data: | ||
111 | _leave(" = -ENOMEM"); | ||
112 | return ERR_PTR(-ENOMEM); | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * attempt to look up the nominated node in this cache | ||
117 | */ | ||
118 | static void cachefiles_lookup_object(struct fscache_object *_object) | ||
119 | { | ||
120 | struct cachefiles_lookup_data *lookup_data; | ||
121 | struct cachefiles_object *parent, *object; | ||
122 | struct cachefiles_cache *cache; | ||
123 | const struct cred *saved_cred; | ||
124 | int ret; | ||
125 | |||
126 | _enter("{OBJ%x}", _object->debug_id); | ||
127 | |||
128 | cache = container_of(_object->cache, struct cachefiles_cache, cache); | ||
129 | parent = container_of(_object->parent, | ||
130 | struct cachefiles_object, fscache); | ||
131 | object = container_of(_object, struct cachefiles_object, fscache); | ||
132 | lookup_data = object->lookup_data; | ||
133 | |||
134 | ASSERTCMP(lookup_data, !=, NULL); | ||
135 | |||
136 | /* look up the key, creating any missing bits */ | ||
137 | cachefiles_begin_secure(cache, &saved_cred); | ||
138 | ret = cachefiles_walk_to_object(parent, object, | ||
139 | lookup_data->key, | ||
140 | lookup_data->auxdata); | ||
141 | cachefiles_end_secure(cache, saved_cred); | ||
142 | |||
143 | /* polish off by setting the attributes of non-index files */ | ||
144 | if (ret == 0 && | ||
145 | object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX) | ||
146 | cachefiles_attr_changed(&object->fscache); | ||
147 | |||
148 | if (ret < 0) { | ||
149 | printk(KERN_WARNING "CacheFiles: Lookup failed error %d\n", | ||
150 | ret); | ||
151 | fscache_object_lookup_error(&object->fscache); | ||
152 | } | ||
153 | |||
154 | _leave(" [%d]", ret); | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * indication of lookup completion | ||
159 | */ | ||
160 | static void cachefiles_lookup_complete(struct fscache_object *_object) | ||
161 | { | ||
162 | struct cachefiles_object *object; | ||
163 | |||
164 | object = container_of(_object, struct cachefiles_object, fscache); | ||
165 | |||
166 | _enter("{OBJ%x,%p}", object->fscache.debug_id, object->lookup_data); | ||
167 | |||
168 | if (object->lookup_data) { | ||
169 | kfree(object->lookup_data->key); | ||
170 | kfree(object->lookup_data->auxdata); | ||
171 | kfree(object->lookup_data); | ||
172 | object->lookup_data = NULL; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * increment the usage count on an inode object (may fail if unmounting) | ||
178 | */ | ||
179 | static | ||
180 | struct fscache_object *cachefiles_grab_object(struct fscache_object *_object) | ||
181 | { | ||
182 | struct cachefiles_object *object = | ||
183 | container_of(_object, struct cachefiles_object, fscache); | ||
184 | |||
185 | _enter("{OBJ%x,%d}", _object->debug_id, atomic_read(&object->usage)); | ||
186 | |||
187 | #ifdef CACHEFILES_DEBUG_SLAB | ||
188 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | ||
189 | #endif | ||
190 | |||
191 | atomic_inc(&object->usage); | ||
192 | return &object->fscache; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * update the auxilliary data for an object object on disk | ||
197 | */ | ||
198 | static void cachefiles_update_object(struct fscache_object *_object) | ||
199 | { | ||
200 | struct cachefiles_object *object; | ||
201 | struct cachefiles_xattr *auxdata; | ||
202 | struct cachefiles_cache *cache; | ||
203 | struct fscache_cookie *cookie; | ||
204 | const struct cred *saved_cred; | ||
205 | unsigned auxlen; | ||
206 | |||
207 | _enter("{OBJ%x}", _object->debug_id); | ||
208 | |||
209 | object = container_of(_object, struct cachefiles_object, fscache); | ||
210 | cache = container_of(object->fscache.cache, struct cachefiles_cache, | ||
211 | cache); | ||
212 | cookie = object->fscache.cookie; | ||
213 | |||
214 | if (!cookie->def->get_aux) { | ||
215 | _leave(" [no aux]"); | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | auxdata = kmalloc(2 + 512 + 3, GFP_KERNEL); | ||
220 | if (!auxdata) { | ||
221 | _leave(" [nomem]"); | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | auxlen = cookie->def->get_aux(cookie->netfs_data, auxdata->data, 511); | ||
226 | ASSERTCMP(auxlen, <, 511); | ||
227 | |||
228 | auxdata->len = auxlen + 1; | ||
229 | auxdata->type = cookie->def->type; | ||
230 | |||
231 | cachefiles_begin_secure(cache, &saved_cred); | ||
232 | cachefiles_update_object_xattr(object, auxdata); | ||
233 | cachefiles_end_secure(cache, saved_cred); | ||
234 | kfree(auxdata); | ||
235 | _leave(""); | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * discard the resources pinned by an object and effect retirement if | ||
240 | * requested | ||
241 | */ | ||
242 | static void cachefiles_drop_object(struct fscache_object *_object) | ||
243 | { | ||
244 | struct cachefiles_object *object; | ||
245 | struct cachefiles_cache *cache; | ||
246 | const struct cred *saved_cred; | ||
247 | |||
248 | ASSERT(_object); | ||
249 | |||
250 | object = container_of(_object, struct cachefiles_object, fscache); | ||
251 | |||
252 | _enter("{OBJ%x,%d}", | ||
253 | object->fscache.debug_id, atomic_read(&object->usage)); | ||
254 | |||
255 | cache = container_of(object->fscache.cache, | ||
256 | struct cachefiles_cache, cache); | ||
257 | |||
258 | #ifdef CACHEFILES_DEBUG_SLAB | ||
259 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | ||
260 | #endif | ||
261 | |||
262 | /* delete retired objects */ | ||
263 | if (object->fscache.state == FSCACHE_OBJECT_RECYCLING && | ||
264 | _object != cache->cache.fsdef | ||
265 | ) { | ||
266 | _debug("- retire object OBJ%x", object->fscache.debug_id); | ||
267 | cachefiles_begin_secure(cache, &saved_cred); | ||
268 | cachefiles_delete_object(cache, object); | ||
269 | cachefiles_end_secure(cache, saved_cred); | ||
270 | } | ||
271 | |||
272 | /* close the filesystem stuff attached to the object */ | ||
273 | if (object->backer != object->dentry) | ||
274 | dput(object->backer); | ||
275 | object->backer = NULL; | ||
276 | |||
277 | /* note that the object is now inactive */ | ||
278 | if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) { | ||
279 | write_lock(&cache->active_lock); | ||
280 | if (!test_and_clear_bit(CACHEFILES_OBJECT_ACTIVE, | ||
281 | &object->flags)) | ||
282 | BUG(); | ||
283 | rb_erase(&object->active_node, &cache->active_nodes); | ||
284 | wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE); | ||
285 | write_unlock(&cache->active_lock); | ||
286 | } | ||
287 | |||
288 | dput(object->dentry); | ||
289 | object->dentry = NULL; | ||
290 | |||
291 | _leave(""); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * dispose of a reference to an object | ||
296 | */ | ||
297 | static void cachefiles_put_object(struct fscache_object *_object) | ||
298 | { | ||
299 | struct cachefiles_object *object; | ||
300 | struct fscache_cache *cache; | ||
301 | |||
302 | ASSERT(_object); | ||
303 | |||
304 | object = container_of(_object, struct cachefiles_object, fscache); | ||
305 | |||
306 | _enter("{OBJ%x,%d}", | ||
307 | object->fscache.debug_id, atomic_read(&object->usage)); | ||
308 | |||
309 | #ifdef CACHEFILES_DEBUG_SLAB | ||
310 | ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000); | ||
311 | #endif | ||
312 | |||
313 | ASSERTIFCMP(object->fscache.parent, | ||
314 | object->fscache.parent->n_children, >, 0); | ||
315 | |||
316 | if (atomic_dec_and_test(&object->usage)) { | ||
317 | _debug("- kill object OBJ%x", object->fscache.debug_id); | ||
318 | |||
319 | ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); | ||
320 | ASSERTCMP(object->fscache.parent, ==, NULL); | ||
321 | ASSERTCMP(object->backer, ==, NULL); | ||
322 | ASSERTCMP(object->dentry, ==, NULL); | ||
323 | ASSERTCMP(object->fscache.n_ops, ==, 0); | ||
324 | ASSERTCMP(object->fscache.n_children, ==, 0); | ||
325 | |||
326 | if (object->lookup_data) { | ||
327 | kfree(object->lookup_data->key); | ||
328 | kfree(object->lookup_data->auxdata); | ||
329 | kfree(object->lookup_data); | ||
330 | object->lookup_data = NULL; | ||
331 | } | ||
332 | |||
333 | cache = object->fscache.cache; | ||
334 | kmem_cache_free(cachefiles_object_jar, object); | ||
335 | fscache_object_destroyed(cache); | ||
336 | } | ||
337 | |||
338 | _leave(""); | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * sync a cache | ||
343 | */ | ||
344 | static void cachefiles_sync_cache(struct fscache_cache *_cache) | ||
345 | { | ||
346 | struct cachefiles_cache *cache; | ||
347 | const struct cred *saved_cred; | ||
348 | int ret; | ||
349 | |||
350 | _enter("%p", _cache); | ||
351 | |||
352 | cache = container_of(_cache, struct cachefiles_cache, cache); | ||
353 | |||
354 | /* make sure all pages pinned by operations on behalf of the netfs are | ||
355 | * written to disc */ | ||
356 | cachefiles_begin_secure(cache, &saved_cred); | ||
357 | ret = fsync_super(cache->mnt->mnt_sb); | ||
358 | cachefiles_end_secure(cache, saved_cred); | ||
359 | |||
360 | if (ret == -EIO) | ||
361 | cachefiles_io_error(cache, | ||
362 | "Attempt to sync backing fs superblock" | ||
363 | " returned error %d", | ||
364 | ret); | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * notification the attributes on an object have changed | ||
369 | * - called with reads/writes excluded by FS-Cache | ||
370 | */ | ||
371 | static int cachefiles_attr_changed(struct fscache_object *_object) | ||
372 | { | ||
373 | struct cachefiles_object *object; | ||
374 | struct cachefiles_cache *cache; | ||
375 | const struct cred *saved_cred; | ||
376 | struct iattr newattrs; | ||
377 | uint64_t ni_size; | ||
378 | loff_t oi_size; | ||
379 | int ret; | ||
380 | |||
381 | _object->cookie->def->get_attr(_object->cookie->netfs_data, &ni_size); | ||
382 | |||
383 | _enter("{OBJ%x},[%llu]", | ||
384 | _object->debug_id, (unsigned long long) ni_size); | ||
385 | |||
386 | object = container_of(_object, struct cachefiles_object, fscache); | ||
387 | cache = container_of(object->fscache.cache, | ||
388 | struct cachefiles_cache, cache); | ||
389 | |||
390 | if (ni_size == object->i_size) | ||
391 | return 0; | ||
392 | |||
393 | if (!object->backer) | ||
394 | return -ENOBUFS; | ||
395 | |||
396 | ASSERT(S_ISREG(object->backer->d_inode->i_mode)); | ||
397 | |||
398 | fscache_set_store_limit(&object->fscache, ni_size); | ||
399 | |||
400 | oi_size = i_size_read(object->backer->d_inode); | ||
401 | if (oi_size == ni_size) | ||
402 | return 0; | ||
403 | |||
404 | newattrs.ia_size = ni_size; | ||
405 | newattrs.ia_valid = ATTR_SIZE; | ||
406 | |||
407 | cachefiles_begin_secure(cache, &saved_cred); | ||
408 | mutex_lock(&object->backer->d_inode->i_mutex); | ||
409 | ret = notify_change(object->backer, &newattrs); | ||
410 | mutex_unlock(&object->backer->d_inode->i_mutex); | ||
411 | cachefiles_end_secure(cache, saved_cred); | ||
412 | |||
413 | if (ret == -EIO) { | ||
414 | fscache_set_store_limit(&object->fscache, 0); | ||
415 | cachefiles_io_error_obj(object, "Size set failed"); | ||
416 | ret = -ENOBUFS; | ||
417 | } | ||
418 | |||
419 | _leave(" = %d", ret); | ||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * dissociate a cache from all the pages it was backing | ||
425 | */ | ||
426 | static void cachefiles_dissociate_pages(struct fscache_cache *cache) | ||
427 | { | ||
428 | _enter(""); | ||
429 | } | ||
430 | |||
431 | const struct fscache_cache_ops cachefiles_cache_ops = { | ||
432 | .name = "cachefiles", | ||
433 | .alloc_object = cachefiles_alloc_object, | ||
434 | .lookup_object = cachefiles_lookup_object, | ||
435 | .lookup_complete = cachefiles_lookup_complete, | ||
436 | .grab_object = cachefiles_grab_object, | ||
437 | .update_object = cachefiles_update_object, | ||
438 | .drop_object = cachefiles_drop_object, | ||
439 | .put_object = cachefiles_put_object, | ||
440 | .sync_cache = cachefiles_sync_cache, | ||
441 | .attr_changed = cachefiles_attr_changed, | ||
442 | .read_or_alloc_page = cachefiles_read_or_alloc_page, | ||
443 | .read_or_alloc_pages = cachefiles_read_or_alloc_pages, | ||
444 | .allocate_page = cachefiles_allocate_page, | ||
445 | .allocate_pages = cachefiles_allocate_pages, | ||
446 | .write_page = cachefiles_write_page, | ||
447 | .uncache_page = cachefiles_uncache_page, | ||
448 | .dissociate_pages = cachefiles_dissociate_pages, | ||
449 | }; | ||
diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h new file mode 100644 index 000000000000..19218e1463d6 --- /dev/null +++ b/fs/cachefiles/internal.h | |||
@@ -0,0 +1,360 @@ | |||
1 | /* General netfs cache on cache files internal defs | ||
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/fscache-cache.h> | ||
13 | #include <linux/timer.h> | ||
14 | #include <linux/wait.h> | ||
15 | #include <linux/workqueue.h> | ||
16 | #include <linux/security.h> | ||
17 | |||
18 | struct cachefiles_cache; | ||
19 | struct cachefiles_object; | ||
20 | |||
21 | extern unsigned cachefiles_debug; | ||
22 | #define CACHEFILES_DEBUG_KENTER 1 | ||
23 | #define CACHEFILES_DEBUG_KLEAVE 2 | ||
24 | #define CACHEFILES_DEBUG_KDEBUG 4 | ||
25 | |||
26 | /* | ||
27 | * node records | ||
28 | */ | ||
29 | struct cachefiles_object { | ||
30 | struct fscache_object fscache; /* fscache handle */ | ||
31 | struct cachefiles_lookup_data *lookup_data; /* cached lookup data */ | ||
32 | struct dentry *dentry; /* the file/dir representing this object */ | ||
33 | struct dentry *backer; /* backing file */ | ||
34 | loff_t i_size; /* object size */ | ||
35 | unsigned long flags; | ||
36 | #define CACHEFILES_OBJECT_ACTIVE 0 /* T if marked active */ | ||
37 | atomic_t usage; /* object usage count */ | ||
38 | uint8_t type; /* object type */ | ||
39 | uint8_t new; /* T if object new */ | ||
40 | spinlock_t work_lock; | ||
41 | struct rb_node active_node; /* link in active tree (dentry is key) */ | ||
42 | }; | ||
43 | |||
44 | extern struct kmem_cache *cachefiles_object_jar; | ||
45 | |||
46 | /* | ||
47 | * Cache files cache definition | ||
48 | */ | ||
49 | struct cachefiles_cache { | ||
50 | struct fscache_cache cache; /* FS-Cache record */ | ||
51 | struct vfsmount *mnt; /* mountpoint holding the cache */ | ||
52 | struct dentry *graveyard; /* directory into which dead objects go */ | ||
53 | struct file *cachefilesd; /* manager daemon handle */ | ||
54 | const struct cred *cache_cred; /* security override for accessing cache */ | ||
55 | struct mutex daemon_mutex; /* command serialisation mutex */ | ||
56 | wait_queue_head_t daemon_pollwq; /* poll waitqueue for daemon */ | ||
57 | struct rb_root active_nodes; /* active nodes (can't be culled) */ | ||
58 | rwlock_t active_lock; /* lock for active_nodes */ | ||
59 | atomic_t gravecounter; /* graveyard uniquifier */ | ||
60 | unsigned frun_percent; /* when to stop culling (% files) */ | ||
61 | unsigned fcull_percent; /* when to start culling (% files) */ | ||
62 | unsigned fstop_percent; /* when to stop allocating (% files) */ | ||
63 | unsigned brun_percent; /* when to stop culling (% blocks) */ | ||
64 | unsigned bcull_percent; /* when to start culling (% blocks) */ | ||
65 | unsigned bstop_percent; /* when to stop allocating (% blocks) */ | ||
66 | unsigned bsize; /* cache's block size */ | ||
67 | unsigned bshift; /* min(ilog2(PAGE_SIZE / bsize), 0) */ | ||
68 | uint64_t frun; /* when to stop culling */ | ||
69 | uint64_t fcull; /* when to start culling */ | ||
70 | uint64_t fstop; /* when to stop allocating */ | ||
71 | sector_t brun; /* when to stop culling */ | ||
72 | sector_t bcull; /* when to start culling */ | ||
73 | sector_t bstop; /* when to stop allocating */ | ||
74 | unsigned long flags; | ||
75 | #define CACHEFILES_READY 0 /* T if cache prepared */ | ||
76 | #define CACHEFILES_DEAD 1 /* T if cache dead */ | ||
77 | #define CACHEFILES_CULLING 2 /* T if cull engaged */ | ||
78 | #define CACHEFILES_STATE_CHANGED 3 /* T if state changed (poll trigger) */ | ||
79 | char *rootdirname; /* name of cache root directory */ | ||
80 | char *secctx; /* LSM security context */ | ||
81 | char *tag; /* cache binding tag */ | ||
82 | }; | ||
83 | |||
84 | /* | ||
85 | * backing file read tracking | ||
86 | */ | ||
87 | struct cachefiles_one_read { | ||
88 | wait_queue_t monitor; /* link into monitored waitqueue */ | ||
89 | struct page *back_page; /* backing file page we're waiting for */ | ||
90 | struct page *netfs_page; /* netfs page we're going to fill */ | ||
91 | struct fscache_retrieval *op; /* retrieval op covering this */ | ||
92 | struct list_head op_link; /* link in op's todo list */ | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * backing file write tracking | ||
97 | */ | ||
98 | struct cachefiles_one_write { | ||
99 | struct page *netfs_page; /* netfs page to copy */ | ||
100 | struct cachefiles_object *object; | ||
101 | struct list_head obj_link; /* link in object's lists */ | ||
102 | fscache_rw_complete_t end_io_func; | ||
103 | void *context; | ||
104 | }; | ||
105 | |||
106 | /* | ||
107 | * auxiliary data xattr buffer | ||
108 | */ | ||
109 | struct cachefiles_xattr { | ||
110 | uint16_t len; | ||
111 | uint8_t type; | ||
112 | uint8_t data[]; | ||
113 | }; | ||
114 | |||
115 | /* | ||
116 | * note change of state for daemon | ||
117 | */ | ||
118 | static inline void cachefiles_state_changed(struct cachefiles_cache *cache) | ||
119 | { | ||
120 | set_bit(CACHEFILES_STATE_CHANGED, &cache->flags); | ||
121 | wake_up_all(&cache->daemon_pollwq); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * cf-bind.c | ||
126 | */ | ||
127 | extern int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args); | ||
128 | extern void cachefiles_daemon_unbind(struct cachefiles_cache *cache); | ||
129 | |||
130 | /* | ||
131 | * cf-daemon.c | ||
132 | */ | ||
133 | extern const struct file_operations cachefiles_daemon_fops; | ||
134 | |||
135 | extern int cachefiles_has_space(struct cachefiles_cache *cache, | ||
136 | unsigned fnr, unsigned bnr); | ||
137 | |||
138 | /* | ||
139 | * cf-interface.c | ||
140 | */ | ||
141 | extern const struct fscache_cache_ops cachefiles_cache_ops; | ||
142 | |||
143 | /* | ||
144 | * cf-key.c | ||
145 | */ | ||
146 | extern char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type); | ||
147 | |||
148 | /* | ||
149 | * cf-namei.c | ||
150 | */ | ||
151 | extern int cachefiles_delete_object(struct cachefiles_cache *cache, | ||
152 | struct cachefiles_object *object); | ||
153 | extern int cachefiles_walk_to_object(struct cachefiles_object *parent, | ||
154 | struct cachefiles_object *object, | ||
155 | const char *key, | ||
156 | struct cachefiles_xattr *auxdata); | ||
157 | extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, | ||
158 | struct dentry *dir, | ||
159 | const char *name); | ||
160 | |||
161 | extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, | ||
162 | char *filename); | ||
163 | |||
164 | extern int cachefiles_check_in_use(struct cachefiles_cache *cache, | ||
165 | struct dentry *dir, char *filename); | ||
166 | |||
167 | /* | ||
168 | * cf-proc.c | ||
169 | */ | ||
170 | #ifdef CONFIG_CACHEFILES_HISTOGRAM | ||
171 | extern atomic_t cachefiles_lookup_histogram[HZ]; | ||
172 | extern atomic_t cachefiles_mkdir_histogram[HZ]; | ||
173 | extern atomic_t cachefiles_create_histogram[HZ]; | ||
174 | |||
175 | extern int __init cachefiles_proc_init(void); | ||
176 | extern void cachefiles_proc_cleanup(void); | ||
177 | static inline | ||
178 | void cachefiles_hist(atomic_t histogram[], unsigned long start_jif) | ||
179 | { | ||
180 | unsigned long jif = jiffies - start_jif; | ||
181 | if (jif >= HZ) | ||
182 | jif = HZ - 1; | ||
183 | atomic_inc(&histogram[jif]); | ||
184 | } | ||
185 | |||
186 | #else | ||
187 | #define cachefiles_proc_init() (0) | ||
188 | #define cachefiles_proc_cleanup() do {} while (0) | ||
189 | #define cachefiles_hist(hist, start_jif) do {} while (0) | ||
190 | #endif | ||
191 | |||
192 | /* | ||
193 | * cf-rdwr.c | ||
194 | */ | ||
195 | extern int cachefiles_read_or_alloc_page(struct fscache_retrieval *, | ||
196 | struct page *, gfp_t); | ||
197 | extern int cachefiles_read_or_alloc_pages(struct fscache_retrieval *, | ||
198 | struct list_head *, unsigned *, | ||
199 | gfp_t); | ||
200 | extern int cachefiles_allocate_page(struct fscache_retrieval *, struct page *, | ||
201 | gfp_t); | ||
202 | extern int cachefiles_allocate_pages(struct fscache_retrieval *, | ||
203 | struct list_head *, unsigned *, gfp_t); | ||
204 | extern int cachefiles_write_page(struct fscache_storage *, struct page *); | ||
205 | extern void cachefiles_uncache_page(struct fscache_object *, struct page *); | ||
206 | |||
207 | /* | ||
208 | * cf-security.c | ||
209 | */ | ||
210 | extern int cachefiles_get_security_ID(struct cachefiles_cache *cache); | ||
211 | extern int cachefiles_determine_cache_security(struct cachefiles_cache *cache, | ||
212 | struct dentry *root, | ||
213 | const struct cred **_saved_cred); | ||
214 | |||
215 | static inline void cachefiles_begin_secure(struct cachefiles_cache *cache, | ||
216 | const struct cred **_saved_cred) | ||
217 | { | ||
218 | *_saved_cred = override_creds(cache->cache_cred); | ||
219 | } | ||
220 | |||
221 | static inline void cachefiles_end_secure(struct cachefiles_cache *cache, | ||
222 | const struct cred *saved_cred) | ||
223 | { | ||
224 | revert_creds(saved_cred); | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * cf-xattr.c | ||
229 | */ | ||
230 | extern int cachefiles_check_object_type(struct cachefiles_object *object); | ||
231 | extern int cachefiles_set_object_xattr(struct cachefiles_object *object, | ||
232 | struct cachefiles_xattr *auxdata); | ||
233 | extern int cachefiles_update_object_xattr(struct cachefiles_object *object, | ||
234 | struct cachefiles_xattr *auxdata); | ||
235 | extern int cachefiles_check_object_xattr(struct cachefiles_object *object, | ||
236 | struct cachefiles_xattr *auxdata); | ||
237 | extern int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, | ||
238 | struct dentry *dentry); | ||
239 | |||
240 | |||
241 | /* | ||
242 | * error handling | ||
243 | */ | ||
244 | #define kerror(FMT, ...) printk(KERN_ERR "CacheFiles: "FMT"\n", ##__VA_ARGS__) | ||
245 | |||
246 | #define cachefiles_io_error(___cache, FMT, ...) \ | ||
247 | do { \ | ||
248 | kerror("I/O Error: " FMT, ##__VA_ARGS__); \ | ||
249 | fscache_io_error(&(___cache)->cache); \ | ||
250 | set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ | ||
251 | } while (0) | ||
252 | |||
253 | #define cachefiles_io_error_obj(object, FMT, ...) \ | ||
254 | do { \ | ||
255 | struct cachefiles_cache *___cache; \ | ||
256 | \ | ||
257 | ___cache = container_of((object)->fscache.cache, \ | ||
258 | struct cachefiles_cache, cache); \ | ||
259 | cachefiles_io_error(___cache, FMT, ##__VA_ARGS__); \ | ||
260 | } while (0) | ||
261 | |||
262 | |||
263 | /* | ||
264 | * debug tracing | ||
265 | */ | ||
266 | #define dbgprintk(FMT, ...) \ | ||
267 | printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) | ||
268 | |||
269 | /* make sure we maintain the format strings, even when debugging is disabled */ | ||
270 | static inline void _dbprintk(const char *fmt, ...) | ||
271 | __attribute__((format(printf, 1, 2))); | ||
272 | static inline void _dbprintk(const char *fmt, ...) | ||
273 | { | ||
274 | } | ||
275 | |||
276 | #define kenter(FMT, ...) dbgprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) | ||
277 | #define kleave(FMT, ...) dbgprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) | ||
278 | #define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) | ||
279 | |||
280 | |||
281 | #if defined(__KDEBUG) | ||
282 | #define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) | ||
283 | #define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__) | ||
284 | #define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__) | ||
285 | |||
286 | #elif defined(CONFIG_CACHEFILES_DEBUG) | ||
287 | #define _enter(FMT, ...) \ | ||
288 | do { \ | ||
289 | if (cachefiles_debug & CACHEFILES_DEBUG_KENTER) \ | ||
290 | kenter(FMT, ##__VA_ARGS__); \ | ||
291 | } while (0) | ||
292 | |||
293 | #define _leave(FMT, ...) \ | ||
294 | do { \ | ||
295 | if (cachefiles_debug & CACHEFILES_DEBUG_KLEAVE) \ | ||
296 | kleave(FMT, ##__VA_ARGS__); \ | ||
297 | } while (0) | ||
298 | |||
299 | #define _debug(FMT, ...) \ | ||
300 | do { \ | ||
301 | if (cachefiles_debug & CACHEFILES_DEBUG_KDEBUG) \ | ||
302 | kdebug(FMT, ##__VA_ARGS__); \ | ||
303 | } while (0) | ||
304 | |||
305 | #else | ||
306 | #define _enter(FMT, ...) _dbprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) | ||
307 | #define _leave(FMT, ...) _dbprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) | ||
308 | #define _debug(FMT, ...) _dbprintk(FMT, ##__VA_ARGS__) | ||
309 | #endif | ||
310 | |||
311 | #if 1 /* defined(__KDEBUGALL) */ | ||
312 | |||
313 | #define ASSERT(X) \ | ||
314 | do { \ | ||
315 | if (unlikely(!(X))) { \ | ||
316 | printk(KERN_ERR "\n"); \ | ||
317 | printk(KERN_ERR "CacheFiles: Assertion failed\n"); \ | ||
318 | BUG(); \ | ||
319 | } \ | ||
320 | } while (0) | ||
321 | |||
322 | #define ASSERTCMP(X, OP, Y) \ | ||
323 | do { \ | ||
324 | if (unlikely(!((X) OP (Y)))) { \ | ||
325 | printk(KERN_ERR "\n"); \ | ||
326 | printk(KERN_ERR "CacheFiles: Assertion failed\n"); \ | ||
327 | printk(KERN_ERR "%lx " #OP " %lx is false\n", \ | ||
328 | (unsigned long)(X), (unsigned long)(Y)); \ | ||
329 | BUG(); \ | ||
330 | } \ | ||
331 | } while (0) | ||
332 | |||
333 | #define ASSERTIF(C, X) \ | ||
334 | do { \ | ||
335 | if (unlikely((C) && !(X))) { \ | ||
336 | printk(KERN_ERR "\n"); \ | ||
337 | printk(KERN_ERR "CacheFiles: Assertion failed\n"); \ | ||
338 | BUG(); \ | ||
339 | } \ | ||
340 | } while (0) | ||
341 | |||
342 | #define ASSERTIFCMP(C, X, OP, Y) \ | ||
343 | do { \ | ||
344 | if (unlikely((C) && !((X) OP (Y)))) { \ | ||
345 | printk(KERN_ERR "\n"); \ | ||
346 | printk(KERN_ERR "CacheFiles: Assertion failed\n"); \ | ||
347 | printk(KERN_ERR "%lx " #OP " %lx is false\n", \ | ||
348 | (unsigned long)(X), (unsigned long)(Y)); \ | ||
349 | BUG(); \ | ||
350 | } \ | ||
351 | } while (0) | ||
352 | |||
353 | #else | ||
354 | |||
355 | #define ASSERT(X) do {} while (0) | ||
356 | #define ASSERTCMP(X, OP, Y) do {} while (0) | ||
357 | #define ASSERTIF(C, X) do {} while (0) | ||
358 | #define ASSERTIFCMP(C, X, OP, Y) do {} while (0) | ||
359 | |||
360 | #endif | ||
diff --git a/fs/cachefiles/key.c b/fs/cachefiles/key.c new file mode 100644 index 000000000000..81b8b2b3a674 --- /dev/null +++ b/fs/cachefiles/key.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* Key to pathname encoder | ||
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/slab.h> | ||
13 | #include "internal.h" | ||
14 | |||
15 | static const char cachefiles_charmap[64] = | ||
16 | "0123456789" /* 0 - 9 */ | ||
17 | "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ | ||
18 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */ | ||
19 | "_-" /* 62 - 63 */ | ||
20 | ; | ||
21 | |||
22 | static const char cachefiles_filecharmap[256] = { | ||
23 | /* we skip space and tab and control chars */ | ||
24 | [33 ... 46] = 1, /* '!' -> '.' */ | ||
25 | /* we skip '/' as it's significant to pathwalk */ | ||
26 | [48 ... 127] = 1, /* '0' -> '~' */ | ||
27 | }; | ||
28 | |||
29 | /* | ||
30 | * turn the raw key into something cooked | ||
31 | * - the raw key should include the length in the two bytes at the front | ||
32 | * - the key may be up to 514 bytes in length (including the length word) | ||
33 | * - "base64" encode the strange keys, mapping 3 bytes of raw to four of | ||
34 | * cooked | ||
35 | * - need to cut the cooked key into 252 char lengths (189 raw bytes) | ||
36 | */ | ||
37 | char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type) | ||
38 | { | ||
39 | unsigned char csum, ch; | ||
40 | unsigned int acc; | ||
41 | char *key; | ||
42 | int loop, len, max, seg, mark, print; | ||
43 | |||
44 | _enter(",%d", keylen); | ||
45 | |||
46 | BUG_ON(keylen < 2 || keylen > 514); | ||
47 | |||
48 | csum = raw[0] + raw[1]; | ||
49 | print = 1; | ||
50 | for (loop = 2; loop < keylen; loop++) { | ||
51 | ch = raw[loop]; | ||
52 | csum += ch; | ||
53 | print &= cachefiles_filecharmap[ch]; | ||
54 | } | ||
55 | |||
56 | if (print) { | ||
57 | /* if the path is usable ASCII, then we render it directly */ | ||
58 | max = keylen - 2; | ||
59 | max += 2; /* two base64'd length chars on the front */ | ||
60 | max += 5; /* @checksum/M */ | ||
61 | max += 3 * 2; /* maximum number of segment dividers (".../M") | ||
62 | * is ((514 + 251) / 252) = 3 | ||
63 | */ | ||
64 | max += 1; /* NUL on end */ | ||
65 | } else { | ||
66 | /* calculate the maximum length of the cooked key */ | ||
67 | keylen = (keylen + 2) / 3; | ||
68 | |||
69 | max = keylen * 4; | ||
70 | max += 5; /* @checksum/M */ | ||
71 | max += 3 * 2; /* maximum number of segment dividers (".../M") | ||
72 | * is ((514 + 188) / 189) = 3 | ||
73 | */ | ||
74 | max += 1; /* NUL on end */ | ||
75 | } | ||
76 | |||
77 | max += 1; /* 2nd NUL on end */ | ||
78 | |||
79 | _debug("max: %d", max); | ||
80 | |||
81 | key = kmalloc(max, GFP_KERNEL); | ||
82 | if (!key) | ||
83 | return NULL; | ||
84 | |||
85 | len = 0; | ||
86 | |||
87 | /* build the cooked key */ | ||
88 | sprintf(key, "@%02x%c+", (unsigned) csum, 0); | ||
89 | len = 5; | ||
90 | mark = len - 1; | ||
91 | |||
92 | if (print) { | ||
93 | acc = *(uint16_t *) raw; | ||
94 | raw += 2; | ||
95 | |||
96 | key[len + 1] = cachefiles_charmap[acc & 63]; | ||
97 | acc >>= 6; | ||
98 | key[len] = cachefiles_charmap[acc & 63]; | ||
99 | len += 2; | ||
100 | |||
101 | seg = 250; | ||
102 | for (loop = keylen; loop > 0; loop--) { | ||
103 | if (seg <= 0) { | ||
104 | key[len++] = '\0'; | ||
105 | mark = len; | ||
106 | key[len++] = '+'; | ||
107 | seg = 252; | ||
108 | } | ||
109 | |||
110 | key[len++] = *raw++; | ||
111 | ASSERT(len < max); | ||
112 | } | ||
113 | |||
114 | switch (type) { | ||
115 | case FSCACHE_COOKIE_TYPE_INDEX: type = 'I'; break; | ||
116 | case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'D'; break; | ||
117 | default: type = 'S'; break; | ||
118 | } | ||
119 | } else { | ||
120 | seg = 252; | ||
121 | for (loop = keylen; loop > 0; loop--) { | ||
122 | if (seg <= 0) { | ||
123 | key[len++] = '\0'; | ||
124 | mark = len; | ||
125 | key[len++] = '+'; | ||
126 | seg = 252; | ||
127 | } | ||
128 | |||
129 | acc = *raw++; | ||
130 | acc |= *raw++ << 8; | ||
131 | acc |= *raw++ << 16; | ||
132 | |||
133 | _debug("acc: %06x", acc); | ||
134 | |||
135 | key[len++] = cachefiles_charmap[acc & 63]; | ||
136 | acc >>= 6; | ||
137 | key[len++] = cachefiles_charmap[acc & 63]; | ||
138 | acc >>= 6; | ||
139 | key[len++] = cachefiles_charmap[acc & 63]; | ||
140 | acc >>= 6; | ||
141 | key[len++] = cachefiles_charmap[acc & 63]; | ||
142 | |||
143 | ASSERT(len < max); | ||
144 | } | ||
145 | |||
146 | switch (type) { | ||
147 | case FSCACHE_COOKIE_TYPE_INDEX: type = 'J'; break; | ||
148 | case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'E'; break; | ||
149 | default: type = 'T'; break; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | key[mark] = type; | ||
154 | key[len++] = 0; | ||
155 | key[len] = 0; | ||
156 | |||
157 | _leave(" = %p %d", key, len); | ||
158 | return key; | ||
159 | } | ||
diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c new file mode 100644 index 000000000000..4bfa8cf43bf5 --- /dev/null +++ b/fs/cachefiles/main.c | |||
@@ -0,0 +1,106 @@ | |||
1 | /* Network filesystem caching backend to use cache files on a premounted | ||
2 | * filesystem | ||
3 | * | ||
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
5 | * Written by David Howells (dhowells@redhat.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/completion.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/file.h> | ||
20 | #include <linux/namei.h> | ||
21 | #include <linux/mount.h> | ||
22 | #include <linux/statfs.h> | ||
23 | #include <linux/sysctl.h> | ||
24 | #include <linux/miscdevice.h> | ||
25 | #include "internal.h" | ||
26 | |||
27 | unsigned cachefiles_debug; | ||
28 | module_param_named(debug, cachefiles_debug, uint, S_IWUSR | S_IRUGO); | ||
29 | MODULE_PARM_DESC(cachefiles_debug, "CacheFiles debugging mask"); | ||
30 | |||
31 | MODULE_DESCRIPTION("Mounted-filesystem based cache"); | ||
32 | MODULE_AUTHOR("Red Hat, Inc."); | ||
33 | MODULE_LICENSE("GPL"); | ||
34 | |||
35 | struct kmem_cache *cachefiles_object_jar; | ||
36 | |||
37 | static struct miscdevice cachefiles_dev = { | ||
38 | .minor = MISC_DYNAMIC_MINOR, | ||
39 | .name = "cachefiles", | ||
40 | .fops = &cachefiles_daemon_fops, | ||
41 | }; | ||
42 | |||
43 | static void cachefiles_object_init_once(void *_object) | ||
44 | { | ||
45 | struct cachefiles_object *object = _object; | ||
46 | |||
47 | memset(object, 0, sizeof(*object)); | ||
48 | spin_lock_init(&object->work_lock); | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * initialise the fs caching module | ||
53 | */ | ||
54 | static int __init cachefiles_init(void) | ||
55 | { | ||
56 | int ret; | ||
57 | |||
58 | ret = misc_register(&cachefiles_dev); | ||
59 | if (ret < 0) | ||
60 | goto error_dev; | ||
61 | |||
62 | /* create an object jar */ | ||
63 | ret = -ENOMEM; | ||
64 | cachefiles_object_jar = | ||
65 | kmem_cache_create("cachefiles_object_jar", | ||
66 | sizeof(struct cachefiles_object), | ||
67 | 0, | ||
68 | SLAB_HWCACHE_ALIGN, | ||
69 | cachefiles_object_init_once); | ||
70 | if (!cachefiles_object_jar) { | ||
71 | printk(KERN_NOTICE | ||
72 | "CacheFiles: Failed to allocate an object jar\n"); | ||
73 | goto error_object_jar; | ||
74 | } | ||
75 | |||
76 | ret = cachefiles_proc_init(); | ||
77 | if (ret < 0) | ||
78 | goto error_proc; | ||
79 | |||
80 | printk(KERN_INFO "CacheFiles: Loaded\n"); | ||
81 | return 0; | ||
82 | |||
83 | error_proc: | ||
84 | kmem_cache_destroy(cachefiles_object_jar); | ||
85 | error_object_jar: | ||
86 | misc_deregister(&cachefiles_dev); | ||
87 | error_dev: | ||
88 | kerror("failed to register: %d", ret); | ||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | fs_initcall(cachefiles_init); | ||
93 | |||
94 | /* | ||
95 | * clean up on module removal | ||
96 | */ | ||
97 | static void __exit cachefiles_exit(void) | ||
98 | { | ||
99 | printk(KERN_INFO "CacheFiles: Unloading\n"); | ||
100 | |||
101 | cachefiles_proc_cleanup(); | ||
102 | kmem_cache_destroy(cachefiles_object_jar); | ||
103 | misc_deregister(&cachefiles_dev); | ||
104 | } | ||
105 | |||
106 | module_exit(cachefiles_exit); | ||
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c new file mode 100644 index 000000000000..4ce818ae39ea --- /dev/null +++ b/fs/cachefiles/namei.c | |||
@@ -0,0 +1,771 @@ | |||
1 | /* CacheFiles path walking and related routines | ||
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/sched.h> | ||
14 | #include <linux/file.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/fsnotify.h> | ||
17 | #include <linux/quotaops.h> | ||
18 | #include <linux/xattr.h> | ||
19 | #include <linux/mount.h> | ||
20 | #include <linux/namei.h> | ||
21 | #include <linux/security.h> | ||
22 | #include "internal.h" | ||
23 | |||
24 | static int cachefiles_wait_bit(void *flags) | ||
25 | { | ||
26 | schedule(); | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | /* | ||
31 | * record the fact that an object is now active | ||
32 | */ | ||
33 | static void cachefiles_mark_object_active(struct cachefiles_cache *cache, | ||
34 | struct cachefiles_object *object) | ||
35 | { | ||
36 | struct cachefiles_object *xobject; | ||
37 | struct rb_node **_p, *_parent = NULL; | ||
38 | struct dentry *dentry; | ||
39 | |||
40 | _enter(",%p", object); | ||
41 | |||
42 | try_again: | ||
43 | write_lock(&cache->active_lock); | ||
44 | |||
45 | if (test_and_set_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) | ||
46 | BUG(); | ||
47 | |||
48 | dentry = object->dentry; | ||
49 | _p = &cache->active_nodes.rb_node; | ||
50 | while (*_p) { | ||
51 | _parent = *_p; | ||
52 | xobject = rb_entry(_parent, | ||
53 | struct cachefiles_object, active_node); | ||
54 | |||
55 | ASSERT(xobject != object); | ||
56 | |||
57 | if (xobject->dentry > dentry) | ||
58 | _p = &(*_p)->rb_left; | ||
59 | else if (xobject->dentry < dentry) | ||
60 | _p = &(*_p)->rb_right; | ||
61 | else | ||
62 | goto wait_for_old_object; | ||
63 | } | ||
64 | |||
65 | rb_link_node(&object->active_node, _parent, _p); | ||
66 | rb_insert_color(&object->active_node, &cache->active_nodes); | ||
67 | |||
68 | write_unlock(&cache->active_lock); | ||
69 | _leave(""); | ||
70 | return; | ||
71 | |||
72 | /* an old object from a previous incarnation is hogging the slot - we | ||
73 | * need to wait for it to be destroyed */ | ||
74 | wait_for_old_object: | ||
75 | if (xobject->fscache.state < FSCACHE_OBJECT_DYING) { | ||
76 | printk(KERN_ERR "\n"); | ||
77 | printk(KERN_ERR "CacheFiles: Error:" | ||
78 | " Unexpected object collision\n"); | ||
79 | printk(KERN_ERR "xobject: OBJ%x\n", | ||
80 | xobject->fscache.debug_id); | ||
81 | printk(KERN_ERR "xobjstate=%s\n", | ||
82 | fscache_object_states[xobject->fscache.state]); | ||
83 | printk(KERN_ERR "xobjflags=%lx\n", xobject->fscache.flags); | ||
84 | printk(KERN_ERR "xobjevent=%lx [%lx]\n", | ||
85 | xobject->fscache.events, xobject->fscache.event_mask); | ||
86 | printk(KERN_ERR "xops=%u inp=%u exc=%u\n", | ||
87 | xobject->fscache.n_ops, xobject->fscache.n_in_progress, | ||
88 | xobject->fscache.n_exclusive); | ||
89 | printk(KERN_ERR "xcookie=%p [pr=%p nd=%p fl=%lx]\n", | ||
90 | xobject->fscache.cookie, | ||
91 | xobject->fscache.cookie->parent, | ||
92 | xobject->fscache.cookie->netfs_data, | ||
93 | xobject->fscache.cookie->flags); | ||
94 | printk(KERN_ERR "xparent=%p\n", | ||
95 | xobject->fscache.parent); | ||
96 | printk(KERN_ERR "object: OBJ%x\n", | ||
97 | object->fscache.debug_id); | ||
98 | printk(KERN_ERR "cookie=%p [pr=%p nd=%p fl=%lx]\n", | ||
99 | object->fscache.cookie, | ||
100 | object->fscache.cookie->parent, | ||
101 | object->fscache.cookie->netfs_data, | ||
102 | object->fscache.cookie->flags); | ||
103 | printk(KERN_ERR "parent=%p\n", | ||
104 | object->fscache.parent); | ||
105 | BUG(); | ||
106 | } | ||
107 | atomic_inc(&xobject->usage); | ||
108 | write_unlock(&cache->active_lock); | ||
109 | |||
110 | _debug(">>> wait"); | ||
111 | wait_on_bit(&xobject->flags, CACHEFILES_OBJECT_ACTIVE, | ||
112 | cachefiles_wait_bit, TASK_UNINTERRUPTIBLE); | ||
113 | _debug("<<< waited"); | ||
114 | |||
115 | cache->cache.ops->put_object(&xobject->fscache); | ||
116 | goto try_again; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * delete an object representation from the cache | ||
121 | * - file backed objects are unlinked | ||
122 | * - directory backed objects are stuffed into the graveyard for userspace to | ||
123 | * delete | ||
124 | * - unlocks the directory mutex | ||
125 | */ | ||
126 | static int cachefiles_bury_object(struct cachefiles_cache *cache, | ||
127 | struct dentry *dir, | ||
128 | struct dentry *rep) | ||
129 | { | ||
130 | struct dentry *grave, *trap; | ||
131 | char nbuffer[8 + 8 + 1]; | ||
132 | int ret; | ||
133 | |||
134 | _enter(",'%*.*s','%*.*s'", | ||
135 | dir->d_name.len, dir->d_name.len, dir->d_name.name, | ||
136 | rep->d_name.len, rep->d_name.len, rep->d_name.name); | ||
137 | |||
138 | /* non-directories can just be unlinked */ | ||
139 | if (!S_ISDIR(rep->d_inode->i_mode)) { | ||
140 | _debug("unlink stale object"); | ||
141 | ret = vfs_unlink(dir->d_inode, rep); | ||
142 | |||
143 | mutex_unlock(&dir->d_inode->i_mutex); | ||
144 | |||
145 | if (ret == -EIO) | ||
146 | cachefiles_io_error(cache, "Unlink failed"); | ||
147 | |||
148 | _leave(" = %d", ret); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | /* directories have to be moved to the graveyard */ | ||
153 | _debug("move stale object to graveyard"); | ||
154 | mutex_unlock(&dir->d_inode->i_mutex); | ||
155 | |||
156 | try_again: | ||
157 | /* first step is to make up a grave dentry in the graveyard */ | ||
158 | sprintf(nbuffer, "%08x%08x", | ||
159 | (uint32_t) get_seconds(), | ||
160 | (uint32_t) atomic_inc_return(&cache->gravecounter)); | ||
161 | |||
162 | /* do the multiway lock magic */ | ||
163 | trap = lock_rename(cache->graveyard, dir); | ||
164 | |||
165 | /* do some checks before getting the grave dentry */ | ||
166 | if (rep->d_parent != dir) { | ||
167 | /* the entry was probably culled when we dropped the parent dir | ||
168 | * lock */ | ||
169 | unlock_rename(cache->graveyard, dir); | ||
170 | _leave(" = 0 [culled?]"); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | if (!S_ISDIR(cache->graveyard->d_inode->i_mode)) { | ||
175 | unlock_rename(cache->graveyard, dir); | ||
176 | cachefiles_io_error(cache, "Graveyard no longer a directory"); | ||
177 | return -EIO; | ||
178 | } | ||
179 | |||
180 | if (trap == rep) { | ||
181 | unlock_rename(cache->graveyard, dir); | ||
182 | cachefiles_io_error(cache, "May not make directory loop"); | ||
183 | return -EIO; | ||
184 | } | ||
185 | |||
186 | if (d_mountpoint(rep)) { | ||
187 | unlock_rename(cache->graveyard, dir); | ||
188 | cachefiles_io_error(cache, "Mountpoint in cache"); | ||
189 | return -EIO; | ||
190 | } | ||
191 | |||
192 | grave = lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer)); | ||
193 | if (IS_ERR(grave)) { | ||
194 | unlock_rename(cache->graveyard, dir); | ||
195 | |||
196 | if (PTR_ERR(grave) == -ENOMEM) { | ||
197 | _leave(" = -ENOMEM"); | ||
198 | return -ENOMEM; | ||
199 | } | ||
200 | |||
201 | cachefiles_io_error(cache, "Lookup error %ld", | ||
202 | PTR_ERR(grave)); | ||
203 | return -EIO; | ||
204 | } | ||
205 | |||
206 | if (grave->d_inode) { | ||
207 | unlock_rename(cache->graveyard, dir); | ||
208 | dput(grave); | ||
209 | grave = NULL; | ||
210 | cond_resched(); | ||
211 | goto try_again; | ||
212 | } | ||
213 | |||
214 | if (d_mountpoint(grave)) { | ||
215 | unlock_rename(cache->graveyard, dir); | ||
216 | dput(grave); | ||
217 | cachefiles_io_error(cache, "Mountpoint in graveyard"); | ||
218 | return -EIO; | ||
219 | } | ||
220 | |||
221 | /* target should not be an ancestor of source */ | ||
222 | if (trap == grave) { | ||
223 | unlock_rename(cache->graveyard, dir); | ||
224 | dput(grave); | ||
225 | cachefiles_io_error(cache, "May not make directory loop"); | ||
226 | return -EIO; | ||
227 | } | ||
228 | |||
229 | /* attempt the rename */ | ||
230 | ret = vfs_rename(dir->d_inode, rep, cache->graveyard->d_inode, grave); | ||
231 | if (ret != 0 && ret != -ENOMEM) | ||
232 | cachefiles_io_error(cache, "Rename failed with error %d", ret); | ||
233 | |||
234 | unlock_rename(cache->graveyard, dir); | ||
235 | dput(grave); | ||
236 | _leave(" = 0"); | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * delete an object representation from the cache | ||
242 | */ | ||
243 | int cachefiles_delete_object(struct cachefiles_cache *cache, | ||
244 | struct cachefiles_object *object) | ||
245 | { | ||
246 | struct dentry *dir; | ||
247 | int ret; | ||
248 | |||
249 | _enter(",{%p}", object->dentry); | ||
250 | |||
251 | ASSERT(object->dentry); | ||
252 | ASSERT(object->dentry->d_inode); | ||
253 | ASSERT(object->dentry->d_parent); | ||
254 | |||
255 | dir = dget_parent(object->dentry); | ||
256 | |||
257 | mutex_lock(&dir->d_inode->i_mutex); | ||
258 | ret = cachefiles_bury_object(cache, dir, object->dentry); | ||
259 | |||
260 | dput(dir); | ||
261 | _leave(" = %d", ret); | ||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * walk from the parent object to the child object through the backing | ||
267 | * filesystem, creating directories as we go | ||
268 | */ | ||
269 | int cachefiles_walk_to_object(struct cachefiles_object *parent, | ||
270 | struct cachefiles_object *object, | ||
271 | const char *key, | ||
272 | struct cachefiles_xattr *auxdata) | ||
273 | { | ||
274 | struct cachefiles_cache *cache; | ||
275 | struct dentry *dir, *next = NULL; | ||
276 | unsigned long start; | ||
277 | const char *name; | ||
278 | int ret, nlen; | ||
279 | |||
280 | _enter("{%p},,%s,", parent->dentry, key); | ||
281 | |||
282 | cache = container_of(parent->fscache.cache, | ||
283 | struct cachefiles_cache, cache); | ||
284 | |||
285 | ASSERT(parent->dentry); | ||
286 | ASSERT(parent->dentry->d_inode); | ||
287 | |||
288 | if (!(S_ISDIR(parent->dentry->d_inode->i_mode))) { | ||
289 | // TODO: convert file to dir | ||
290 | _leave("looking up in none directory"); | ||
291 | return -ENOBUFS; | ||
292 | } | ||
293 | |||
294 | dir = dget(parent->dentry); | ||
295 | |||
296 | advance: | ||
297 | /* attempt to transit the first directory component */ | ||
298 | name = key; | ||
299 | nlen = strlen(key); | ||
300 | |||
301 | /* key ends in a double NUL */ | ||
302 | key = key + nlen + 1; | ||
303 | if (!*key) | ||
304 | key = NULL; | ||
305 | |||
306 | lookup_again: | ||
307 | /* search the current directory for the element name */ | ||
308 | _debug("lookup '%s'", name); | ||
309 | |||
310 | mutex_lock(&dir->d_inode->i_mutex); | ||
311 | |||
312 | start = jiffies; | ||
313 | next = lookup_one_len(name, dir, nlen); | ||
314 | cachefiles_hist(cachefiles_lookup_histogram, start); | ||
315 | if (IS_ERR(next)) | ||
316 | goto lookup_error; | ||
317 | |||
318 | _debug("next -> %p %s", next, next->d_inode ? "positive" : "negative"); | ||
319 | |||
320 | if (!key) | ||
321 | object->new = !next->d_inode; | ||
322 | |||
323 | /* if this element of the path doesn't exist, then the lookup phase | ||
324 | * failed, and we can release any readers in the certain knowledge that | ||
325 | * there's nothing for them to actually read */ | ||
326 | if (!next->d_inode) | ||
327 | fscache_object_lookup_negative(&object->fscache); | ||
328 | |||
329 | /* we need to create the object if it's negative */ | ||
330 | if (key || object->type == FSCACHE_COOKIE_TYPE_INDEX) { | ||
331 | /* index objects and intervening tree levels must be subdirs */ | ||
332 | if (!next->d_inode) { | ||
333 | ret = cachefiles_has_space(cache, 1, 0); | ||
334 | if (ret < 0) | ||
335 | goto create_error; | ||
336 | |||
337 | start = jiffies; | ||
338 | ret = vfs_mkdir(dir->d_inode, next, 0); | ||
339 | cachefiles_hist(cachefiles_mkdir_histogram, start); | ||
340 | if (ret < 0) | ||
341 | goto create_error; | ||
342 | |||
343 | ASSERT(next->d_inode); | ||
344 | |||
345 | _debug("mkdir -> %p{%p{ino=%lu}}", | ||
346 | next, next->d_inode, next->d_inode->i_ino); | ||
347 | |||
348 | } else if (!S_ISDIR(next->d_inode->i_mode)) { | ||
349 | kerror("inode %lu is not a directory", | ||
350 | next->d_inode->i_ino); | ||
351 | ret = -ENOBUFS; | ||
352 | goto error; | ||
353 | } | ||
354 | |||
355 | } else { | ||
356 | /* non-index objects start out life as files */ | ||
357 | if (!next->d_inode) { | ||
358 | ret = cachefiles_has_space(cache, 1, 0); | ||
359 | if (ret < 0) | ||
360 | goto create_error; | ||
361 | |||
362 | start = jiffies; | ||
363 | ret = vfs_create(dir->d_inode, next, S_IFREG, NULL); | ||
364 | cachefiles_hist(cachefiles_create_histogram, start); | ||
365 | if (ret < 0) | ||
366 | goto create_error; | ||
367 | |||
368 | ASSERT(next->d_inode); | ||
369 | |||
370 | _debug("create -> %p{%p{ino=%lu}}", | ||
371 | next, next->d_inode, next->d_inode->i_ino); | ||
372 | |||
373 | } else if (!S_ISDIR(next->d_inode->i_mode) && | ||
374 | !S_ISREG(next->d_inode->i_mode) | ||
375 | ) { | ||
376 | kerror("inode %lu is not a file or directory", | ||
377 | next->d_inode->i_ino); | ||
378 | ret = -ENOBUFS; | ||
379 | goto error; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | /* process the next component */ | ||
384 | if (key) { | ||
385 | _debug("advance"); | ||
386 | mutex_unlock(&dir->d_inode->i_mutex); | ||
387 | dput(dir); | ||
388 | dir = next; | ||
389 | next = NULL; | ||
390 | goto advance; | ||
391 | } | ||
392 | |||
393 | /* we've found the object we were looking for */ | ||
394 | object->dentry = next; | ||
395 | |||
396 | /* if we've found that the terminal object exists, then we need to | ||
397 | * check its attributes and delete it if it's out of date */ | ||
398 | if (!object->new) { | ||
399 | _debug("validate '%*.*s'", | ||
400 | next->d_name.len, next->d_name.len, next->d_name.name); | ||
401 | |||
402 | ret = cachefiles_check_object_xattr(object, auxdata); | ||
403 | if (ret == -ESTALE) { | ||
404 | /* delete the object (the deleter drops the directory | ||
405 | * mutex) */ | ||
406 | object->dentry = NULL; | ||
407 | |||
408 | ret = cachefiles_bury_object(cache, dir, next); | ||
409 | dput(next); | ||
410 | next = NULL; | ||
411 | |||
412 | if (ret < 0) | ||
413 | goto delete_error; | ||
414 | |||
415 | _debug("redo lookup"); | ||
416 | goto lookup_again; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | /* note that we're now using this object */ | ||
421 | cachefiles_mark_object_active(cache, object); | ||
422 | |||
423 | mutex_unlock(&dir->d_inode->i_mutex); | ||
424 | dput(dir); | ||
425 | dir = NULL; | ||
426 | |||
427 | _debug("=== OBTAINED_OBJECT ==="); | ||
428 | |||
429 | if (object->new) { | ||
430 | /* attach data to a newly constructed terminal object */ | ||
431 | ret = cachefiles_set_object_xattr(object, auxdata); | ||
432 | if (ret < 0) | ||
433 | goto check_error; | ||
434 | } else { | ||
435 | /* always update the atime on an object we've just looked up | ||
436 | * (this is used to keep track of culling, and atimes are only | ||
437 | * updated by read, write and readdir but not lookup or | ||
438 | * open) */ | ||
439 | touch_atime(cache->mnt, next); | ||
440 | } | ||
441 | |||
442 | /* open a file interface onto a data file */ | ||
443 | if (object->type != FSCACHE_COOKIE_TYPE_INDEX) { | ||
444 | if (S_ISREG(object->dentry->d_inode->i_mode)) { | ||
445 | const struct address_space_operations *aops; | ||
446 | |||
447 | ret = -EPERM; | ||
448 | aops = object->dentry->d_inode->i_mapping->a_ops; | ||
449 | if (!aops->bmap) | ||
450 | goto check_error; | ||
451 | |||
452 | object->backer = object->dentry; | ||
453 | } else { | ||
454 | BUG(); // TODO: open file in data-class subdir | ||
455 | } | ||
456 | } | ||
457 | |||
458 | object->new = 0; | ||
459 | fscache_obtained_object(&object->fscache); | ||
460 | |||
461 | _leave(" = 0 [%lu]", object->dentry->d_inode->i_ino); | ||
462 | return 0; | ||
463 | |||
464 | create_error: | ||
465 | _debug("create error %d", ret); | ||
466 | if (ret == -EIO) | ||
467 | cachefiles_io_error(cache, "Create/mkdir failed"); | ||
468 | goto error; | ||
469 | |||
470 | check_error: | ||
471 | _debug("check error %d", ret); | ||
472 | write_lock(&cache->active_lock); | ||
473 | rb_erase(&object->active_node, &cache->active_nodes); | ||
474 | clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags); | ||
475 | wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE); | ||
476 | write_unlock(&cache->active_lock); | ||
477 | |||
478 | dput(object->dentry); | ||
479 | object->dentry = NULL; | ||
480 | goto error_out; | ||
481 | |||
482 | delete_error: | ||
483 | _debug("delete error %d", ret); | ||
484 | goto error_out2; | ||
485 | |||
486 | lookup_error: | ||
487 | _debug("lookup error %ld", PTR_ERR(next)); | ||
488 | ret = PTR_ERR(next); | ||
489 | if (ret == -EIO) | ||
490 | cachefiles_io_error(cache, "Lookup failed"); | ||
491 | next = NULL; | ||
492 | error: | ||
493 | mutex_unlock(&dir->d_inode->i_mutex); | ||
494 | dput(next); | ||
495 | error_out2: | ||
496 | dput(dir); | ||
497 | error_out: | ||
498 | if (ret == -ENOSPC) | ||
499 | ret = -ENOBUFS; | ||
500 | |||
501 | _leave(" = error %d", -ret); | ||
502 | return ret; | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * get a subdirectory | ||
507 | */ | ||
508 | struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, | ||
509 | struct dentry *dir, | ||
510 | const char *dirname) | ||
511 | { | ||
512 | struct dentry *subdir; | ||
513 | unsigned long start; | ||
514 | int ret; | ||
515 | |||
516 | _enter(",,%s", dirname); | ||
517 | |||
518 | /* search the current directory for the element name */ | ||
519 | mutex_lock(&dir->d_inode->i_mutex); | ||
520 | |||
521 | start = jiffies; | ||
522 | subdir = lookup_one_len(dirname, dir, strlen(dirname)); | ||
523 | cachefiles_hist(cachefiles_lookup_histogram, start); | ||
524 | if (IS_ERR(subdir)) { | ||
525 | if (PTR_ERR(subdir) == -ENOMEM) | ||
526 | goto nomem_d_alloc; | ||
527 | goto lookup_error; | ||
528 | } | ||
529 | |||
530 | _debug("subdir -> %p %s", | ||
531 | subdir, subdir->d_inode ? "positive" : "negative"); | ||
532 | |||
533 | /* we need to create the subdir if it doesn't exist yet */ | ||
534 | if (!subdir->d_inode) { | ||
535 | ret = cachefiles_has_space(cache, 1, 0); | ||
536 | if (ret < 0) | ||
537 | goto mkdir_error; | ||
538 | |||
539 | _debug("attempt mkdir"); | ||
540 | |||
541 | ret = vfs_mkdir(dir->d_inode, subdir, 0700); | ||
542 | if (ret < 0) | ||
543 | goto mkdir_error; | ||
544 | |||
545 | ASSERT(subdir->d_inode); | ||
546 | |||
547 | _debug("mkdir -> %p{%p{ino=%lu}}", | ||
548 | subdir, | ||
549 | subdir->d_inode, | ||
550 | subdir->d_inode->i_ino); | ||
551 | } | ||
552 | |||
553 | mutex_unlock(&dir->d_inode->i_mutex); | ||
554 | |||
555 | /* we need to make sure the subdir is a directory */ | ||
556 | ASSERT(subdir->d_inode); | ||
557 | |||
558 | if (!S_ISDIR(subdir->d_inode->i_mode)) { | ||
559 | kerror("%s is not a directory", dirname); | ||
560 | ret = -EIO; | ||
561 | goto check_error; | ||
562 | } | ||
563 | |||
564 | ret = -EPERM; | ||
565 | if (!subdir->d_inode->i_op || | ||
566 | !subdir->d_inode->i_op->setxattr || | ||
567 | !subdir->d_inode->i_op->getxattr || | ||
568 | !subdir->d_inode->i_op->lookup || | ||
569 | !subdir->d_inode->i_op->mkdir || | ||
570 | !subdir->d_inode->i_op->create || | ||
571 | !subdir->d_inode->i_op->rename || | ||
572 | !subdir->d_inode->i_op->rmdir || | ||
573 | !subdir->d_inode->i_op->unlink) | ||
574 | goto check_error; | ||
575 | |||
576 | _leave(" = [%lu]", subdir->d_inode->i_ino); | ||
577 | return subdir; | ||
578 | |||
579 | check_error: | ||
580 | dput(subdir); | ||
581 | _leave(" = %d [check]", ret); | ||
582 | return ERR_PTR(ret); | ||
583 | |||
584 | mkdir_error: | ||
585 | mutex_unlock(&dir->d_inode->i_mutex); | ||
586 | dput(subdir); | ||
587 | kerror("mkdir %s failed with error %d", dirname, ret); | ||
588 | return ERR_PTR(ret); | ||
589 | |||
590 | lookup_error: | ||
591 | mutex_unlock(&dir->d_inode->i_mutex); | ||
592 | ret = PTR_ERR(subdir); | ||
593 | kerror("Lookup %s failed with error %d", dirname, ret); | ||
594 | return ERR_PTR(ret); | ||
595 | |||
596 | nomem_d_alloc: | ||
597 | mutex_unlock(&dir->d_inode->i_mutex); | ||
598 | _leave(" = -ENOMEM"); | ||
599 | return ERR_PTR(-ENOMEM); | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * find out if an object is in use or not | ||
604 | * - if finds object and it's not in use: | ||
605 | * - returns a pointer to the object and a reference on it | ||
606 | * - returns with the directory locked | ||
607 | */ | ||
608 | static struct dentry *cachefiles_check_active(struct cachefiles_cache *cache, | ||
609 | struct dentry *dir, | ||
610 | char *filename) | ||
611 | { | ||
612 | struct cachefiles_object *object; | ||
613 | struct rb_node *_n; | ||
614 | struct dentry *victim; | ||
615 | unsigned long start; | ||
616 | int ret; | ||
617 | |||
618 | //_enter(",%*.*s/,%s", | ||
619 | // dir->d_name.len, dir->d_name.len, dir->d_name.name, filename); | ||
620 | |||
621 | /* look up the victim */ | ||
622 | mutex_lock_nested(&dir->d_inode->i_mutex, 1); | ||
623 | |||
624 | start = jiffies; | ||
625 | victim = lookup_one_len(filename, dir, strlen(filename)); | ||
626 | cachefiles_hist(cachefiles_lookup_histogram, start); | ||
627 | if (IS_ERR(victim)) | ||
628 | goto lookup_error; | ||
629 | |||
630 | //_debug("victim -> %p %s", | ||
631 | // victim, victim->d_inode ? "positive" : "negative"); | ||
632 | |||
633 | /* if the object is no longer there then we probably retired the object | ||
634 | * at the netfs's request whilst the cull was in progress | ||
635 | */ | ||
636 | if (!victim->d_inode) { | ||
637 | mutex_unlock(&dir->d_inode->i_mutex); | ||
638 | dput(victim); | ||
639 | _leave(" = -ENOENT [absent]"); | ||
640 | return ERR_PTR(-ENOENT); | ||
641 | } | ||
642 | |||
643 | /* check to see if we're using this object */ | ||
644 | read_lock(&cache->active_lock); | ||
645 | |||
646 | _n = cache->active_nodes.rb_node; | ||
647 | |||
648 | while (_n) { | ||
649 | object = rb_entry(_n, struct cachefiles_object, active_node); | ||
650 | |||
651 | if (object->dentry > victim) | ||
652 | _n = _n->rb_left; | ||
653 | else if (object->dentry < victim) | ||
654 | _n = _n->rb_right; | ||
655 | else | ||
656 | goto object_in_use; | ||
657 | } | ||
658 | |||
659 | read_unlock(&cache->active_lock); | ||
660 | |||
661 | //_leave(" = %p", victim); | ||
662 | return victim; | ||
663 | |||
664 | object_in_use: | ||
665 | read_unlock(&cache->active_lock); | ||
666 | mutex_unlock(&dir->d_inode->i_mutex); | ||
667 | dput(victim); | ||
668 | //_leave(" = -EBUSY [in use]"); | ||
669 | return ERR_PTR(-EBUSY); | ||
670 | |||
671 | lookup_error: | ||
672 | mutex_unlock(&dir->d_inode->i_mutex); | ||
673 | ret = PTR_ERR(victim); | ||
674 | if (ret == -ENOENT) { | ||
675 | /* file or dir now absent - probably retired by netfs */ | ||
676 | _leave(" = -ESTALE [absent]"); | ||
677 | return ERR_PTR(-ESTALE); | ||
678 | } | ||
679 | |||
680 | if (ret == -EIO) { | ||
681 | cachefiles_io_error(cache, "Lookup failed"); | ||
682 | } else if (ret != -ENOMEM) { | ||
683 | kerror("Internal error: %d", ret); | ||
684 | ret = -EIO; | ||
685 | } | ||
686 | |||
687 | _leave(" = %d", ret); | ||
688 | return ERR_PTR(ret); | ||
689 | } | ||
690 | |||
691 | /* | ||
692 | * cull an object if it's not in use | ||
693 | * - called only by cache manager daemon | ||
694 | */ | ||
695 | int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, | ||
696 | char *filename) | ||
697 | { | ||
698 | struct dentry *victim; | ||
699 | int ret; | ||
700 | |||
701 | _enter(",%*.*s/,%s", | ||
702 | dir->d_name.len, dir->d_name.len, dir->d_name.name, filename); | ||
703 | |||
704 | victim = cachefiles_check_active(cache, dir, filename); | ||
705 | if (IS_ERR(victim)) | ||
706 | return PTR_ERR(victim); | ||
707 | |||
708 | _debug("victim -> %p %s", | ||
709 | victim, victim->d_inode ? "positive" : "negative"); | ||
710 | |||
711 | /* okay... the victim is not being used so we can cull it | ||
712 | * - start by marking it as stale | ||
713 | */ | ||
714 | _debug("victim is cullable"); | ||
715 | |||
716 | ret = cachefiles_remove_object_xattr(cache, victim); | ||
717 | if (ret < 0) | ||
718 | goto error_unlock; | ||
719 | |||
720 | /* actually remove the victim (drops the dir mutex) */ | ||
721 | _debug("bury"); | ||
722 | |||
723 | ret = cachefiles_bury_object(cache, dir, victim); | ||
724 | if (ret < 0) | ||
725 | goto error; | ||
726 | |||
727 | dput(victim); | ||
728 | _leave(" = 0"); | ||
729 | return 0; | ||
730 | |||
731 | error_unlock: | ||
732 | mutex_unlock(&dir->d_inode->i_mutex); | ||
733 | error: | ||
734 | dput(victim); | ||
735 | if (ret == -ENOENT) { | ||
736 | /* file or dir now absent - probably retired by netfs */ | ||
737 | _leave(" = -ESTALE [absent]"); | ||
738 | return -ESTALE; | ||
739 | } | ||
740 | |||
741 | if (ret != -ENOMEM) { | ||
742 | kerror("Internal error: %d", ret); | ||
743 | ret = -EIO; | ||
744 | } | ||
745 | |||
746 | _leave(" = %d", ret); | ||
747 | return ret; | ||
748 | } | ||
749 | |||
750 | /* | ||
751 | * find out if an object is in use or not | ||
752 | * - called only by cache manager daemon | ||
753 | * - returns -EBUSY or 0 to indicate whether an object is in use or not | ||
754 | */ | ||
755 | int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir, | ||
756 | char *filename) | ||
757 | { | ||
758 | struct dentry *victim; | ||
759 | |||
760 | //_enter(",%*.*s/,%s", | ||
761 | // dir->d_name.len, dir->d_name.len, dir->d_name.name, filename); | ||
762 | |||
763 | victim = cachefiles_check_active(cache, dir, filename); | ||
764 | if (IS_ERR(victim)) | ||
765 | return PTR_ERR(victim); | ||
766 | |||
767 | mutex_unlock(&dir->d_inode->i_mutex); | ||
768 | dput(victim); | ||
769 | //_leave(" = 0"); | ||
770 | return 0; | ||
771 | } | ||
diff --git a/fs/cachefiles/proc.c b/fs/cachefiles/proc.c new file mode 100644 index 000000000000..eccd33941199 --- /dev/null +++ b/fs/cachefiles/proc.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* CacheFiles statistics | ||
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/proc_fs.h> | ||
14 | #include <linux/seq_file.h> | ||
15 | #include "internal.h" | ||
16 | |||
17 | atomic_t cachefiles_lookup_histogram[HZ]; | ||
18 | atomic_t cachefiles_mkdir_histogram[HZ]; | ||
19 | atomic_t cachefiles_create_histogram[HZ]; | ||
20 | |||
21 | /* | ||
22 | * display the latency histogram | ||
23 | */ | ||
24 | static int cachefiles_histogram_show(struct seq_file *m, void *v) | ||
25 | { | ||
26 | unsigned long index; | ||
27 | unsigned x, y, z, t; | ||
28 | |||
29 | switch ((unsigned long) v) { | ||
30 | case 1: | ||
31 | seq_puts(m, "JIFS SECS LOOKUPS MKDIRS CREATES\n"); | ||
32 | return 0; | ||
33 | case 2: | ||
34 | seq_puts(m, "===== ===== ========= ========= =========\n"); | ||
35 | return 0; | ||
36 | default: | ||
37 | index = (unsigned long) v - 3; | ||
38 | x = atomic_read(&cachefiles_lookup_histogram[index]); | ||
39 | y = atomic_read(&cachefiles_mkdir_histogram[index]); | ||
40 | z = atomic_read(&cachefiles_create_histogram[index]); | ||
41 | if (x == 0 && y == 0 && z == 0) | ||
42 | return 0; | ||
43 | |||
44 | t = (index * 1000) / HZ; | ||
45 | |||
46 | seq_printf(m, "%4lu 0.%03u %9u %9u %9u\n", index, t, x, y, z); | ||
47 | return 0; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * set up the iterator to start reading from the first line | ||
53 | */ | ||
54 | static void *cachefiles_histogram_start(struct seq_file *m, loff_t *_pos) | ||
55 | { | ||
56 | if ((unsigned long long)*_pos >= HZ + 2) | ||
57 | return NULL; | ||
58 | if (*_pos == 0) | ||
59 | *_pos = 1; | ||
60 | return (void *)(unsigned long) *_pos; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * move to the next line | ||
65 | */ | ||
66 | static void *cachefiles_histogram_next(struct seq_file *m, void *v, loff_t *pos) | ||
67 | { | ||
68 | (*pos)++; | ||
69 | return (unsigned long long)*pos > HZ + 2 ? | ||
70 | NULL : (void *)(unsigned long) *pos; | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * clean up after reading | ||
75 | */ | ||
76 | static void cachefiles_histogram_stop(struct seq_file *m, void *v) | ||
77 | { | ||
78 | } | ||
79 | |||
80 | static const struct seq_operations cachefiles_histogram_ops = { | ||
81 | .start = cachefiles_histogram_start, | ||
82 | .stop = cachefiles_histogram_stop, | ||
83 | .next = cachefiles_histogram_next, | ||
84 | .show = cachefiles_histogram_show, | ||
85 | }; | ||
86 | |||
87 | /* | ||
88 | * open "/proc/fs/cachefiles/XXX" which provide statistics summaries | ||
89 | */ | ||
90 | static int cachefiles_histogram_open(struct inode *inode, struct file *file) | ||
91 | { | ||
92 | return seq_open(file, &cachefiles_histogram_ops); | ||
93 | } | ||
94 | |||
95 | static const struct file_operations cachefiles_histogram_fops = { | ||
96 | .owner = THIS_MODULE, | ||
97 | .open = cachefiles_histogram_open, | ||
98 | .read = seq_read, | ||
99 | .llseek = seq_lseek, | ||
100 | .release = seq_release, | ||
101 | }; | ||
102 | |||
103 | /* | ||
104 | * initialise the /proc/fs/cachefiles/ directory | ||
105 | */ | ||
106 | int __init cachefiles_proc_init(void) | ||
107 | { | ||
108 | _enter(""); | ||
109 | |||
110 | if (!proc_mkdir("fs/cachefiles", NULL)) | ||
111 | goto error_dir; | ||
112 | |||
113 | if (!proc_create("fs/cachefiles/histogram", S_IFREG | 0444, NULL, | ||
114 | &cachefiles_histogram_fops)) | ||
115 | goto error_histogram; | ||
116 | |||
117 | _leave(" = 0"); | ||
118 | return 0; | ||
119 | |||
120 | error_histogram: | ||
121 | remove_proc_entry("fs/cachefiles", NULL); | ||
122 | error_dir: | ||
123 | _leave(" = -ENOMEM"); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * clean up the /proc/fs/cachefiles/ directory | ||
129 | */ | ||
130 | void cachefiles_proc_cleanup(void) | ||
131 | { | ||
132 | remove_proc_entry("fs/cachefiles/histogram", NULL); | ||
133 | remove_proc_entry("fs/cachefiles", NULL); | ||
134 | } | ||
diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c new file mode 100644 index 000000000000..a69787e7dd96 --- /dev/null +++ b/fs/cachefiles/rdwr.c | |||
@@ -0,0 +1,879 @@ | |||
1 | /* Storage object read/write | ||
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/mount.h> | ||
13 | #include <linux/file.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | /* | ||
17 | * detect wake up events generated by the unlocking of pages in which we're | ||
18 | * interested | ||
19 | * - we use this to detect read completion of backing pages | ||
20 | * - the caller holds the waitqueue lock | ||
21 | */ | ||
22 | static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode, | ||
23 | int sync, void *_key) | ||
24 | { | ||
25 | struct cachefiles_one_read *monitor = | ||
26 | container_of(wait, struct cachefiles_one_read, monitor); | ||
27 | struct cachefiles_object *object; | ||
28 | struct wait_bit_key *key = _key; | ||
29 | struct page *page = wait->private; | ||
30 | |||
31 | ASSERT(key); | ||
32 | |||
33 | _enter("{%lu},%u,%d,{%p,%u}", | ||
34 | monitor->netfs_page->index, mode, sync, | ||
35 | key->flags, key->bit_nr); | ||
36 | |||
37 | if (key->flags != &page->flags || | ||
38 | key->bit_nr != PG_locked) | ||
39 | return 0; | ||
40 | |||
41 | _debug("--- monitor %p %lx ---", page, page->flags); | ||
42 | |||
43 | if (!PageUptodate(page) && !PageError(page)) | ||
44 | dump_stack(); | ||
45 | |||
46 | /* remove from the waitqueue */ | ||
47 | list_del(&wait->task_list); | ||
48 | |||
49 | /* move onto the action list and queue for FS-Cache thread pool */ | ||
50 | ASSERT(monitor->op); | ||
51 | |||
52 | object = container_of(monitor->op->op.object, | ||
53 | struct cachefiles_object, fscache); | ||
54 | |||
55 | spin_lock(&object->work_lock); | ||
56 | list_add_tail(&monitor->op_link, &monitor->op->to_do); | ||
57 | spin_unlock(&object->work_lock); | ||
58 | |||
59 | fscache_enqueue_retrieval(monitor->op); | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * copy data from backing pages to netfs pages to complete a read operation | ||
65 | * - driven by FS-Cache's thread pool | ||
66 | */ | ||
67 | static void cachefiles_read_copier(struct fscache_operation *_op) | ||
68 | { | ||
69 | struct cachefiles_one_read *monitor; | ||
70 | struct cachefiles_object *object; | ||
71 | struct fscache_retrieval *op; | ||
72 | struct pagevec pagevec; | ||
73 | int error, max; | ||
74 | |||
75 | op = container_of(_op, struct fscache_retrieval, op); | ||
76 | object = container_of(op->op.object, | ||
77 | struct cachefiles_object, fscache); | ||
78 | |||
79 | _enter("{ino=%lu}", object->backer->d_inode->i_ino); | ||
80 | |||
81 | pagevec_init(&pagevec, 0); | ||
82 | |||
83 | max = 8; | ||
84 | spin_lock_irq(&object->work_lock); | ||
85 | |||
86 | while (!list_empty(&op->to_do)) { | ||
87 | monitor = list_entry(op->to_do.next, | ||
88 | struct cachefiles_one_read, op_link); | ||
89 | list_del(&monitor->op_link); | ||
90 | |||
91 | spin_unlock_irq(&object->work_lock); | ||
92 | |||
93 | _debug("- copy {%lu}", monitor->back_page->index); | ||
94 | |||
95 | error = -EIO; | ||
96 | if (PageUptodate(monitor->back_page)) { | ||
97 | copy_highpage(monitor->netfs_page, monitor->back_page); | ||
98 | |||
99 | pagevec_add(&pagevec, monitor->netfs_page); | ||
100 | fscache_mark_pages_cached(monitor->op, &pagevec); | ||
101 | error = 0; | ||
102 | } | ||
103 | |||
104 | if (error) | ||
105 | cachefiles_io_error_obj( | ||
106 | object, | ||
107 | "Readpage failed on backing file %lx", | ||
108 | (unsigned long) monitor->back_page->flags); | ||
109 | |||
110 | page_cache_release(monitor->back_page); | ||
111 | |||
112 | fscache_end_io(op, monitor->netfs_page, error); | ||
113 | page_cache_release(monitor->netfs_page); | ||
114 | fscache_put_retrieval(op); | ||
115 | kfree(monitor); | ||
116 | |||
117 | /* let the thread pool have some air occasionally */ | ||
118 | max--; | ||
119 | if (max < 0 || need_resched()) { | ||
120 | if (!list_empty(&op->to_do)) | ||
121 | fscache_enqueue_retrieval(op); | ||
122 | _leave(" [maxed out]"); | ||
123 | return; | ||
124 | } | ||
125 | |||
126 | spin_lock_irq(&object->work_lock); | ||
127 | } | ||
128 | |||
129 | spin_unlock_irq(&object->work_lock); | ||
130 | _leave(""); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * read the corresponding page to the given set from the backing file | ||
135 | * - an uncertain page is simply discarded, to be tried again another time | ||
136 | */ | ||
137 | static int cachefiles_read_backing_file_one(struct cachefiles_object *object, | ||
138 | struct fscache_retrieval *op, | ||
139 | struct page *netpage, | ||
140 | struct pagevec *pagevec) | ||
141 | { | ||
142 | struct cachefiles_one_read *monitor; | ||
143 | struct address_space *bmapping; | ||
144 | struct page *newpage, *backpage; | ||
145 | int ret; | ||
146 | |||
147 | _enter(""); | ||
148 | |||
149 | pagevec_reinit(pagevec); | ||
150 | |||
151 | _debug("read back %p{%lu,%d}", | ||
152 | netpage, netpage->index, page_count(netpage)); | ||
153 | |||
154 | monitor = kzalloc(sizeof(*monitor), GFP_KERNEL); | ||
155 | if (!monitor) | ||
156 | goto nomem; | ||
157 | |||
158 | monitor->netfs_page = netpage; | ||
159 | monitor->op = fscache_get_retrieval(op); | ||
160 | |||
161 | init_waitqueue_func_entry(&monitor->monitor, cachefiles_read_waiter); | ||
162 | |||
163 | /* attempt to get hold of the backing page */ | ||
164 | bmapping = object->backer->d_inode->i_mapping; | ||
165 | newpage = NULL; | ||
166 | |||
167 | for (;;) { | ||
168 | backpage = find_get_page(bmapping, netpage->index); | ||
169 | if (backpage) | ||
170 | goto backing_page_already_present; | ||
171 | |||
172 | if (!newpage) { | ||
173 | newpage = page_cache_alloc_cold(bmapping); | ||
174 | if (!newpage) | ||
175 | goto nomem_monitor; | ||
176 | } | ||
177 | |||
178 | ret = add_to_page_cache(newpage, bmapping, | ||
179 | netpage->index, GFP_KERNEL); | ||
180 | if (ret == 0) | ||
181 | goto installed_new_backing_page; | ||
182 | if (ret != -EEXIST) | ||
183 | goto nomem_page; | ||
184 | } | ||
185 | |||
186 | /* we've installed a new backing page, so now we need to add it | ||
187 | * to the LRU list and start it reading */ | ||
188 | installed_new_backing_page: | ||
189 | _debug("- new %p", newpage); | ||
190 | |||
191 | backpage = newpage; | ||
192 | newpage = NULL; | ||
193 | |||
194 | page_cache_get(backpage); | ||
195 | pagevec_add(pagevec, backpage); | ||
196 | __pagevec_lru_add_file(pagevec); | ||
197 | |||
198 | read_backing_page: | ||
199 | ret = bmapping->a_ops->readpage(NULL, backpage); | ||
200 | if (ret < 0) | ||
201 | goto read_error; | ||
202 | |||
203 | /* set the monitor to transfer the data across */ | ||
204 | monitor_backing_page: | ||
205 | _debug("- monitor add"); | ||
206 | |||
207 | /* install the monitor */ | ||
208 | page_cache_get(monitor->netfs_page); | ||
209 | page_cache_get(backpage); | ||
210 | monitor->back_page = backpage; | ||
211 | monitor->monitor.private = backpage; | ||
212 | add_page_wait_queue(backpage, &monitor->monitor); | ||
213 | monitor = NULL; | ||
214 | |||
215 | /* but the page may have been read before the monitor was installed, so | ||
216 | * the monitor may miss the event - so we have to ensure that we do get | ||
217 | * one in such a case */ | ||
218 | if (trylock_page(backpage)) { | ||
219 | _debug("jumpstart %p {%lx}", backpage, backpage->flags); | ||
220 | unlock_page(backpage); | ||
221 | } | ||
222 | goto success; | ||
223 | |||
224 | /* if the backing page is already present, it can be in one of | ||
225 | * three states: read in progress, read failed or read okay */ | ||
226 | backing_page_already_present: | ||
227 | _debug("- present"); | ||
228 | |||
229 | if (newpage) { | ||
230 | page_cache_release(newpage); | ||
231 | newpage = NULL; | ||
232 | } | ||
233 | |||
234 | if (PageError(backpage)) | ||
235 | goto io_error; | ||
236 | |||
237 | if (PageUptodate(backpage)) | ||
238 | goto backing_page_already_uptodate; | ||
239 | |||
240 | if (!trylock_page(backpage)) | ||
241 | goto monitor_backing_page; | ||
242 | _debug("read %p {%lx}", backpage, backpage->flags); | ||
243 | goto read_backing_page; | ||
244 | |||
245 | /* the backing page is already up to date, attach the netfs | ||
246 | * page to the pagecache and LRU and copy the data across */ | ||
247 | backing_page_already_uptodate: | ||
248 | _debug("- uptodate"); | ||
249 | |||
250 | pagevec_add(pagevec, netpage); | ||
251 | fscache_mark_pages_cached(op, pagevec); | ||
252 | |||
253 | copy_highpage(netpage, backpage); | ||
254 | fscache_end_io(op, netpage, 0); | ||
255 | |||
256 | success: | ||
257 | _debug("success"); | ||
258 | ret = 0; | ||
259 | |||
260 | out: | ||
261 | if (backpage) | ||
262 | page_cache_release(backpage); | ||
263 | if (monitor) { | ||
264 | fscache_put_retrieval(monitor->op); | ||
265 | kfree(monitor); | ||
266 | } | ||
267 | _leave(" = %d", ret); | ||
268 | return ret; | ||
269 | |||
270 | read_error: | ||
271 | _debug("read error %d", ret); | ||
272 | if (ret == -ENOMEM) | ||
273 | goto out; | ||
274 | io_error: | ||
275 | cachefiles_io_error_obj(object, "Page read error on backing file"); | ||
276 | ret = -ENOBUFS; | ||
277 | goto out; | ||
278 | |||
279 | nomem_page: | ||
280 | page_cache_release(newpage); | ||
281 | nomem_monitor: | ||
282 | fscache_put_retrieval(monitor->op); | ||
283 | kfree(monitor); | ||
284 | nomem: | ||
285 | _leave(" = -ENOMEM"); | ||
286 | return -ENOMEM; | ||
287 | } | ||
288 | |||
289 | /* | ||
290 | * read a page from the cache or allocate a block in which to store it | ||
291 | * - cache withdrawal is prevented by the caller | ||
292 | * - returns -EINTR if interrupted | ||
293 | * - returns -ENOMEM if ran out of memory | ||
294 | * - returns -ENOBUFS if no buffers can be made available | ||
295 | * - returns -ENOBUFS if page is beyond EOF | ||
296 | * - if the page is backed by a block in the cache: | ||
297 | * - a read will be started which will call the callback on completion | ||
298 | * - 0 will be returned | ||
299 | * - else if the page is unbacked: | ||
300 | * - the metadata will be retained | ||
301 | * - -ENODATA will be returned | ||
302 | */ | ||
303 | int cachefiles_read_or_alloc_page(struct fscache_retrieval *op, | ||
304 | struct page *page, | ||
305 | gfp_t gfp) | ||
306 | { | ||
307 | struct cachefiles_object *object; | ||
308 | struct cachefiles_cache *cache; | ||
309 | struct pagevec pagevec; | ||
310 | struct inode *inode; | ||
311 | sector_t block0, block; | ||
312 | unsigned shift; | ||
313 | int ret; | ||
314 | |||
315 | object = container_of(op->op.object, | ||
316 | struct cachefiles_object, fscache); | ||
317 | cache = container_of(object->fscache.cache, | ||
318 | struct cachefiles_cache, cache); | ||
319 | |||
320 | _enter("{%p},{%lx},,,", object, page->index); | ||
321 | |||
322 | if (!object->backer) | ||
323 | return -ENOBUFS; | ||
324 | |||
325 | inode = object->backer->d_inode; | ||
326 | ASSERT(S_ISREG(inode->i_mode)); | ||
327 | ASSERT(inode->i_mapping->a_ops->bmap); | ||
328 | ASSERT(inode->i_mapping->a_ops->readpages); | ||
329 | |||
330 | /* calculate the shift required to use bmap */ | ||
331 | if (inode->i_sb->s_blocksize > PAGE_SIZE) | ||
332 | return -ENOBUFS; | ||
333 | |||
334 | shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; | ||
335 | |||
336 | op->op.flags = FSCACHE_OP_FAST; | ||
337 | op->op.processor = cachefiles_read_copier; | ||
338 | |||
339 | pagevec_init(&pagevec, 0); | ||
340 | |||
341 | /* we assume the absence or presence of the first block is a good | ||
342 | * enough indication for the page as a whole | ||
343 | * - TODO: don't use bmap() for this as it is _not_ actually good | ||
344 | * enough for this as it doesn't indicate errors, but it's all we've | ||
345 | * got for the moment | ||
346 | */ | ||
347 | block0 = page->index; | ||
348 | block0 <<= shift; | ||
349 | |||
350 | block = inode->i_mapping->a_ops->bmap(inode->i_mapping, block0); | ||
351 | _debug("%llx -> %llx", | ||
352 | (unsigned long long) block0, | ||
353 | (unsigned long long) block); | ||
354 | |||
355 | if (block) { | ||
356 | /* submit the apparently valid page to the backing fs to be | ||
357 | * read from disk */ | ||
358 | ret = cachefiles_read_backing_file_one(object, op, page, | ||
359 | &pagevec); | ||
360 | } else if (cachefiles_has_space(cache, 0, 1) == 0) { | ||
361 | /* there's space in the cache we can use */ | ||
362 | pagevec_add(&pagevec, page); | ||
363 | fscache_mark_pages_cached(op, &pagevec); | ||
364 | ret = -ENODATA; | ||
365 | } else { | ||
366 | ret = -ENOBUFS; | ||
367 | } | ||
368 | |||
369 | _leave(" = %d", ret); | ||
370 | return ret; | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * read the corresponding pages to the given set from the backing file | ||
375 | * - any uncertain pages are simply discarded, to be tried again another time | ||
376 | */ | ||
377 | static int cachefiles_read_backing_file(struct cachefiles_object *object, | ||
378 | struct fscache_retrieval *op, | ||
379 | struct list_head *list, | ||
380 | struct pagevec *mark_pvec) | ||
381 | { | ||
382 | struct cachefiles_one_read *monitor = NULL; | ||
383 | struct address_space *bmapping = object->backer->d_inode->i_mapping; | ||
384 | struct pagevec lru_pvec; | ||
385 | struct page *newpage = NULL, *netpage, *_n, *backpage = NULL; | ||
386 | int ret = 0; | ||
387 | |||
388 | _enter(""); | ||
389 | |||
390 | pagevec_init(&lru_pvec, 0); | ||
391 | |||
392 | list_for_each_entry_safe(netpage, _n, list, lru) { | ||
393 | list_del(&netpage->lru); | ||
394 | |||
395 | _debug("read back %p{%lu,%d}", | ||
396 | netpage, netpage->index, page_count(netpage)); | ||
397 | |||
398 | if (!monitor) { | ||
399 | monitor = kzalloc(sizeof(*monitor), GFP_KERNEL); | ||
400 | if (!monitor) | ||
401 | goto nomem; | ||
402 | |||
403 | monitor->op = fscache_get_retrieval(op); | ||
404 | init_waitqueue_func_entry(&monitor->monitor, | ||
405 | cachefiles_read_waiter); | ||
406 | } | ||
407 | |||
408 | for (;;) { | ||
409 | backpage = find_get_page(bmapping, netpage->index); | ||
410 | if (backpage) | ||
411 | goto backing_page_already_present; | ||
412 | |||
413 | if (!newpage) { | ||
414 | newpage = page_cache_alloc_cold(bmapping); | ||
415 | if (!newpage) | ||
416 | goto nomem; | ||
417 | } | ||
418 | |||
419 | ret = add_to_page_cache(newpage, bmapping, | ||
420 | netpage->index, GFP_KERNEL); | ||
421 | if (ret == 0) | ||
422 | goto installed_new_backing_page; | ||
423 | if (ret != -EEXIST) | ||
424 | goto nomem; | ||
425 | } | ||
426 | |||
427 | /* we've installed a new backing page, so now we need to add it | ||
428 | * to the LRU list and start it reading */ | ||
429 | installed_new_backing_page: | ||
430 | _debug("- new %p", newpage); | ||
431 | |||
432 | backpage = newpage; | ||
433 | newpage = NULL; | ||
434 | |||
435 | page_cache_get(backpage); | ||
436 | if (!pagevec_add(&lru_pvec, backpage)) | ||
437 | __pagevec_lru_add_file(&lru_pvec); | ||
438 | |||
439 | reread_backing_page: | ||
440 | ret = bmapping->a_ops->readpage(NULL, backpage); | ||
441 | if (ret < 0) | ||
442 | goto read_error; | ||
443 | |||
444 | /* add the netfs page to the pagecache and LRU, and set the | ||
445 | * monitor to transfer the data across */ | ||
446 | monitor_backing_page: | ||
447 | _debug("- monitor add"); | ||
448 | |||
449 | ret = add_to_page_cache(netpage, op->mapping, netpage->index, | ||
450 | GFP_KERNEL); | ||
451 | if (ret < 0) { | ||
452 | if (ret == -EEXIST) { | ||
453 | page_cache_release(netpage); | ||
454 | continue; | ||
455 | } | ||
456 | goto nomem; | ||
457 | } | ||
458 | |||
459 | page_cache_get(netpage); | ||
460 | if (!pagevec_add(&lru_pvec, netpage)) | ||
461 | __pagevec_lru_add_file(&lru_pvec); | ||
462 | |||
463 | /* install a monitor */ | ||
464 | page_cache_get(netpage); | ||
465 | monitor->netfs_page = netpage; | ||
466 | |||
467 | page_cache_get(backpage); | ||
468 | monitor->back_page = backpage; | ||
469 | monitor->monitor.private = backpage; | ||
470 | add_page_wait_queue(backpage, &monitor->monitor); | ||
471 | monitor = NULL; | ||
472 | |||
473 | /* but the page may have been read before the monitor was | ||
474 | * installed, so the monitor may miss the event - so we have to | ||
475 | * ensure that we do get one in such a case */ | ||
476 | if (trylock_page(backpage)) { | ||
477 | _debug("2unlock %p {%lx}", backpage, backpage->flags); | ||
478 | unlock_page(backpage); | ||
479 | } | ||
480 | |||
481 | page_cache_release(backpage); | ||
482 | backpage = NULL; | ||
483 | |||
484 | page_cache_release(netpage); | ||
485 | netpage = NULL; | ||
486 | continue; | ||
487 | |||
488 | /* if the backing page is already present, it can be in one of | ||
489 | * three states: read in progress, read failed or read okay */ | ||
490 | backing_page_already_present: | ||
491 | _debug("- present %p", backpage); | ||
492 | |||
493 | if (PageError(backpage)) | ||
494 | goto io_error; | ||
495 | |||
496 | if (PageUptodate(backpage)) | ||
497 | goto backing_page_already_uptodate; | ||
498 | |||
499 | _debug("- not ready %p{%lx}", backpage, backpage->flags); | ||
500 | |||
501 | if (!trylock_page(backpage)) | ||
502 | goto monitor_backing_page; | ||
503 | |||
504 | if (PageError(backpage)) { | ||
505 | _debug("error %lx", backpage->flags); | ||
506 | unlock_page(backpage); | ||
507 | goto io_error; | ||
508 | } | ||
509 | |||
510 | if (PageUptodate(backpage)) | ||
511 | goto backing_page_already_uptodate_unlock; | ||
512 | |||
513 | /* we've locked a page that's neither up to date nor erroneous, | ||
514 | * so we need to attempt to read it again */ | ||
515 | goto reread_backing_page; | ||
516 | |||
517 | /* the backing page is already up to date, attach the netfs | ||
518 | * page to the pagecache and LRU and copy the data across */ | ||
519 | backing_page_already_uptodate_unlock: | ||
520 | _debug("uptodate %lx", backpage->flags); | ||
521 | unlock_page(backpage); | ||
522 | backing_page_already_uptodate: | ||
523 | _debug("- uptodate"); | ||
524 | |||
525 | ret = add_to_page_cache(netpage, op->mapping, netpage->index, | ||
526 | GFP_KERNEL); | ||
527 | if (ret < 0) { | ||
528 | if (ret == -EEXIST) { | ||
529 | page_cache_release(netpage); | ||
530 | continue; | ||
531 | } | ||
532 | goto nomem; | ||
533 | } | ||
534 | |||
535 | copy_highpage(netpage, backpage); | ||
536 | |||
537 | page_cache_release(backpage); | ||
538 | backpage = NULL; | ||
539 | |||
540 | if (!pagevec_add(mark_pvec, netpage)) | ||
541 | fscache_mark_pages_cached(op, mark_pvec); | ||
542 | |||
543 | page_cache_get(netpage); | ||
544 | if (!pagevec_add(&lru_pvec, netpage)) | ||
545 | __pagevec_lru_add_file(&lru_pvec); | ||
546 | |||
547 | fscache_end_io(op, netpage, 0); | ||
548 | page_cache_release(netpage); | ||
549 | netpage = NULL; | ||
550 | continue; | ||
551 | } | ||
552 | |||
553 | netpage = NULL; | ||
554 | |||
555 | _debug("out"); | ||
556 | |||
557 | out: | ||
558 | /* tidy up */ | ||
559 | pagevec_lru_add_file(&lru_pvec); | ||
560 | |||
561 | if (newpage) | ||
562 | page_cache_release(newpage); | ||
563 | if (netpage) | ||
564 | page_cache_release(netpage); | ||
565 | if (backpage) | ||
566 | page_cache_release(backpage); | ||
567 | if (monitor) { | ||
568 | fscache_put_retrieval(op); | ||
569 | kfree(monitor); | ||
570 | } | ||
571 | |||
572 | list_for_each_entry_safe(netpage, _n, list, lru) { | ||
573 | list_del(&netpage->lru); | ||
574 | page_cache_release(netpage); | ||
575 | } | ||
576 | |||
577 | _leave(" = %d", ret); | ||
578 | return ret; | ||
579 | |||
580 | nomem: | ||
581 | _debug("nomem"); | ||
582 | ret = -ENOMEM; | ||
583 | goto out; | ||
584 | |||
585 | read_error: | ||
586 | _debug("read error %d", ret); | ||
587 | if (ret == -ENOMEM) | ||
588 | goto out; | ||
589 | io_error: | ||
590 | cachefiles_io_error_obj(object, "Page read error on backing file"); | ||
591 | ret = -ENOBUFS; | ||
592 | goto out; | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * read a list of pages from the cache or allocate blocks in which to store | ||
597 | * them | ||
598 | */ | ||
599 | int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op, | ||
600 | struct list_head *pages, | ||
601 | unsigned *nr_pages, | ||
602 | gfp_t gfp) | ||
603 | { | ||
604 | struct cachefiles_object *object; | ||
605 | struct cachefiles_cache *cache; | ||
606 | struct list_head backpages; | ||
607 | struct pagevec pagevec; | ||
608 | struct inode *inode; | ||
609 | struct page *page, *_n; | ||
610 | unsigned shift, nrbackpages; | ||
611 | int ret, ret2, space; | ||
612 | |||
613 | object = container_of(op->op.object, | ||
614 | struct cachefiles_object, fscache); | ||
615 | cache = container_of(object->fscache.cache, | ||
616 | struct cachefiles_cache, cache); | ||
617 | |||
618 | _enter("{OBJ%x,%d},,%d,,", | ||
619 | object->fscache.debug_id, atomic_read(&op->op.usage), | ||
620 | *nr_pages); | ||
621 | |||
622 | if (!object->backer) | ||
623 | return -ENOBUFS; | ||
624 | |||
625 | space = 1; | ||
626 | if (cachefiles_has_space(cache, 0, *nr_pages) < 0) | ||
627 | space = 0; | ||
628 | |||
629 | inode = object->backer->d_inode; | ||
630 | ASSERT(S_ISREG(inode->i_mode)); | ||
631 | ASSERT(inode->i_mapping->a_ops->bmap); | ||
632 | ASSERT(inode->i_mapping->a_ops->readpages); | ||
633 | |||
634 | /* calculate the shift required to use bmap */ | ||
635 | if (inode->i_sb->s_blocksize > PAGE_SIZE) | ||
636 | return -ENOBUFS; | ||
637 | |||
638 | shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; | ||
639 | |||
640 | pagevec_init(&pagevec, 0); | ||
641 | |||
642 | op->op.flags = FSCACHE_OP_FAST; | ||
643 | op->op.processor = cachefiles_read_copier; | ||
644 | |||
645 | INIT_LIST_HEAD(&backpages); | ||
646 | nrbackpages = 0; | ||
647 | |||
648 | ret = space ? -ENODATA : -ENOBUFS; | ||
649 | list_for_each_entry_safe(page, _n, pages, lru) { | ||
650 | sector_t block0, block; | ||
651 | |||
652 | /* we assume the absence or presence of the first block is a | ||
653 | * good enough indication for the page as a whole | ||
654 | * - TODO: don't use bmap() for this as it is _not_ actually | ||
655 | * good enough for this as it doesn't indicate errors, but | ||
656 | * it's all we've got for the moment | ||
657 | */ | ||
658 | block0 = page->index; | ||
659 | block0 <<= shift; | ||
660 | |||
661 | block = inode->i_mapping->a_ops->bmap(inode->i_mapping, | ||
662 | block0); | ||
663 | _debug("%llx -> %llx", | ||
664 | (unsigned long long) block0, | ||
665 | (unsigned long long) block); | ||
666 | |||
667 | if (block) { | ||
668 | /* we have data - add it to the list to give to the | ||
669 | * backing fs */ | ||
670 | list_move(&page->lru, &backpages); | ||
671 | (*nr_pages)--; | ||
672 | nrbackpages++; | ||
673 | } else if (space && pagevec_add(&pagevec, page) == 0) { | ||
674 | fscache_mark_pages_cached(op, &pagevec); | ||
675 | ret = -ENODATA; | ||
676 | } | ||
677 | } | ||
678 | |||
679 | if (pagevec_count(&pagevec) > 0) | ||
680 | fscache_mark_pages_cached(op, &pagevec); | ||
681 | |||
682 | if (list_empty(pages)) | ||
683 | ret = 0; | ||
684 | |||
685 | /* submit the apparently valid pages to the backing fs to be read from | ||
686 | * disk */ | ||
687 | if (nrbackpages > 0) { | ||
688 | ret2 = cachefiles_read_backing_file(object, op, &backpages, | ||
689 | &pagevec); | ||
690 | if (ret2 == -ENOMEM || ret2 == -EINTR) | ||
691 | ret = ret2; | ||
692 | } | ||
693 | |||
694 | if (pagevec_count(&pagevec) > 0) | ||
695 | fscache_mark_pages_cached(op, &pagevec); | ||
696 | |||
697 | _leave(" = %d [nr=%u%s]", | ||
698 | ret, *nr_pages, list_empty(pages) ? " empty" : ""); | ||
699 | return ret; | ||
700 | } | ||
701 | |||
702 | /* | ||
703 | * allocate a block in the cache in which to store a page | ||
704 | * - cache withdrawal is prevented by the caller | ||
705 | * - returns -EINTR if interrupted | ||
706 | * - returns -ENOMEM if ran out of memory | ||
707 | * - returns -ENOBUFS if no buffers can be made available | ||
708 | * - returns -ENOBUFS if page is beyond EOF | ||
709 | * - otherwise: | ||
710 | * - the metadata will be retained | ||
711 | * - 0 will be returned | ||
712 | */ | ||
713 | int cachefiles_allocate_page(struct fscache_retrieval *op, | ||
714 | struct page *page, | ||
715 | gfp_t gfp) | ||
716 | { | ||
717 | struct cachefiles_object *object; | ||
718 | struct cachefiles_cache *cache; | ||
719 | struct pagevec pagevec; | ||
720 | int ret; | ||
721 | |||
722 | object = container_of(op->op.object, | ||
723 | struct cachefiles_object, fscache); | ||
724 | cache = container_of(object->fscache.cache, | ||
725 | struct cachefiles_cache, cache); | ||
726 | |||
727 | _enter("%p,{%lx},", object, page->index); | ||
728 | |||
729 | ret = cachefiles_has_space(cache, 0, 1); | ||
730 | if (ret == 0) { | ||
731 | pagevec_init(&pagevec, 0); | ||
732 | pagevec_add(&pagevec, page); | ||
733 | fscache_mark_pages_cached(op, &pagevec); | ||
734 | } else { | ||
735 | ret = -ENOBUFS; | ||
736 | } | ||
737 | |||
738 | _leave(" = %d", ret); | ||
739 | return ret; | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | * allocate blocks in the cache in which to store a set of pages | ||
744 | * - cache withdrawal is prevented by the caller | ||
745 | * - returns -EINTR if interrupted | ||
746 | * - returns -ENOMEM if ran out of memory | ||
747 | * - returns -ENOBUFS if some buffers couldn't be made available | ||
748 | * - returns -ENOBUFS if some pages are beyond EOF | ||
749 | * - otherwise: | ||
750 | * - -ENODATA will be returned | ||
751 | * - metadata will be retained for any page marked | ||
752 | */ | ||
753 | int cachefiles_allocate_pages(struct fscache_retrieval *op, | ||
754 | struct list_head *pages, | ||
755 | unsigned *nr_pages, | ||
756 | gfp_t gfp) | ||
757 | { | ||
758 | struct cachefiles_object *object; | ||
759 | struct cachefiles_cache *cache; | ||
760 | struct pagevec pagevec; | ||
761 | struct page *page; | ||
762 | int ret; | ||
763 | |||
764 | object = container_of(op->op.object, | ||
765 | struct cachefiles_object, fscache); | ||
766 | cache = container_of(object->fscache.cache, | ||
767 | struct cachefiles_cache, cache); | ||
768 | |||
769 | _enter("%p,,,%d,", object, *nr_pages); | ||
770 | |||
771 | ret = cachefiles_has_space(cache, 0, *nr_pages); | ||
772 | if (ret == 0) { | ||
773 | pagevec_init(&pagevec, 0); | ||
774 | |||
775 | list_for_each_entry(page, pages, lru) { | ||
776 | if (pagevec_add(&pagevec, page) == 0) | ||
777 | fscache_mark_pages_cached(op, &pagevec); | ||
778 | } | ||
779 | |||
780 | if (pagevec_count(&pagevec) > 0) | ||
781 | fscache_mark_pages_cached(op, &pagevec); | ||
782 | ret = -ENODATA; | ||
783 | } else { | ||
784 | ret = -ENOBUFS; | ||
785 | } | ||
786 | |||
787 | _leave(" = %d", ret); | ||
788 | return ret; | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | * request a page be stored in the cache | ||
793 | * - cache withdrawal is prevented by the caller | ||
794 | * - this request may be ignored if there's no cache block available, in which | ||
795 | * case -ENOBUFS will be returned | ||
796 | * - if the op is in progress, 0 will be returned | ||
797 | */ | ||
798 | int cachefiles_write_page(struct fscache_storage *op, struct page *page) | ||
799 | { | ||
800 | struct cachefiles_object *object; | ||
801 | struct cachefiles_cache *cache; | ||
802 | mm_segment_t old_fs; | ||
803 | struct file *file; | ||
804 | loff_t pos; | ||
805 | void *data; | ||
806 | int ret; | ||
807 | |||
808 | ASSERT(op != NULL); | ||
809 | ASSERT(page != NULL); | ||
810 | |||
811 | object = container_of(op->op.object, | ||
812 | struct cachefiles_object, fscache); | ||
813 | |||
814 | _enter("%p,%p{%lx},,,", object, page, page->index); | ||
815 | |||
816 | if (!object->backer) { | ||
817 | _leave(" = -ENOBUFS"); | ||
818 | return -ENOBUFS; | ||
819 | } | ||
820 | |||
821 | ASSERT(S_ISREG(object->backer->d_inode->i_mode)); | ||
822 | |||
823 | cache = container_of(object->fscache.cache, | ||
824 | struct cachefiles_cache, cache); | ||
825 | |||
826 | /* write the page to the backing filesystem and let it store it in its | ||
827 | * own time */ | ||
828 | dget(object->backer); | ||
829 | mntget(cache->mnt); | ||
830 | file = dentry_open(object->backer, cache->mnt, O_RDWR, | ||
831 | cache->cache_cred); | ||
832 | if (IS_ERR(file)) { | ||
833 | ret = PTR_ERR(file); | ||
834 | } else { | ||
835 | ret = -EIO; | ||
836 | if (file->f_op->write) { | ||
837 | pos = (loff_t) page->index << PAGE_SHIFT; | ||
838 | data = kmap(page); | ||
839 | old_fs = get_fs(); | ||
840 | set_fs(KERNEL_DS); | ||
841 | ret = file->f_op->write( | ||
842 | file, (const void __user *) data, PAGE_SIZE, | ||
843 | &pos); | ||
844 | set_fs(old_fs); | ||
845 | kunmap(page); | ||
846 | if (ret != PAGE_SIZE) | ||
847 | ret = -EIO; | ||
848 | } | ||
849 | fput(file); | ||
850 | } | ||
851 | |||
852 | if (ret < 0) { | ||
853 | if (ret == -EIO) | ||
854 | cachefiles_io_error_obj( | ||
855 | object, "Write page to backing file failed"); | ||
856 | ret = -ENOBUFS; | ||
857 | } | ||
858 | |||
859 | _leave(" = %d", ret); | ||
860 | return ret; | ||
861 | } | ||
862 | |||
863 | /* | ||
864 | * detach a backing block from a page | ||
865 | * - cache withdrawal is prevented by the caller | ||
866 | */ | ||
867 | void cachefiles_uncache_page(struct fscache_object *_object, struct page *page) | ||
868 | { | ||
869 | struct cachefiles_object *object; | ||
870 | struct cachefiles_cache *cache; | ||
871 | |||
872 | object = container_of(_object, struct cachefiles_object, fscache); | ||
873 | cache = container_of(object->fscache.cache, | ||
874 | struct cachefiles_cache, cache); | ||
875 | |||
876 | _enter("%p,{%lu}", object, page->index); | ||
877 | |||
878 | spin_unlock(&object->fscache.cookie->lock); | ||
879 | } | ||
diff --git a/fs/cachefiles/security.c b/fs/cachefiles/security.c new file mode 100644 index 000000000000..b5808cdb2232 --- /dev/null +++ b/fs/cachefiles/security.c | |||
@@ -0,0 +1,116 @@ | |||
1 | /* CacheFiles security management | ||
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/fs.h> | ||
13 | #include <linux/cred.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | /* | ||
17 | * determine the security context within which we access the cache from within | ||
18 | * the kernel | ||
19 | */ | ||
20 | int cachefiles_get_security_ID(struct cachefiles_cache *cache) | ||
21 | { | ||
22 | struct cred *new; | ||
23 | int ret; | ||
24 | |||
25 | _enter("{%s}", cache->secctx); | ||
26 | |||
27 | new = prepare_kernel_cred(current); | ||
28 | if (!new) { | ||
29 | ret = -ENOMEM; | ||
30 | goto error; | ||
31 | } | ||
32 | |||
33 | if (cache->secctx) { | ||
34 | ret = set_security_override_from_ctx(new, cache->secctx); | ||
35 | if (ret < 0) { | ||
36 | put_cred(new); | ||
37 | printk(KERN_ERR "CacheFiles:" | ||
38 | " Security denies permission to nominate" | ||
39 | " security context: error %d\n", | ||
40 | ret); | ||
41 | goto error; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | cache->cache_cred = new; | ||
46 | ret = 0; | ||
47 | error: | ||
48 | _leave(" = %d", ret); | ||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * see if mkdir and create can be performed in the root directory | ||
54 | */ | ||
55 | static int cachefiles_check_cache_dir(struct cachefiles_cache *cache, | ||
56 | struct dentry *root) | ||
57 | { | ||
58 | int ret; | ||
59 | |||
60 | ret = security_inode_mkdir(root->d_inode, root, 0); | ||
61 | if (ret < 0) { | ||
62 | printk(KERN_ERR "CacheFiles:" | ||
63 | " Security denies permission to make dirs: error %d", | ||
64 | ret); | ||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | ret = security_inode_create(root->d_inode, root, 0); | ||
69 | if (ret < 0) | ||
70 | printk(KERN_ERR "CacheFiles:" | ||
71 | " Security denies permission to create files: error %d", | ||
72 | ret); | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * check the security details of the on-disk cache | ||
79 | * - must be called with security override in force | ||
80 | */ | ||
81 | int cachefiles_determine_cache_security(struct cachefiles_cache *cache, | ||
82 | struct dentry *root, | ||
83 | const struct cred **_saved_cred) | ||
84 | { | ||
85 | struct cred *new; | ||
86 | int ret; | ||
87 | |||
88 | _enter(""); | ||
89 | |||
90 | /* duplicate the cache creds for COW (the override is currently in | ||
91 | * force, so we can use prepare_creds() to do this) */ | ||
92 | new = prepare_creds(); | ||
93 | if (!new) | ||
94 | return -ENOMEM; | ||
95 | |||
96 | cachefiles_end_secure(cache, *_saved_cred); | ||
97 | |||
98 | /* use the cache root dir's security context as the basis with | ||
99 | * which create files */ | ||
100 | ret = set_create_files_as(new, root->d_inode); | ||
101 | if (ret < 0) { | ||
102 | _leave(" = %d [cfa]", ret); | ||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | put_cred(cache->cache_cred); | ||
107 | cache->cache_cred = new; | ||
108 | |||
109 | cachefiles_begin_secure(cache, _saved_cred); | ||
110 | ret = cachefiles_check_cache_dir(cache, root); | ||
111 | |||
112 | if (ret == -EOPNOTSUPP) | ||
113 | ret = 0; | ||
114 | _leave(" = %d", ret); | ||
115 | return ret; | ||
116 | } | ||
diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c new file mode 100644 index 000000000000..f3e7a0bf068b --- /dev/null +++ b/fs/cachefiles/xattr.c | |||
@@ -0,0 +1,291 @@ | |||
1 | /* CacheFiles extended attribute management | ||
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/sched.h> | ||
14 | #include <linux/file.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/fsnotify.h> | ||
17 | #include <linux/quotaops.h> | ||
18 | #include <linux/xattr.h> | ||
19 | #include "internal.h" | ||
20 | |||
21 | static const char cachefiles_xattr_cache[] = | ||
22 | XATTR_USER_PREFIX "CacheFiles.cache"; | ||
23 | |||
24 | /* | ||
25 | * check the type label on an object | ||
26 | * - done using xattrs | ||
27 | */ | ||
28 | int cachefiles_check_object_type(struct cachefiles_object *object) | ||
29 | { | ||
30 | struct dentry *dentry = object->dentry; | ||
31 | char type[3], xtype[3]; | ||
32 | int ret; | ||
33 | |||
34 | ASSERT(dentry); | ||
35 | ASSERT(dentry->d_inode); | ||
36 | |||
37 | if (!object->fscache.cookie) | ||
38 | strcpy(type, "C3"); | ||
39 | else | ||
40 | snprintf(type, 3, "%02x", object->fscache.cookie->def->type); | ||
41 | |||
42 | _enter("%p{%s}", object, type); | ||
43 | |||
44 | /* attempt to install a type label directly */ | ||
45 | ret = vfs_setxattr(dentry, cachefiles_xattr_cache, type, 2, | ||
46 | XATTR_CREATE); | ||
47 | if (ret == 0) { | ||
48 | _debug("SET"); /* we succeeded */ | ||
49 | goto error; | ||
50 | } | ||
51 | |||
52 | if (ret != -EEXIST) { | ||
53 | kerror("Can't set xattr on %*.*s [%lu] (err %d)", | ||
54 | dentry->d_name.len, dentry->d_name.len, | ||
55 | dentry->d_name.name, dentry->d_inode->i_ino, | ||
56 | -ret); | ||
57 | goto error; | ||
58 | } | ||
59 | |||
60 | /* read the current type label */ | ||
61 | ret = vfs_getxattr(dentry, cachefiles_xattr_cache, xtype, 3); | ||
62 | if (ret < 0) { | ||
63 | if (ret == -ERANGE) | ||
64 | goto bad_type_length; | ||
65 | |||
66 | kerror("Can't read xattr on %*.*s [%lu] (err %d)", | ||
67 | dentry->d_name.len, dentry->d_name.len, | ||
68 | dentry->d_name.name, dentry->d_inode->i_ino, | ||
69 | -ret); | ||
70 | goto error; | ||
71 | } | ||
72 | |||
73 | /* check the type is what we're expecting */ | ||
74 | if (ret != 2) | ||
75 | goto bad_type_length; | ||
76 | |||
77 | if (xtype[0] != type[0] || xtype[1] != type[1]) | ||
78 | goto bad_type; | ||
79 | |||
80 | ret = 0; | ||
81 | |||
82 | error: | ||
83 | _leave(" = %d", ret); | ||
84 | return ret; | ||
85 | |||
86 | bad_type_length: | ||
87 | kerror("Cache object %lu type xattr length incorrect", | ||
88 | dentry->d_inode->i_ino); | ||
89 | ret = -EIO; | ||
90 | goto error; | ||
91 | |||
92 | bad_type: | ||
93 | xtype[2] = 0; | ||
94 | kerror("Cache object %*.*s [%lu] type %s not %s", | ||
95 | dentry->d_name.len, dentry->d_name.len, | ||
96 | dentry->d_name.name, dentry->d_inode->i_ino, | ||
97 | xtype, type); | ||
98 | ret = -EIO; | ||
99 | goto error; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * set the state xattr on a cache file | ||
104 | */ | ||
105 | int cachefiles_set_object_xattr(struct cachefiles_object *object, | ||
106 | struct cachefiles_xattr *auxdata) | ||
107 | { | ||
108 | struct dentry *dentry = object->dentry; | ||
109 | int ret; | ||
110 | |||
111 | ASSERT(object->fscache.cookie); | ||
112 | ASSERT(dentry); | ||
113 | |||
114 | _enter("%p,#%d", object, auxdata->len); | ||
115 | |||
116 | /* attempt to install the cache metadata directly */ | ||
117 | _debug("SET %s #%u", object->fscache.cookie->def->name, auxdata->len); | ||
118 | |||
119 | ret = vfs_setxattr(dentry, cachefiles_xattr_cache, | ||
120 | &auxdata->type, auxdata->len, | ||
121 | XATTR_CREATE); | ||
122 | if (ret < 0 && ret != -ENOMEM) | ||
123 | cachefiles_io_error_obj( | ||
124 | object, | ||
125 | "Failed to set xattr with error %d", ret); | ||
126 | |||
127 | _leave(" = %d", ret); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * update the state xattr on a cache file | ||
133 | */ | ||
134 | int cachefiles_update_object_xattr(struct cachefiles_object *object, | ||
135 | struct cachefiles_xattr *auxdata) | ||
136 | { | ||
137 | struct dentry *dentry = object->dentry; | ||
138 | int ret; | ||
139 | |||
140 | ASSERT(object->fscache.cookie); | ||
141 | ASSERT(dentry); | ||
142 | |||
143 | _enter("%p,#%d", object, auxdata->len); | ||
144 | |||
145 | /* attempt to install the cache metadata directly */ | ||
146 | _debug("SET %s #%u", object->fscache.cookie->def->name, auxdata->len); | ||
147 | |||
148 | ret = vfs_setxattr(dentry, cachefiles_xattr_cache, | ||
149 | &auxdata->type, auxdata->len, | ||
150 | XATTR_REPLACE); | ||
151 | if (ret < 0 && ret != -ENOMEM) | ||
152 | cachefiles_io_error_obj( | ||
153 | object, | ||
154 | "Failed to update xattr with error %d", ret); | ||
155 | |||
156 | _leave(" = %d", ret); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * check the state xattr on a cache file | ||
162 | * - return -ESTALE if the object should be deleted | ||
163 | */ | ||
164 | int cachefiles_check_object_xattr(struct cachefiles_object *object, | ||
165 | struct cachefiles_xattr *auxdata) | ||
166 | { | ||
167 | struct cachefiles_xattr *auxbuf; | ||
168 | struct dentry *dentry = object->dentry; | ||
169 | int ret; | ||
170 | |||
171 | _enter("%p,#%d", object, auxdata->len); | ||
172 | |||
173 | ASSERT(dentry); | ||
174 | ASSERT(dentry->d_inode); | ||
175 | |||
176 | auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, GFP_KERNEL); | ||
177 | if (!auxbuf) { | ||
178 | _leave(" = -ENOMEM"); | ||
179 | return -ENOMEM; | ||
180 | } | ||
181 | |||
182 | /* read the current type label */ | ||
183 | ret = vfs_getxattr(dentry, cachefiles_xattr_cache, | ||
184 | &auxbuf->type, 512 + 1); | ||
185 | if (ret < 0) { | ||
186 | if (ret == -ENODATA) | ||
187 | goto stale; /* no attribute - power went off | ||
188 | * mid-cull? */ | ||
189 | |||
190 | if (ret == -ERANGE) | ||
191 | goto bad_type_length; | ||
192 | |||
193 | cachefiles_io_error_obj(object, | ||
194 | "Can't read xattr on %lu (err %d)", | ||
195 | dentry->d_inode->i_ino, -ret); | ||
196 | goto error; | ||
197 | } | ||
198 | |||
199 | /* check the on-disk object */ | ||
200 | if (ret < 1) | ||
201 | goto bad_type_length; | ||
202 | |||
203 | if (auxbuf->type != auxdata->type) | ||
204 | goto stale; | ||
205 | |||
206 | auxbuf->len = ret; | ||
207 | |||
208 | /* consult the netfs */ | ||
209 | if (object->fscache.cookie->def->check_aux) { | ||
210 | enum fscache_checkaux result; | ||
211 | unsigned int dlen; | ||
212 | |||
213 | dlen = auxbuf->len - 1; | ||
214 | |||
215 | _debug("checkaux %s #%u", | ||
216 | object->fscache.cookie->def->name, dlen); | ||
217 | |||
218 | result = fscache_check_aux(&object->fscache, | ||
219 | &auxbuf->data, dlen); | ||
220 | |||
221 | switch (result) { | ||
222 | /* entry okay as is */ | ||
223 | case FSCACHE_CHECKAUX_OKAY: | ||
224 | goto okay; | ||
225 | |||
226 | /* entry requires update */ | ||
227 | case FSCACHE_CHECKAUX_NEEDS_UPDATE: | ||
228 | break; | ||
229 | |||
230 | /* entry requires deletion */ | ||
231 | case FSCACHE_CHECKAUX_OBSOLETE: | ||
232 | goto stale; | ||
233 | |||
234 | default: | ||
235 | BUG(); | ||
236 | } | ||
237 | |||
238 | /* update the current label */ | ||
239 | ret = vfs_setxattr(dentry, cachefiles_xattr_cache, | ||
240 | &auxdata->type, auxdata->len, | ||
241 | XATTR_REPLACE); | ||
242 | if (ret < 0) { | ||
243 | cachefiles_io_error_obj(object, | ||
244 | "Can't update xattr on %lu" | ||
245 | " (error %d)", | ||
246 | dentry->d_inode->i_ino, -ret); | ||
247 | goto error; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | okay: | ||
252 | ret = 0; | ||
253 | |||
254 | error: | ||
255 | kfree(auxbuf); | ||
256 | _leave(" = %d", ret); | ||
257 | return ret; | ||
258 | |||
259 | bad_type_length: | ||
260 | kerror("Cache object %lu xattr length incorrect", | ||
261 | dentry->d_inode->i_ino); | ||
262 | ret = -EIO; | ||
263 | goto error; | ||
264 | |||
265 | stale: | ||
266 | ret = -ESTALE; | ||
267 | goto error; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | * remove the object's xattr to mark it stale | ||
272 | */ | ||
273 | int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, | ||
274 | struct dentry *dentry) | ||
275 | { | ||
276 | int ret; | ||
277 | |||
278 | ret = vfs_removexattr(dentry, cachefiles_xattr_cache); | ||
279 | if (ret < 0) { | ||
280 | if (ret == -ENOENT || ret == -ENODATA) | ||
281 | ret = 0; | ||
282 | else if (ret != -ENOMEM) | ||
283 | cachefiles_io_error(cache, | ||
284 | "Can't remove xattr from %lu" | ||
285 | " (error %d)", | ||
286 | dentry->d_inode->i_ino, -ret); | ||
287 | } | ||
288 | |||
289 | _leave(" = %d", ret); | ||
290 | return ret; | ||
291 | } | ||