1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
3 *
4 * Copyright (C) 2000 Stelias Computing, Inc.
5 * Copyright (C) 2000 Red Hat, Inc.
6 * Copyright (C) 2000 Mountain View Data, Inc.
7 *
8 * Extended Attribute Support
9 * Copyright (C) 2001 Shirish H. Phatak, Tacit Networks, Inc.
10 *
11 * This file is part of InterMezzo, http://www.inter-mezzo.org.
12 *
13 * InterMezzo is free software; you can redistribute it and/or
14 * modify it under the terms of version 2 of the GNU General Public
15 * License as published by the Free Software Foundation.
16 *
17 * InterMezzo is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with InterMezzo; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28 #include <stdarg.h>
29
30 #include <asm/bitops.h>
31 #include <asm/uaccess.h>
32 #include <asm/system.h>
33
34 #include <linux/errno.h>
35 #include <linux/fs.h>
36 #include <linux/ext2_fs.h>
37 #include <linux/slab.h>
38 #include <linux/vmalloc.h>
39 #include <linux/sched.h>
40 #include <linux/stat.h>
41 #include <linux/string.h>
42 #include <linux/locks.h>
43 #include <linux/blkdev.h>
44 #include <linux/init.h>
45 #define __NO_VERSION__
46 #include <linux/module.h>
47
48 #include <linux/fsfilter.h>
49 #include <linux/intermezzo_fs.h>
50
51
52 int filter_print_entry = 0;
53 int filter_debug = 0xfffffff;
54 /*
55 * The function in this file are responsible for setting up the
56 * correct methods layered file systems like InterMezzo and snapfs
57 */
58
59
60 static struct filter_fs filter_oppar[FILTER_FS_TYPES];
61
62 /* get to the upper methods (intermezzo, snapfs) */
filter_c2usops(struct filter_fs * cache)63 inline struct super_operations *filter_c2usops(struct filter_fs *cache)
64 {
65 return &cache->o_fops.filter_sops;
66 }
67
filter_c2udiops(struct filter_fs * cache)68 inline struct inode_operations *filter_c2udiops(struct filter_fs *cache)
69 {
70 return &cache->o_fops.filter_dir_iops;
71 }
72
73
filter_c2ufiops(struct filter_fs * cache)74 inline struct inode_operations *filter_c2ufiops(struct filter_fs *cache)
75 {
76 return &cache->o_fops.filter_file_iops;
77 }
78
filter_c2usiops(struct filter_fs * cache)79 inline struct inode_operations *filter_c2usiops(struct filter_fs *cache)
80 {
81 return &cache->o_fops.filter_sym_iops;
82 }
83
84
filter_c2udfops(struct filter_fs * cache)85 inline struct file_operations *filter_c2udfops(struct filter_fs *cache)
86 {
87 return &cache->o_fops.filter_dir_fops;
88 }
89
filter_c2uffops(struct filter_fs * cache)90 inline struct file_operations *filter_c2uffops(struct filter_fs *cache)
91 {
92 return &cache->o_fops.filter_file_fops;
93 }
94
filter_c2usfops(struct filter_fs * cache)95 inline struct file_operations *filter_c2usfops(struct filter_fs *cache)
96 {
97 return &cache->o_fops.filter_sym_fops;
98 }
99
filter_c2udops(struct filter_fs * cache)100 inline struct dentry_operations *filter_c2udops(struct filter_fs *cache)
101 {
102 return &cache->o_fops.filter_dentry_ops;
103 }
104
105 /* get to the cache (lower) methods */
filter_c2csops(struct filter_fs * cache)106 inline struct super_operations *filter_c2csops(struct filter_fs *cache)
107 {
108 return cache->o_caops.cache_sops;
109 }
110
filter_c2cdiops(struct filter_fs * cache)111 inline struct inode_operations *filter_c2cdiops(struct filter_fs *cache)
112 {
113 return cache->o_caops.cache_dir_iops;
114 }
115
filter_c2cfiops(struct filter_fs * cache)116 inline struct inode_operations *filter_c2cfiops(struct filter_fs *cache)
117 {
118 return cache->o_caops.cache_file_iops;
119 }
120
filter_c2csiops(struct filter_fs * cache)121 inline struct inode_operations *filter_c2csiops(struct filter_fs *cache)
122 {
123 return cache->o_caops.cache_sym_iops;
124 }
125
filter_c2cdfops(struct filter_fs * cache)126 inline struct file_operations *filter_c2cdfops(struct filter_fs *cache)
127 {
128 return cache->o_caops.cache_dir_fops;
129 }
130
filter_c2cffops(struct filter_fs * cache)131 inline struct file_operations *filter_c2cffops(struct filter_fs *cache)
132 {
133 return cache->o_caops.cache_file_fops;
134 }
135
filter_c2csfops(struct filter_fs * cache)136 inline struct file_operations *filter_c2csfops(struct filter_fs *cache)
137 {
138 return cache->o_caops.cache_sym_fops;
139 }
140
filter_c2cdops(struct filter_fs * cache)141 inline struct dentry_operations *filter_c2cdops(struct filter_fs *cache)
142 {
143 return cache->o_caops.cache_dentry_ops;
144 }
145
146
filter_setup_journal_ops(struct filter_fs * ops,char * cache_type)147 void filter_setup_journal_ops(struct filter_fs *ops, char *cache_type)
148 {
149 if ( strlen(cache_type) == strlen("ext2") &&
150 memcmp(cache_type, "ext2", strlen("ext2")) == 0 ) {
151 #if CONFIG_EXT2_FS
152 ops->o_trops = &presto_ext2_journal_ops;
153 #else
154 ops->o_trops = NULL;
155 #endif
156 FDEBUG(D_SUPER, "ops at %p\n", ops);
157 }
158
159 if ( strlen(cache_type) == strlen("ext3") &&
160 memcmp(cache_type, "ext3", strlen("ext3")) == 0 ) {
161 #if defined(CONFIG_EXT3_FS) || defined (CONFIG_EXT3_FS_MODULE)
162 ops->o_trops = &presto_ext3_journal_ops;
163 #else
164 ops->o_trops = NULL;
165 #endif
166 FDEBUG(D_SUPER, "ops at %p\n", ops);
167 }
168
169 if ( strlen(cache_type) == strlen("tmpfs") &&
170 memcmp(cache_type, "tmpfs", strlen("tmpfs")) == 0 ) {
171 #if defined(CONFIG_TMPFS)
172 ops->o_trops = &presto_tmpfs_journal_ops;
173 #else
174 ops->o_trops = NULL;
175 #endif
176 FDEBUG(D_SUPER, "ops at %p\n", ops);
177 }
178
179 if ( strlen(cache_type) == strlen("reiserfs") &&
180 memcmp(cache_type, "reiserfs", strlen("reiserfs")) == 0 ) {
181 #if 0
182 /* #if defined(CONFIG_REISERFS_FS) || defined(CONFIG_REISERFS_FS_MODULE) */
183 ops->o_trops = &presto_reiserfs_journal_ops;
184 #else
185 ops->o_trops = NULL;
186 #endif
187 FDEBUG(D_SUPER, "ops at %p\n", ops);
188 }
189
190 if ( strlen(cache_type) == strlen("xfs") &&
191 memcmp(cache_type, "xfs", strlen("xfs")) == 0 ) {
192 #if 0
193 /*#if defined(CONFIG_XFS_FS) || defined (CONFIG_XFS_FS_MODULE) */
194 ops->o_trops = &presto_xfs_journal_ops;
195 #else
196 ops->o_trops = NULL;
197 #endif
198 FDEBUG(D_SUPER, "ops at %p\n", ops);
199 }
200
201 if ( strlen(cache_type) == strlen("obdfs") &&
202 memcmp(cache_type, "obdfs", strlen("obdfs")) == 0 ) {
203 #if defined(CONFIG_OBDFS_FS) || defined (CONFIG_OBDFS_FS_MODULE)
204 ops->o_trops = presto_obdfs_journal_ops;
205 #else
206 ops->o_trops = NULL;
207 #endif
208 FDEBUG(D_SUPER, "ops at %p\n", ops);
209 }
210 }
211
212
213 /* find the cache for this FS */
filter_get_filter_fs(const char * cache_type)214 struct filter_fs *filter_get_filter_fs(const char *cache_type)
215 {
216 struct filter_fs *ops = NULL;
217 FENTRY;
218
219 if ( strlen(cache_type) == strlen("ext2") &&
220 memcmp(cache_type, "ext2", strlen("ext2")) == 0 ) {
221 ops = &filter_oppar[FILTER_FS_EXT2];
222 FDEBUG(D_SUPER, "ops at %p\n", ops);
223 }
224
225 if ( strlen(cache_type) == strlen("xfs") &&
226 memcmp(cache_type, "xfs", strlen("xfs")) == 0 ) {
227 ops = &filter_oppar[FILTER_FS_XFS];
228 FDEBUG(D_SUPER, "ops at %p\n", ops);
229 }
230
231 if ( strlen(cache_type) == strlen("ext3") &&
232 memcmp(cache_type, "ext3", strlen("ext3")) == 0 ) {
233 ops = &filter_oppar[FILTER_FS_EXT3];
234 FDEBUG(D_SUPER, "ops at %p\n", ops);
235 }
236
237 if ( strlen(cache_type) == strlen("tmpfs") &&
238 memcmp(cache_type, "tmpfs", strlen("tmpfs")) == 0 ) {
239 ops = &filter_oppar[FILTER_FS_TMPFS];
240 FDEBUG(D_SUPER, "ops at %p\n", ops);
241 }
242
243 if ( strlen(cache_type) == strlen("reiserfs") &&
244 memcmp(cache_type, "reiserfs", strlen("reiserfs")) == 0 ) {
245 ops = &filter_oppar[FILTER_FS_REISERFS];
246 FDEBUG(D_SUPER, "ops at %p\n", ops);
247 }
248 if ( strlen(cache_type) == strlen("obdfs") &&
249 memcmp(cache_type, "obdfs", strlen("obdfs")) == 0 ) {
250 ops = &filter_oppar[FILTER_FS_OBDFS];
251 FDEBUG(D_SUPER, "ops at %p\n", ops);
252 }
253
254 if (ops == NULL) {
255 CERROR("prepare to die: unrecognized cache type for Filter\n");
256 }
257 FEXIT;
258 return ops;
259 }
260
261
262 /*
263 * Frobnicate the InterMezzo operations
264 * this establishes the link between the InterMezzo file system
265 * and the underlying file system used for the cache.
266 */
267
filter_setup_super_ops(struct filter_fs * cache,struct super_operations * cache_sops,struct super_operations * filter_sops)268 void filter_setup_super_ops(struct filter_fs *cache, struct super_operations *cache_sops, struct super_operations *filter_sops)
269 {
270 /* Get ptr to the shared struct snapfs_ops structure. */
271 struct filter_ops *props = &cache->o_fops;
272 /* Get ptr to the shared struct cache_ops structure. */
273 struct cache_ops *caops = &cache->o_caops;
274
275 FENTRY;
276
277 if ( cache->o_flags & FILTER_DID_SUPER_OPS ) {
278 FEXIT;
279 return;
280 }
281 cache->o_flags |= FILTER_DID_SUPER_OPS;
282
283 /* Set the cache superblock operations to point to the
284 superblock operations of the underlying file system. */
285 caops->cache_sops = cache_sops;
286
287 /*
288 * Copy the cache (real fs) superblock ops to the "filter"
289 * superblock ops as defaults. Some will be changed below
290 */
291 memcpy(&props->filter_sops, cache_sops, sizeof(*cache_sops));
292
293 /* 'put_super' unconditionally is that of filter */
294 if (filter_sops->put_super) {
295 props->filter_sops.put_super = filter_sops->put_super;
296 }
297
298 if (cache_sops->read_inode) {
299 props->filter_sops.read_inode = filter_sops->read_inode;
300 FDEBUG(D_INODE, "setting filter_read_inode, cache_ops %p, cache %p, ri at %p\n",
301 cache, cache, props->filter_sops.read_inode);
302 }
303
304 if (cache_sops->remount_fs)
305 props->filter_sops.remount_fs = filter_sops->remount_fs;
306 FEXIT;
307 }
308
309
filter_setup_dir_ops(struct filter_fs * cache,struct inode * inode,struct inode_operations * filter_iops,struct file_operations * filter_fops)310 void filter_setup_dir_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
311 {
312 struct inode_operations *cache_filter_iops;
313 struct inode_operations *cache_iops = inode->i_op;
314 struct file_operations *cache_fops = inode->i_fop;
315 FENTRY;
316
317 if ( cache->o_flags & FILTER_DID_DIR_OPS ) {
318 FEXIT;
319 return;
320 }
321 cache->o_flags |= FILTER_DID_DIR_OPS;
322
323 /* former ops become cache_ops */
324 cache->o_caops.cache_dir_iops = cache_iops;
325 cache->o_caops.cache_dir_fops = cache_fops;
326 FDEBUG(D_SUPER, "filter at %p, cache iops %p, iops %p\n",
327 cache, cache_iops, filter_c2udiops(cache));
328
329 /* setup our dir iops: copy and modify */
330 memcpy(filter_c2udiops(cache), cache_iops, sizeof(*cache_iops));
331
332 /* abbreviate */
333 cache_filter_iops = filter_c2udiops(cache);
334
335 /* methods that filter if cache filesystem has these ops */
336 if (cache_iops->lookup && filter_iops->lookup)
337 cache_filter_iops->lookup = filter_iops->lookup;
338 if (cache_iops->create && filter_iops->create)
339 cache_filter_iops->create = filter_iops->create;
340 if (cache_iops->link && filter_iops->link)
341 cache_filter_iops->link = filter_iops->link;
342 if (cache_iops->unlink && filter_iops->unlink)
343 cache_filter_iops->unlink = filter_iops->unlink;
344 if (cache_iops->mkdir && filter_iops->mkdir)
345 cache_filter_iops->mkdir = filter_iops->mkdir;
346 if (cache_iops->rmdir && filter_iops->rmdir)
347 cache_filter_iops->rmdir = filter_iops->rmdir;
348 if (cache_iops->symlink && filter_iops->symlink)
349 cache_filter_iops->symlink = filter_iops->symlink;
350 if (cache_iops->rename && filter_iops->rename)
351 cache_filter_iops->rename = filter_iops->rename;
352 if (cache_iops->mknod && filter_iops->mknod)
353 cache_filter_iops->mknod = filter_iops->mknod;
354 if (cache_iops->permission && filter_iops->permission)
355 cache_filter_iops->permission = filter_iops->permission;
356 if (cache_iops->getattr)
357 cache_filter_iops->getattr = filter_iops->getattr;
358 /* Some filesystems do not use a setattr method of their own
359 instead relying on inode_setattr/write_inode. We still need to
360 journal these so we make setattr an unconditional operation.
361 XXX: we should probably check for write_inode. SHP
362 */
363 /*if (cache_iops->setattr)*/
364 cache_filter_iops->setattr = filter_iops->setattr;
365 #ifdef CONFIG_FS_EXT_ATTR
366 /* For now we assume that posix acls are handled through extended
367 * attributes. If this is not the case, we must explicitly trap
368 * posix_set_acl. SHP
369 */
370 if (cache_iops->set_ext_attr && filter_iops->set_ext_attr)
371 cache_filter_iops->set_ext_attr = filter_iops->set_ext_attr;
372 #endif
373
374
375 /* copy dir fops */
376 memcpy(filter_c2udfops(cache), cache_fops, sizeof(*cache_fops));
377
378 /* unconditional filtering operations */
379 filter_c2udfops(cache)->ioctl = filter_fops->ioctl;
380
381 FEXIT;
382 }
383
384
filter_setup_file_ops(struct filter_fs * cache,struct inode * inode,struct inode_operations * filter_iops,struct file_operations * filter_fops)385 void filter_setup_file_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
386 {
387 struct inode_operations *pr_iops;
388 struct inode_operations *cache_iops = inode->i_op;
389 struct file_operations *cache_fops = inode->i_fop;
390 FENTRY;
391
392 if ( cache->o_flags & FILTER_DID_FILE_OPS ) {
393 FEXIT;
394 return;
395 }
396 cache->o_flags |= FILTER_DID_FILE_OPS;
397
398 /* steal the old ops */
399 /* former ops become cache_ops */
400 cache->o_caops.cache_file_iops = cache_iops;
401 cache->o_caops.cache_file_fops = cache_fops;
402
403 /* abbreviate */
404 pr_iops = filter_c2ufiops(cache);
405
406 /* setup our dir iops: copy and modify */
407 memcpy(pr_iops, cache_iops, sizeof(*cache_iops));
408
409 /* copy dir fops */
410 CERROR("*** cache file ops at %p\n", cache_fops);
411 memcpy(filter_c2uffops(cache), cache_fops, sizeof(*cache_fops));
412
413 /* assign */
414 /* See comments above in filter_setup_dir_ops. SHP */
415 /*if (cache_iops->setattr)*/
416 pr_iops->setattr = filter_iops->setattr;
417 if (cache_iops->getattr)
418 pr_iops->getattr = filter_iops->getattr;
419 /* XXX Should this be conditional rmr ? */
420 pr_iops->permission = filter_iops->permission;
421 #ifdef CONFIG_FS_EXT_ATTR
422 /* For now we assume that posix acls are handled through extended
423 * attributes. If this is not the case, we must explicitly trap and
424 * posix_set_acl
425 */
426 if (cache_iops->set_ext_attr && filter_iops->set_ext_attr)
427 pr_iops->set_ext_attr = filter_iops->set_ext_attr;
428 #endif
429
430
431 /* unconditional filtering operations */
432 filter_c2uffops(cache)->open = filter_fops->open;
433 filter_c2uffops(cache)->release = filter_fops->release;
434 filter_c2uffops(cache)->write = filter_fops->write;
435 filter_c2uffops(cache)->ioctl = filter_fops->ioctl;
436
437 FEXIT;
438 }
439
440 /* XXX in 2.3 there are "fast" and "slow" symlink ops for ext2 XXX */
filter_setup_symlink_ops(struct filter_fs * cache,struct inode * inode,struct inode_operations * filter_iops,struct file_operations * filter_fops)441 void filter_setup_symlink_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
442 {
443 struct inode_operations *pr_iops;
444 struct inode_operations *cache_iops = inode->i_op;
445 struct file_operations *cache_fops = inode->i_fop;
446 FENTRY;
447
448 if ( cache->o_flags & FILTER_DID_SYMLINK_OPS ) {
449 FEXIT;
450 return;
451 }
452 cache->o_flags |= FILTER_DID_SYMLINK_OPS;
453
454 /* steal the old ops */
455 cache->o_caops.cache_sym_iops = cache_iops;
456 cache->o_caops.cache_sym_fops = cache_fops;
457
458 /* abbreviate */
459 pr_iops = filter_c2usiops(cache);
460
461 /* setup our dir iops: copy and modify */
462 memcpy(pr_iops, cache_iops, sizeof(*cache_iops));
463
464 /* See comments above in filter_setup_dir_ops. SHP */
465 /* if (cache_iops->setattr) */
466 pr_iops->setattr = filter_iops->setattr;
467 if (cache_iops->getattr)
468 pr_iops->getattr = filter_iops->getattr;
469
470 /* assign */
471 /* copy fops - careful for symlinks they might be NULL */
472 if ( cache_fops ) {
473 memcpy(filter_c2usfops(cache), cache_fops, sizeof(*cache_fops));
474 }
475
476 FEXIT;
477 }
478
filter_setup_dentry_ops(struct filter_fs * cache,struct dentry_operations * cache_dop,struct dentry_operations * filter_dop)479 void filter_setup_dentry_ops(struct filter_fs *cache,
480 struct dentry_operations *cache_dop,
481 struct dentry_operations *filter_dop)
482 {
483 if ( cache->o_flags & FILTER_DID_DENTRY_OPS ) {
484 FEXIT;
485 return;
486 }
487 cache->o_flags |= FILTER_DID_DENTRY_OPS;
488
489 cache->o_caops.cache_dentry_ops = cache_dop;
490 memcpy(&cache->o_fops.filter_dentry_ops,
491 filter_dop, sizeof(*filter_dop));
492
493 if (cache_dop && cache_dop != filter_dop && cache_dop->d_revalidate){
494 CERROR("WARNING: filter overriding revalidation!\n");
495 }
496 return;
497 }
498