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
52int filter_print_entry = 0;
53int 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
60static struct filter_fs filter_oppar[FILTER_FS_TYPES];
61
62/* get to the upper methods (intermezzo, snapfs) */
63inline struct super_operations *filter_c2usops(struct filter_fs *cache)
64{
65        return &cache->o_fops.filter_sops;
66}
67
68inline struct inode_operations *filter_c2udiops(struct filter_fs *cache)
69{
70        return &cache->o_fops.filter_dir_iops;
71}
72
73
74inline struct inode_operations *filter_c2ufiops(struct filter_fs *cache)
75{
76        return &cache->o_fops.filter_file_iops;
77}
78
79inline struct inode_operations *filter_c2usiops(struct filter_fs *cache)
80{
81        return &cache->o_fops.filter_sym_iops;
82}
83
84
85inline struct file_operations *filter_c2udfops(struct filter_fs *cache)
86{
87        return &cache->o_fops.filter_dir_fops;
88}
89
90inline struct file_operations *filter_c2uffops(struct filter_fs *cache)
91{
92        return &cache->o_fops.filter_file_fops;
93}
94
95inline struct file_operations *filter_c2usfops(struct filter_fs *cache)
96{
97        return &cache->o_fops.filter_sym_fops;
98}
99
100inline 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 */
106inline struct super_operations *filter_c2csops(struct filter_fs *cache)
107{
108        return cache->o_caops.cache_sops;
109}
110
111inline struct inode_operations *filter_c2cdiops(struct filter_fs *cache)
112{
113        return cache->o_caops.cache_dir_iops;
114}
115
116inline struct inode_operations *filter_c2cfiops(struct filter_fs *cache)
117{
118        return cache->o_caops.cache_file_iops;
119}
120
121inline struct inode_operations *filter_c2csiops(struct filter_fs *cache)
122{
123        return cache->o_caops.cache_sym_iops;
124}
125
126inline struct file_operations *filter_c2cdfops(struct filter_fs *cache)
127{
128        return cache->o_caops.cache_dir_fops;
129}
130
131inline struct file_operations *filter_c2cffops(struct filter_fs *cache)
132{
133        return cache->o_caops.cache_file_fops;
134}
135
136inline struct file_operations *filter_c2csfops(struct filter_fs *cache)
137{
138        return cache->o_caops.cache_sym_fops;
139}
140
141inline struct dentry_operations *filter_c2cdops(struct filter_fs *cache)
142{
143        return cache->o_caops.cache_dentry_ops;
144}
145
146
147void 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                ops->o_trops = NULL;
182                FDEBUG(D_SUPER, "ops at %p\n", ops);
183        }
184
185        if ( strlen(cache_type) == strlen("xfs") &&
186             memcmp(cache_type, "xfs", strlen("xfs")) == 0 ) {
187                ops->o_trops = NULL;
188                FDEBUG(D_SUPER, "ops at %p\n", ops);
189        }
190
191        if ( strlen(cache_type) == strlen("obdfs") &&
192             memcmp(cache_type, "obdfs", strlen("obdfs")) == 0 ) {
193#if defined(CONFIG_OBDFS_FS) || defined(CONFIG_OBDFS_FS_MODULE)
194                ops->o_trops = presto_obdfs_journal_ops;
195#else
196                ops->o_trops = NULL;
197#endif
198                FDEBUG(D_SUPER, "ops at %p\n", ops);
199        }
200}
201
202
203/* find the cache for this FS */
204struct filter_fs *filter_get_filter_fs(const char *cache_type)
205{
206        struct filter_fs *ops = NULL;
207        FENTRY;
208
209        if ( strlen(cache_type) == strlen("ext2") &&
210             memcmp(cache_type, "ext2", strlen("ext2")) == 0 ) {
211                ops = &filter_oppar[FILTER_FS_EXT2];
212                FDEBUG(D_SUPER, "ops at %p\n", ops);
213        }
214
215        if ( strlen(cache_type) == strlen("xfs") &&
216             memcmp(cache_type, "xfs", strlen("xfs")) == 0 ) {
217                ops = &filter_oppar[FILTER_FS_XFS];
218                FDEBUG(D_SUPER, "ops at %p\n", ops);
219        }
220
221        if ( strlen(cache_type) == strlen("ext3") &&
222             memcmp(cache_type, "ext3", strlen("ext3")) == 0 ) {
223                ops = &filter_oppar[FILTER_FS_EXT3];
224                FDEBUG(D_SUPER, "ops at %p\n", ops);
225        }
226
227        if ( strlen(cache_type) == strlen("tmpfs") &&
228             memcmp(cache_type, "tmpfs", strlen("tmpfs")) == 0 ) {
229                ops = &filter_oppar[FILTER_FS_TMPFS];
230                FDEBUG(D_SUPER, "ops at %p\n", ops);
231        }
232
233        if ( strlen(cache_type) == strlen("reiserfs") &&
234             memcmp(cache_type, "reiserfs", strlen("reiserfs")) == 0 ) {
235                ops = &filter_oppar[FILTER_FS_REISERFS];
236                FDEBUG(D_SUPER, "ops at %p\n", ops);
237        }
238        if ( strlen(cache_type) == strlen("obdfs") &&
239             memcmp(cache_type, "obdfs", strlen("obdfs")) == 0 ) {
240                ops = &filter_oppar[FILTER_FS_OBDFS];
241                FDEBUG(D_SUPER, "ops at %p\n", ops);
242        }
243
244        if (ops == NULL) {
245                CERROR("prepare to die: unrecognized cache type for Filter\n");
246        }
247        return ops;
248        FEXIT;
249}
250
251
252/*
253 *  Frobnicate the InterMezzo operations
254 *    this establishes the link between the InterMezzo file system
255 *    and the underlying file system used for the cache.
256 */
257
258void filter_setup_super_ops(struct filter_fs *cache, struct super_operations *cache_sops, struct super_operations *filter_sops)
259{
260        /* Get ptr to the shared struct snapfs_ops structure. */
261        struct filter_ops *props = &cache->o_fops;
262        /* Get ptr to the shared struct cache_ops structure. */
263        struct cache_ops *caops = &cache->o_caops;
264
265        FENTRY;
266
267        if ( cache->o_flags & FILTER_DID_SUPER_OPS ) {
268                FEXIT;
269                return;
270        }
271        cache->o_flags |= FILTER_DID_SUPER_OPS;
272
273        /* Set the cache superblock operations to point to the
274           superblock operations of the underlying file system.  */
275        caops->cache_sops = cache_sops;
276
277        /*
278         * Copy the cache (real fs) superblock ops to the "filter"
279         * superblock ops as defaults. Some will be changed below
280         */
281        memcpy(&props->filter_sops, cache_sops, sizeof(*cache_sops));
282
283        /* 'put_super' unconditionally is that of filter */
284        if (filter_sops->put_super) {
285                props->filter_sops.put_super = filter_sops->put_super;
286        }
287
288        if (cache_sops->read_inode) {
289                props->filter_sops.read_inode = filter_sops->read_inode;
290                FDEBUG(D_INODE, "setting filter_read_inode, cache_ops %p, cache %p, ri at %p\n",
291                      cache, cache, props->filter_sops.read_inode);
292        }
293
294        if (cache_sops->remount_fs)
295                props->filter_sops.remount_fs = filter_sops->remount_fs;
296        FEXIT;
297}
298
299
300void filter_setup_dir_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
301{
302        struct inode_operations *cache_filter_iops;
303        struct inode_operations *cache_iops = inode->i_op;
304        struct file_operations *cache_fops = inode->i_fop;
305        FENTRY;
306
307        if ( cache->o_flags & FILTER_DID_DIR_OPS ) {
308                FEXIT;
309                return;
310        }
311        cache->o_flags |= FILTER_DID_DIR_OPS;
312
313        /* former ops become cache_ops */
314        cache->o_caops.cache_dir_iops = cache_iops;
315        cache->o_caops.cache_dir_fops = cache_fops;
316        FDEBUG(D_SUPER, "filter at %p, cache iops %p, iops %p\n",
317               cache, cache_iops, filter_c2udiops(cache));
318
319        /* setup our dir iops: copy and modify */
320        memcpy(filter_c2udiops(cache), cache_iops, sizeof(*cache_iops));
321
322        /* abbreviate */
323        cache_filter_iops = filter_c2udiops(cache);
324
325        /* methods that filter if cache filesystem has these ops */
326        if (cache_iops->lookup && filter_iops->lookup)
327                cache_filter_iops->lookup = filter_iops->lookup;
328        if (cache_iops->create && filter_iops->create)
329                cache_filter_iops->create = filter_iops->create;
330        if (cache_iops->link && filter_iops->link)
331                cache_filter_iops->link = filter_iops->link;
332        if (cache_iops->unlink && filter_iops->unlink)
333                cache_filter_iops->unlink = filter_iops->unlink;
334        if (cache_iops->mkdir && filter_iops->mkdir)
335                cache_filter_iops->mkdir = filter_iops->mkdir;
336        if (cache_iops->rmdir && filter_iops->rmdir)
337                cache_filter_iops->rmdir = filter_iops->rmdir;
338        if (cache_iops->symlink && filter_iops->symlink)
339                cache_filter_iops->symlink = filter_iops->symlink;
340        if (cache_iops->rename && filter_iops->rename)
341                cache_filter_iops->rename = filter_iops->rename;
342        if (cache_iops->mknod && filter_iops->mknod)
343                cache_filter_iops->mknod = filter_iops->mknod;
344        if (cache_iops->permission && filter_iops->permission)
345                cache_filter_iops->permission = filter_iops->permission;
346        if (cache_iops->getattr)
347                cache_filter_iops->getattr = filter_iops->getattr;
348        /*if (cache_iops->setattr)*/
349                cache_filter_iops->setattr = filter_iops->setattr;
350#ifdef CONFIG_FS_EXT_ATTR
351	/* For now we assume that posix acls are handled through extended
352	* attributes. If this is not the case, we must explicitly trap
353	* posix_set_acl. SHP
354	*/
355	if (cache_iops->set_ext_attr && filter_iops->set_ext_attr)
356		cache_filter_iops->set_ext_attr = filter_iops->set_ext_attr;
357#endif
358
359
360        /* copy dir fops */
361        memcpy(filter_c2udfops(cache), cache_fops, sizeof(*cache_fops));
362
363        /* unconditional filtering operations */
364        filter_c2udfops(cache)->ioctl = filter_fops->ioctl;
365
366        FEXIT;
367}
368
369
370void filter_setup_file_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
371{
372        struct inode_operations *pr_iops;
373        struct inode_operations *cache_iops = inode->i_op;
374        struct file_operations *cache_fops = inode->i_fop;
375        FENTRY;
376
377        if ( cache->o_flags & FILTER_DID_FILE_OPS ) {
378                FEXIT;
379                return;
380        }
381        cache->o_flags |= FILTER_DID_FILE_OPS;
382
383        /* steal the old ops */
384        /* former ops become cache_ops */
385        cache->o_caops.cache_file_iops = cache_iops;
386        cache->o_caops.cache_file_fops = cache_fops;
387
388        /* abbreviate */
389        pr_iops = filter_c2ufiops(cache);
390
391        /* setup our dir iops: copy and modify */
392        memcpy(pr_iops, cache_iops, sizeof(*cache_iops));
393
394        /* copy dir fops */
395        CERROR("*** cache file ops at %p\n", cache_fops);
396        memcpy(filter_c2uffops(cache), cache_fops, sizeof(*cache_fops));
397
398        /* assign */
399        /* See comments above in filter_setup_dir_ops. SHP */
400        /*if (cache_iops->setattr)*/
401                pr_iops->setattr = filter_iops->setattr;
402        if (cache_iops->getattr)
403                pr_iops->getattr = filter_iops->getattr;
404        pr_iops->permission = filter_iops->permission;
405#ifdef CONFIG_FS_EXT_ATTR
406    	/* For now we assume that posix acls are handled through extended
407	* attributes. If this is not the case, we must explicitly trap and
408	* posix_set_acl
409	*/
410	if (cache_iops->set_ext_attr && filter_iops->set_ext_attr)
411		pr_iops->set_ext_attr = filter_iops->set_ext_attr;
412#endif
413
414
415        /* unconditional filtering operations */
416        filter_c2uffops(cache)->open = filter_fops->open;
417        filter_c2uffops(cache)->release = filter_fops->release;
418        filter_c2uffops(cache)->write = filter_fops->write;
419        filter_c2uffops(cache)->ioctl = filter_fops->ioctl;
420
421        FEXIT;
422}
423
424void filter_setup_symlink_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
425{
426        struct inode_operations *pr_iops;
427        struct inode_operations *cache_iops = inode->i_op;
428        struct file_operations *cache_fops = inode->i_fop;
429        FENTRY;
430
431        if ( cache->o_flags & FILTER_DID_SYMLINK_OPS ) {
432                FEXIT;
433                return;
434        }
435        cache->o_flags |= FILTER_DID_SYMLINK_OPS;
436
437        /* steal the old ops */
438        cache->o_caops.cache_sym_iops = cache_iops;
439        cache->o_caops.cache_sym_fops = cache_fops;
440
441        /* abbreviate */
442        pr_iops = filter_c2usiops(cache);
443
444        /* setup our dir iops: copy and modify */
445        memcpy(pr_iops, cache_iops, sizeof(*cache_iops));
446
447        /* See comments above in filter_setup_dir_ops. SHP */
448        /* if (cache_iops->setattr) */
449                pr_iops->setattr = filter_iops->setattr;
450        if (cache_iops->getattr)
451                pr_iops->getattr = filter_iops->getattr;
452
453        /* assign */
454        /* copy fops - careful for symlinks they might be NULL */
455        if ( cache_fops ) {
456                memcpy(filter_c2usfops(cache), cache_fops, sizeof(*cache_fops));
457        }
458
459        FEXIT;
460}
461
462void filter_setup_dentry_ops(struct filter_fs *cache,
463                             struct dentry_operations *cache_dop,
464                             struct dentry_operations *filter_dop)
465{
466        if ( cache->o_flags & FILTER_DID_DENTRY_OPS ) {
467                FEXIT;
468                return;
469        }
470        cache->o_flags |= FILTER_DID_DENTRY_OPS;
471
472        cache->o_caops.cache_dentry_ops = cache_dop;
473        memcpy(&cache->o_fops.filter_dentry_ops,
474               filter_dop, sizeof(*filter_dop));
475
476        if (cache_dop &&  cache_dop != filter_dop && cache_dop->d_revalidate){
477                CERROR("WARNING: filter overriding revalidation!\n");
478        }
479        return;
480}
481