Whamcloud - gitweb
- grrr, they are unsigned ...
[fs/lustre-release.git] / lustre / snapfs / super.c
index e4b4ef8..bfeecde 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/loop.h>
 #include <linux/jbd.h>
 #include <linux/ext3_fs.h>
 #include <linux/snap.h>
@@ -43,118 +44,86 @@ static void put_filesystem(struct file_system_type *fs)
                __MOD_DEC_USE_COUNT(fs->owner);
 }
 
-
-/* returns an allocated string, copied out from data if opt is found */
-static char *read_opt(const char *opt, char *data)
+static struct vfsmount* get_vfsmount(struct super_block *sb)
 {
-       char *value;
-       char *retval;
-
-       CDEBUG(D_SUPER, "option: %s, data %s\n", opt, data);
-       if ( strncmp(opt, data, strlen(opt)) )
-               return NULL;
-
-       if ( (value = strchr(data, '=')) == NULL )
-               return NULL;
-
-       value++;
-       SNAP_ALLOC(retval, strlen(value) + 1);
-       if ( !retval ) {
-               CERROR("snapfs: Out of memory!\n");
-               return NULL;
-       }
-
-       strcpy(retval, value);
-       CDEBUG(D_SUPER, "Assigned option: %s, value %s\n", opt, retval);
-       return retval;
+       struct vfsmount *rootmnt, *mnt, *ret = NULL;
+       struct list_head *end, *list;
+
+       rootmnt = mntget(current->fs->rootmnt);
+       end = list = &rootmnt->mnt_list;
+       do {
+               mnt = list_entry(list, struct vfsmount, mnt_list);
+               if (mnt->mnt_sb == sb) {
+                       ret = mnt;
+                       break;
+               }
+               list = list->next;
+       } while (end != list);
+       mntput(current->fs->rootmnt);
+       return ret;
 }
 
-static inline void store_opt(char **dst, char *opt)
+void get_snap_current_mnt(struct super_block *sb)
 {
-       if (dst) {
-               if (*dst)
-                       SNAP_FREE(*dst, strlen(*dst) + 1);
-               *dst = opt;
-       } else
-               SNAP_FREE(opt, strlen(opt) + 1);
-}
+       struct vfsmount *mnt;
 
