summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/super.c
diff options
context:
space:
mode:
authorYan <yanzheng@21cn.com>2007-08-29 09:11:44 -0400
committerDavid Woodhouse <dwmw2@hera.kernel.org>2007-08-29 09:11:44 -0400
commit4b82d6e4a592016ac75e756ca519923e0fc18f00 (patch)
tree19b272d2d0558b8e6749aec7a79daff522f2cb44 /fs/btrfs/super.c
parent1b4ab1bb4bbd3045cd041259c30b826654599e96 (diff)
downloadlinux-4b82d6e4a592016ac75e756ca519923e0fc18f00.tar.gz
linux-4b82d6e4a592016ac75e756ca519923e0fc18f00.tar.bz2
linux-4b82d6e4a592016ac75e756ca519923e0fc18f00.zip
Btrfs: Add mount into directory support
Modified form of original patch from Christoph Hellwig to make btrfs mount into the default subvolume by default. mount /dev/somedevice:subvolumename to get other subvolumes or mount /dev/somedevice:. to get the root Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/super.c')
-rw-r--r--fs/btrfs/super.c123
1 files changed, 120 insertions, 3 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index a68101ae1a3b..8a94be1a773b 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -16,6 +16,7 @@
* Boston, MA 021110-1307, USA.
*/
+#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
@@ -26,6 +27,7 @@
#include <linux/string.h>
#include <linux/smp_lock.h>
#include <linux/backing-dev.h>
+#include <linux/mount.h>
#include <linux/mpage.h>
#include <linux/swap.h>
#include <linux/writeback.h>
@@ -145,11 +147,126 @@ static void btrfs_write_super(struct super_block *sb)
sb->s_dirt = 0;
}
+/*
+ * This is almost a copy of get_sb_bdev in fs/super.c.
+ * We need the local copy to allow direct mounting of
+ * subvolumes, but this could be easily integrated back
+ * into the generic version. --hch
+ */
+
+/* start copy & paste */
+static int set_bdev_super(struct super_block *s, void *data)
+{
+ s->s_bdev = data;
+ s->s_dev = s->s_bdev->bd_dev;
+ return 0;
+}
+
+static int test_bdev_super(struct super_block *s, void *data)
+{
+ return (void *)s->s_bdev == data;
+}
+
+int btrfs_get_sb_bdev(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ int (*fill_super)(struct super_block *, void *, int),
+ struct vfsmount *mnt, const char *subvol)
+{
+ struct block_device *bdev = NULL;
+ struct super_block *s;
+ struct dentry *root;
+ int error = 0;
+
+ bdev = open_bdev_excl(dev_name, flags, fs_type);
+ if (IS_ERR(bdev))
+ return PTR_ERR(bdev);
+
+ /*
+ * 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
+ */
+ down(&bdev->bd_mount_sem);
+ s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
+ up(&bdev->bd_mount_sem);
+ if (IS_ERR(s))
+ goto error_s;
+
+ if (s->s_root) {
+ if ((flags ^ s->s_flags) & MS_RDONLY) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ error = -EBUSY;
+ goto error_bdev;
+ }
+
+ close_bdev_excl(bdev);
+ } else {
+ char b[BDEVNAME_SIZE];
+
+ s->s_flags = flags;
+ strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
+ sb_set_blocksize(s, block_size(bdev));
+ error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
+ if (error) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ goto error;
+ }
+
+ s->s_flags |= MS_ACTIVE;
+ }
+
+ if (subvol) {
+ root = lookup_one_len(subvol, s->s_root, strlen(subvol));
+ if (IS_ERR(root)) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ error = PTR_ERR(root);
+ goto error;
+ }
+ if (!root->d_inode) {
+ dput(root);
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ error = -ENXIO;
+ goto error;
+ }
+ } else {
+ root = dget(s->s_root);
+ }
+
+ mnt->mnt_sb = s;
+ mnt->mnt_root = root;
+ return 0;
+
+error_s:
+ error = PTR_ERR(s);
+error_bdev:
+ close_bdev_excl(bdev);
+error:
+ return error;
+}
+/* end copy & paste */
+
static int btrfs_get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+ int flags, const char *identifier, void *data, struct vfsmount *mnt)
{
- return get_sb_bdev(fs_type, flags, dev_name, data,
- btrfs_fill_super, mnt);
+ int ret;
+ char *_identifier = kstrdup(identifier, GFP_KERNEL);
+ char *subvol_name;
+ const char *dev_name;
+
+ subvol_name = _identifier;
+ dev_name = strsep(&subvol_name, ":");
+ if (!dev_name)
+ return -ENOMEM;
+
+ ret = btrfs_get_sb_bdev(fs_type, flags, dev_name, data,
+ btrfs_fill_super, mnt,
+ subvol_name ? subvol_name : "default");
+ kfree(_identifier);
+ return ret;
}
static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)