Skip to content
Snippets Groups Projects
Commit 86dcbf50 authored by Wang Di's avatar Wang Di
Browse files

add snapfs to cvs

parent 35c5cf4c
No related branches found
No related tags found
No related merge requests found
/*
*
*
* Copyright (C) 2000 Stelias Computing, Inc.
* Copyright (C) 2000 Red Hat, Inc.
*
*
*/
#include <stdarg.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
/*
* XXX - Not sure for snapfs that the cache functions are even needed.
* Can't all lookups be done by an inode->superblock->u.generic_sbp
* lookup?
*/
/*
This file contains the routines associated with managing a
cache of files . These caches need to be found
fast so they are hashed by the device, with an attempt to have
collision chains of length 1.
*/
/* the intent of this hash is to have collision chains of length 1 */
#define CACHES_BITS 8
#define CACHES_SIZE (1 << CACHES_BITS)
#define CACHES_MASK CACHES_SIZE - 1
static struct list_head snap_caches[CACHES_SIZE];
static inline int snap_cache_hash(kdev_t dev)
{
return (CACHES_MASK) & ((0x000F & (dev)) + ((0x0F00 & (dev)) >>8));
}
inline void snap_cache_add(struct snap_cache *cache, kdev_t dev)
{
list_add(&cache->cache_chain,
&snap_caches[snap_cache_hash(dev)]);
cache->cache_dev = dev;
}
inline void snap_init_cache_hash(void)
{
int i;
for ( i = 0; i < CACHES_SIZE; i++ ) {
INIT_LIST_HEAD(&snap_caches[i]);
}
}
/* map a device to a cache */
struct snap_cache *snap_find_cache(kdev_t dev)
{
struct snap_cache *cache;
struct list_head *lh, *tmp;
lh = tmp = &(snap_caches[snap_cache_hash(dev)]);
while ( (tmp = lh->next) != lh ) {
cache = list_entry(tmp, struct snap_cache, cache_chain);
if ( cache->cache_dev == dev )
return cache;
}
return NULL;
}
/* map an inode to a cache */
struct snap_cache *snap_get_cache(struct inode *inode)
{
struct snap_cache *cache;
/* find the correct snap_cache here, based on the device */
cache = snap_find_cache(inode->i_dev);
if ( !cache ) {
printk("WARNING: no cache for dev %d, ino %ld\n",
inode->i_dev, inode->i_ino);
return NULL;
}
return cache;
}
/* another debugging routine: check fs is InterMezzo fs */
int snap_ispresto(struct inode *inode)
{
struct snap_cache *cache;
if ( !inode )
return 0;
cache = snap_get_cache(inode);
if ( !cache )
return 0;
return (inode->i_dev == cache->cache_dev);
}
/* setup a cache structure when we need one */
struct snap_cache *snap_init_cache(void)
{
struct snap_cache *cache;
/* make a snap_cache structure for the hash */
SNAP_ALLOC(cache, struct snap_cache *, sizeof(struct snap_cache));
if ( cache ) {
memset(cache, 0, sizeof(struct snap_cache));
INIT_LIST_HEAD(&cache->cache_chain);
INIT_LIST_HEAD(&cache->cache_clone_list);
}
return cache;
}
/* free a cache structure and all of the memory it is pointing to */
inline void snap_free_cache(struct snap_cache *cache)
{
if (!cache)
return;
SNAP_FREE(cache, sizeof(struct snap_cache));
}
/*
* Super block/filesystem wide operations
*
* Copryright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
* Michael Callahan <callahan@maths.ox.ac.uk>
*
* Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu>
* Copyright (C) Carnegie Mellon University
*
* Copyright (C) 2000, Mountain View Data, Inc, authors
* Peter Braam <braam@mountainviewdata.com>,
* Harrison Xing <harrisonx@mountainviewdata.com>
*
*/
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <linux/unistd.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <asm/segment.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
/* Clone is a simple file system, read only that just follows redirectors
we have placed the entire implementation except clone_read_super in
this file
*/
struct inode_operations clonefs_dir_inode_operations;
struct inode_operations clonefs_file_inode_operations;
struct inode_operations clonefs_symlink_inode_operations;
struct inode_operations clonefs_special_inode_operations;
struct file_operations clonefs_dir_file_operations;
struct file_operations clonefs_file_file_operations;
struct file_operations clonefs_special_file_operations;
/* support routines for following redirectors */
/* Parameter is clonefs inode, 'inode', and typically this may be
called before read_inode has completed on this clonefs inode,
i.e. we may only assume that i_ino is valid.
We return an underlying (likely disk) fs inode. This involved
handling any redirector inodes found along the way.
This function is used by all clone fs interface functions to get an
underlying fs inode.
*/
struct inode *clonefs_get_inode(struct inode *inode)
{
struct snap_clone_info *clone_sb;
struct inode *cache_inode, *redirected_inode;
ENTRY;
/* this only works if snapfs_current does NOT overwrite read_inode */
clone_sb = (struct snap_clone_info *) &inode->i_sb->u.generic_sbp;
/* basic invariant: clone and current ino's are equal */
cache_inode = iget(clone_sb->clone_cache->cache_sb, inode->i_ino);
redirected_inode = snap_redirect(cache_inode, inode->i_sb);
CDEBUG(D_SNAP, "redirected_inode: %lx, cache_inode %lx\n",
(ulong) redirected_inode, (ulong) cache_inode);
CDEBUG(D_SNAP, "cache_inode: %lx, ino %ld, sb %lx, count %d\n",
(ulong) cache_inode, cache_inode->i_ino,
(ulong) cache_inode->i_sb, cache_inode->i_count);
iput(cache_inode);
EXIT;
return redirected_inode;
}
/* super operations */
static void clonefs_read_inode(struct inode *inode)
{
struct inode *cache_inode;
ENTRY;
CDEBUG(D_SNAP, "inode: %lx, ino %ld, sb %lx, count %d\n",
(ulong) inode , inode->i_ino, (long) inode->i_sb,
inode->i_count);
/* redirecting inode in the cache */
cache_inode = clonefs_get_inode(inode);
if (!cache_inode) {
make_bad_inode(inode);
EXIT;
return;
}
/* copy attrs of that inode to our clone inode */
snapfs_cpy_attrs(inode, cache_inode);
if (S_ISREG(inode->i_mode))
inode->i_op = &clonefs_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &clonefs_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &clonefs_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
inode->i_op = &blkdev_inode_operations;
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
iput(cache_inode);
CDEBUG(D_SNAP, "cache_inode: %lx ino %ld, sb %lx, count %d\n",
(ulong) cache_inode, cache_inode->i_ino,
(ulong) cache_inode->i_sb, cache_inode->i_count);
EXIT;
return;
}
static void clonefs_put_super(struct super_block *sb)
{
struct snap_clone_info *clone_sb;
ENTRY;
CDEBUG(D_SUPER, "sb %lx, &sb->u.generic_sbp: %lx\n",
(ulong) sb, (ulong) &sb->u.generic_sbp);
clone_sb = (struct snap_clone_info *)&sb->u.generic_sbp;
dput( clone_sb->clone_cache->cache_sb->s_root );
list_del(&clone_sb->clone_list_entry);
MOD_DEC_USE_COUNT;
EXIT;
}
static int clonefs_statfs(struct super_block *sb, struct statfs *buf,
int bufsiz)
{
struct snap_clone_info *clone_sb;
struct snap_cache *cache;
ENTRY;
clone_sb = (struct snap_clone_info *)&sb->u.generic_sbp;
cache = clone_sb->clone_cache;
if (!cache) {
printk("clone_statfs: no cache\n");
return -EINVAL;
}
EXIT;
return cache->cache_filter->o_caops.cache_sops->statfs
(cache->cache_sb, buf, bufsiz);
}
struct super_operations clone_super_ops =
{
clonefs_read_inode, /* read_inode */
NULL, /* write_inode */
NULL, /* put_inode */
NULL, /* delete_inode */
NULL, /* notify_change */
clonefs_put_super, /* put_super */
NULL, /* write_super */
clonefs_statfs, /* statfs */
NULL /* remount_fs */
};
/* ***************** end of clonefs super ops ******************* */
/* ***************** begin clonefs dir ops ******************* */
static void d_unalloc(struct dentry *dentry)
{
list_del(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_hash);
dput(dentry); /* this will free the dentry memory */
}
/*
* Return the underlying fs dentry with name in 'dentry' that points
* to the right inode. 'dir' is the clone fs directory to search for
* the 'dentry'.
*/
struct dentry *clonefs_lookup(struct inode *dir, struct dentry *dentry)
{
struct inode *cache_dir;
struct dentry *cache_dentry;
struct inode *cache_inode;
struct dentry *result;
struct inode *inode;
struct snap_clone_info *clone_sb;
ENTRY;
cache_dir = clonefs_get_inode(dir);
cache_dentry = d_alloc(dentry->d_parent, &dentry->d_name);
if (!cache_dentry) {
iput(cache_dir);
EXIT;
return ERR_PTR(-ENOENT);
}
/* Lock cache directory inode. */
down(&cache_dir->i_sem);
/*
* Call underlying fs lookup function to set the 'd_inode' pointer
* to the corresponding directory inode.
*
* Note: If the lookup function does not return NULL, return
* from 'clone_lookup' with an error.
*/
result = cache_dir->i_op->lookup(cache_dir, cache_dentry);
if (result) {
dput(cache_dentry);
up(&cache_dir->i_sem);
iput(cache_dir);
dentry->d_inode = NULL;
EXIT;
return ERR_PTR(-ENOENT);
}
/* Unlock cache directory inode. */
up(&cache_dir->i_sem);
/*
* If there is no inode pointer in the underlying fs 'cache_dentry'
* then the directory doesn't have an entry with this name. In fs/ext2
* we see that we return 0 and put dentry->d_inode = NULL;
*/
cache_inode = cache_dentry->d_inode;
if ( cache_inode == NULL ) {
inode = NULL;
} else {
clone_sb = (struct snap_clone_info *) &dir->i_sb->u.generic_sbp;
/* note, iget below will follow a redirector, since
it calls into clone_read_inode
*/
inode = iget(dir->i_sb, cache_inode->i_ino);
}
/* dput(cache_dentry) will not put the dentry away
* immediately, unless we first arrange that its hash list is
* empty.
*/
if ( cache_inode != NULL ) {
CDEBUG(D_INODE, "cache ino %ld, count %d, dir %ld, count %d\n",
cache_inode->i_ino, cache_inode->i_count, cache_dir->i_ino,
cache_dir->i_count);
}
d_unalloc(cache_dentry);
iput(cache_dir);
/*
* Add 'inode' to the directory entry 'dentry'.
*/
d_add(dentry, inode);
EXIT;
return NULL;
}
/* instantiate a file handle to the cache file */
static void clonefs_prepare_snapfile(struct inode *i,
struct file *clone_file,
struct inode *cache_inode,
struct file *cache_file,
struct dentry *cache_dentry)
{
ENTRY;
cache_file->f_pos = clone_file->f_pos;
cache_file->f_mode = clone_file->f_mode;
cache_file->f_flags = clone_file->f_flags;
cache_file->f_count = clone_file->f_count;
cache_file->f_owner = clone_file->f_owner;
cache_file->f_op = cache_inode->i_op->default_file_ops;
cache_file->f_dentry = cache_dentry;
cache_file->f_dentry->d_inode = cache_inode;
EXIT;
return ;
}
/* update the clonefs file struct after IO in cache file */
static void clonefs_restore_snapfile(struct inode *cache_inode,
struct file *cache_file,
struct inode *clone_inode,
struct file *clone_file)
{
ENTRY;
cache_file->f_pos = clone_file->f_pos;
cache_inode->i_size = clone_inode->i_size;
EXIT;
return;
}
static int clonefs_readdir(struct file *file, void *dirent,
filldir_t filldir)
{
int result;
struct inode *cache_inode;
struct file open_file;
struct dentry open_dentry;
struct inode *inode=file->f_dentry->d_inode;
ENTRY;
if(!inode) {
EXIT;
return -EINVAL;
}
cache_inode = clonefs_get_inode(inode);
if (!cache_inode) {
make_bad_inode(inode);
EXIT;
return -ENOMEM;
}
CDEBUG(D_INODE,"clone ino %ld\n",cache_inode->i_ino);
clonefs_prepare_snapfile(inode, file, cache_inode, &open_file,
&open_dentry);
/* potemkin case: we are handed a directory inode */
result = -ENOENT;
if (open_file.f_op->readdir) {
down(&cache_inode->i_sem);
result = open_file.f_op->readdir(&open_file, dirent, filldir);
up(&cache_inode->i_sem);
}
clonefs_restore_snapfile(inode, file, cache_inode, &open_file);
iput(cache_inode);
EXIT;
return result;
}
struct file_operations clonefs_dir_file_operations = {
NULL, /* lseek */
NULL, /* read -- bad */
NULL, /* write */
clonefs_readdir, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* open */
NULL,
NULL, /* release */
NULL, /* fsync */
NULL,
NULL,
NULL
};
struct inode_operations clonefs_dir_inode_operations =
{
&clonefs_dir_file_operations,
NULL, /* create */
clonefs_lookup, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL, /* smap */
NULL, /* update page */
NULL, /* revalidate */
};
/* ***************** end of clonefs dir ops ******************* */
/* ***************** begin clonefs file ops ******************* */
int clonefs_readpage(struct file *file, struct page *page)
{
int result = 0;
struct inode *cache_inode;
struct file open_file;
struct dentry open_dentry;
struct inode *inode;
ENTRY;
inode = file->f_dentry->d_inode;
cache_inode = clonefs_get_inode(file->f_dentry->d_inode);
if (!cache_inode) {
make_bad_inode(file->f_dentry->d_inode);
EXIT;
return -ENOMEM;
}
clonefs_prepare_snapfile(inode, file, cache_inode, &open_file,
&open_dentry);
/* tell currentfs_readpage the primary inode number */
open_dentry.d_fsdata = (void*)inode->i_ino;
/* potemkin case: we are handed a directory inode */
down(&cache_inode->i_sem);
/* XXX - readpage NULL on directories... */
if (cache_inode->i_op->readpage == NULL)
printk("Yes, Grigori, directories are a problem.\n");
else
cache_inode->i_op->readpage(&open_file, page);
up(&cache_inode->i_sem);
clonefs_restore_snapfile(inode, file, cache_inode, &open_file);
iput(cache_inode);
EXIT;
return result;
}
struct file_operations clonefs_file_file_operations = {
NULL, /* lseek */
generic_file_read, /* read -- bad */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
generic_file_mmap, /* mmap */
NULL, /* open */
NULL,
NULL, /* release */
NULL, /* fsync */
NULL,
NULL,
NULL
};
struct inode_operations clonefs_file_inode_operations =
{
&clonefs_file_file_operations,
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
clonefs_readpage, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL, /* smap */
NULL, /* update page */
NULL, /* revalidate */
};
/* ***************** end of clonefs file ops ******************* */
/* ***************** begin clonefs symlink ops ******************* */
int clonefs_readlink(struct dentry *dentry, char *buf, int len)
{
int res;
struct inode * cache_inode;
struct inode * old_inode;
ENTRY;
cache_inode = clonefs_get_inode(dentry->d_inode);
res = -ENOENT;
if ( ! cache_inode ) {
CDEBUG(D_INODE, "clonefs_get_inode failed, NULL\n");
EXIT;
return res;
}
/* XXX: shall we allocate a new dentry ?
The following is safe for ext2, etc. because ext2_readlink only
use the inode info */
/* save the old dentry inode */
old_inode = dentry->d_inode;
/* set dentry inode to cache inode */
dentry->d_inode = cache_inode;
if ( cache_inode->i_op->readlink ) {
res = cache_inode->i_op->readlink(dentry, buf, len);
}else {
CDEBUG(D_INODE,"NO readlink for ino %lu\n", cache_inode->i_ino);
}
/* restore the old inode */
dentry->d_inode = old_inode;
iput(cache_inode);
EXIT;
return res;
}
struct dentry * clonefs_follow_link(struct dentry * dentry,
struct dentry *base,
unsigned int follow)
{
struct dentry * res;
struct inode * cache_inode;
struct inode * old_inode;
ENTRY;
res = ERR_PTR(-ENOENT);
cache_inode = clonefs_get_inode(dentry->d_inode);
if ( ! cache_inode ) {
CDEBUG(D_INODE, "clonefs_get_inode failed, NULL\n");
EXIT;
return res;
}
/* XXX: shall we allocate a new dentry ?
The following is safe for ext2, etc. because ext2_follow_link
only use the inode info */
/* save the old dentry inode */
old_inode = dentry->d_inode;
/* set dentry inode to cache inode */
dentry->d_inode = cache_inode;
if ( cache_inode->i_op->follow_link ) {
res = cache_inode->i_op->follow_link(dentry, base, follow);
}
/* restore the old inode */
dentry->d_inode = old_inode;
iput(cache_inode);
EXIT;
return res;
}
struct inode_operations clonefs_symlink_inode_operations =
{
NULL, /* no file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
clonefs_readlink, /* readlink */
clonefs_follow_link,/* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL, /* smap */
NULL, /* update page */
NULL, /* revalidate */
};
/*
* Directory operations for SnapFS filesystem
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
/* called when a cache lookup succeeds */
/* XXX PJB: the intent here is to make sure that inodes which are
currently primary inodes under .snap directories are dropped when
they are COWED. It seems hard to me to get semantics that are equally
good as for mounted snap_clone file systems, but we should try to get
close
*/
static int currentfs_dentry_revalidate(struct dentry *de, int flag)
{
// struct inode *inode = de->d_inode;
ENTRY;
/* unless an ancestor is a .snap directory there is nothing to do */
#if 0
if ( !currentfs_is_under_dotsnap(dentry) ) {
EXIT;
return 1;
}
/* XXX PJB get this to work guys! */
if ( de->d_parent == "dotsnap inode" &&
inode_is_newer_than(find_time_by_name(de->d_parent->d_name.name))){
1. drop this dentry
2. make sure the VFS does a new lookup
3. probably all you need to do is
return 0;
}
#else
return 1;
#endif
}
struct dentry_operations currentfs_dentry_ops =
{
d_revalidate: currentfs_dentry_revalidate
};
/*
* dir.c
*/
#define EXPORT_SYMTAB
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/quotaops.h>
#include <linux/list.h>
#include <linux/file.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
#ifdef CONFIG_SNAPFS_EXT3
void ext3_orphan_del(handle_t *handle, struct inode *inode);
#endif
static ino_t get_parent_ino(struct inode * inode)
{
ino_t ino = 0;
struct dentry * dentry;
if (list_empty(&inode->i_dentry)) {
printk("snapfs ERROR: no dentry for ino %lu\n", inode->i_ino);
return 0;
}
dentry = dget(list_entry(inode->i_dentry.next, struct dentry, d_alias));
if(dentry->d_parent->d_inode)
ino = dentry->d_parent->d_inode->i_ino;
dput(dentry);
return ino;
}
static void d_unadd_iput(struct dentry *dentry)
{
list_del(&dentry->d_alias);
INIT_LIST_HEAD(&dentry->d_alias);
list_del(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_hash);
iput(dentry->d_inode);
dentry->d_inode = NULL;
}
/* XXX check the return values */
static struct dentry *currentfs_lookup(struct inode * dir,struct dentry *dentry)
{
struct snap_cache *cache;
struct dentry *rc;
struct inode_operations *iops;
struct inode *cache_inode;
int index;
ENTRY;
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return ERR_PTR(-EINVAL);
}
if ( dentry->d_name.len == strlen(".snap") &&
(memcmp(dentry->d_name.name, ".snap", strlen(".snap")) == 0) ) {
struct inode *snap;
ino_t ino;
/* Don't permit .snap in clonefs */
if( dentry->d_sb != cache->cache_sb )
return ERR_PTR(-ENOENT);
/* Don't permit .snap under .snap */
if( currentfs_is_under_dotsnap(dentry) )
return ERR_PTR(-ENOENT);
ino = 0xF0000000 | dir->i_ino;
snap = iget(dir->i_sb, ino);
CDEBUG(D_INODE, ".snap inode ino %ld, mode %o\n", snap->i_ino, snap->i_mode);
d_add(dentry, snap);
EXIT;
return NULL;
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops || !iops->lookup) {
EXIT;
return ERR_PTR(-EINVAL);
}
rc = iops->lookup(dir, dentry);
if ( rc || !dentry->d_inode) {
EXIT;
return NULL;
}
/*
* If we are under dotsnap, we need save extra data into
* dentry->d_fsdata: For dir, we only need _this_ snapshot's index;
* For others, save primary ino, with it we could found index later
* anyway
*/
cache_inode = dentry->d_inode;
if ( (index = currentfs_is_under_dotsnap(dentry)) ) {
struct snapshot_operations *snapops;
struct inode *ind_inode;
ino_t pri_ino, ind_ino;
pri_ino = cache_inode->i_ino;
snapops = filter_c2csnapops(cache->cache_filter);
if( !snapops )
goto err_out;
ind_ino = snapops->get_indirect_ino(cache_inode, index);
if( ind_ino <=0 && ind_ino != -ENOATTR )
goto err_out;
else if( ind_ino != -ENOATTR ){
ind_inode = iget(cache_inode->i_sb, ind_ino);
if( !ind_inode ){
goto err_out;
}
list_del(&dentry->d_alias);
INIT_LIST_HEAD(&dentry->d_alias);
list_add(&dentry->d_alias, &ind_inode->i_dentry);
dentry->d_inode = ind_inode;
iput(cache_inode);
}
if( S_ISDIR(dentry->d_inode->i_mode) )
dentry->d_fsdata = (void*)index;
else
dentry->d_fsdata = (void*)pri_ino;
}
EXIT;
return NULL;
#if 0
/* XXX: PJB these need to be set up again. See dcache.c */
printk("set up dentry ops\n");
CDEBUG(D_CACHE, "\n");
filter_setup_dentry_ops(cache->cache_filter,
dentry->d_op, &currentfs_dentry_ops);
dentry->d_op = filter_c2udops(cache->cache_filter);
CDEBUG(D_CACHE, "\n");
#endif
err_out:
d_unadd_iput(dentry);
EXIT;
return ERR_PTR(-EINVAL);
}
static int currentfs_create(struct inode *dir, struct dentry *dentry, int mode)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_CREATE);
if ( snap_needs_cow(dir) != -1 ) {
printk("snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_CREATE, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->create) {
rc = -EINVAL;
goto exit;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_CREATE, 2);
rc = iops->create(dir, dentry, mode);
/* XXX now set the correct snap_{file,dir,sym}_iops */
if ( ! dentry->d_inode) {
printk("Error in currentfs_create, dentry->d_inode is NULL\n");
goto exit;
}
if ( S_ISDIR(dentry->d_inode->i_mode) )
dentry->d_inode->i_op = filter_c2udiops(cache->cache_filter);
else if ( S_ISREG(dentry->d_inode->i_mode) ) {
if ( !filter_c2cfiops(cache->cache_filter) ) {
filter_setup_file_ops(cache->cache_filter,
dentry->d_inode->i_op, &currentfs_file_iops);
}
dentry->d_inode->i_op = filter_c2ufiops(cache->cache_filter);
}
printk("inode %lu, i_op %p\n", dentry->d_inode->i_ino, dentry->d_inode->i_op);
snap_debug_device_fail(dir->i_dev, SNAP_OP_CREATE, 3);
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_MKDIR);
if ( snap_needs_cow(dir) != -1 ) {
CDEBUG(D_FILE, "snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_MKDIR, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->mkdir) {
rc = -EINVAL;
goto exit;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_MKDIR, 2);
rc = iops->mkdir(dir, dentry, mode);
if ( rc )
goto exit;
/* XXX now set the correct snap_{file,dir,sym}_iops */
if ( dentry->d_inode) {
dentry->d_inode->i_op = filter_c2udiops(cache->cache_filter);
printk("inode %lu, i_op %p\n", dentry->d_inode->i_ino, dentry->d_inode->i_op);
} else {
printk("Error in currentfs_mkdir, dentry->d_inode is NULL\n");
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_MKDIR, 3);
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_link (struct dentry * old_dentry, struct inode * dir,
struct dentry *dentry)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_LINK);
if ( snap_needs_cow(dir) != -1 ) {
printk("snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_LINK, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
if ( snap_needs_cow(old_dentry->d_inode) != -1 ) {
printk("snap_needs_cow for ino %lu \n",old_dentry->d_inode->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_LINK, 2);
snap_do_cow(old_dentry->d_inode, dir->i_ino, 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->link) {
rc = -EINVAL;
goto exit;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_LINK, 2);
rc = iops->link(old_dentry,dir, dentry);
snap_debug_device_fail(dir->i_dev, SNAP_OP_LINK, 3);
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_symlink(struct inode *dir, struct dentry *dentry,
const char * symname)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_SYMLINK);
if ( snap_needs_cow(dir) != -1 ) {
printk("snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_SYMLINK, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->symlink) {
rc = -EINVAL;
goto exit;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_SYMLINK, 2);
rc = iops->symlink(dir, dentry, symname);
snap_debug_device_fail(dir->i_dev, SNAP_OP_SYMLINK, 3);
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
int rdev)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_MKNOD);
if ( snap_needs_cow(dir) != -1 ) {
printk("snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_MKNOD, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->mknod) {
rc = -EINVAL;
goto exit;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_MKNOD, 2);
rc = iops->mknod(dir, dentry, mode, rdev);
snap_debug_device_fail(dir->i_dev, SNAP_OP_MKNOD, 3);
/* XXX do we need to set the correct snap_{*}_iops */
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
struct inode *inode = NULL;
// time_t i_ctime = 0;
nlink_t i_nlink = 0;
off_t i_size = 0;
ino_t ino = 0;
int keep_inode = 0;
// struct dentry_operations *save_dop = NULL;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_RMDIR);
if ( snap_needs_cow(dir) != -1 ) {
printk("snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_RMDIR, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->rmdir) {
rc = -EINVAL;
goto exit;
}
/* XXX : there are two cases that we can't remove this inode from disk.
1. the inode needs to be cowed.
2. the inode is a redirector.
then we must keep this inode(dir) so that the inode
will not be deleted after rmdir, will only remove dentry
*/
if( snap_needs_cow(dentry->d_inode) != -1) {
snap_debug_device_fail(dir->i_dev, SNAP_OP_RMDIR, 2);
snap_do_cow (dentry->d_inode, dir->i_ino,
SNAP_DEL_PRI_WITHOUT_IND);
keep_inode = 1;
}
else if( snap_is_redirector(dentry->d_inode) ) {
snap_debug_device_fail(dir->i_dev, SNAP_OP_RMDIR, 3);
snap_do_cow(dentry->d_inode, dir->i_ino, SNAP_DEL_PRI_WITH_IND);
keep_inode = 1;
}
#if 0
if ( keep_inode ) {
printk("set up dentry ops, before %p\n",dentry->d_op);
save_dop = dentry->d_op;
filter_setup_dentry_ops(cache->cache_filter,
dentry->d_op, &currentfs_dentry_ops);
dentry->d_op = filter_c2udops(cache->cache_filter);
printk("set up dentry ops, after %p\n",dentry->d_op);
}
#endif
if( keep_inode && dentry->d_inode ) {
ino = dentry->d_inode->i_ino;
// i_ctime = dentry->d_inode->i_ctime;
i_nlink = dentry->d_inode->i_nlink;
i_size = dentry->d_inode->i_size;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_RMDIR, 4);
rc = iops->rmdir(dir, dentry);
snap_debug_device_fail(dir->i_dev, SNAP_OP_RMDIR, 5);
/* XXX : check this */
#if 0
if ( keep_inode ) {
dentry->d_op = save_dop;
printk("restore dentry ops, now at %p\n",dentry->d_op);
}
#endif
if( keep_inode && ino) {
inode = iget ( dir->i_sb, ino);
if( inode) {
// inode->i_ctime = i_ctime;
inode->i_nlink = i_nlink;
inode->i_size = i_size;
mark_inode_dirty(inode);
iput( inode);
#ifdef CONFIG_SNAPFS_EXT3
/*
* In Ext3, rmdir() will put this inode into
* orphan list, we must remove it out. It's ugly!!
*/
if( cache->cache_type == FILTER_FS_EXT3 )
ext3_orphan_del(handle, inode);
#endif
snap_debug_device_fail(dir->i_dev, SNAP_OP_RMDIR, 6);
}
}
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, dir, SNAP_OP_UNLINK);
if ( snap_needs_cow(dir) != -1 ) {
printk("snap_needs_cow for ino %lu \n",dir->i_ino);
snap_debug_device_fail(dir->i_dev, SNAP_OP_UNLINK, 1);
snap_do_cow(dir, get_parent_ino(dir), 0);
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->unlink) {
rc = -EINVAL;
goto exit;
}
/* XXX : if nlink for this inode is 1, there are two cases that we
can't remove this inode from disk.
1. the inode needs to be cowed.
2. the inode is a redirector.
then we increament dentry->d_inode->i_nlink so that the inode
will not be deleted after unlink, will only remove dentry
*/
if( snap_needs_cow (inode) != -1) {
/* call snap_do_cow with DEL_WITHOUT_IND option */
snap_debug_device_fail(dir->i_dev, SNAP_OP_UNLINK, 2);
snap_do_cow(inode, dir->i_ino,SNAP_DEL_PRI_WITHOUT_IND);
if( inode->i_nlink == 1 )
inode->i_nlink++;
}
else if( snap_is_redirector (inode) && inode->i_nlink == 1 ) {
/* call snap_do_cow with DEL_WITH_IND option
* just free the blocks of inode, not really delete it
*/
snap_debug_device_fail(dir->i_dev, SNAP_OP_UNLINK, 3);
snap_do_cow (inode, dir->i_ino, SNAP_DEL_PRI_WITH_IND);
inode->i_nlink++;
}
snap_debug_device_fail(dir->i_dev, SNAP_OP_UNLINK, 4);
rc = iops->unlink(dir, dentry);
snap_debug_device_fail(dir->i_dev, SNAP_OP_UNLINK, 5);
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_rename (struct inode * old_dir, struct dentry *old_dentry,
struct inode * new_dir, struct dentry *new_dentry)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
void *handle = NULL;
ENTRY;
if (currentfs_is_under_dotsnap(old_dentry) ||
currentfs_is_under_dotsnap(new_dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(old_dir->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
handle = snap_trans_start(cache, old_dir, SNAP_OP_RENAME);
/* Always cow the old dir and old dentry->d_inode */
if ( snap_needs_cow(old_dir) != -1 ) {
printk("rename: needs_cow for old_dir %lu\n",old_dir->i_ino);
snap_debug_device_fail(old_dir->i_dev, SNAP_OP_RENAME, 1);
snap_do_cow(old_dir, get_parent_ino(old_dir), 0);
}
if( snap_needs_cow (old_dentry->d_inode) != -1) {
printk("rename: needs_cow for old_dentry, ino %lu\n",
old_dentry->d_inode->i_ino);
snap_debug_device_fail(old_dir->i_dev, SNAP_OP_RENAME, 2);
snap_do_cow(old_dentry->d_inode, old_dir->i_ino,0);
}
/* If it's not in the same dir, whether the new_dentry is NULL or not,
* we should cow the new_dir. Because rename will use the ino of
* old_dentry as the ino of the new_dentry in new_dir.
*/
if(( old_dir != new_dir) ) {
if( snap_needs_cow(new_dir) !=-1 ){
printk("rename:snap_needs_cow for new_dir %lu\n",
new_dir->i_ino);
snap_debug_device_fail(old_dir->i_dev,SNAP_OP_RENAME,3);
snap_do_cow(new_dir, get_parent_ino(new_dir),0);
}
}
#if 0
if( ( old_dir != new_dir) && ( new_dentry->d_inode )) {
if(snap_needs_cow(new_dentry->d_inode) !=-1 ){
printk("rename:needs_cow for new_entry ,ino %lu\n",
new_dentry->d_inode->i_ino);
snap_debug_device_fail(old_dir->i_dev, SNAP_OP_RENAME, 4);
snap_do_cow (new_dentry->d_inode,
new_dentry->d_parent->d_inode->i_ino, 0);
}
}
#endif
/* The inode for the new_dentry will be freed for normal rename option.
* But we should keep this inode since we need to keep it available
* for the clone and for snap rollback
*/
if( new_dentry->d_inode && new_dentry->d_inode->i_nlink == 1 ) {
if( snap_needs_cow (new_dentry->d_inode) != -1) {
/* call snap_do_cow with DEL_WITHOUT_IND option */
snap_debug_device_fail(old_dir->i_dev,SNAP_OP_RENAME,4);
snap_do_cow(new_dentry->d_inode, new_dir->i_ino,
SNAP_DEL_PRI_WITHOUT_IND);
new_dentry->d_inode->i_nlink++;
}
else if( snap_is_redirector (new_dentry->d_inode) ) {
/* call snap_do_cow with DEL_WITH_IND option
* just free the blocks of inode, not really delete it
*/
snap_debug_device_fail(old_dir->i_dev,SNAP_OP_RENAME,4);
snap_do_cow (new_dentry->d_inode, new_dir->i_ino,
SNAP_DEL_PRI_WITH_IND);
new_dentry->d_inode->i_nlink++;
}
}
iops = filter_c2cdiops(cache->cache_filter);
if (!iops ||
!iops->rename) {
rc = -EINVAL;
goto exit;
}
snap_debug_device_fail(old_dir->i_dev, SNAP_OP_RENAME, 5);
rc = iops->rename(old_dir, old_dentry, new_dir, new_dentry);
snap_debug_device_fail(old_dir->i_dev, SNAP_OP_RENAME, 6);
exit:
snap_trans_commit(cache, handle);
EXIT;
return rc;
}
static int currentfs_readdir(struct file *filp, void *dirent,
filldir_t filldir)
{
struct snap_cache *cache;
struct file_operations *fops;
int rc;
ENTRY;
if( !filp || !filp->f_dentry || !filp->f_dentry->d_inode ) {
EXIT;
return -EINVAL;
}
cache = snap_find_cache(filp->f_dentry->d_inode->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
fops = filter_c2cdfops( cache->cache_filter );
if( !fops ) {
EXIT;
return -EINVAL;
}
/*
* no action if we are under clonefs or .snap
*/
if( cache->cache_show_dotsnap &&
(filp->f_dentry->d_sb == cache->cache_sb) &&
!currentfs_is_under_dotsnap(filp->f_dentry) ){
if( filp->f_pos == 0 ){
if( filldir(dirent, ".snap",
strlen(".snap")+1, filp->f_pos,
0xF0000000|filp->f_dentry->d_inode->i_ino) ){
return -EINVAL;
}
filp->f_pos += strlen(".snap")+1;
}
filp->f_pos -= strlen(".snap")+1;
rc = fops->readdir(filp, dirent, filldir);
filp->f_pos += strlen(".snap")+1;
}else
rc = fops->readdir(filp, dirent, filldir);
return rc;
}
struct file_operations currentfs_dir_fops = {
readdir: currentfs_readdir
};
struct inode_operations currentfs_dir_iops = {
default_file_ops: &currentfs_dir_fops,
create: currentfs_create,
mkdir: currentfs_mkdir,
link: currentfs_link,
symlink: currentfs_symlink,
mknod: currentfs_mknod,
rmdir: currentfs_rmdir,
unlink: currentfs_unlink,
rename: currentfs_rename,
lookup: currentfs_lookup
};
/*
* dotsnap.c - support for .snap directories
*/
#define EXPORT_SYMTAB
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/quotaops.h>
#include <linux/list.h>
#include <linux/file.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
struct inode_operations dotsnap_inode_operations;
struct file_operations dotsnap_file_operations;
int currentfs_is_under_dotsnap(struct dentry *de)
{
int index = 0;
while(de && de->d_parent != de) {
if ( de->d_inode && de->d_inode->i_ino & 0xF0000000 ) {
EXIT;
return index;
}
index = (int)de->d_fsdata;
de = de->d_parent;
}
EXIT;
return 0;
}
void currentfs_dotsnap_read_inode(struct snap_cache *cache,
struct inode *inode)
{
int tableno = cache->cache_snap_tableno;
struct snap_table *table;
ENTRY;
table = &snap_tables[tableno];
inode->i_mode = S_IFDIR | 0755 ;
inode->i_op = &dotsnap_inode_operations;
inode->i_size = table->tbl_count - 1;
/* all except current form a subdirectory and . and .. */
inode->i_nlink = table->tbl_count -1 + 2;
inode->i_uid = 0;
inode->i_gid = 0;
EXIT;
}
struct dentry *dotsnap_lookup(struct inode *dir, struct dentry *dentry)
{
struct snap_table *table;
struct snap_cache *cache;
int i;
int index;
int tableno;
ino_t ino;
struct inode *inode;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(dir->i_dev);
if ( !cache ) {
printk("dotsnap_readdir: cannot find cache\n");
make_bad_inode(dir);
EXIT;
return ERR_PTR(-EINVAL);
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->get_indirect_ino) {
EXIT;
return ERR_PTR(-EINVAL);
}
tableno = cache->cache_snap_tableno;
table = &snap_tables[tableno];
if( table->tbl_count <= 1 )
return NULL;
index = table->tbl_index[0];
for ( i = 1 ; i < table->tbl_count ; i++ ) {
if ( (dentry->d_name.len == strlen(table->tbl_name[i])) &&
(memcmp(dentry->d_name.name, table->tbl_name[i],
dentry->d_name.len) == 0) ) {
index = table->tbl_index[i];
break;
}
}
if( i >= table->tbl_count )
return ERR_PTR(-ENOENT);
inode = iget(dir->i_sb, dir->i_ino & (~0xF0000000));
if ( !inode )
return ERR_PTR(-EINVAL);
ino = snapops->get_indirect_ino(inode, index);
iput(inode);
if ( ino == -ENOATTR || ino == 0 ) {
ino = dir->i_ino & (~0xF0000000);
}
if ( ino == -EINVAL ) {
return ERR_PTR(-EINVAL);
}
CDEBUG(D_INODE, "index %d, ino is %lu\n",index, ino);
inode = iget(dir->i_sb, ino);
d_add(dentry, inode);
dentry->d_fsdata = (void*)index;
inode->i_op = dentry->d_parent->d_parent->d_inode->i_op;
return NULL;
}
static int dotsnap_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
unsigned int i;
int tableno;
struct snap_cache *cache;
struct snap_table *table;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(filp->f_dentry->d_inode->i_dev);
if ( !cache ) {
printk("dotsnap_readdir: cannot find cache\n");
make_bad_inode(filp->f_dentry->d_inode);
EXIT;
return -EINVAL;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->get_indirect_ino) {
EXIT;
return -EINVAL;
}
tableno = cache->cache_snap_tableno;
table = &snap_tables[tableno];
CDEBUG(D_INODE, "\n");
for (i = filp->f_pos ; i < table->tbl_count -1 ; i++) {
int index;
struct inode *inode;
ino_t ino;
CDEBUG(D_INODE, "%d\n",i);
inode = filp->f_dentry->d_inode;
index = table->tbl_index[i+1];
ino = snapops->get_indirect_ino
(filp->f_dentry->d_inode, index);
CDEBUG(D_INODE, "\n");
if ( ino == -ENOATTR || ino == 0 ) {
ino = filp->f_dentry->d_parent->d_inode->i_ino;
}
CDEBUG(D_INODE, "\n");
if ( ino == -EINVAL ) {
return -EINVAL;
}
CDEBUG(D_INODE, "Listing %s\n", table->tbl_name[i+1]);
if (filldir(dirent, table->tbl_name[i+1],
strlen(table->tbl_name[i+1]),
filp->f_pos, ino) < 0){
CDEBUG(D_INODE, "\n");
break;
}
filp->f_pos++;
}
EXIT;
return 0;
}
struct file_operations dotsnap_file_operations = {
readdir: dotsnap_readdir, /* readdir */
};
struct inode_operations dotsnap_inode_operations =
{
default_file_ops: &dotsnap_file_operations,
lookup: dotsnap_lookup
};
/*
* file.c
*/
#define EXPORT_SYMTAB
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/quotaops.h>
#include <linux/list.h>
#include <linux/file.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
/* instantiate a file handle to the cache file */
static void currentfs_prepare_snapfile(struct inode *inode,
struct file *clone_file,
struct inode *cache_inode,
struct file *cache_file,
struct dentry *cache_dentry)
{
ENTRY;
cache_file->f_pos = clone_file->f_pos;
cache_file->f_mode = clone_file->f_mode;
cache_file->f_flags = clone_file->f_flags;
cache_file->f_count = clone_file->f_count;
cache_file->f_owner = clone_file->f_owner;
cache_file->f_op = cache_inode->i_op->default_file_ops;
cache_file->f_dentry = cache_dentry;
cache_file->f_dentry->d_inode = cache_inode;
EXIT;
return ;
}
/* update the currentfs file struct after IO in cache file */
static void currentfs_restore_snapfile(struct inode *cache_inode,
struct file *cache_file,
struct inode *clone_inode,
struct file *clone_file)
{
ENTRY;
cache_file->f_pos = clone_file->f_pos;
EXIT;
return;
}
static ssize_t currentfs_write (struct file *filp, const char *buf,
size_t count, loff_t *ppos)
{
struct snap_cache *cache;
struct inode *inode = filp->f_dentry->d_inode;
ssize_t rc;
struct file_operations *fops;
loff_t pos;
long block[2]={-1,-1}, mask, i;
struct snap_table *table;
int slot = 0;
int index = 0;
struct inode_operations *ciops;
struct inode *cache_inode = NULL;
struct snapshot_operations *snapops;
ENTRY;
if (currentfs_is_under_dotsnap(filp->f_dentry)) {
EXIT;
return -ENOSPC;
}
cache = snap_find_cache(inode->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
if ( snap_needs_cow(inode) != -1 ) {
CDEBUG(D_FILE, "snap_needs_cow for ino %lu \n",inode->i_ino);
snap_do_cow(inode, filp->f_dentry->d_parent->d_inode->i_ino, 0);
}
fops = filter_c2cffops(cache->cache_filter);
if (!fops ||
!fops->write) {
EXIT;
return -EINVAL;
}
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else {
pos = *ppos;
if (pos != *ppos)
return -EINVAL;
}
/*
* we only need to copy back the first and last blocks
*/
mask = inode->i_sb->s_blocksize-1;
if( pos & mask )
block[0] = pos >> inode->i_sb->s_blocksize_bits;
pos += count - 1;
if( (pos+1) & mask )
block[1] = pos >> inode->i_sb->s_blocksize_bits;
if( block[0] == block[1] )
block[1] = -1;
ciops = filter_c2cfiops(cache->cache_filter);
snapops = filter_c2csnapops(cache->cache_filter);
for( i=0; i<2; i++ ){
if( block[i]!=-1 && !ciops->bmap(inode, block[i]) ) {
table = &snap_tables[cache->cache_snap_tableno];
for (slot = table->tbl_count ; slot >= 1; slot--)
{
cache_inode = NULL;
index = table->tbl_index[slot];
cache_inode = snap_get_indirect(inode, NULL, index);
if ( !cache_inode ) continue;
if (cache_inode->i_op->bmap(cache_inode, block[i])) {
CDEBUG(D_FILE, "find cache_ino %lu\n",
cache_inode->i_ino);
if( snapops && snapops->copy_block) {
snapops->copy_block( inode,
cache_inode, block[i]);
}
iput(cache_inode);
break;
}
iput(cache_inode);
}
}
}
rc = fops->write(filp, buf, count, ppos);
EXIT;
return rc;
}
static int currentfs_readpage(struct file *file, struct page *page)
{
int result = 0;
struct inode *inode = file->f_dentry->d_inode;
unsigned long ind_ino = inode->i_ino;
struct inode *pri_inode = NULL;
struct inode *cache_inode = NULL;
struct file open_file;
struct dentry open_dentry ;
struct inode_operations *ciops;
struct snap_cache *cache;
long block;
struct snap_table *table;
int slot = 0;
int index = 0;
int search_older = 0;
ENTRY;
cache = snap_find_cache(inode->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
ciops = filter_c2cfiops(cache->cache_filter);
block = page->offset >> inode->i_sb->s_blocksize_bits;
/* if there is a block in the cache, return the cache readpage */
if( inode->i_blocks && ciops->bmap(inode, block) ) {
CDEBUG(D_FILE, "block %lu in cache, ino %lu\n",
block, inode->i_ino);
result = ciops->readpage(file, page);
EXIT;
return result;
}
/*
* clonefs_readpage will fill this with primary ino number
* we need it to follow the cloned chain of primary inode
*/
if( file->f_dentry->d_fsdata ){
pri_inode = iget(inode->i_sb, (unsigned long)file->f_dentry->d_fsdata);
if( !pri_inode )
return -EINVAL;
inode = pri_inode;
search_older = 1;
}
table = &snap_tables[cache->cache_snap_tableno];
for (slot = table->tbl_count ; slot >= 1; slot--)
{
cache_inode = NULL;
index = table->tbl_index[slot];
cache_inode = snap_get_indirect(inode, NULL, index);
if ( !cache_inode ) continue;
/* we only want slots between cache_inode to the oldest one */
if( search_older && cache_inode->i_ino == ind_ino )
search_older = 0;
if ( !search_older && cache_inode->i_op->bmap(cache_inode, block)) {
break;
}
iput(cache_inode);
}
if( pri_inode )
iput(pri_inode);
if ( !cache_inode ) {
EXIT;
return -EINVAL;
}
currentfs_prepare_snapfile(inode, file, cache_inode, &open_file,
&open_dentry);
down(&cache_inode->i_sem);
if( ciops->readpage ) {
CDEBUG(D_FILE, "block %lu NOT in cache, use redirected ino %lu\n", block, cache_inode->i_ino );
result = ciops->readpage(&open_file, page);
}else {
CDEBUG(D_FILE, "cache ino %lu, readpage is NULL\n",
cache_inode->i_ino);
}
up(&cache_inode->i_sem);
currentfs_restore_snapfile(inode, file, cache_inode, &open_file);
iput(cache_inode);
EXIT;
return result;
}
struct file_operations currentfs_file_fops = {
write:currentfs_write,
};
struct inode_operations currentfs_file_iops = {
default_file_ops: &currentfs_file_fops,
readpage: currentfs_readpage,
};
/*
*
*
* Copyright (C) 2000 Stelias Computing, Inc.
* Copyright (C) 2000 Red Hat, Inc.
* Copyright (C) 2000 Mountain View Data, Inc.
*
*
*/
#include <stdarg.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/filter.h>
int filter_print_entry = 1;
int filter_debug = 0xfffffff;
/*
* 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 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 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;
FENTRY;
if ( strlen(cache_type) == strlen("ext2") &&
memcmp(cache_type, "ext2", strlen("ext2")) == 0 ) {
ops = &filter_oppar[FILTER_FS_EXT2];
FDEBUG(D_SUPER, "ops at %p\n", ops);
}
if ( strlen(cache_type) == strlen("ext3") &&
memcmp(cache_type, "ext3", strlen("ext3")) == 0 ) {
ops = &filter_oppar[FILTER_FS_EXT3];
FDEBUG(D_SUPER, "ops at %p\n", ops);
}
if ( strlen(cache_type) == strlen("reiser") &&
memcmp(cache_type, "reiser", strlen("reiser")) == 0 ) {
ops = &filter_oppar[FILTER_FS_REISER];
FDEBUG(D_SUPER, "ops at %p\n", ops);
}
if (ops == NULL) {
printk("prepare to die: unrecognized cache type for Filter\n");
}
FEXIT;
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;
FENTRY;
if ( cache->o_flags & FILTER_DID_SUPER_OPS ) {
FEXIT;
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;
FDEBUG(D_INODE, "setting filter_read_inode, cache_ops %p, cache %p, ri at %p\n",
cache, cache, uops->filter_sops.read_inode);
}
if (cache_sops->notify_change && uops->filter_sops.notify_change)
uops->filter_sops.notify_change = filter_sops->notify_change;
if (cache_sops->remount_fs && uops->filter_sops.remount_fs)
uops->filter_sops.remount_fs = filter_sops->remount_fs;
FEXIT;
}
void filter_setup_dir_ops(struct filter_fs *cache, struct inode_operations *cache_iops, struct inode_operations *filter_iops)
{
struct inode_operations *u_iops;
struct file_operations *u_fops, *c_fops, *f_fops;
FENTRY;
if ( cache->o_flags & FILTER_DID_DIR_OPS ) {
FEXIT;
return;
}
FDEBUG(D_SUPER, "\n");
cache->o_flags |= FILTER_DID_DIR_OPS;
/* steal the old ops */
cache->o_caops.cache_dir_iops = cache_iops;
cache->o_caops.cache_dir_fops =
cache_iops->default_file_ops;
FDEBUG(D_SUPER, "\n");
/* abbreviate */
u_iops = &cache->o_fops.filter_dir_iops;
/* setup our dir iops: copy and modify */
memcpy(u_iops, cache_iops, sizeof(*cache_iops));
FDEBUG(D_SUPER, "\n");
/* methods that filter if cache filesystem has these ops */
if ( cache_iops->lookup && filter_iops->lookup ) {
FDEBUG(D_SUPER, "\n");
u_iops->lookup = filter_iops->lookup;
FDEBUG(D_SUPER, "lookup at %p\n", &filter_iops->lookup);
}
if (cache_iops->create && filter_iops->create)
u_iops->create = filter_iops->create;
FDEBUG(D_SUPER, "\n");
if (cache_iops->link && filter_iops->link)
u_iops->link = filter_iops->link;
FDEBUG(D_SUPER, "\n");
if (cache_iops->unlink && filter_iops->unlink)
u_iops->unlink = filter_iops->unlink;
FDEBUG(D_SUPER, "\n");
if (cache_iops->mkdir && filter_iops->mkdir)
u_iops->mkdir = filter_iops->mkdir;
FDEBUG(D_SUPER, "\n");
if (cache_iops->rmdir && filter_iops->rmdir)
u_iops->rmdir = filter_iops->rmdir;
FDEBUG(D_SUPER, "\n");
if (cache_iops->symlink && filter_iops->symlink)
u_iops->symlink = filter_iops->symlink;
FDEBUG(D_SUPER, "\n");
if (cache_iops->rename && filter_iops->rename)
u_iops->rename = filter_iops->rename;
FDEBUG(D_SUPER, "\n");
if (cache_iops->mknod && filter_iops->mknod)
u_iops->mknod = filter_iops->mknod;
FDEBUG(D_SUPER, "\n");
if (cache_iops->permission && filter_iops->permission)
u_iops->permission = filter_iops->permission;
/* copy dir fops */
FDEBUG(D_SUPER, "\n");
u_fops = &cache->o_fops.filter_dir_fops;
c_fops = cache_iops->default_file_ops;
f_fops = filter_iops->default_file_ops;
memcpy(u_fops, c_fops, sizeof(*c_fops));
if( c_fops->readdir && f_fops->readdir )
u_fops->readdir = f_fops->readdir;
/* assign */
FDEBUG(D_SUPER, "\n");
filter_c2udiops(cache)->default_file_ops = filter_c2udfops(cache);
FDEBUG(D_SUPER, "\n");
/* unconditional filtering operations */
if ( filter_iops->default_file_ops &&
filter_iops->default_file_ops->open )
filter_c2udfops(cache)->open =
filter_iops->default_file_ops->open;
FEXIT;
}
void filter_setup_file_ops(struct filter_fs *cache, struct inode_operations *cache_iops, struct inode_operations *filter_iops)
{
struct inode_operations *u_iops;
FENTRY;
if ( cache->o_flags & FILTER_DID_FILE_OPS ) {
FEXIT;
return;
}
cache->o_flags |= FILTER_DID_FILE_OPS;
/* steal the old ops */
cache->o_caops.cache_file_iops = cache_iops;
cache->o_caops.cache_file_fops =
cache_iops->default_file_ops;
/* abbreviate */
u_iops = filter_c2ufiops(cache);
/* setup our dir iops: copy and modify */
memcpy(u_iops, cache_iops, sizeof(*cache_iops));
/* copy dir fops */
memcpy(filter_c2uffops(cache), cache_iops->default_file_ops,
sizeof(*cache_iops->default_file_ops));
/* assign */
filter_c2ufiops(cache)->default_file_ops = filter_c2uffops(cache);
/* unconditional filtering operations */
if (filter_iops->default_file_ops &&
filter_iops->default_file_ops->open )
filter_c2uffops(cache)->open =
filter_iops->default_file_ops->open;
if (filter_iops->default_file_ops &&
filter_iops->default_file_ops->release )
filter_c2uffops(cache)->release =
filter_iops->default_file_ops->release;
if (filter_iops->default_file_ops &&
filter_iops->default_file_ops->write )
filter_c2uffops(cache)->write =
filter_iops->default_file_ops->write;
/* set up readpage */
if (filter_iops->readpage)
filter_c2ufiops(cache)->readpage = filter_iops->readpage;
FEXIT;
}
/* XXX in 2.3 there are "fast" and "slow" symlink ops for ext2 XXX */
void filter_setup_symlink_ops(struct filter_fs *cache, struct inode_operations *cache_iops, struct inode_operations *filter_iops)
{
struct inode_operations *u_iops;
FENTRY;
if ( cache->o_flags & FILTER_DID_SYMLINK_OPS ) {
FEXIT;
return;
}
cache->o_flags |= FILTER_DID_SYMLINK_OPS;
/* steal the old ops */
cache->o_caops.cache_sym_iops = cache_iops;
cache->o_caops.cache_sym_fops =
cache_iops->default_file_ops;
/* abbreviate */
u_iops = filter_c2usiops(cache);
/* setup our dir iops: copy and modify */
memcpy(u_iops, cache_iops, sizeof(*cache_iops));
/* copy fops - careful for symlinks they might be NULL */
if ( cache_iops->default_file_ops ) {
memcpy(filter_c2usfops(cache), cache_iops->default_file_ops,
sizeof(*cache_iops->default_file_ops));
}
/* assign */
filter_c2usiops(cache)->default_file_ops = filter_c2usfops(cache);
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;
FEXIT;
}
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 ) {
FEXIT;
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){
printk("WARNING: filter overriding revalidation!\n");
}
return;
}
/* snapfs : for snapshot operations */
void filter_setup_snapshot_ops (struct filter_fs *cache,
struct snapshot_operations *cache_snapops)
{
FENTRY;
if ( cache->o_flags & FILTER_DID_SNAPSHOT_OPS ) {
FEXIT;
return;
}
cache->o_flags |= FILTER_DID_SNAPSHOT_OPS;
cache->o_snapops = cache_snapops;
FEXIT;
}
void filter_setup_journal_ops (struct filter_fs *cache,
struct journal_ops *cache_journal_ops)
{
FENTRY;
if( cache->o_flags & FILTER_DID_JOURNAL_OPS ){
FEXIT;
return;
}
cache->o_flags |= FILTER_DID_JOURNAL_OPS;
cache->o_trops = cache_journal_ops;
FEXIT;
}
/*
* fs/snap/snap.c
*
* A snap shot file system.
*
*/
#define EXPORT_SYMTAB
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/quotaops.h>
#include <linux/list.h>
#include <linux/file.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#ifdef CONFIG_SNAPFS_EXT2
#include <linux/ext2_fs.h>
#endif
#ifdef CONFIG_SNAPFS_EXT3
#include <linux/ext3_fs.h>
#endif
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
extern int currentfs_remount(struct super_block * sb, int *flags, char *data);
/* XXX PJB: this is exactly what we need to put things under
filters - we don't want the ext2 methods hardcoded, we want them
in the filter (in read_super) and then call those methods.
See how InterMezzo gets the journal operations .
*/
extern void currentfs_dotsnap_read_inode(struct snap_cache *, struct inode *);
/* Superblock operations. */
static void currentfs_read_inode(struct inode *inode)
{
struct snap_cache *cache;
ENTRY;
if( !inode )
{
EXIT;
return;
}
CDEBUG(D_INODE, "read_inode ino %lu\n", inode->i_ino);
cache = snap_find_cache(inode->i_dev);
if ( !cache ) {
printk("currentfs_read_inode: cannot find cache\n");
make_bad_inode(inode);
EXIT;
return ;
}
if ( inode->i_ino & 0xF0000000 ) {
CDEBUG(D_INODE, "\n");
currentfs_dotsnap_read_inode(cache, inode);
EXIT;
return ;
}
if( filter_c2csops(cache->cache_filter) )
filter_c2csops(cache->cache_filter)->read_inode(inode);
/* XXX now set the correct snap_{file,dir,sym}_iops */
if ( S_ISDIR(inode->i_mode) )
inode->i_op = filter_c2udiops(cache->cache_filter);
else if ( S_ISREG(inode->i_mode) ) {
if ( !filter_c2cfiops(cache->cache_filter) ) {
filter_setup_file_ops(cache->cache_filter,
inode->i_op, &currentfs_file_iops);
}
inode->i_op = filter_c2ufiops(cache->cache_filter);
printk("inode %lu, i_op at %p\n", inode->i_ino, inode->i_op);
}
else if ( S_ISLNK(inode->i_mode) ) {
if ( !filter_c2csiops(cache->cache_filter) ) {
filter_setup_symlink_ops(cache->cache_filter,
inode->i_op, &currentfs_sym_iops);
}
inode->i_op = filter_c2usiops(cache->cache_filter);
printk("inode %lu, i_op at %p\n", inode->i_ino, inode->i_op);
}
EXIT;
return;
}
static int currentfs_notify_change(struct dentry *dentry, struct iattr *iattr)
{
struct snap_cache *cache;
int rc;
struct super_operations *sops;
ENTRY;
if (currentfs_is_under_dotsnap(dentry)) {
EXIT;
return -EPERM;
}
cache = snap_find_cache(dentry->d_inode->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
/* XXX better alloc a new dentry */
if ( snap_needs_cow(dentry->d_inode) != -1 ) {
printk("notify_change:snap_needs_cow for ino %lu \n",
dentry->d_inode->i_ino);
snap_do_cow(dentry->d_inode,
dentry->d_parent->d_inode->i_ino, 0);
}
sops = filter_c2csops(cache->cache_filter);
if (!sops ||
!sops->notify_change) {
EXIT;
return -EINVAL;
}
rc = sops->notify_change(dentry, iattr);
EXIT;
return rc;
}
static void currentfs_put_super(struct super_block *sb)
{
struct snap_cache *cache;
ENTRY;
CDEBUG(D_SUPER, "sb %lx, sb->u.generic_sbp: %lx\n",
(ulong) sb, (ulong) sb->u.generic_sbp);
cache = snap_find_cache(sb->s_dev);
if (!cache) {
EXIT;
goto exit;
}
/* handle COMPAT_FEATUREs */
#ifdef CONFIG_SNAPFS_EXT2
else if( cache->cache_type == FILTER_FS_EXT2 ){
if( !EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_SNAPFS) ){
sb->u.ext2_sb.s_feature_compat &=
~EXT2_FEATURE_COMPAT_BLOCKCOW;
sb->u.ext2_sb.s_es->s_feature_compat &=
cpu_to_le32(~EXT2_FEATURE_COMPAT_BLOCKCOW);
}
}
#endif
#ifdef CONFIG_SNAPFS_EXT3
else if( cache->cache_type == FILTER_FS_EXT3 ){
if( !EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_SNAPFS) ){
sb->u.ext3_sb.s_es->s_feature_compat &=
cpu_to_le32(~EXT3_FEATURE_COMPAT_BLOCKCOW);
}
}
#endif
/*
* If there is a saved 'put_super' function for the underlying
* fs then call it.
*/
if (cache->cache_filter->o_caops.cache_sops->put_super) {
cache->cache_filter->o_caops.cache_sops->put_super(sb);
}
if (!list_empty(&cache->cache_clone_list)) {
printk("Warning: snap_put_super: clones exist!\n");
}
list_del(&cache->cache_chain);
snap_free_cache(cache);
CDEBUG(D_SUPER, "sb %lx, sb->u.generic_sbp: %lx\n",
(ulong) sb, (ulong) sb->u.generic_sbp);
exit:
CDEBUG(D_MALLOC, "after umount: kmem %ld, vmem %ld\n",
snap_kmemory, snap_vmemory);
MOD_DEC_USE_COUNT;
EXIT;
return ;
}
struct super_operations currentfs_super_ops = {
currentfs_read_inode,
NULL, /* write inode */
NULL, /* put inode */
NULL, /* delete inode */
currentfs_notify_change,
currentfs_put_super,
NULL, /* write super */
NULL,
NULL, /* remount */
};
/*
* Snapfs. (C) 2000 Peter J. Braam
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#ifdef CONFIG_SNAPFS_EXT3
#include <linux/ext3_jfs.h>
#endif
#include "linux/filter.h"
#include "linux/snapfs.h"
#include "linux/snapsupport.h"
#ifdef CONFIG_SNAPFS_EXT3
#define EXT3_EA_TRANS_BLOCKS EXT3_DATA_TRANS_BLOCKS
/*
* must follow the changes of ext3_create_indirect() in fs/ext3/snap.c
*/
#define COW_CREDITS (2 * EXT3_EA_TRANS_BLOCKS + 17 + 2 * EXT3_DATA_TRANS_BLOCKS )
/* start the filesystem journal operations */
static void *snap_e3_trans_start(struct inode *inode, int op)
{
int jblocks;
/* XXX needs to be fixed up when we get reiserfs support */
switch (op) {
case SNAP_OP_CREATE:
jblocks = COW_CREDITS + EXT3_DATA_TRANS_BLOCKS + 3;
break;
case SNAP_OP_LINK:
jblocks = COW_CREDITS + EXT3_DATA_TRANS_BLOCKS;
break;
case SNAP_OP_UNLINK:
jblocks = COW_CREDITS + EXT3_DELETE_TRANS_BLOCKS;
break;
case SNAP_OP_SYMLINK:
jblocks = COW_CREDITS + EXT3_DATA_TRANS_BLOCKS + 5;
break;
case SNAP_OP_MKDIR:
jblocks = COW_CREDITS + EXT3_DATA_TRANS_BLOCKS + 4;
break;
case SNAP_OP_RMDIR:
jblocks = 2 * COW_CREDITS + EXT3_DELETE_TRANS_BLOCKS;
break;
case SNAP_OP_MKNOD:
jblocks = COW_CREDITS + EXT3_DATA_TRANS_BLOCKS + 3;
break;
case SNAP_OP_RENAME:
jblocks = 4 * COW_CREDITS + 2 * EXT3_DATA_TRANS_BLOCKS + 2;
break;
default:
CDEBUG(D_JOURNAL, "invalid operation %d for journal\n", op);
return NULL;
}
CDEBUG(D_JOURNAL, "creating journal handle (%d blocks)\n", jblocks);
return journal_start(EXT3_JOURNAL(inode), jblocks);
}
static void snap_e3_trans_commit(void *handle)
{
journal_stop(current->j_handle);
}
struct journal_ops snap_ext3_journal_ops = {
snap_e3_trans_start,
snap_e3_trans_commit
};
#endif /* CONFIG_EXT3_FS */
/*
* A file system filter driver in the style of InterMezzo
* to manage file system snapshots
*
* Author: Peter J. Braam <braam@mountainviewdata.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#define EXPORT_SYMTAB
#include <linux/config.h> /* for CONFIG_PROC_FS */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
/* #include <linux/kmod.h> for request_module() */
#include <linux/sched.h>
#include <linux/lp.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/list.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/poll.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
#if 1 /* XXX - enable for debug messages */
int snap_print_entry = 1;
int snap_debug_level = ~D_INFO;
#else
int snap_print_entry = 0;
int snap_debug_level = 0;
#endif
int snap_inodes = 0;
long snap_memory = 0;
struct snap_control_device snap_dev;
extern int snap_ioctl (struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg);
/* called when opening /dev/device */
static int snap_psdev_open(struct inode * inode, struct file * file)
{
int dev;
ENTRY;
if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev);
if (dev != SNAP_PSDEV_MINOR)
return -ENODEV;
MOD_INC_USE_COUNT;
EXIT;
return 0;
}
/* called when closing /dev/device */
static int snap_psdev_release(struct inode * inode, struct file * file)
{
int dev;
ENTRY;
if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev);
if (dev != SNAP_PSDEV_MINOR)
return -ENODEV;
MOD_DEC_USE_COUNT;
EXIT;
return 0;
}
/* XXX need ioctls here to do snap_delete and snap_restore, snap_backup */
/* declare character device */
static struct file_operations snapcontrol_fops = {
NULL, /* llseek */
NULL, /* read */
NULL, /* write */
NULL, /* presto_psdev_readdir */
NULL, /* poll */
snap_ioctl, /* ioctl */
NULL, /* presto_psdev_mmap */
snap_psdev_open, /* open */
NULL,
snap_psdev_release, /* release */
NULL, /* fsync */
NULL, /* fasync */
NULL /* lock */
};
#define SNAPFS_MINOR 240
static struct miscdevice snapcontrol_dev = {
SNAPFS_MINOR,
"snapcontrol",
&snapcontrol_fops
};
int init_snap_psdev(void)
{
printk(KERN_INFO "SNAP psdev driver v0.01, braam@mountainviewdata.com\n");
misc_register( &snapcontrol_dev );
return 0;
}
void snap_cleanup_psdev(void)
{
ENTRY;
misc_deregister(&snapcontrol_dev);
EXIT;
}
#ifdef MODULE
MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
MODULE_DESCRIPTION("Snapfs file system filters v0.01");
extern int init_snapfs(void);
extern int cleanup_snapfs(void);
extern int init_clonefs(void);
extern int init_snap_sysctl(void);
int init_module(void)
{
int err;
if ( (err = init_snap_psdev()) ) {
printk("Error initializing snap_psdev, %d\n", err);
return -EINVAL;
}
if ( (err = init_snapfs()) ) {
printk("Error initializing snapfs, %d\n", err);
return -EINVAL;
}
if ( (err = init_snapfs_proc_sys()) ) {
printk("Error initializing snapfs proc sys, %d\n", err);
return -EINVAL;
}
return 0;
}
void cleanup_module(void)
{
cleanup_snapfs();
snap_cleanup_psdev();
}
#endif
/*
* fs/snap/snap.c
*
* A snap shot file system.
*
*/
#define EXPORT_SYMTAB
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/quotaops.h>
#include <linux/list.h>
#include <linux/file.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
/*
* Return true if the inode is a redirector inode.
*/
int snap_is_redirector(struct inode *cache_inode)
{
struct snap_cache *cache;
struct snapshot_operations *snapops;
cache = snap_find_cache(cache_inode->i_dev);
if (!cache) {
EXIT;
return 0;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->is_redirector) {
EXIT;
return 0;
}
CDEBUG(D_SNAP, "ino %ld\n", cache_inode->i_ino);
return snapops->is_redirector(cache_inode);
}
/*
* Using a cache inode and clone super block find the real one.
*/
struct inode *snap_redirect(struct inode *cache_inode,
struct super_block *clone_sb)
{
struct snap_clone_info *clone_info;
struct snap_table *table;
struct inode *redirected;
struct snap_cache *cache;
struct snapshot_operations *snapops;
int slot = 0;
int my_table[SNAP_MAX];
int clone_slot;
ENTRY;
cache = snap_find_cache(cache_inode->i_dev);
if (!cache) {
EXIT;
return NULL;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->get_indirect) {
EXIT;
return NULL;
}
CDEBUG(D_SNAP, "cache ino %ld\n", cache_inode->i_ino);
clone_info = (struct snap_clone_info *)&clone_sb->u.generic_sbp;
table = &snap_tables[clone_info->clone_cache->cache_snap_tableno];
/* first find if there are indirected at the clone_index */
redirected = snapops->get_indirect(cache_inode, NULL,
clone_info->clone_index);
/* if not found, get the FIRST index after this and before NOW */
/* XXX fix this later, now use tbl_count, not NOW */
if(!redirected) {
clone_slot = snap_index2slot(table, clone_info->clone_index);
for(slot = table->tbl_count; slot >= clone_slot; slot --)
{
my_table[slot-clone_slot+1] = table->tbl_index[slot];
}
redirected = snapops->get_indirect
(cache_inode, my_table, table->tbl_count - clone_slot + 1);
}
/* old version
redirected = snapops->get_indirect
(cache_inode, table->tbl_index,
snap_index2slot(table, clone_info->clone_index));
*/
if(redirected) CDEBUG(D_SNAP,"redirected ino %ld\n",redirected->i_ino);
EXIT;
return redirected;
}
/*
* Make a copy of the data and plug a redirector in between if there
* is no redirector yet.
*/
int snap_do_cow(struct inode *inode, ino_t parent_ino, int del)
{
struct snap_cache *cache;
struct snap snap;
struct inode *ind = NULL;
struct snapshot_operations *snapops;
ENTRY;
CDEBUG(D_SNAP, "snap_do_cow, ino %ld\n", inode->i_ino);
cache = snap_find_cache(inode->i_dev);
if (!cache) {
EXIT;
return -EINVAL;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->create_indirect) {
EXIT;
return -EINVAL;
}
snap_last(cache, &snap);
ind = snapops->create_indirect(inode, parent_ino, snap.index, del);
EXIT;
if(ind) {
iput(ind);
return 0;
}
else
return -EINVAL;
}
int snap_iterate(struct super_block *sb,
int (*repeat)(struct inode *inode, void *priv),
struct inode **start, void *priv, int flag)
{
struct inode *inode = sb->s_root->d_inode;
struct snap_cache *cache;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(inode->i_dev);
if (!cache) {
EXIT;
return 0;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->iterate) {
EXIT;
return 0;
}
EXIT;
return snapops->iterate(sb, repeat, start, priv, flag);
}
int snap_destroy_indirect(struct inode *pri, int index, struct inode *next_ind )
{
struct snap_cache *cache;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(pri->i_dev);
if (!cache) {
EXIT;
return 0;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->destroy_indirect) {
EXIT;
return 0;
}
EXIT;
return snapops->destroy_indirect(pri, index, next_ind);
}
int snap_restore_indirect(struct inode *pri, int index )
{
struct snap_cache *cache;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(pri->i_dev);
if (!cache) {
EXIT;
return 0;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->restore_indirect) {
EXIT;
return 0;
}
EXIT;
return snapops->restore_indirect(pri, index);
}
struct inode *snap_get_indirect(struct inode *pri, int *table, int slot)
{
struct snap_cache *cache;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(pri->i_dev);
if (!cache) {
EXIT;
return NULL;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->get_indirect) {
EXIT;
return NULL;
}
EXIT;
return snapops->get_indirect(pri, table, slot);
}
int snap_migrate_data(struct inode *dst, struct inode *src)
{
struct snap_cache *cache;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(src->i_dev);
if (!cache) {
EXIT;
return 0;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->migrate_data) {
EXIT;
return 0;
}
EXIT;
return snapops->migrate_data(dst, src);
}
int snap_set_indirect(struct inode *pri, ino_t ind_ino, int index, ino_t parent_ino)
{
struct snap_cache *cache;
struct snapshot_operations *snapops;
ENTRY;
cache = snap_find_cache(pri->i_dev);
if (!cache) {
EXIT;
return -EINVAL;
}
snapops = filter_c2csnapops(cache->cache_filter);
if (!snapops || !snapops->set_indirect) {
EXIT;
return -EINVAL;
}
EXIT;
return snapops->set_indirect(pri, ind_ino, index, parent_ino);
}
This diff is collapsed.
This diff is collapsed.
/*
* fs/snap/snap.c
*
* A snap shot file system.
*
*/
#define EXPORT_SYMTAB
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/quotaops.h>
#include <linux/list.h>
#include <linux/file.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
static inline int inode_has_ea(struct inode *inode)
{
return (inode->u.ext2_i.i_file_acl != 0);
}
static int currentfs_readlink(struct dentry * dentry, char * buffer, int buflen)
{
struct snap_cache *cache;
int rc;
struct inode_operations *iops;
struct inode * inode = dentry->d_inode;
int bpib = inode->i_sb->s_blocksize >> 9;
__u32 save_i_blocks;
ENTRY;
cache = snap_find_cache(inode->i_dev);
if ( !cache ) {
EXIT;
return -EINVAL;
}
iops = filter_c2csiops(cache->cache_filter);
if (!iops ||
!iops->readlink) {
rc = -EINVAL;
goto exit;
}
save_i_blocks = inode->i_blocks;
/* If this link has ea and its i_blocks is ea's block,
* then we should treate it as a fast symlink
*/
if( inode_has_ea(inode) && inode->i_blocks == bpib ) {
inode->i_blocks = 0;
}
rc = iops->readlink(dentry, buffer, buflen);
if( inode->i_blocks != save_i_blocks ){
inode->i_blocks = save_i_blocks;
mark_inode_dirty(inode);
}
exit:
EXIT;
return rc;
}
static int cat_str_ahead(char *buf, int pos, const char* str)
{
int len = strlen(str);
if( pos - len -1 < 0 )
return pos;
buf[--pos] = '/';
memcpy(&buf[pos-len], str, len);
return pos-len;
}
/*
* Adjust the following path if we are under dotsnap (skip .snap/clonexx...)
* in following two case, we just return null and let caller do
* the normal follow_link:
* (1) we are not lies in .snap
* (2) we are already in the root's .snap
*/
static struct dentry * dotsnap_follow_link(struct dentry *base,
struct dentry *dentry,
int follow)
{
struct super_block *sb = dentry->d_inode->i_sb;
struct dentry *rc = NULL;
struct dentry *de = dentry, *de_save1=NULL, *de_save2=NULL;
char *buf = NULL;
int pos = D_MAXLEN;
SNAP_ALLOC(buf, char*, D_MAXLEN);
if( !buf )
return ERR_PTR(-ENOMEM);
/*
* iterate upward to construct the path
*/
do {
if( de_save2 )
pos = cat_str_ahead(buf, pos, de_save2->d_name.name);
if ( de->d_inode && de->d_inode->i_ino & 0xF0000000 )
goto lookup;
de_save2 = de_save1;
de_save1 = de;
de = de->d_parent;
} while (de->d_parent != de);
/* we are not under dotsnap */
goto exit;
lookup:
/* See if we already under root's .snap */
de = de->d_parent;
if( de == sb->s_root )
goto exit;
while( (de->d_parent != de) && (de != sb->s_root) ){
pos = cat_str_ahead(buf, pos, de->d_name.name);
de = de->d_parent;
}
if( de_save1 )
pos = cat_str_ahead(buf, pos, de_save1->d_name.name);
pos = cat_str_ahead(buf, pos, ".snap");
buf[D_MAXLEN-1] = 0;
CDEBUG(D_FILE, "constructed path: %s\n", &buf[pos]);
/* FIXME lookup_dentry will never return NULL ?? */
rc = lookup_dentry(&buf[pos], dget(sb->s_root), follow);
if( !rc ){
rc = ERR_PTR(-ENOENT);
CDEBUG(D_FILE, "lookup_dentry return NULL~!@#$^&*\n");
}
dput(base);
exit:
SNAP_FREE(buf, D_MAXLEN);
return rc;
}
static struct dentry * currentfs_follow_link ( struct dentry *dentry,
struct dentry *base,
unsigned int follow)
{
struct snap_cache *cache;
struct dentry * rc;
struct inode_operations *iops;
struct inode * inode = dentry->d_inode;
int bpib = inode->i_sb->s_blocksize >> 9;
__u32 save_i_blocks;
ENTRY;
cache = snap_find_cache(inode->i_dev);
if ( !cache ) {
EXIT;
return ERR_PTR(-EINVAL);
}
iops = filter_c2csiops(cache->cache_filter);
if (!iops ||
!iops->follow_link) {
rc = ERR_PTR(-EINVAL);
goto exit;
}
if( currentfs_is_under_dotsnap(dentry) ){
rc = dotsnap_follow_link( base, dentry, follow );
if( rc )
goto exit;
}
save_i_blocks = inode->i_blocks;
/* If this link has ea and its i_blocks is ea's block,
* then we should treate it as a fast symlink
*/
if( inode_has_ea(inode) && inode->i_blocks == bpib ) {
inode->i_blocks = 0;
}
rc = iops->follow_link(dentry, base, follow);
if( inode->i_blocks != save_i_blocks ){
inode->i_blocks = save_i_blocks;
mark_inode_dirty(inode);
}
exit:
EXIT;
return rc;
}
struct inode_operations currentfs_sym_iops = {
readlink: currentfs_readlink,
follow_link: currentfs_follow_link
};
struct file_operations currentfs_sym_fops;
/*
* Sysctrl entries for Snapfs
*/
#define __NO_VERSION__
#include <linux/config.h> /* for CONFIG_PROC_FS */
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/swapctl.h>
#include <linux/proc_fs.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/stat.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/utsname.h>
#include <linux/blk.h>
#include <linux/filter.h>
#include <linux/snapfs.h>
#include <linux/snapsupport.h>
/* /proc entries */
#ifdef CONFIG_PROC_FS
static void snapfs_proc_modcount(struct inode *inode, int fill)
{
if (fill)
MOD_INC_USE_COUNT;
else
MOD_DEC_USE_COUNT;
}
struct proc_dir_entry proc_fs_snapfs = {
0, 10, "snapfs",
S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
0, &proc_dir_inode_operations,
NULL, NULL,
NULL,
NULL, NULL
};
#endif
/* SYSCTL below */
static struct ctl_table_header *snapfs_table_header = NULL;
/* 0x100 to avoid any chance of collisions at any point in the tree with
* non-directories
*/
#define PSDEV_SNAPFS (0x120)
#define PSDEV_DEBUG 1 /* control debugging */
#define PSDEV_TRACE 2 /* control enter/leave pattern */
/* These are global control options */
#define ENTRY_CNT 3
/* XXX - doesn't seem to be working in 2.2.15 */
static struct ctl_table snapfs_ctltable[ENTRY_CNT] =
{
{PSDEV_DEBUG, "debug", &snap_debug_level, sizeof(int), 0644, NULL, &proc_dointvec},
{PSDEV_TRACE, "trace", &snap_print_entry, sizeof(int), 0644, NULL, &proc_dointvec},
{0}
};
static ctl_table snapfs_table[2] = {
{PSDEV_SNAPFS, "snapfs", NULL, 0, 0555, snapfs_ctltable},
{0}
};
int /* __init */ init_snapfs_proc_sys(void)
{
#ifdef CONFIG_SYSCTL
if ( !snapfs_table_header )
snapfs_table_header =
register_sysctl_table(snapfs_table, 0);
#endif
#ifdef CONFIG_PROC_FS
proc_register(&proc_root_fs, &proc_fs_snapfs);
proc_fs_snapfs.fill_inode = &snapfs_proc_modcount;
#endif
return 0;
}
void cleanup_snapfs_proc_sys(void) {
#ifdef CONFIG_SYSCTL
if ( snapfs_table_header )
unregister_sysctl_table(snapfs_table_header);
snapfs_table_header = NULL;
#endif
#if CONFIG_PROC_FS
proc_unregister(&proc_root_fs, proc_fs_snapfs.low_ino);
#endif
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment