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