diff options
-rw-r--r-- | fs/btrfs/super.c | 123 |
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 */ | ||
158 | static 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 | |||
165 | static int test_bdev_super(struct super_block *s, void *data) | ||
166 | { | ||
167 | return (void *)s->s_bdev == data; | ||
168 | } | ||
169 | |||
170 | int 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 | |||
243 | error_s: | ||
244 | error = PTR_ERR(s); | ||
245 | error_bdev: | ||
246 | close_bdev_excl(bdev); | ||
247 | error: | ||
248 | return error; | ||
249 | } | ||
250 | /* end copy & paste */ | ||
251 | |||
148 | static int btrfs_get_sb(struct file_system_type *fs_type, | 252 | static 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 | ||
155 | static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) | 272 | static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) |