/* * filter.c */ #define DEBUG_SUBSYSTEM S_SNAP #include #include #include #include #include #include #include #include #include #include "snapfs_internal.h" /* * The function in this file are responsible for setting up the * correct methods layered file systems like InterMezzo and SnapFS */ static struct filter_fs filter_oppar[FILTER_FS_TYPES]; /* get to the upper methods (intermezzo, snapfs) */ inline struct super_operations *filter_c2usops(struct filter_fs *cache) { return &cache->o_fops.filter_sops; } inline struct inode_operations *filter_c2udiops(struct filter_fs *cache) { return &cache->o_fops.filter_dir_iops; } inline struct inode_operations *filter_c2ufiops(struct filter_fs *cache) { return &cache->o_fops.filter_file_iops; } inline struct inode_operations *filter_c2usiops(struct filter_fs *cache) { return &cache->o_fops.filter_sym_iops; } inline struct file_operations *filter_c2udfops(struct filter_fs *cache) { return &cache->o_fops.filter_dir_fops; } inline struct file_operations *filter_c2uffops(struct filter_fs *cache) { return &cache->o_fops.filter_file_fops; } inline struct address_space_operations *filter_c2ufaops(struct filter_fs *cache) { return &cache->o_fops.filter_file_aops; } inline struct file_operations *filter_c2usfops(struct filter_fs *cache) { return &cache->o_fops.filter_sym_fops; } inline struct dentry_operations *filter_c2udops(struct filter_fs *cache) { return &cache->o_fops.filter_dentry_ops; } /* get to the cache (lower) methods */ inline struct super_operations *filter_c2csops(struct filter_fs *cache) { return cache->o_caops.cache_sops; } inline struct inode_operations *filter_c2cdiops(struct filter_fs *cache) { return cache->o_caops.cache_dir_iops; } inline struct inode_operations *filter_c2cfiops(struct filter_fs *cache) { return cache->o_caops.cache_file_iops; } inline struct address_space_operations *filter_c2cfaops(struct filter_fs *cache) { return cache->o_caops.cache_file_aops; } inline struct inode_operations *filter_c2csiops(struct filter_fs *cache) { return cache->o_caops.cache_sym_iops; } inline struct file_operations *filter_c2cdfops(struct filter_fs *cache) { return cache->o_caops.cache_dir_fops; } inline struct file_operations *filter_c2cffops(struct filter_fs *cache) { return cache->o_caops.cache_file_fops; } inline struct file_operations *filter_c2csfops(struct filter_fs *cache) { return cache->o_caops.cache_sym_fops; } inline struct dentry_operations *filter_c2cdops(struct filter_fs *cache) { return cache->o_caops.cache_dentry_ops; } /* snapfs: for snapshot operations */ inline struct snapshot_operations *filter_c2csnapops(struct filter_fs *cache) { return cache->o_snapops; } /* find the cache for this FS */ struct filter_fs *filter_get_filter_fs(const char *cache_type) { struct filter_fs *ops = NULL; ENTRY; if ((strlen(cache_type) == strlen("ext3") && memcmp(cache_type, "ext3", strlen("ext3")) == 0)) { ops = &filter_oppar[FILTER_FS_EXT3]; CDEBUG(D_SUPER, "ops at %p\n", ops); } else if ( strlen(cache_type) == strlen("reiser") && memcmp(cache_type, "reiser", strlen("reiser")) == 0 ) { ops = &filter_oppar[FILTER_FS_REISER]; CDEBUG(D_SUPER, "ops at %p\n", ops); } else { CERROR("prepare to die: unrecognized cache type for Filter\n"); } EXIT; return ops; } /* * Frobnicate the InterMezzo/SnapFS operations * this establishes the link between the InterMezzo/SnapFS file system * and the underlying file system used for the cache. */ void filter_setup_super_ops(struct filter_fs *cache, struct super_operations *cache_sops, struct super_operations *filter_sops) { /* Get ptr to the shared struct snapfs_ops structure. */ struct filter_ops *uops = &cache->o_fops; /* Get ptr to the shared struct cache_ops structure. */ struct cache_ops *caops = &cache->o_caops; ENTRY; if ( cache->o_flags & FILTER_DID_SUPER_OPS ) { EXIT; return; } cache->o_flags |= FILTER_DID_SUPER_OPS; /* Set the cache superblock operations to point to the superblock operations of the underlying file system. */ caops->cache_sops = cache_sops; /* * Copy the cache (real fs) superblock ops to the "filter" * superblock ops as defaults. Some will be changed below */ memcpy(&uops->filter_sops, cache_sops, sizeof(*cache_sops)); /* now overwrite with filtering ops */ if (cache_sops->put_super && uops->filter_sops.put_super) { uops->filter_sops.put_super = filter_sops->put_super; } if (cache_sops->read_inode && uops->filter_sops.read_inode) { uops->filter_sops.read_inode = filter_sops->read_inode; CDEBUG(D_INODE, "setting filter_read_inode, cache_ops %p, cache %p, ri at %p\n", cache, cache, uops->filter_sops.read_inode); } uops->filter_sops.clear_inode = filter_sops->clear_inode; EXIT; } void filter_setup_dir_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops) { struct inode_operations *u_iops; struct file_operations *u_fops; ENTRY; if (cache->o_flags & FILTER_DID_DIR_OPS) { EXIT; return; } cache->o_flags |= FILTER_DID_DIR_OPS; /* steal the old ops */ cache->o_caops.cache_dir_iops = inode->i_op; cache->o_caops.cache_dir_fops = inode->i_fop; u_iops = filter_c2udiops(cache); u_fops = filter_c2udfops(cache); /* setup our dir iops and fops: copy and modify */ memcpy(u_iops, inode->i_op, sizeof(struct inode_operations)); memcpy(u_fops, inode->i_fop, sizeof(struct file_operations)); /* methods that filter if cache filesystem has these ops */ if (filter_iops) { struct inode_operations *cache_iops = inode->i_op; if (cache_iops->lookup && filter_iops->lookup) u_iops->lookup = filter_iops->lookup; if (cache_iops->create && filter_iops->create) u_iops->create = filter_iops->create; if (cache_iops->link && filter_iops->link) u_iops->link = filter_iops->link; if (cache_iops->unlink && filter_iops->unlink) u_iops->unlink = filter_iops->unlink; if (cache_iops->mkdir && filter_iops->mkdir) u_iops->mkdir = filter_iops->mkdir; if (cache_iops->rmdir && filter_iops->rmdir) u_iops->rmdir = filter_iops->rmdir; if (cache_iops->symlink && filter_iops->symlink) u_iops->symlink = filter_iops->symlink; if (cache_iops->rename && filter_iops->rename) u_iops->rename = filter_iops->rename; if (cache_iops->mknod && filter_iops->mknod) u_iops->mknod = filter_iops->mknod; if (cache_iops->permission && filter_iops->permission) u_iops->permission = filter_iops->permission; if (cache_iops->setattr && filter_iops->setattr) u_iops->setattr = filter_iops->setattr; if (cache_iops->setxattr && filter_iops->setxattr) u_iops->setxattr = filter_iops->setxattr; if (cache_iops->removexattr && filter_iops->removexattr) u_iops->removexattr = filter_iops->removexattr; } /* copy dir fops */ if (filter_fops) { struct file_operations *cache_fops = inode->i_fop; if(cache_fops->readdir && filter_fops->readdir) u_fops->readdir = filter_fops->readdir; } EXIT; } void filter_setup_file_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops, struct address_space_operations *filter_aops) { struct inode_operations *u_iops; struct file_operations *u_fops; struct address_space_operations *u_aops; ENTRY; if (cache->o_flags & FILTER_DID_FILE_OPS || !inode ) { EXIT; return; } cache->o_flags |= FILTER_DID_FILE_OPS; /* steal the old ops */ cache->o_caops.cache_file_iops = inode->i_op; cache->o_caops.cache_file_fops = inode->i_fop; /* abbreviate */ u_iops = filter_c2ufiops(cache); u_fops = filter_c2uffops(cache); u_aops = filter_c2ufaops(cache); /* setup our dir iops: copy and modify */ memcpy(u_iops, inode->i_op, sizeof(struct inode_operations)); memcpy(u_fops, inode->i_fop, sizeof(struct file_operations)); if (inode->i_mapping && inode->i_mapping->a_ops) { cache->o_caops.cache_file_aops = inode->i_mapping->a_ops; memcpy(u_aops, inode->i_mapping->a_ops, sizeof(struct address_space_operations)); } if (filter_iops) { if (filter_iops->revalidate) u_iops->revalidate = filter_iops->revalidate; if (filter_iops->removexattr) u_iops->removexattr = filter_iops->removexattr; if (filter_iops->setxattr) u_iops->setxattr = filter_iops->setxattr; if (filter_iops->setattr) u_iops->setattr = filter_iops->setattr; } if (filter_fops) { if (filter_fops->read) u_fops->read = filter_fops->read; if (filter_fops->write) u_fops->write = filter_fops->write; } if (filter_aops) { if (filter_aops->readpage) u_aops->readpage = filter_aops->readpage; } EXIT; } void filter_setup_symlink_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops) { struct inode_operations *u_iops; struct file_operations *u_fops; ENTRY; if (cache->o_flags & FILTER_DID_SYMLINK_OPS || !inode ) { EXIT; return; } cache->o_flags |= FILTER_DID_SYMLINK_OPS; /* steal the old ops */ cache->o_caops.cache_sym_iops = inode->i_op; cache->o_caops.cache_sym_fops = inode->i_fop; /* abbreviate */ u_iops = filter_c2usiops(cache); u_fops = filter_c2usfops(cache); /* setup our dir iops: copy and modify */ memcpy(u_iops, inode->i_op, sizeof(struct inode_operations)); memcpy(u_fops, inode->i_fop, sizeof(struct file_operations)); if (filter_iops) { struct inode_operations *cache_iops = inode->i_op; if (cache_iops->readlink && filter_iops->readlink) u_iops->readlink = filter_iops->readlink; if (cache_iops->follow_link && filter_iops->follow_link) u_iops->follow_link = filter_iops->follow_link; if (cache_iops->getxattr && filter_iops->getxattr) u_iops->getxattr = filter_iops->getxattr; if (cache_iops->listxattr && filter_iops->listxattr) u_iops->listxattr = filter_iops->listxattr; } EXIT; } void filter_setup_dentry_ops(struct filter_fs *cache, struct dentry_operations *cache_dop, struct dentry_operations *filter_dop) { if ( cache->o_flags & FILTER_DID_DENTRY_OPS ) { EXIT; return; } cache->o_flags |= FILTER_DID_DENTRY_OPS; cache->o_caops.cache_dentry_ops = cache_dop; memcpy(&cache->o_fops.filter_dentry_ops, filter_dop, sizeof(*filter_dop)); if (cache_dop && cache_dop != filter_dop && cache_dop->d_revalidate){ CWARN("filter overriding revalidation!\n"); } EXIT; return; } /* snapfs : for snapshot operations */ void filter_setup_snapshot_ops (struct filter_fs *cache, struct snapshot_operations *cache_snapops) { ENTRY; if ( cache->o_flags & FILTER_DID_SNAPSHOT_OPS ) { EXIT; return; } cache->o_flags |= FILTER_DID_SNAPSHOT_OPS; cache->o_snapops = cache_snapops; EXIT; } void filter_setup_journal_ops (struct filter_fs *cache, struct journal_ops *cache_journal_ops) { ENTRY; if( cache->o_flags & FILTER_DID_JOURNAL_OPS ){ EXIT; return; } cache->o_flags |= FILTER_DID_JOURNAL_OPS; cache->o_trops = cache_journal_ops; EXIT; }