nilfs2: make snapshots in checkpoint tree exportable
[pandora-kernel.git] / fs / nilfs2 / super.c
index 9222633..adbf582 100644 (file)
 #include <linux/parser.h>
 #include <linux/random.h>
 #include <linux/crc32.h>
-#include <linux/smp_lock.h>
 #include <linux/vfs.h>
 #include <linux/writeback.h>
 #include <linux/kobject.h>
-#include <linux/exportfs.h>
 #include <linux/seq_file.h>
 #include <linux/mount.h>
 #include "nilfs.h"
+#include "export.h"
 #include "mdt.h"
 #include "alloc.h"
 #include "btree.h"
@@ -156,6 +155,7 @@ struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs)
                return NULL;
        ii->i_bh = NULL;
        ii->i_state = 0;
+       ii->i_cno = 0;
        ii->vfs_inode.i_version = 1;
        nilfs_btnode_cache_init(&ii->i_btnode_cache, nilfs->ns_bdi);
        return &ii->vfs_inode;
@@ -168,6 +168,12 @@ struct inode *nilfs_alloc_inode(struct super_block *sb)
 
 void nilfs_destroy_inode(struct inode *inode)
 {
+       struct nilfs_mdt_info *mdi = NILFS_MDT(inode);
+
+       if (mdi) {
+               kfree(mdi->mi_bgl); /* kfree(NULL) is safe */
+               kfree(mdi);
+       }
        kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode));
 }
 
@@ -342,8 +348,6 @@ static void nilfs_put_super(struct super_block *sb)
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
        struct the_nilfs *nilfs = sbi->s_nilfs;
 
-       lock_kernel();
-
        nilfs_detach_segment_constructor(sbi);
 
        if (!(sb->s_flags & MS_RDONLY)) {
@@ -361,8 +365,6 @@ static void nilfs_put_super(struct super_block *sb)
        sbi->s_super = NULL;
        sb->s_fs_info = NULL;
        nilfs_put_sbinfo(sbi);
-
-       unlock_kernel();
 }
 
 static int nilfs_sync_fs(struct super_block *sb, int wait)
@@ -389,18 +391,24 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
        return err;
 }
 
-int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
+int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt,
+                           struct nilfs_root **rootp)
 {
        struct the_nilfs *nilfs = sbi->s_nilfs;
+       struct nilfs_root *root;
        struct nilfs_checkpoint *raw_cp;
        struct buffer_head *bh_cp;
-       int err;
+       int err = -ENOMEM;
+
+       root = nilfs_find_or_create_root(
+               nilfs, curr_mnt ? NILFS_CPTREE_CURRENT_CNO : cno);
+       if (!root)
+               return err;
 
        down_write(&nilfs->ns_super_sem);
        list_add(&sbi->s_list, &nilfs->ns_supers);
        up_write(&nilfs->ns_super_sem);
 
-       err = -ENOMEM;
        sbi->s_ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size);
        if (!sbi->s_ifile)
                goto delist;
@@ -426,6 +434,8 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
        atomic_set(&sbi->s_blocks_count, le64_to_cpu(raw_cp->cp_blocks_count));
 
        nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp);
+
+       *rootp = root;
        return 0;
 
  failed_bh:
@@ -438,6 +448,7 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
        down_write(&nilfs->ns_super_sem);
        list_del_init(&sbi->s_list);
        up_write(&nilfs->ns_super_sem);
+       nilfs_put_root(root);
 
        return err;
 }
@@ -545,48 +556,6 @@ static const struct super_operations nilfs_sops = {
        .show_options = nilfs_show_options
 };
 
-static struct inode *
-nilfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation)
-{
-       struct inode *inode;
-
-       if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO &&
-           ino != NILFS_SKETCH_INO)
-               return ERR_PTR(-ESTALE);
-
-       inode = nilfs_iget(sb, ino);
-       if (IS_ERR(inode))
-               return ERR_CAST(inode);
-       if (generation && inode->i_generation != generation) {
-               iput(inode);
-               return ERR_PTR(-ESTALE);
-       }
-
-       return inode;
-}
-
-static struct dentry *
-nilfs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
-                  int fh_type)
-{
-       return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
-                                   nilfs_nfs_get_inode);
-}
-
-static struct dentry *
-nilfs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len,
-                  int fh_type)
-{
-       return generic_fh_to_parent(sb, fid, fh_len, fh_type,
-                                   nilfs_nfs_get_inode);
-}
-
-static const struct export_operations nilfs_export_ops = {
-       .fh_to_dentry = nilfs_fh_to_dentry,
-       .fh_to_parent = nilfs_fh_to_parent,
-       .get_parent = nilfs_get_parent,
-};
-
 enum {
        Opt_err_cont, Opt_err_panic, Opt_err_ro,
        Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
@@ -813,9 +782,10 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
                 struct the_nilfs *nilfs)
 {
        struct nilfs_sb_info *sbi;
+       struct nilfs_root *fsroot;
        struct inode *root;
        __u64 cno;
-       int err;
+       int err, curr_mnt;
 
        sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
        if (!sbi)
@@ -857,6 +827,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
                goto failed_sbi;
 
        cno = nilfs_last_cno(nilfs);
+       curr_mnt = true;
 
        if (sb->s_flags & MS_RDONLY) {
                if (nilfs_test_opt(sbi, SNAPSHOT)) {
@@ -879,10 +850,11 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
                                goto failed_sbi;
                        }
                        cno = sbi->s_snapshot_cno;
+                       curr_mnt = false;
                }
        }
 
-       err = nilfs_attach_checkpoint(sbi, cno);
+       err = nilfs_attach_checkpoint(sbi, cno, curr_mnt, &fsroot);
        if (err) {
                printk(KERN_ERR "NILFS: error loading a checkpoint"
                       " (checkpoint number=%llu).\n", (unsigned long long)cno);
@@ -895,7 +867,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
                        goto failed_checkpoint;
        }
 
-       root = nilfs_iget(sb, NILFS_ROOT_INO);
+       root = nilfs_iget(sb, fsroot, NILFS_ROOT_INO);
        if (IS_ERR(root)) {
                printk(KERN_ERR "NILFS: get root inode failed\n");
                err = PTR_ERR(root);
@@ -915,6 +887,8 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
                goto failed_segctor;
        }
 
+       nilfs_put_root(fsroot);
+
        if (!(sb->s_flags & MS_RDONLY)) {
                down_write(&nilfs->ns_sem);
                nilfs_setup_super(sbi);
@@ -933,6 +907,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
 
  failed_checkpoint:
        nilfs_detach_checkpoint(sbi);
+       nilfs_put_root(fsroot);
 
  failed_sbi:
        put_nilfs(nilfs);
@@ -949,8 +924,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
        struct nilfs_mount_options old_opts;
        int was_snapshot, err;
 
-       lock_kernel();
-
        down_write(&nilfs->ns_super_sem);
        old_sb_flags = sb->s_flags;
        old_opts.mount_opt = sbi->s_mount_opt;
@@ -1024,7 +997,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
        }
  out:
        up_write(&nilfs->ns_super_sem);
-       unlock_kernel();
        return 0;
 
  restore_opts:
@@ -1032,7 +1004,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
        sbi->s_mount_opt = old_opts.mount_opt;
        sbi->s_snapshot_cno = old_opts.snapshot_cno;
        up_write(&nilfs->ns_super_sem);
-       unlock_kernel();
        return err;
 }
 
@@ -1205,7 +1176,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
        put_nilfs(nilfs);
  failed:
        close_bdev_exclusive(sd.bdev, mode);
-
        return err;
 
  cancel_new: