diff options
author | Christoph Hellwig <hch@lst.de> | 2008-06-10 10:40:29 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:03 -0400 |
commit | edf24abe51493ccda384644d487fe2f796ac21c8 (patch) | |
tree | 91244ac3bb85fd08c22f109011cc4a625037677f /fs | |
parent | 306929f364b993581c91596230807fa1c022268a (diff) |
btrfs: sanity mount option parsing and early mount code
Also adds lots of comments to describe what's going on here.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ctree.h | 3 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 5 | ||||
-rw-r--r-- | fs/btrfs/super.c | 241 |
3 files changed, 141 insertions, 108 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 1dcf4fb5b688..49cbc62b42f9 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1616,7 +1616,6 @@ int btrfs_delete_xattrs(struct btrfs_trans_handle *trans, | |||
1616 | struct btrfs_root *root, struct inode *inode); | 1616 | struct btrfs_root *root, struct inode *inode); |
1617 | /* super.c */ | 1617 | /* super.c */ |
1618 | u64 btrfs_parse_size(char *str); | 1618 | u64 btrfs_parse_size(char *str); |
1619 | int btrfs_parse_options(char *options, struct btrfs_root *root, | 1619 | int btrfs_parse_options(struct btrfs_root *root, char *options); |
1620 | char **subvol_name); | ||
1621 | int btrfs_sync_fs(struct super_block *sb, int wait); | 1620 | int btrfs_sync_fs(struct super_block *sb, int wait); |
1622 | #endif | 1621 | #endif |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 3805e7eab82d..b9a53646ceb2 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -1266,8 +1266,11 @@ struct btrfs_root *open_ctree(struct super_block *sb, | |||
1266 | if (!btrfs_super_root(disk_super)) | 1266 | if (!btrfs_super_root(disk_super)) |
1267 | goto fail_sb_buffer; | 1267 | goto fail_sb_buffer; |
1268 | 1268 | ||
1269 | btrfs_parse_options(options, tree_root, NULL); | 1269 | err = btrfs_parse_options(tree_root, options); |
1270 | if (err) | ||
1271 | goto fail_sb_buffer; | ||
1270 | 1272 | ||
1273 | err = -EINVAL; | ||
1271 | if (btrfs_super_num_devices(disk_super) > fs_devices->open_devices) { | 1274 | if (btrfs_super_num_devices(disk_super) > fs_devices->open_devices) { |
1272 | printk("Btrfs: wanted %llu devices, but found %llu\n", | 1275 | printk("Btrfs: wanted %llu devices, but found %llu\n", |
1273 | (unsigned long long)btrfs_super_num_devices(disk_super), | 1276 | (unsigned long long)btrfs_super_num_devices(disk_super), |
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 39bb86945ed0..288300fa5848 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -108,15 +108,18 @@ u64 btrfs_parse_size(char *str) | |||
108 | return res; | 108 | return res; |
109 | } | 109 | } |
110 | 110 | ||
111 | int btrfs_parse_options(char *options, struct btrfs_root *root, | 111 | /* |
112 | char **subvol_name) | 112 | * Regular mount options parser. Everything that is needed only when |
113 | * reading in a new superblock is parsed here. | ||
114 | */ | ||
115 | int btrfs_parse_options(struct btrfs_root *root, char *options) | ||
113 | { | 116 | { |
114 | char * p; | 117 | struct btrfs_fs_info *info = root->fs_info; |
115 | struct btrfs_fs_info *info = NULL; | ||
116 | substring_t args[MAX_OPT_ARGS]; | 118 | substring_t args[MAX_OPT_ARGS]; |
119 | char *p, *num; | ||
117 | 120 | ||
118 | if (!options) | 121 | if (!options) |
119 | return 1; | 122 | return 0; |
120 | 123 | ||
121 | /* | 124 | /* |
122 | * strsep changes the string, duplicate it because parse_options | 125 | * strsep changes the string, duplicate it because parse_options |
@@ -126,10 +129,8 @@ int btrfs_parse_options(char *options, struct btrfs_root *root, | |||
126 | if (!options) | 129 | if (!options) |
127 | return -ENOMEM; | 130 | return -ENOMEM; |
128 | 131 | ||
129 | if (root) | ||
130 | info = root->fs_info; | ||
131 | 132 | ||
132 | while ((p = strsep (&options, ",")) != NULL) { | 133 | while ((p = strsep(&options, ",")) != NULL) { |
133 | int token; | 134 | int token; |
134 | if (!*p) | 135 | if (!*p) |
135 | continue; | 136 | continue; |
@@ -137,83 +138,64 @@ int btrfs_parse_options(char *options, struct btrfs_root *root, | |||
137 | token = match_token(p, tokens, args); | 138 | token = match_token(p, tokens, args); |
138 | switch (token) { | 139 | switch (token) { |
139 | case Opt_degraded: | 140 | case Opt_degraded: |
140 | if (info) { | 141 | printk(KERN_INFO "btrfs: allowing degraded mounts\n"); |
141 | printk("btrfs: allowing degraded mounts\n"); | 142 | btrfs_set_opt(info->mount_opt, DEGRADED); |
142 | btrfs_set_opt(info->mount_opt, DEGRADED); | ||
143 | } | ||
144 | break; | 143 | break; |
145 | case Opt_subvol: | 144 | case Opt_subvol: |
146 | if (subvol_name) { | 145 | /* |
147 | *subvol_name = match_strdup(&args[0]); | 146 | * This one is parsed by btrfs_parse_early_options |
148 | } | 147 | * and can be happily ignored here. |
148 | */ | ||
149 | break; | 149 | break; |
150 | case Opt_nodatasum: | 150 | case Opt_nodatasum: |
151 | if (info) { | 151 | printk(KERN_INFO "btrfs: setting nodatacsum\n"); |
152 | printk("btrfs: setting nodatacsum\n"); | 152 | btrfs_set_opt(info->mount_opt, NODATASUM); |
153 | btrfs_set_opt(info->mount_opt, NODATASUM); | ||
154 | } | ||
155 | break; | 153 | break; |
156 | case Opt_nodatacow: | 154 | case Opt_nodatacow: |
157 | if (info) { | 155 | printk(KERN_INFO "btrfs: setting nodatacow\n"); |
158 | printk("btrfs: setting nodatacow\n"); | 156 | btrfs_set_opt(info->mount_opt, NODATACOW); |
159 | btrfs_set_opt(info->mount_opt, NODATACOW); | 157 | btrfs_set_opt(info->mount_opt, NODATASUM); |
160 | btrfs_set_opt(info->mount_opt, NODATASUM); | ||
161 | } | ||
162 | break; | 158 | break; |
163 | case Opt_ssd: | 159 | case Opt_ssd: |
164 | if (info) { | 160 | printk(KERN_INFO "btrfs: use ssd allocation scheme\n"); |
165 | printk("btrfs: use ssd allocation scheme\n"); | 161 | btrfs_set_opt(info->mount_opt, SSD); |
166 | btrfs_set_opt(info->mount_opt, SSD); | ||
167 | } | ||
168 | break; | 162 | break; |
169 | case Opt_nobarrier: | 163 | case Opt_nobarrier: |
170 | if (info) { | 164 | printk(KERN_INFO "btrfs: turning off barriers\n"); |
171 | printk("btrfs: turning off barriers\n"); | 165 | btrfs_set_opt(info->mount_opt, NOBARRIER); |
172 | btrfs_set_opt(info->mount_opt, NOBARRIER); | ||
173 | } | ||
174 | break; | 166 | break; |
175 | case Opt_max_extent: | 167 | case Opt_max_extent: |
176 | if (info) { | 168 | num = match_strdup(&args[0]); |
177 | char *num = match_strdup(&args[0]); | 169 | if (num) { |
178 | if (num) { | 170 | info->max_extent = btrfs_parse_size(num); |
179 | info->max_extent = | 171 | kfree(num); |
180 | btrfs_parse_size(num); | 172 | |
181 | kfree(num); | 173 | info->max_extent = max_t(u64, |
182 | 174 | info->max_extent, root->sectorsize); | |
183 | info->max_extent = max_t(u64, | 175 | printk(KERN_INFO "btrfs: max_extent at %llu\n", |
184 | info->max_extent, | 176 | info->max_extent); |
185 | root->sectorsize); | ||
186 | printk("btrfs: max_extent at %Lu\n", | ||
187 | info->max_extent); | ||
188 | } | ||
189 | } | 177 | } |
190 | break; | 178 | break; |
191 | case Opt_max_inline: | 179 | case Opt_max_inline: |
192 | if (info) { | 180 | num = match_strdup(&args[0]); |
193 | char *num = match_strdup(&args[0]); | 181 | if (num) { |
194 | if (num) { | 182 | info->max_inline = btrfs_parse_size(num); |
195 | info->max_inline = | 183 | kfree(num); |
196 | btrfs_parse_size(num); | 184 | |
197 | kfree(num); | 185 | info->max_inline = max_t(u64, |
198 | 186 | info->max_inline, root->sectorsize); | |
199 | info->max_inline = max_t(u64, | 187 | printk(KERN_INFO "btrfs: max_inline at %llu\n", |
200 | info->max_inline, | 188 | info->max_inline); |
201 | root->sectorsize); | ||
202 | printk("btrfs: max_inline at %Lu\n", | ||
203 | info->max_inline); | ||
204 | } | ||
205 | } | 189 | } |
206 | break; | 190 | break; |
207 | case Opt_alloc_start: | 191 | case Opt_alloc_start: |
208 | if (info) { | 192 | num = match_strdup(&args[0]); |
209 | char *num = match_strdup(&args[0]); | 193 | if (num) { |
210 | if (num) { | 194 | info->alloc_start = btrfs_parse_size(num); |
211 | info->alloc_start = | 195 | kfree(num); |
212 | btrfs_parse_size(num); | 196 | printk(KERN_INFO |
213 | kfree(num); | 197 | "btrfs: allocations start at %llu\n", |
214 | printk("btrfs: allocations start at " | 198 | info->alloc_start); |
215 | "%Lu\n", info->alloc_start); | ||
216 | } | ||
217 | } | 199 | } |
218 | break; | 200 | break; |
219 | default: | 201 | default: |
@@ -221,7 +203,61 @@ int btrfs_parse_options(char *options, struct btrfs_root *root, | |||
221 | } | 203 | } |
222 | } | 204 | } |
223 | kfree(options); | 205 | kfree(options); |
224 | return 1; | 206 | return 0; |
207 | } | ||
208 | |||
209 | /* | ||
210 | * Parse mount options that are required early in the mount process. | ||
211 | * | ||
212 | * All other options will be parsed on much later in the mount process and | ||
213 | * only when we need to allocate a new super block. | ||
214 | */ | ||
215 | static int btrfs_parse_early_options(const char *options, | ||
216 | char **subvol_name) | ||
217 | { | ||
218 | substring_t args[MAX_OPT_ARGS]; | ||
219 | char *opts, *p; | ||
220 | int error = 0; | ||
221 | |||
222 | if (!options) | ||
223 | goto out; | ||
224 | |||
225 | /* | ||
226 | * strsep changes the string, duplicate it because parse_options | ||
227 | * gets called twice | ||
228 | */ | ||
229 | opts = kstrdup(options, GFP_KERNEL); | ||
230 | if (!opts) | ||
231 | return -ENOMEM; | ||
232 | |||
233 | while ((p = strsep(&opts, ",")) != NULL) { | ||
234 | int token; | ||
235 | if (!*p) | ||
236 | continue; | ||
237 | |||
238 | token = match_token(p, tokens, args); | ||
239 | switch (token) { | ||
240 | case Opt_subvol: | ||
241 | *subvol_name = match_strdup(&args[0]); | ||
242 | break; | ||
243 | default: | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | kfree(opts); | ||
249 | out: | ||
250 | /* | ||
251 | * If no subvolume name is specified we use the default one. Allocate | ||
252 | * a copy of the string "default" here so that code later in the | ||
253 | * mount path doesn't care if it's the default volume or another one. | ||
254 | */ | ||
255 | if (!*subvol_name) { | ||
256 | *subvol_name = kstrdup("default", GFP_KERNEL); | ||
257 | if (!*subvol_name) | ||
258 | return -ENOMEM; | ||
259 | } | ||
260 | return error; | ||
225 | } | 261 | } |
226 | 262 | ||
227 | static int btrfs_fill_super(struct super_block * sb, | 263 | static int btrfs_fill_super(struct super_block * sb, |
@@ -328,23 +364,33 @@ static int btrfs_test_super(struct super_block *s, void *data) | |||
328 | return root->fs_info->fs_devices == test_fs_devices; | 364 | return root->fs_info->fs_devices == test_fs_devices; |
329 | } | 365 | } |
330 | 366 | ||
331 | int btrfs_get_sb_bdev(struct file_system_type *fs_type, | 367 | /* |
332 | int flags, const char *dev_name, void *data, | 368 | * Find a superblock for the given device / mount point. |
333 | struct vfsmount *mnt, const char *subvol) | 369 | * |
370 | * Note: This is based on get_sb_bdev from fs/super.c with a few additions | ||
371 | * for multiple device setup. Make sure to keep it in sync. | ||
372 | */ | ||
373 | static int btrfs_get_sb(struct file_system_type *fs_type, int flags, | ||
374 | const char *dev_name, void *data, struct vfsmount *mnt) | ||
334 | { | 375 | { |
376 | char *subvol_name = NULL; | ||
335 | struct block_device *bdev = NULL; | 377 | struct block_device *bdev = NULL; |
336 | struct super_block *s; | 378 | struct super_block *s; |
337 | struct dentry *root; | 379 | struct dentry *root; |
338 | struct btrfs_fs_devices *fs_devices = NULL; | 380 | struct btrfs_fs_devices *fs_devices = NULL; |
339 | int error = 0; | 381 | int error = 0; |
340 | 382 | ||
383 | error = btrfs_parse_early_options(data, &subvol_name); | ||
384 | if (error) | ||
385 | goto error; | ||
386 | |||
341 | error = btrfs_scan_one_device(dev_name, flags, fs_type, &fs_devices); | 387 | error = btrfs_scan_one_device(dev_name, flags, fs_type, &fs_devices); |
342 | if (error) | 388 | if (error) |
343 | return error; | 389 | goto error_free_subvol_name; |
344 | 390 | ||
345 | error = btrfs_open_devices(fs_devices, flags, fs_type); | 391 | error = btrfs_open_devices(fs_devices, flags, fs_type); |
346 | if (error) | 392 | if (error) |
347 | return error; | 393 | goto error_free_subvol_name; |
348 | 394 | ||
349 | bdev = fs_devices->latest_bdev; | 395 | bdev = fs_devices->latest_bdev; |
350 | btrfs_lock_volumes(); | 396 | btrfs_lock_volumes(); |
@@ -378,51 +424,36 @@ int btrfs_get_sb_bdev(struct file_system_type *fs_type, | |||
378 | s->s_flags |= MS_ACTIVE; | 424 | s->s_flags |= MS_ACTIVE; |
379 | } | 425 | } |
380 | 426 | ||
381 | if (subvol) { | 427 | root = lookup_one_len(subvol_name, s->s_root, strlen(subvol_name)); |
382 | root = lookup_one_len(subvol, s->s_root, strlen(subvol)); | 428 | if (IS_ERR(root)) { |
383 | if (IS_ERR(root)) { | 429 | up_write(&s->s_umount); |
384 | up_write(&s->s_umount); | 430 | deactivate_super(s); |
385 | deactivate_super(s); | 431 | error = PTR_ERR(root); |
386 | error = PTR_ERR(root); | 432 | goto error; |
387 | goto error; | 433 | } |
388 | } | 434 | if (!root->d_inode) { |
389 | if (!root->d_inode) { | 435 | dput(root); |
390 | dput(root); | 436 | up_write(&s->s_umount); |
391 | up_write(&s->s_umount); | 437 | deactivate_super(s); |
392 | deactivate_super(s); | 438 | error = -ENXIO; |
393 | error = -ENXIO; | 439 | goto error; |
394 | goto error; | ||
395 | } | ||
396 | } else { | ||
397 | root = dget(s->s_root); | ||
398 | } | 440 | } |
399 | 441 | ||
400 | mnt->mnt_sb = s; | 442 | mnt->mnt_sb = s; |
401 | mnt->mnt_root = root; | 443 | mnt->mnt_root = root; |
444 | |||
445 | kfree(subvol_name); | ||
402 | return 0; | 446 | return 0; |
403 | 447 | ||
404 | error_s: | 448 | error_s: |
405 | error = PTR_ERR(s); | 449 | error = PTR_ERR(s); |
406 | error_bdev: | 450 | error_bdev: |
407 | btrfs_close_devices(fs_devices); | 451 | btrfs_close_devices(fs_devices); |
452 | error_free_subvol_name: | ||
453 | kfree(subvol_name); | ||
408 | error: | 454 | error: |
409 | return error; | 455 | return error; |
410 | } | 456 | } |
411 | /* end copy & paste */ | ||
412 | |||
413 | static int btrfs_get_sb(struct file_system_type *fs_type, | ||
414 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) | ||
415 | { | ||
416 | int ret; | ||
417 | char *subvol_name = NULL; | ||
418 | |||
419 | btrfs_parse_options((char *)data, NULL, &subvol_name); | ||
420 | ret = btrfs_get_sb_bdev(fs_type, flags, dev_name, data, mnt, | ||
421 | subvol_name ? subvol_name : "default"); | ||
422 | if (subvol_name) | ||
423 | kfree(subvol_name); | ||
424 | return ret; | ||
425 | } | ||
426 | 457 | ||
427 | static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) | 458 | static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) |
428 | { | 459 | { |