aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs')
-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 @@
16 * Boston, MA 021110-1307, USA. 16 * Boston, MA 021110-1307, USA.
17 */ 17 */
18 18
19#include <linux/blkdev.h>
19#include <linux/module.h> 20#include <linux/module.h>
20#include <linux/buffer_head.h> 21#include <linux/buffer_head.h>
21#include <linux/fs.h> 22#include <linux/fs.h>
@@ -26,6 +27,7 @@
26#include <linux/string.h> 27#include <linux/string.h>
27#include <linux/smp_lock.h> 28#include <linux/smp_lock.h>
28#include <linux/backing-dev.h> 29#include <linux/backing-dev.h>
30#include <linux/mount.h>
29#include <linux/mpage.h> 31#include <linux/mpage.h>
30#include <linux/swap.h> 32#include <linux/swap.h>
31#include <linux/writeback.h> 33#include <linux/writeback.h>
@@ -145,11 +147,126 @@ static void btrfs_write_super(struct super_block *sb)
145 sb->s_dirt = 0; 147 sb->s_dirt = 0;
146} 148}
147 149
150/*
151 * This is almost a copy of get_sb_bdev in fs/super.c.
152 * We need the local copy to allow direct mounting of
153 * subvolumes, but this could be easily integrated back
154 * into the generic version. --hch
155 */
156
157/* start copy & paste */
158static int set_bdev_super(struct super_block *s, void *data)
159{
160 s->s_bdev = data;
161 s->s_dev = s->s_bdev->bd_dev;
162 return 0;
163}
164
165static int test_bdev_super(struct super_block *s, void *data)
166{
167 return (void *)s->s_bdev == data;
168}
169
170int btrfs_get_sb_bdev(struct file_system_type *fs_type,
171 int flags, const char *dev_name, void *data,
172 int (*fill_super)(struct super_block *, void *, int),
173 struct vfsmount *mnt, const char *subvol)
174{
175 struct block_device *bdev = NULL;
176 struct super_block *s;
177 struct dentry *root;
178 int error = 0;
179
180 bdev = open_bdev_excl(dev_name, flags, fs_type);
181 if (IS_ERR(bdev))
182 return PTR_ERR(bdev);
183
184 /*
185 * once the super is inserted into the list by sget, s_umount
186 * will protect the lockfs code from trying to start a snapshot
187 * while we are mounting
188 */
189 down(&bdev->bd_mount_sem);
190 s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
191 up(&bdev->bd_mount_sem);
192 if (IS_ERR(s))
193 goto error_s;
194
195 if (s->s_root) {
196 if ((flags ^ s->s_flags) & MS_RDONLY) {
197 up_write(&s->s_umount);
198 deactivate_super(s);
199 error = -EBUSY;
200 goto error_bdev;
201 }
202
203 close_bdev_excl(bdev);
204 } else {
205 char b[BDEVNAME_SIZE];
206
207 s->s_flags = flags;
208 strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
209 sb_set_blocksize(s, block_size(bdev));
210 error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
211 if (error) {
212 up_write(&s->s_umount);
213 deactivate_super(s);
214 goto error;
215 }
216
217 s->s_flags |= MS_ACTIVE;
218 }
219
220 if (subvol) {
221 root = lookup_one_len(subvol, s->s_root, strlen(subvol));
222 if (IS_ERR(root)) {
223 up_write(&s->s_umount);
224 deactivate_super(s);
225 error = PTR_ERR(root);
226 goto error;
227 }
228 if (!root->d_inode) {
229 dput(root);
230 up_write(&s->s_umount);
231 deactivate_super(s);
232 error = -ENXIO;
233 goto error;
234 }
235 } else {
236 root = dget(s->s_root);
237 }
238
239 mnt->mnt_sb = s;
240 mnt->mnt_root = root;
241 return 0;
242
243error_s:
244 error = PTR_ERR(s);
245error_bdev:
246 close_bdev_excl(bdev);
247error:
248 return error;
249}
250/* end copy & paste */
251
148static int btrfs_get_sb(struct file_system_type *fs_type, 252static int btrfs_get_sb(struct file_system_type *fs_type,
149 int flags, const char *dev_name, void *data, struct vfsmount *mnt) 253 int flags, const char *identifier, void *data, struct vfsmount *mnt)
150{ 254{
151 return get_sb_bdev(fs_type, flags, dev_name, data, 255 int ret;
152 btrfs_fill_super, mnt); 256 char *_identifier = kstrdup(identifier, GFP_KERNEL);
257 char *subvol_name;
258 const char *dev_name;
259
260 subvol_name = _identifier;
261 dev_name = strsep(&subvol_name, ":");
262 if (!dev_name)
263 return -ENOMEM;
264
265 ret = btrfs_get_sb_bdev(fs_type, flags, dev_name, data,
266 btrfs_fill_super, mnt,
267 subvol_name ? subvol_name : "default");
268 kfree(_identifier);
269 return ret;
153} 270}
154 271
155static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) 272static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)