-/* Find the options for snapfs in "options", saving them into the
- * passed pointers.  If the pointer is null, the option is discarded.
- * Copy out all non-snapfs options into cache_data (to be passed
- * to the read_super operation of the cache).  The return value will
- * be a pointer to the end of the cache_data.
- */
-static char *snapfs_options(char *options, char *cache_data,
-                           char **cache_type, char **cow_type,
-                           char **snaptable)
+       mnt = get_vfsmount(sb);
+       if (mnt) 
+               mntget(mnt);
+}
+void put_snap_current_mnt(struct super_block *sb)
 {
-       char *this_char;
-       char *cache_data_end = cache_data;
-
-       /* set the defaults here */
-       if (cache_type && !*cache_type) {
-               SNAP_ALLOC(*cache_type, strlen("ext3") + 1);
-               strcpy(*cache_type, "ext3");
-       }
-       if (cow_type && !*cow_type) {
-               SNAP_ALLOC(*cow_type, strlen("block") + 1);
-               strcpy(*cow_type, "block");
-       }
-       if (snaptable && !*snaptable) {
-               SNAP_ALLOC(*snaptable, strlen("-1")+1);
-               strcpy(*snaptable, "-1");
-       }
-
-       if (!options || !cache_data)
-               return cache_data_end;
+       struct vfsmount *mnt;
 
-       CDEBUG(D_SUPER, "parsing options\n");
-       for (this_char = strtok (options, ",");
-            this_char != NULL;
-            this_char = strtok (NULL, ",")) {
-               char *opt;
-               CDEBUG(D_SUPER, "this_char %s\n", this_char);
+       mnt = get_vfsmount(sb);
+       if (mnt) 
+               mntput(mnt);
+}
 
-               if ( (opt = read_opt("cache_type", this_char)) ) {
-                       store_opt(cache_type, opt);
-                       continue;
-               }
-               if ( (opt = read_opt("cow_type", this_char)) ){
-                       store_opt(cow_type, opt);
-                       continue;
-               }
-               if ( (opt = read_opt("table", this_char)) ) {
-                       store_opt(snaptable, opt);
-                       continue;
+/* In get_opt we get options in opt, value in opt_value
+ * we must remember to free opt and opt_value*/
+static char * snapfs_options(char *options, char **cache_type, 
+                            char **cow_type, char **snaptable)
+{
+       struct option *opt_value;
+       char *pos;
+       
+       while (!(get_opt(&opt_value, &pos))) {                  
+               if (!strcmp(opt_value->opt, "cache_type")) {
+                       if (cache_type != NULL)
+                               *cache_type = opt_value->value;
+               } else if (!strcmp(opt_value->opt, "cow_type")) {
+                       if (cow_type != NULL)
+                               *cow_type = opt_value->value;
+               } else if (!strcmp(opt_value->opt, "snap_table")) {
+                       if (snaptable != NULL)
+                               *snaptable = opt_value->value;
+               } else {
+                       break;
                }
-
-               cache_data_end += sprintf(cache_data_end, "%s%s",
-                                         cache_data_end != cache_data ? ",":"",
-                                         this_char);
        }
-
-       return cache_data_end;
+       if (!*cache_type && cache_type) 
+               *cache_type = "ext3"; 
+       if (!*cow_type && cow_type) 
+               *cow_type = "block";
+       if (!*snaptable && snaptable)
+               *snaptable = "0";
+       return pos;
 }
-
 int snapfs_remount(struct super_block * sb, int *flags, char *data)
 {
-       char *cache_data = NULL;
-       char *snapno = NULL;
-       char *cache_data_end;
-       struct snap_cache *cache = NULL;
        struct super_operations *sops;
+       struct snap_cache *cache = NULL;
+       char *snapno = NULL, *pos = NULL;
+       char *cache_data = NULL;
        int err = 0;
 
        ENTRY;
        CDEBUG(D_SUPER, "remount opts: %s\n", data ? (char *)data : "(none)");
-       if (data) {
-               /* reserve space for the cache's data */
-               SNAP_ALLOC(cache_data, PAGE_SIZE);
-               if ( !cache_data ) 
-                       GOTO(out_err, err = -ENOMEM);
-       }
 
+       if ((err = init_option(data))) {
+               GOTO(out_err, 0);       
+       }
        cache = snap_find_cache(sb->s_dev);
        if (!cache) {
                CERROR("cannot find cache on remount\n");
@@ -166,36 +135,16 @@ int snapfs_remount(struct super_block * sb, int *flags, char *data)
         * the option pointer, which means that the snapfs option
         * will be parsed but discarded.
         */
-       cache_data_end = snapfs_options(data, cache_data, NULL, NULL, &snapno);
-
-       if (cache_data) {
-               if (cache_data_end == cache_data) {
-                       SNAP_FREE(cache_data, PAGE_SIZE);
-                       cache_data = NULL;
-               } else {
-                       CDEBUG(D_SUPER, "cache_data at %p is: %s\n", cache_data,
-                              cache_data);
-               }
-       }
-
+       cache_data = snapfs_options(data, NULL, NULL, &snapno);
 
+       CDEBUG(D_SUPER, "cache_data at %p is: %s\n", cache_data, cache_data); 
        sops = filter_c2csops(cache->cache_filter);
-       if (sops->remount_fs) {
-               err = sops->remount_fs(sb, flags, cache_data);
-       }
-
-       EXIT;
+       if (sops->remount_fs) 
+               err = sops->remount_fs(sb, flags, pos);
 out_err:
-       if (cache_data)
-               SNAP_FREE(cache_data, PAGE_SIZE);
-       return err;
+       cleanup_option();
+       RETURN(err);
 }
-
-/* XXXX remount: needed if snapfs was mounted RO at boot time
-   without a snaptable 
-*/ 
-
-
 /*
  * snapfs super block read.
  *
@@ -213,56 +162,31 @@ snapfs_read_super (
 {
        struct file_system_type *fstype;
        struct snap_cache *cache = NULL;
-       char *cache_data = NULL;
-       char *cache_data_end;
-       char *cache_type = NULL;
-       char *cow_type = NULL;
-       char *snapno = NULL;
-       char *endptr;
-       int tableno;
+       char *cache_type = NULL, *cow_type = NULL;
+       char *snapno = NULL, *cache_data = NULL;
+       int tableno, rc = 0;
 
        ENTRY;
 
-       /* reserve space for the cache's data */
-       SNAP_ALLOC(cache_data, PAGE_SIZE);
-       if ( !cache_data ) {
-               CERROR("snapfs_read_super: Cannot allocate data page.\n");
-               GOTO(out_err, 0);
-       }
-
-       CDEBUG(D_SUPER, "mount opts: %s\n", data ? (char *)data : "(none)");
-
-       /* read and validate options */
-       cache_data_end = snapfs_options(data, cache_data, &cache_type, &cow_type, &snapno);
-
-       /* Need to free cache_type and snapno when it's not in use */
-
-       /* was there anything for the cache filesystem in the data? */
-       if (cache_data_end == cache_data) {
-               SNAP_FREE(cache_data, PAGE_SIZE);
-               cache_data = NULL;
-       } else {
-               CDEBUG(D_SUPER, "cache_data at %p is: %s\n", cache_data,
-                      cache_data);
-       }
-
+       init_option(data);
+       cache_data = snapfs_options(data, &cache_type, &cow_type, &snapno);
        /* set up the cache */
        cache = snap_init_cache();
        if ( !cache ) {
                CERROR("snapfs_read_super: failure allocating cache.\n");
-               GOTO(out_err, 0);
+               GOTO(out_err, rc = -EINVAL);
        }
+       /*get cache and cache filter type */    
+       fstype = get_fs_type((const char *)cache_type);
 
-       fstype = get_fs_type(cache_type);
        if ( !fstype || !fstype->read_super) {
-               GOTO(out_err, 0);
+               CERROR("Unrecognized cache type %s \n", cache_type);
+               GOTO(out_err, rc = -EINVAL);
        }
-       
        cache->cache_filter = filter_get_filter_fs((const char *)cache_type); 
-       
        if (!cache->cache_filter) {
                CERROR("Unrecognized cache type %s \n", cache_type);
-               GOTO(out_err, 0);
+               GOTO(out_err, rc = -EINVAL);
        }
 
        /*
@@ -274,16 +198,8 @@ snapfs_read_super (
          */
        if (fstype->read_super(sb, cache_data, silent) != sb) {
                CERROR("snapfs: cache mount failure.\n");
-               GOTO(out_err, 0);
+               GOTO(out_err, rc = -EINVAL);
         }
-
-       /* this might have been freed above */
-       if (cache_data) {
-               SNAP_FREE(cache_data, PAGE_SIZE);
-               cache_data = NULL;
-       }
-
-
        /*
          * We now know the dev of the cache: hash the cache.
          *
@@ -292,11 +208,10 @@ snapfs_read_super (
          */
        snap_cache_add(cache, sb->s_dev);
 
-       tableno  =  simple_strtoul(snapno, &endptr, 0);
+       tableno = simple_strtoul(snapno, NULL, 0);
        cache->cache_snap_tableno = tableno;
-
-
        CDEBUG(D_SUPER, "get tableno %d\n", cache->cache_snap_tableno);
+       
        /*
          * make sure we have our own super operations
          *
@@ -316,7 +231,7 @@ snapfs_read_super (
        /* set up snapshot ops, handle COMPAT_FEATUREs */
        if( 0 ){
        }
-       else if ( strcmp (cache_type,"ext3") == 0 ){
+       else if (strcmp (cache_type,"ext3") == 0 || !cache_type){
                cache->cache_type = FILTER_FS_EXT3;
                filter_setup_snapshot_ops(cache->cache_filter,
                                        &ext3_snap_operations);
@@ -345,6 +260,7 @@ snapfs_read_super (
                                        sb->s_root->d_op, 
                                        &currentfs_dentry_ops);
                sb->s_root->d_op = filter_c2udops(cache->cache_filter);
+               init_filter_data(sb->s_root->d_inode, 0); 
        }
         /*
          * Save a pointer to the snap_cache structure in the
@@ -356,83 +272,65 @@ snapfs_read_super (
        
        CDEBUG(D_SUPER, "sb %lx, sb->u.generic_sbp: %lx\n",
                 (ulong) sb, (ulong) sb->u.generic_sbp);
-
-       /* we can free snapno and cache_type now, because it's not used */
-       if (snapno) {
-               SNAP_FREE(snapno, strlen(snapno) + 1);
-               snapno = NULL;
-       }
-       if (cache_type) {
-               SNAP_FREE(cache_type, strlen(cache_type) + 1);
-               snapno = NULL;
-       }
-       if (cow_type) {
-               SNAP_FREE(cow_type, strlen(cow_type) + 1);
-               cow_type = NULL;
-       }
+out_err:
+       cleanup_option();
        /* Inc in get_fs_type, Dec in put_fs_type*/
-
-       put_filesystem(fstype);
-       
-       return sb;
-
- out_err:
-       CDEBUG(D_SUPER, "out_err called\n");
-       if (cache)
-               SNAP_FREE(cache, sizeof(struct snap_cache));
-       if (cache_data)
-               SNAP_FREE(cache_data, PAGE_SIZE);
-       if (snapno)
-               SNAP_FREE(snapno, strlen(snapno) + 1);
-       if (cache_type)
-               SNAP_FREE(cache_type, strlen(cache_type) + 1);
-       if (cow_type)
-               SNAP_FREE(cow_type, strlen(cow_type) + 1);
        if (fstype)
                put_filesystem(fstype);
-
-       return NULL;
+       if (rc) 
+               return NULL;
+       return sb; 
 }
 
-static DECLARE_FSTYPE_DEV(snapfs_current_type, "snap_current", snapfs_read_super);
-
+static DECLARE_FSTYPE_DEV(snapfs_current_type, 
+                         "snap_current", snapfs_read_super);
 
 /* Find the options for the clone. These consist of a cache device
    and an index in the snaptable associated with that device. 
 */
-static char *clonefs_options(char *options, char *cache_data,
-                           char **devstr, char **namestr)
+static char *clonefs_options(char *options, char **devstr, char **namestr)
 {
-       char *this_char;
-       char *cache_data_end = cache_data;
-
-       if (!options || !cache_data)
-               return cache_data_end;
-
-       CDEBUG(D_SUPER, "parsing options\n");
-       for (this_char = strtok (options, ",");
-            this_char != NULL;
-            this_char = strtok (NULL, ",")) {
-               char *opt;
-               CDEBUG(D_SUPER, "this_char %s\n", this_char);
-
-               if ( (opt = read_opt("dev", this_char)) ) {
-                       store_opt(devstr, opt);
-                       continue;
+       struct option *opt_value = NULL;
+       char *pos;
+       
+       while (!(get_opt(&opt_value, &pos))) {                  
+               if (!strcmp(opt_value->opt, "dev")) {
+                       if (devstr != NULL)
+                               *devstr = opt_value->value;
+               } else if (!strcmp(opt_value->opt, "name")) {
+                       if (namestr != NULL)
+                               *namestr = opt_value->value;
+               } else {
+                       break;
                }
-               if ( (opt = read_opt("name", this_char)) ) {
-                       store_opt(namestr, opt);
-                       continue;
+       }
+       return pos;
+}
+static int snap_cache_lookup_ino_cb(struct snap_cache *cache, void *in, unsigned long *out)
+{
+       ino_t ino = *((unsigned long*)in);
+
+       if (cache) {
+               struct super_block *sb = cache->cache_sb;
+               kdev_t dev = sb->s_dev;
+
+               if (MAJOR(dev) != LOOP_MAJOR) 
+                       return 0;
+               if (sb->s_bdev->bd_op && sb->s_bdev->bd_op->ioctl) {
+                       struct inode *inode = sb->s_bdev->bd_inode;
+                       struct loop_info loop_info;
+
+                       sb->s_bdev->bd_op->ioctl(inode, NULL, LOOP_GET_INFO, 
+                                                (unsigned long)&loop_info);
+                       
+                       if(loop_info.lo_inode == ino) {
+                               *out = sb->s_dev; 
+                               return 1;
+                       }
                }
-
-               cache_data_end += sprintf(cache_data_end, "%s%s",
-                                         cache_data_end != cache_data ? ",":"",
-                                         this_char);
        }
-
-       return cache_data_end;
+       return 0;       
 }
-
 static int snapfs_path2dev(char *dev_path, kdev_t *dev)
 {
        struct dentry *dentry;
@@ -448,20 +346,30 @@ static int snapfs_path2dev(char *dev_path, kdev_t *dev)
 
        dentry = nd.dentry;
 
-       if (!dentry->d_inode || !S_ISBLK(dentry->d_inode->i_mode) || 
-           is_bad_inode(dentry->d_inode) ) {
+       if (!dentry->d_inode || is_bad_inode(dentry->d_inode) || 
+           (!S_ISBLK(dentry->d_inode->i_mode) && 
+             !S_ISREG(dentry->d_inode->i_mode))){
                path_release(&nd);
                return -ENODEV;
        }
+       if (S_ISBLK(dentry->d_inode->i_mode)) {
+               *dev = kdev_t_to_nr(dentry->d_inode->i_rdev);
+       } else {
+               /*here we must walk through all the snap cache to 
+                *find the loop device */
+               kdev_t tmp;
 
-       *dev = dentry->d_inode->i_rdev;
+               if (snap_cache_process(snap_cache_lookup_ino_cb,
+                                      &dentry->d_inode->i_ino, 
+                                      (unsigned long*)&tmp))
+                       return -EINVAL;
+               *dev = tmp;
+       }
        path_release(&nd);
        return 0;
 }
 
-
 extern struct super_operations clone_super_ops;
-
 /*
  * We always need to remove the snapfs options before passing
  * to bottom FS.
@@ -474,65 +382,41 @@ clone_read_super(
 {
        struct snap_clone_info *clone_sb;
        struct snap_cache *snap_cache = NULL;
-       int err;
-       char *cache_data = NULL;
-       char *cache_data_end;
-       char *devstr = NULL;
+       struct inode *root_inode = NULL;
+       char *devstr = NULL, *namestr = NULL;
+       char *cache_data;
        kdev_t dev;
-       char *namestr = NULL;
-       //char *endptr;
        int index;
        ino_t root_ino;
-       struct inode *root_inode;
+       int err = 0;
 
        ENTRY;
 
-
-       /* reserve space for the cache's data */
-       SNAP_ALLOC(cache_data, PAGE_SIZE);
-       if ( !cache_data ) {
-               CERROR("clone_read_super: Cannot allocate data page.\n");
-               GOTO(out_err, 0);
-       }
-
        CDEBUG(D_SUPER, "mount opts: %s\n", data ? (char *)data : "(none)");
-
+       
+       init_option(data);
        /* read and validate options */
-       cache_data_end = clonefs_options(data, cache_data, &devstr, &namestr);
-
-       /* was there anything for the cache filesystem in the data? */
-       if (cache_data_end == cache_data) {
-               SNAP_FREE(cache_data, PAGE_SIZE);
-               cache_data = NULL;
-       } else {
-               CERROR("clonefs: invalid mount option %s\n", cache_data);
-               GOTO(out_err, 0);
+       cache_data = clonefs_options(data, &devstr, &namestr);
+       if (*cache_data) {
+               CERROR("clonefs: invalid mount option %s\n", (char*)data);
+               GOTO(out_err, err=-EINVAL);
        }
-
        if (!namestr || !devstr) {
                CERROR("snapfs: mount options name and dev mandatory\n");
-               GOTO(out_err, 0);
+               GOTO(out_err, err=-EINVAL);
        }
 
        err = snapfs_path2dev(devstr, &dev);
        if ( err ) {
                CERROR("snap: incorrect device option %s\n", devstr);
-               GOTO(out_err, 0);
+               GOTO(out_err, err=-EINVAL);
        }
        
        snap_cache = snap_find_cache(dev);
        if ( !snap_cache ) {
                CERROR("snap: incorrect device option %s\n", devstr);
-               GOTO(out_err, 0);
-       }
-
-       /*index =  simple_strtoul(indexstr, &endptr, 0);
-       if ( indexstr == endptr ) {
-               printk("No valid index passed to mount\n"); 
-               EXIT;
-               goto out_err;
+               GOTO(out_err, err=-EINVAL);
        }
-       */
 
        index = snap_get_index_from_name (snap_cache->cache_snap_tableno, 
                                        namestr);
@@ -541,7 +425,7 @@ clone_read_super(
 
        if(index < 0 ) {
                CERROR("No valid index for name %s passed to mount\n",namestr); 
-               GOTO(out_err, 0);
+               GOTO(out_err, err=-EINVAL);
        }
 
         /*
@@ -566,24 +450,22 @@ clone_read_super(
               sb->s_op->read_inode, root_ino, root_inode);
 
        sb->s_root = d_alloc_root(root_inode);
+       
        if (!sb->s_root) {
                list_del(&clone_sb->clone_list_entry);
-               sb = NULL;
+               GOTO(out_err, err=-EINVAL);
        }
-
        dget(snap_cache->cache_sb->s_root);
 
-       if (cache_data)
-               SNAP_FREE(cache_data, PAGE_SIZE);
-       if (devstr)
-               SNAP_FREE(devstr, strlen(devstr) + 1);
-       if (namestr)
-               SNAP_FREE(namestr, strlen(namestr) + 1);
        CDEBUG(D_SUPER, "sb %lx, &sb->u.generic_sbp: %lx\n",
                 (ulong) sb, (ulong) &sb->u.generic_sbp);
-       return sb;
+       
+       get_snap_current_mnt(snap_cache->cache_sb);
  out_err:
-       return NULL;
+       cleanup_option();
+       if (err)
+               return NULL;
+       return sb;
 }
 
 static DECLARE_FSTYPE(snapfs_clone_type, "snap_clone", clone_read_super, 0);
@@ -595,16 +477,17 @@ int init_snapfs(void)
        snap_init_cache_hash();
        init_filter_info_cache();
 
+       status = register_filesystem(&snapfs_current_type);
+       if (status) {
+               CERROR("snapfs: failed in register current filesystem!\n");
+       }
+
        status = register_filesystem(&snapfs_clone_type);
        if (status) {
                unregister_filesystem(&snapfs_current_type);
                CERROR("snapfs: failed in register clone filesystem!\n");
        }
 
-       status = register_filesystem(&snapfs_current_type);
-       if (status) {
-               CERROR("snapfs: failed in register current filesystem!\n");
-       }
        return status;
 }