|
@@ -48,10 +48,10 @@
|
|
|
#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"
|
|
@@ -68,11 +68,12 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem "
|
|
|
"(NILFS)");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
-struct kmem_cache *nilfs_inode_cachep;
|
|
|
+static struct kmem_cache *nilfs_inode_cachep;
|
|
|
struct kmem_cache *nilfs_transaction_cachep;
|
|
|
struct kmem_cache *nilfs_segbuf_cachep;
|
|
|
struct kmem_cache *nilfs_btree_path_cache;
|
|
|
|
|
|
+static int nilfs_setup_super(struct nilfs_sb_info *sbi, int is_mount);
|
|
|
static int nilfs_remount(struct super_block *sb, int *flags, char *data);
|
|
|
|
|
|
static void nilfs_set_error(struct nilfs_sb_info *sbi)
|
|
@@ -146,7 +147,7 @@ void nilfs_warning(struct super_block *sb, const char *function,
|
|
|
}
|
|
|
|
|
|
|
|
|
-struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs)
|
|
|
+struct inode *nilfs_alloc_inode(struct super_block *sb)
|
|
|
{
|
|
|
struct nilfs_inode_info *ii;
|
|
|
|
|
@@ -155,18 +156,20 @@ 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);
|
|
|
+ nilfs_btnode_cache_init(&ii->i_btnode_cache, sb->s_bdi);
|
|
|
return &ii->vfs_inode;
|
|
|
}
|
|
|
|
|
|
-struct inode *nilfs_alloc_inode(struct super_block *sb)
|
|
|
-{
|
|
|
- return nilfs_alloc_inode_common(NILFS_SB(sb)->s_nilfs);
|
|
|
-}
|
|
|
-
|
|
|
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));
|
|
|
}
|
|
|
|
|
@@ -340,16 +343,15 @@ static void nilfs_put_super(struct super_block *sb)
|
|
|
nilfs_cleanup_super(sbi);
|
|
|
up_write(&nilfs->ns_sem);
|
|
|
}
|
|
|
- down_write(&nilfs->ns_super_sem);
|
|
|
- if (nilfs->ns_current == sbi)
|
|
|
- nilfs->ns_current = NULL;
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
|
|
|
- nilfs_detach_checkpoint(sbi);
|
|
|
- put_nilfs(sbi->s_nilfs);
|
|
|
+ iput(nilfs->ns_sufile);
|
|
|
+ iput(nilfs->ns_cpfile);
|
|
|
+ iput(nilfs->ns_dat);
|
|
|
+
|
|
|
+ destroy_nilfs(nilfs);
|
|
|
sbi->s_super = NULL;
|
|
|
sb->s_fs_info = NULL;
|
|
|
- nilfs_put_sbinfo(sbi);
|
|
|
+ kfree(sbi);
|
|
|
}
|
|
|
|
|
|
static int nilfs_sync_fs(struct super_block *sb, int wait)
|
|
@@ -376,21 +378,22 @@ 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;
|
|
|
|
|
|
- down_write(&nilfs->ns_super_sem);
|
|
|
- list_add(&sbi->s_list, &nilfs->ns_supers);
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
+ root = nilfs_find_or_create_root(
|
|
|
+ nilfs, curr_mnt ? NILFS_CPTREE_CURRENT_CNO : cno);
|
|
|
+ if (!root)
|
|
|
+ return err;
|
|
|
|
|
|
- err = -ENOMEM;
|
|
|
- sbi->s_ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size);
|
|
|
- if (!sbi->s_ifile)
|
|
|
- goto delist;
|
|
|
+ if (root->ifile)
|
|
|
+ goto reuse; /* already attached checkpoint */
|
|
|
|
|
|
down_read(&nilfs->ns_segctor_sem);
|
|
|
err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp,
|
|
@@ -406,45 +409,64 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
|
|
|
}
|
|
|
goto failed;
|
|
|
}
|
|
|
- err = nilfs_read_inode_common(sbi->s_ifile, &raw_cp->cp_ifile_inode);
|
|
|
- if (unlikely(err))
|
|
|
+
|
|
|
+ err = nilfs_ifile_read(sbi->s_super, root, nilfs->ns_inode_size,
|
|
|
+ &raw_cp->cp_ifile_inode, &root->ifile);
|
|
|
+ if (err)
|
|
|
goto failed_bh;
|
|
|
- atomic_set(&sbi->s_inodes_count, le64_to_cpu(raw_cp->cp_inodes_count));
|
|
|
- atomic_set(&sbi->s_blocks_count, le64_to_cpu(raw_cp->cp_blocks_count));
|
|
|
+
|
|
|
+ atomic_set(&root->inodes_count, le64_to_cpu(raw_cp->cp_inodes_count));
|
|
|
+ atomic_set(&root->blocks_count, le64_to_cpu(raw_cp->cp_blocks_count));
|
|
|
|
|
|
nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp);
|
|
|
+
|
|
|
+ reuse:
|
|
|
+ *rootp = root;
|
|
|
return 0;
|
|
|
|
|
|
failed_bh:
|
|
|
nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp);
|
|
|
failed:
|
|
|
- nilfs_mdt_destroy(sbi->s_ifile);
|
|
|
- sbi->s_ifile = NULL;
|
|
|
+ nilfs_put_root(root);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
|
|
|
- delist:
|
|
|
- down_write(&nilfs->ns_super_sem);
|
|
|
- list_del_init(&sbi->s_list);
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
+static int nilfs_freeze(struct super_block *sb)
|
|
|
+{
|
|
|
+ struct nilfs_sb_info *sbi = NILFS_SB(sb);
|
|
|
+ struct the_nilfs *nilfs = sbi->s_nilfs;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (sb->s_flags & MS_RDONLY)
|
|
|
+ return 0;
|
|
|
|
|
|
+ /* Mark super block clean */
|
|
|
+ down_write(&nilfs->ns_sem);
|
|
|
+ err = nilfs_cleanup_super(sbi);
|
|
|
+ up_write(&nilfs->ns_sem);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi)
|
|
|
+static int nilfs_unfreeze(struct super_block *sb)
|
|
|
{
|
|
|
+ struct nilfs_sb_info *sbi = NILFS_SB(sb);
|
|
|
struct the_nilfs *nilfs = sbi->s_nilfs;
|
|
|
|
|
|
- nilfs_mdt_destroy(sbi->s_ifile);
|
|
|
- sbi->s_ifile = NULL;
|
|
|
- down_write(&nilfs->ns_super_sem);
|
|
|
- list_del_init(&sbi->s_list);
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
+ if (sb->s_flags & MS_RDONLY)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ down_write(&nilfs->ns_sem);
|
|
|
+ nilfs_setup_super(sbi, false);
|
|
|
+ up_write(&nilfs->ns_sem);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
|
{
|
|
|
struct super_block *sb = dentry->d_sb;
|
|
|
- struct nilfs_sb_info *sbi = NILFS_SB(sb);
|
|
|
- struct the_nilfs *nilfs = sbi->s_nilfs;
|
|
|
+ struct nilfs_root *root = NILFS_I(dentry->d_inode)->i_root;
|
|
|
+ struct the_nilfs *nilfs = root->nilfs;
|
|
|
u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
|
|
|
unsigned long long blocks;
|
|
|
unsigned long overhead;
|
|
@@ -480,7 +502,7 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
|
buf->f_bfree = nfreeblocks;
|
|
|
buf->f_bavail = (buf->f_bfree >= nrsvblocks) ?
|
|
|
(buf->f_bfree - nrsvblocks) : 0;
|
|
|
- buf->f_files = atomic_read(&sbi->s_inodes_count);
|
|
|
+ buf->f_files = atomic_read(&root->inodes_count);
|
|
|
buf->f_ffree = 0; /* nilfs_count_free_inodes(sb); */
|
|
|
buf->f_namelen = NILFS_NAME_LEN;
|
|
|
buf->f_fsid.val[0] = (u32)id;
|
|
@@ -493,12 +515,12 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
|
|
|
{
|
|
|
struct super_block *sb = vfs->mnt_sb;
|
|
|
struct nilfs_sb_info *sbi = NILFS_SB(sb);
|
|
|
+ struct nilfs_root *root = NILFS_I(vfs->mnt_root->d_inode)->i_root;
|
|
|
|
|
|
if (!nilfs_test_opt(sbi, BARRIER))
|
|
|
seq_puts(seq, ",nobarrier");
|
|
|
- if (nilfs_test_opt(sbi, SNAPSHOT))
|
|
|
- seq_printf(seq, ",cp=%llu",
|
|
|
- (unsigned long long int)sbi->s_snapshot_cno);
|
|
|
+ if (root->cno != NILFS_CPTREE_CURRENT_CNO)
|
|
|
+ seq_printf(seq, ",cp=%llu", (unsigned long long)root->cno);
|
|
|
if (nilfs_test_opt(sbi, ERRORS_PANIC))
|
|
|
seq_puts(seq, ",errors=panic");
|
|
|
if (nilfs_test_opt(sbi, ERRORS_CONT))
|
|
@@ -524,6 +546,8 @@ static const struct super_operations nilfs_sops = {
|
|
|
.put_super = nilfs_put_super,
|
|
|
/* .write_super = nilfs_write_super, */
|
|
|
.sync_fs = nilfs_sync_fs,
|
|
|
+ .freeze_fs = nilfs_freeze,
|
|
|
+ .unfreeze_fs = nilfs_unfreeze,
|
|
|
/* .write_super_lockfs */
|
|
|
/* .unlockfs */
|
|
|
.statfs = nilfs_statfs,
|
|
@@ -532,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,
|
|
@@ -599,7 +581,6 @@ static int parse_options(char *options, struct super_block *sb, int is_remount)
|
|
|
struct nilfs_sb_info *sbi = NILFS_SB(sb);
|
|
|
char *p;
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
- int option;
|
|
|
|
|
|
if (!options)
|
|
|
return 1;
|
|
@@ -637,30 +618,12 @@ static int parse_options(char *options, struct super_block *sb, int is_remount)
|
|
|
nilfs_write_opt(sbi, ERROR_MODE, ERRORS_CONT);
|
|
|
break;
|
|
|
case Opt_snapshot:
|
|
|
- if (match_int(&args[0], &option) || option <= 0)
|
|
|
- return 0;
|
|
|
if (is_remount) {
|
|
|
- if (!nilfs_test_opt(sbi, SNAPSHOT)) {
|
|
|
- printk(KERN_ERR
|
|
|
- "NILFS: cannot change regular "
|
|
|
- "mount to snapshot.\n");
|
|
|
- return 0;
|
|
|
- } else if (option != sbi->s_snapshot_cno) {
|
|
|
- printk(KERN_ERR
|
|
|
- "NILFS: cannot remount to a "
|
|
|
- "different snapshot.\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- if (!(sb->s_flags & MS_RDONLY)) {
|
|
|
- printk(KERN_ERR "NILFS: cannot mount snapshot "
|
|
|
- "read/write. A read-only option is "
|
|
|
- "required.\n");
|
|
|
+ printk(KERN_ERR
|
|
|
+ "NILFS: \"%s\" option is invalid "
|
|
|
+ "for remount.\n", p);
|
|
|
return 0;
|
|
|
}
|
|
|
- sbi->s_snapshot_cno = option;
|
|
|
- nilfs_set_opt(sbi, SNAPSHOT);
|
|
|
break;
|
|
|
case Opt_norecovery:
|
|
|
nilfs_set_opt(sbi, NORECOVERY);
|
|
@@ -688,7 +651,7 @@ nilfs_set_default_options(struct nilfs_sb_info *sbi,
|
|
|
NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER;
|
|
|
}
|
|
|
|
|
|
-static int nilfs_setup_super(struct nilfs_sb_info *sbi)
|
|
|
+static int nilfs_setup_super(struct nilfs_sb_info *sbi, int is_mount)
|
|
|
{
|
|
|
struct the_nilfs *nilfs = sbi->s_nilfs;
|
|
|
struct nilfs_super_block **sbp;
|
|
@@ -700,6 +663,9 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
|
|
|
if (!sbp)
|
|
|
return -EIO;
|
|
|
|
|
|
+ if (!is_mount)
|
|
|
+ goto skip_mount_setup;
|
|
|
+
|
|
|
max_mnt_count = le16_to_cpu(sbp[0]->s_max_mnt_count);
|
|
|
mnt_count = le16_to_cpu(sbp[0]->s_mnt_count);
|
|
|
|
|
@@ -716,9 +682,11 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
|
|
|
sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT);
|
|
|
|
|
|
sbp[0]->s_mnt_count = cpu_to_le16(mnt_count + 1);
|
|
|
+ sbp[0]->s_mtime = cpu_to_le64(get_seconds());
|
|
|
+
|
|
|
+skip_mount_setup:
|
|
|
sbp[0]->s_state =
|
|
|
cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS);
|
|
|
- sbp[0]->s_mtime = cpu_to_le64(get_seconds());
|
|
|
/* synchronize sbp[1] with sbp[0] */
|
|
|
memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
|
|
|
return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL);
|
|
@@ -785,22 +753,156 @@ int nilfs_check_feature_compatibility(struct super_block *sb,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int nilfs_get_root_dentry(struct super_block *sb,
|
|
|
+ struct nilfs_root *root,
|
|
|
+ struct dentry **root_dentry)
|
|
|
+{
|
|
|
+ struct inode *inode;
|
|
|
+ struct dentry *dentry;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ inode = nilfs_iget(sb, root, NILFS_ROOT_INO);
|
|
|
+ if (IS_ERR(inode)) {
|
|
|
+ printk(KERN_ERR "NILFS: get root inode failed\n");
|
|
|
+ ret = PTR_ERR(inode);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (!S_ISDIR(inode->i_mode) || !inode->i_blocks || !inode->i_size) {
|
|
|
+ iput(inode);
|
|
|
+ printk(KERN_ERR "NILFS: corrupt root inode.\n");
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (root->cno == NILFS_CPTREE_CURRENT_CNO) {
|
|
|
+ dentry = d_find_alias(inode);
|
|
|
+ if (!dentry) {
|
|
|
+ dentry = d_alloc_root(inode);
|
|
|
+ if (!dentry) {
|
|
|
+ iput(inode);
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto failed_dentry;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ iput(inode);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dentry = d_obtain_alias(inode);
|
|
|
+ if (IS_ERR(dentry)) {
|
|
|
+ ret = PTR_ERR(dentry);
|
|
|
+ goto failed_dentry;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ *root_dentry = dentry;
|
|
|
+ out:
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ failed_dentry:
|
|
|
+ printk(KERN_ERR "NILFS: get root dentry failed\n");
|
|
|
+ goto out;
|
|
|
+}
|
|
|
+
|
|
|
+static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
|
|
|
+ struct dentry **root_dentry)
|
|
|
+{
|
|
|
+ struct the_nilfs *nilfs = NILFS_SB(s)->s_nilfs;
|
|
|
+ struct nilfs_root *root;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ down_read(&nilfs->ns_segctor_sem);
|
|
|
+ ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno);
|
|
|
+ up_read(&nilfs->ns_segctor_sem);
|
|
|
+ if (ret < 0) {
|
|
|
+ ret = (ret == -ENOENT) ? -EINVAL : ret;
|
|
|
+ goto out;
|
|
|
+ } else if (!ret) {
|
|
|
+ printk(KERN_ERR "NILFS: The specified checkpoint is "
|
|
|
+ "not a snapshot (checkpoint number=%llu).\n",
|
|
|
+ (unsigned long long)cno);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = nilfs_attach_checkpoint(NILFS_SB(s), cno, false, &root);
|
|
|
+ if (ret) {
|
|
|
+ printk(KERN_ERR "NILFS: error loading snapshot "
|
|
|
+ "(checkpoint number=%llu).\n",
|
|
|
+ (unsigned long long)cno);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ ret = nilfs_get_root_dentry(s, root, root_dentry);
|
|
|
+ nilfs_put_root(root);
|
|
|
+ out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int nilfs_tree_was_touched(struct dentry *root_dentry)
|
|
|
+{
|
|
|
+ return atomic_read(&root_dentry->d_count) > 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * nilfs_try_to_shrink_tree() - try to shrink dentries of a checkpoint
|
|
|
+ * @root_dentry: root dentry of the tree to be shrunk
|
|
|
+ *
|
|
|
+ * This function returns true if the tree was in-use.
|
|
|
+ */
|
|
|
+static int nilfs_try_to_shrink_tree(struct dentry *root_dentry)
|
|
|
+{
|
|
|
+ if (have_submounts(root_dentry))
|
|
|
+ return true;
|
|
|
+ shrink_dcache_parent(root_dentry);
|
|
|
+ return nilfs_tree_was_touched(root_dentry);
|
|
|
+}
|
|
|
+
|
|
|
+int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno)
|
|
|
+{
|
|
|
+ struct the_nilfs *nilfs = NILFS_SB(sb)->s_nilfs;
|
|
|
+ struct nilfs_root *root;
|
|
|
+ struct inode *inode;
|
|
|
+ struct dentry *dentry;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (cno < 0 || cno > nilfs->ns_cno)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (cno >= nilfs_last_cno(nilfs))
|
|
|
+ return true; /* protect recent checkpoints */
|
|
|
+
|
|
|
+ ret = false;
|
|
|
+ root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, cno);
|
|
|
+ if (root) {
|
|
|
+ inode = nilfs_ilookup(sb, root, NILFS_ROOT_INO);
|
|
|
+ if (inode) {
|
|
|
+ dentry = d_find_alias(inode);
|
|
|
+ if (dentry) {
|
|
|
+ if (nilfs_tree_was_touched(dentry))
|
|
|
+ ret = nilfs_try_to_shrink_tree(dentry);
|
|
|
+ dput(dentry);
|
|
|
+ }
|
|
|
+ iput(inode);
|
|
|
+ }
|
|
|
+ nilfs_put_root(root);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* nilfs_fill_super() - initialize a super block instance
|
|
|
* @sb: super_block
|
|
|
* @data: mount options
|
|
|
* @silent: silent mode flag
|
|
|
- * @nilfs: the_nilfs struct
|
|
|
*
|
|
|
* This function is called exclusively by nilfs->ns_mount_mutex.
|
|
|
* So, the recovery process is protected from other simultaneous mounts.
|
|
|
*/
|
|
|
static int
|
|
|
-nilfs_fill_super(struct super_block *sb, void *data, int silent,
|
|
|
- struct the_nilfs *nilfs)
|
|
|
+nilfs_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
{
|
|
|
+ struct the_nilfs *nilfs;
|
|
|
struct nilfs_sb_info *sbi;
|
|
|
- struct inode *root;
|
|
|
+ struct nilfs_root *fsroot;
|
|
|
+ struct backing_dev_info *bdi;
|
|
|
__u64 cno;
|
|
|
int err;
|
|
|
|
|
@@ -809,19 +911,21 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
|
|
|
return -ENOMEM;
|
|
|
|
|
|
sb->s_fs_info = sbi;
|
|
|
+ sbi->s_super = sb;
|
|
|
|
|
|
- get_nilfs(nilfs);
|
|
|
+ nilfs = alloc_nilfs(sb->s_bdev);
|
|
|
+ if (!nilfs) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto failed_sbi;
|
|
|
+ }
|
|
|
sbi->s_nilfs = nilfs;
|
|
|
- sbi->s_super = sb;
|
|
|
- atomic_set(&sbi->s_count, 1);
|
|
|
|
|
|
err = init_nilfs(nilfs, sbi, (char *)data);
|
|
|
if (err)
|
|
|
- goto failed_sbi;
|
|
|
+ goto failed_nilfs;
|
|
|
|
|
|
spin_lock_init(&sbi->s_inode_lock);
|
|
|
INIT_LIST_HEAD(&sbi->s_dirty_files);
|
|
|
- INIT_LIST_HEAD(&sbi->s_list);
|
|
|
|
|
|
/*
|
|
|
* Following initialization is overlapped because
|
|
@@ -837,94 +941,59 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
|
|
|
sb->s_export_op = &nilfs_export_ops;
|
|
|
sb->s_root = NULL;
|
|
|
sb->s_time_gran = 1;
|
|
|
- sb->s_bdi = nilfs->ns_bdi;
|
|
|
+
|
|
|
+ bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
|
|
|
+ sb->s_bdi = bdi ? : &default_backing_dev_info;
|
|
|
|
|
|
err = load_nilfs(nilfs, sbi);
|
|
|
if (err)
|
|
|
- goto failed_sbi;
|
|
|
+ goto failed_nilfs;
|
|
|
|
|
|
cno = nilfs_last_cno(nilfs);
|
|
|
-
|
|
|
- if (sb->s_flags & MS_RDONLY) {
|
|
|
- if (nilfs_test_opt(sbi, SNAPSHOT)) {
|
|
|
- down_read(&nilfs->ns_segctor_sem);
|
|
|
- err = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile,
|
|
|
- sbi->s_snapshot_cno);
|
|
|
- up_read(&nilfs->ns_segctor_sem);
|
|
|
- if (err < 0) {
|
|
|
- if (err == -ENOENT)
|
|
|
- err = -EINVAL;
|
|
|
- goto failed_sbi;
|
|
|
- }
|
|
|
- if (!err) {
|
|
|
- printk(KERN_ERR
|
|
|
- "NILFS: The specified checkpoint is "
|
|
|
- "not a snapshot "
|
|
|
- "(checkpoint number=%llu).\n",
|
|
|
- (unsigned long long)sbi->s_snapshot_cno);
|
|
|
- err = -EINVAL;
|
|
|
- goto failed_sbi;
|
|
|
- }
|
|
|
- cno = sbi->s_snapshot_cno;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- err = nilfs_attach_checkpoint(sbi, cno);
|
|
|
+ err = nilfs_attach_checkpoint(sbi, cno, true, &fsroot);
|
|
|
if (err) {
|
|
|
- printk(KERN_ERR "NILFS: error loading a checkpoint"
|
|
|
- " (checkpoint number=%llu).\n", (unsigned long long)cno);
|
|
|
- goto failed_sbi;
|
|
|
+ printk(KERN_ERR "NILFS: error loading last checkpoint "
|
|
|
+ "(checkpoint number=%llu).\n", (unsigned long long)cno);
|
|
|
+ goto failed_unload;
|
|
|
}
|
|
|
|
|
|
if (!(sb->s_flags & MS_RDONLY)) {
|
|
|
- err = nilfs_attach_segment_constructor(sbi);
|
|
|
+ err = nilfs_attach_segment_constructor(sbi, fsroot);
|
|
|
if (err)
|
|
|
goto failed_checkpoint;
|
|
|
}
|
|
|
|
|
|
- root = nilfs_iget(sb, NILFS_ROOT_INO);
|
|
|
- if (IS_ERR(root)) {
|
|
|
- printk(KERN_ERR "NILFS: get root inode failed\n");
|
|
|
- err = PTR_ERR(root);
|
|
|
- goto failed_segctor;
|
|
|
- }
|
|
|
- if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
|
|
|
- iput(root);
|
|
|
- printk(KERN_ERR "NILFS: corrupt root inode.\n");
|
|
|
- err = -EINVAL;
|
|
|
- goto failed_segctor;
|
|
|
- }
|
|
|
- sb->s_root = d_alloc_root(root);
|
|
|
- if (!sb->s_root) {
|
|
|
- iput(root);
|
|
|
- printk(KERN_ERR "NILFS: get root dentry failed\n");
|
|
|
- err = -ENOMEM;
|
|
|
+ err = nilfs_get_root_dentry(sb, fsroot, &sb->s_root);
|
|
|
+ if (err)
|
|
|
goto failed_segctor;
|
|
|
- }
|
|
|
+
|
|
|
+ nilfs_put_root(fsroot);
|
|
|
|
|
|
if (!(sb->s_flags & MS_RDONLY)) {
|
|
|
down_write(&nilfs->ns_sem);
|
|
|
- nilfs_setup_super(sbi);
|
|
|
+ nilfs_setup_super(sbi, true);
|
|
|
up_write(&nilfs->ns_sem);
|
|
|
}
|
|
|
|
|
|
- down_write(&nilfs->ns_super_sem);
|
|
|
- if (!nilfs_test_opt(sbi, SNAPSHOT))
|
|
|
- nilfs->ns_current = sbi;
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
failed_segctor:
|
|
|
nilfs_detach_segment_constructor(sbi);
|
|
|
|
|
|
failed_checkpoint:
|
|
|
- nilfs_detach_checkpoint(sbi);
|
|
|
+ nilfs_put_root(fsroot);
|
|
|
+
|
|
|
+ failed_unload:
|
|
|
+ iput(nilfs->ns_sufile);
|
|
|
+ iput(nilfs->ns_cpfile);
|
|
|
+ iput(nilfs->ns_dat);
|
|
|
+
|
|
|
+ failed_nilfs:
|
|
|
+ destroy_nilfs(nilfs);
|
|
|
|
|
|
failed_sbi:
|
|
|
- put_nilfs(nilfs);
|
|
|
sb->s_fs_info = NULL;
|
|
|
- nilfs_put_sbinfo(sbi);
|
|
|
+ kfree(sbi);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -934,13 +1003,10 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
struct the_nilfs *nilfs = sbi->s_nilfs;
|
|
|
unsigned long old_sb_flags;
|
|
|
struct nilfs_mount_options old_opts;
|
|
|
- int was_snapshot, err;
|
|
|
+ int err;
|
|
|
|
|
|
- down_write(&nilfs->ns_super_sem);
|
|
|
old_sb_flags = sb->s_flags;
|
|
|
old_opts.mount_opt = sbi->s_mount_opt;
|
|
|
- old_opts.snapshot_cno = sbi->s_snapshot_cno;
|
|
|
- was_snapshot = nilfs_test_opt(sbi, SNAPSHOT);
|
|
|
|
|
|
if (!parse_options(data, sb, 1)) {
|
|
|
err = -EINVAL;
|
|
@@ -949,11 +1015,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
sb->s_flags = (sb->s_flags & ~MS_POSIXACL);
|
|
|
|
|
|
err = -EINVAL;
|
|
|
- if (was_snapshot && !(*flags & MS_RDONLY)) {
|
|
|
- printk(KERN_ERR "NILFS (device %s): cannot remount snapshot "
|
|
|
- "read/write.\n", sb->s_id);
|
|
|
- goto restore_opts;
|
|
|
- }
|
|
|
|
|
|
if (!nilfs_valid_fs(nilfs)) {
|
|
|
printk(KERN_WARNING "NILFS (device %s): couldn't "
|
|
@@ -978,6 +1039,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
up_write(&nilfs->ns_sem);
|
|
|
} else {
|
|
|
__u64 features;
|
|
|
+ struct nilfs_root *root;
|
|
|
|
|
|
/*
|
|
|
* Mounting a RDONLY partition read-write, so reread and
|
|
@@ -999,23 +1061,21 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
|
|
|
sb->s_flags &= ~MS_RDONLY;
|
|
|
|
|
|
- err = nilfs_attach_segment_constructor(sbi);
|
|
|
+ root = NILFS_I(sb->s_root->d_inode)->i_root;
|
|
|
+ err = nilfs_attach_segment_constructor(sbi, root);
|
|
|
if (err)
|
|
|
goto restore_opts;
|
|
|
|
|
|
down_write(&nilfs->ns_sem);
|
|
|
- nilfs_setup_super(sbi);
|
|
|
+ nilfs_setup_super(sbi, true);
|
|
|
up_write(&nilfs->ns_sem);
|
|
|
}
|
|
|
out:
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
return 0;
|
|
|
|
|
|
restore_opts:
|
|
|
sb->s_flags = old_sb_flags;
|
|
|
sbi->s_mount_opt = old_opts.mount_opt;
|
|
|
- sbi->s_snapshot_cno = old_opts.snapshot_cno;
|
|
|
- up_write(&nilfs->ns_super_sem);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1035,7 +1095,7 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd)
|
|
|
{
|
|
|
char *p, *options = data;
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
- int option, token;
|
|
|
+ int token;
|
|
|
int ret = 0;
|
|
|
|
|
|
do {
|
|
@@ -1043,16 +1103,18 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd)
|
|
|
if (p != NULL && *p) {
|
|
|
token = match_token(p, tokens, args);
|
|
|
if (token == Opt_snapshot) {
|
|
|
- if (!(sd->flags & MS_RDONLY))
|
|
|
+ if (!(sd->flags & MS_RDONLY)) {
|
|
|
ret++;
|
|
|
- else {
|
|
|
- ret = match_int(&args[0], &option);
|
|
|
- if (!ret) {
|
|
|
- if (option > 0)
|
|
|
- sd->cno = option;
|
|
|
- else
|
|
|
- ret++;
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ sd->cno = simple_strtoull(args[0].from,
|
|
|
+ NULL, 0);
|
|
|
+ /*
|
|
|
+ * No need to see the end pointer;
|
|
|
+ * match_token() has done syntax
|
|
|
+ * checking.
|
|
|
+ */
|
|
|
+ if (sd->cno == 0)
|
|
|
+ ret++;
|
|
|
}
|
|
|
}
|
|
|
if (ret)
|
|
@@ -1069,18 +1131,14 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd)
|
|
|
|
|
|
static int nilfs_set_bdev_super(struct super_block *s, void *data)
|
|
|
{
|
|
|
- struct nilfs_super_data *sd = data;
|
|
|
-
|
|
|
- s->s_bdev = sd->bdev;
|
|
|
+ s->s_bdev = data;
|
|
|
s->s_dev = s->s_bdev->bd_dev;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int nilfs_test_bdev_super(struct super_block *s, void *data)
|
|
|
{
|
|
|
- struct nilfs_super_data *sd = data;
|
|
|
-
|
|
|
- return sd->sbi && s->s_fs_info == (void *)sd->sbi;
|
|
|
+ return (void *)s->s_bdev == data;
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -1090,8 +1148,8 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
|
|
|
struct nilfs_super_data sd;
|
|
|
struct super_block *s;
|
|
|
fmode_t mode = FMODE_READ;
|
|
|
- struct the_nilfs *nilfs;
|
|
|
- int err, need_to_close = 1;
|
|
|
+ struct dentry *root_dentry;
|
|
|
+ int err, s_new = false;
|
|
|
|
|
|
if (!(flags & MS_RDONLY))
|
|
|
mode |= FMODE_WRITE;
|
|
@@ -1100,12 +1158,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
|
|
|
if (IS_ERR(sd.bdev))
|
|
|
return PTR_ERR(sd.bdev);
|
|
|
|
|
|
- /*
|
|
|
- * To get mount instance using sget() vfs-routine, NILFS needs
|
|
|
- * much more information than normal filesystems to identify mount
|
|
|
- * instance. For snapshot mounts, not only a mount type (ro-mount
|
|
|
- * or rw-mount) but also a checkpoint number is required.
|
|
|
- */
|
|
|
sd.cno = 0;
|
|
|
sd.flags = flags;
|
|
|
if (nilfs_identify((char *)data, &sd)) {
|
|
@@ -1113,93 +1165,86 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
|
|
|
goto failed;
|
|
|
}
|
|
|
|
|
|
- nilfs = find_or_create_nilfs(sd.bdev);
|
|
|
- if (!nilfs) {
|
|
|
- err = -ENOMEM;
|
|
|
- goto failed;
|
|
|
- }
|
|
|
-
|
|
|
- mutex_lock(&nilfs->ns_mount_mutex);
|
|
|
-
|
|
|
- if (!sd.cno) {
|
|
|
- /*
|
|
|
- * Check if an exclusive mount exists or not.
|
|
|
- * Snapshot mounts coexist with a current mount
|
|
|
- * (i.e. rw-mount or ro-mount), whereas rw-mount and
|
|
|
- * ro-mount are mutually exclusive.
|
|
|
- */
|
|
|
- down_read(&nilfs->ns_super_sem);
|
|
|
- if (nilfs->ns_current &&
|
|
|
- ((nilfs->ns_current->s_super->s_flags ^ flags)
|
|
|
- & MS_RDONLY)) {
|
|
|
- up_read(&nilfs->ns_super_sem);
|
|
|
- err = -EBUSY;
|
|
|
- goto failed_unlock;
|
|
|
- }
|
|
|
- up_read(&nilfs->ns_super_sem);
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Find existing nilfs_sb_info struct
|
|
|
- */
|
|
|
- sd.sbi = nilfs_find_sbinfo(nilfs, !(flags & MS_RDONLY), sd.cno);
|
|
|
-
|
|
|
/*
|
|
|
- * Get super block instance holding the nilfs_sb_info struct.
|
|
|
- * A new instance is allocated if no existing mount is present or
|
|
|
- * existing instance has been unmounted.
|
|
|
+ * once the super is inserted into the list by sget, s_umount
|
|
|
+ * will protect the lockfs code from trying to start a snapshot
|
|
|
+ * while we are mounting
|
|
|
*/
|
|
|
- s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd);
|
|
|
- if (sd.sbi)
|
|
|
- nilfs_put_sbinfo(sd.sbi);
|
|
|
-
|
|
|
+ mutex_lock(&sd.bdev->bd_fsfreeze_mutex);
|
|
|
+ if (sd.bdev->bd_fsfreeze_count > 0) {
|
|
|
+ mutex_unlock(&sd.bdev->bd_fsfreeze_mutex);
|
|
|
+ err = -EBUSY;
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+ s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, sd.bdev);
|
|
|
+ mutex_unlock(&sd.bdev->bd_fsfreeze_mutex);
|
|
|
if (IS_ERR(s)) {
|
|
|
err = PTR_ERR(s);
|
|
|
- goto failed_unlock;
|
|
|
+ goto failed;
|
|
|
}
|
|
|
|
|
|
if (!s->s_root) {
|
|
|
char b[BDEVNAME_SIZE];
|
|
|
|
|
|
+ s_new = true;
|
|
|
+
|
|
|
/* New superblock instance created */
|
|
|
s->s_flags = flags;
|
|
|
s->s_mode = mode;
|
|
|
strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id));
|
|
|
sb_set_blocksize(s, block_size(sd.bdev));
|
|
|
|
|
|
- err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0,
|
|
|
- nilfs);
|
|
|
+ err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
|
|
|
if (err)
|
|
|
- goto cancel_new;
|
|
|
+ goto failed_super;
|
|
|
|
|
|
s->s_flags |= MS_ACTIVE;
|
|
|
- need_to_close = 0;
|
|
|
+ } else if (!sd.cno) {
|
|
|
+ int busy = false;
|
|
|
+
|
|
|
+ if (nilfs_tree_was_touched(s->s_root)) {
|
|
|
+ busy = nilfs_try_to_shrink_tree(s->s_root);
|
|
|
+ if (busy && (flags ^ s->s_flags) & MS_RDONLY) {
|
|
|
+ printk(KERN_ERR "NILFS: the device already "
|
|
|
+ "has a %s mount.\n",
|
|
|
+ (s->s_flags & MS_RDONLY) ?
|
|
|
+ "read-only" : "read/write");
|
|
|
+ err = -EBUSY;
|
|
|
+ goto failed_super;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!busy) {
|
|
|
+ /*
|
|
|
+ * Try remount to setup mount states if the current
|
|
|
+ * tree is not mounted and only snapshots use this sb.
|
|
|
+ */
|
|
|
+ err = nilfs_remount(s, &flags, data);
|
|
|
+ if (err)
|
|
|
+ goto failed_super;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- mutex_unlock(&nilfs->ns_mount_mutex);
|
|
|
- put_nilfs(nilfs);
|
|
|
- if (need_to_close)
|
|
|
+ if (sd.cno) {
|
|
|
+ err = nilfs_attach_snapshot(s, sd.cno, &root_dentry);
|
|
|
+ if (err)
|
|
|
+ goto failed_super;
|
|
|
+ } else {
|
|
|
+ root_dentry = dget(s->s_root);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!s_new)
|
|
|
close_bdev_exclusive(sd.bdev, mode);
|
|
|
- simple_set_mnt(mnt, s);
|
|
|
- return 0;
|
|
|
|
|
|
- failed_unlock:
|
|
|
- mutex_unlock(&nilfs->ns_mount_mutex);
|
|
|
- put_nilfs(nilfs);
|
|
|
- failed:
|
|
|
- close_bdev_exclusive(sd.bdev, mode);
|
|
|
- return err;
|
|
|
+ mnt->mnt_sb = s;
|
|
|
+ mnt->mnt_root = root_dentry;
|
|
|
+ return 0;
|
|
|
|
|
|
- cancel_new:
|
|
|
- /* Abandoning the newly allocated superblock */
|
|
|
- mutex_unlock(&nilfs->ns_mount_mutex);
|
|
|
- put_nilfs(nilfs);
|
|
|
+ failed_super:
|
|
|
deactivate_locked_super(s);
|
|
|
- /*
|
|
|
- * deactivate_locked_super() invokes close_bdev_exclusive().
|
|
|
- * We must finish all post-cleaning before this call;
|
|
|
- * put_nilfs() needs the block device.
|
|
|
- */
|
|
|
+
|
|
|
+ failed:
|
|
|
+ if (!s_new)
|
|
|
+ close_bdev_exclusive(sd.bdev, mode);
|
|
|
return err;
|
|
|
}
|
|
|
|