diff options
author | Erez Zadok <ezk@fsl.cs.sunysb.edu> | 2014-10-23 18:14:38 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-10-23 18:14:38 -0400 |
commit | f45827e84186af152492c6d0dcf4105b4a605f9b (patch) | |
tree | c202fa1dc66dec8c0cacd98d38492026a0359965 /fs | |
parent | cc2596392af3b1404421aaef828a255303c46f93 (diff) |
overlayfs: implement show_options
This is useful because of the stacking nature of overlayfs. Users like to
find out (via /proc/mounts) which lower/upper directory were used at mount
time.
AV: even failing ovl_parse_opt() could've done some kstrdup()
AV: failure of ovl_alloc_entry() should end up with ENOMEM, not EINVAL
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/overlayfs/super.c | 76 |
1 files changed, 48 insertions, 28 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index aaf562b9f937..7dcc24e84417 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
19 | #include <linux/statfs.h> | 19 | #include <linux/statfs.h> |
20 | #include <linux/seq_file.h> | ||
20 | #include "overlayfs.h" | 21 | #include "overlayfs.h" |
21 | 22 | ||
22 | MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); | 23 | MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); |
@@ -25,12 +26,20 @@ MODULE_LICENSE("GPL"); | |||
25 | 26 | ||
26 | #define OVERLAYFS_SUPER_MAGIC 0x794c764f | 27 | #define OVERLAYFS_SUPER_MAGIC 0x794c764f |
27 | 28 | ||
29 | struct ovl_config { | ||
30 | char *lowerdir; | ||
31 | char *upperdir; | ||
32 | char *workdir; | ||
33 | }; | ||
34 | |||
28 | /* private information held for overlayfs's superblock */ | 35 | /* private information held for overlayfs's superblock */ |
29 | struct ovl_fs { | 36 | struct ovl_fs { |
30 | struct vfsmount *upper_mnt; | 37 | struct vfsmount *upper_mnt; |
31 | struct vfsmount *lower_mnt; | 38 | struct vfsmount *lower_mnt; |
32 | struct dentry *workdir; | 39 | struct dentry *workdir; |
33 | long lower_namelen; | 40 | long lower_namelen; |
41 | /* pathnames of lower and upper dirs, for show_options */ | ||
42 | struct ovl_config config; | ||
34 | }; | 43 | }; |
35 | 44 | ||
36 | struct ovl_dir_cache; | 45 | struct ovl_dir_cache; |
@@ -384,6 +393,9 @@ static void ovl_put_super(struct super_block *sb) | |||
384 | mntput(ufs->upper_mnt); | 393 | mntput(ufs->upper_mnt); |
385 | mntput(ufs->lower_mnt); | 394 | mntput(ufs->lower_mnt); |
386 | 395 | ||
396 | kfree(ufs->config.lowerdir); | ||
397 | kfree(ufs->config.upperdir); | ||
398 | kfree(ufs->config.workdir); | ||
387 | kfree(ufs); | 399 | kfree(ufs); |
388 | } | 400 | } |
389 | 401 | ||
@@ -413,15 +425,27 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
413 | return err; | 425 | return err; |
414 | } | 426 | } |
415 | 427 | ||
428 | /** | ||
429 | * ovl_show_options | ||
430 | * | ||
431 | * Prints the mount options for a given superblock. | ||
432 | * Returns zero; does not fail. | ||
433 | */ | ||
434 | static int ovl_show_options(struct seq_file *m, struct dentry *dentry) | ||
435 | { | ||
436 | struct super_block *sb = dentry->d_sb; | ||
437 | struct ovl_fs *ufs = sb->s_fs_info; | ||
438 | |||
439 | seq_printf(m, ",lowerdir=%s", ufs->config.lowerdir); | ||
440 | seq_printf(m, ",upperdir=%s", ufs->config.upperdir); | ||
441 | seq_printf(m, ",workdir=%s", ufs->config.workdir); | ||
442 | return 0; | ||
443 | } | ||
444 | |||
416 | static const struct super_operations ovl_super_operations = { | 445 | static const struct super_operations ovl_super_operations = { |
417 | .put_super = ovl_put_super, | 446 | .put_super = ovl_put_super, |
418 | .statfs = ovl_statfs, | 447 | .statfs = ovl_statfs, |
419 | }; | 448 | .show_options = ovl_show_options, |
420 | |||
421 | struct ovl_config { | ||
422 | char *lowerdir; | ||
423 | char *upperdir; | ||
424 | char *workdir; | ||
425 | }; | 449 | }; |
426 | 450 | ||
427 | enum { | 451 | enum { |
@@ -442,10 +466,6 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) | |||
442 | { | 466 | { |
443 | char *p; | 467 | char *p; |
444 | 468 | ||
445 | config->upperdir = NULL; | ||
446 | config->lowerdir = NULL; | ||
447 | config->workdir = NULL; | ||
448 | |||
449 | while ((p = strsep(&opt, ",")) != NULL) { | 469 | while ((p = strsep(&opt, ",")) != NULL) { |
450 | int token; | 470 | int token; |
451 | substring_t args[MAX_OPT_ARGS]; | 471 | substring_t args[MAX_OPT_ARGS]; |
@@ -586,39 +606,40 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
586 | struct dentry *root_dentry; | 606 | struct dentry *root_dentry; |
587 | struct ovl_entry *oe; | 607 | struct ovl_entry *oe; |
588 | struct ovl_fs *ufs; | 608 | struct ovl_fs *ufs; |
589 | struct ovl_config config; | ||
590 | struct kstatfs statfs; | 609 | struct kstatfs statfs; |
591 | int err; | 610 | int err; |
592 | 611 | ||
593 | err = ovl_parse_opt((char *) data, &config); | 612 | err = -ENOMEM; |
594 | if (err) | 613 | ufs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); |
614 | if (!ufs) | ||
595 | goto out; | 615 | goto out; |
596 | 616 | ||
617 | err = ovl_parse_opt((char *) data, &ufs->config); | ||
618 | if (err) | ||
619 | goto out_free_config; | ||
620 | |||
597 | /* FIXME: workdir is not needed for a R/O mount */ | 621 | /* FIXME: workdir is not needed for a R/O mount */ |
598 | err = -EINVAL; | 622 | err = -EINVAL; |
599 | if (!config.upperdir || !config.lowerdir || !config.workdir) { | 623 | if (!ufs->config.upperdir || !ufs->config.lowerdir || |
624 | !ufs->config.workdir) { | ||
600 | pr_err("overlayfs: missing upperdir or lowerdir or workdir\n"); | 625 | pr_err("overlayfs: missing upperdir or lowerdir or workdir\n"); |
601 | goto out_free_config; | 626 | goto out_free_config; |
602 | } | 627 | } |
603 | 628 | ||
604 | err = -ENOMEM; | 629 | err = -ENOMEM; |
605 | ufs = kmalloc(sizeof(struct ovl_fs), GFP_KERNEL); | ||
606 | if (!ufs) | ||
607 | goto out_free_config; | ||
608 | |||
609 | oe = ovl_alloc_entry(); | 630 | oe = ovl_alloc_entry(); |
610 | if (oe == NULL) | 631 | if (oe == NULL) |
611 | goto out_free_ufs; | 632 | goto out_free_config; |
612 | 633 | ||
613 | err = ovl_mount_dir(config.upperdir, &upperpath); | 634 | err = ovl_mount_dir(ufs->config.upperdir, &upperpath); |
614 | if (err) | 635 | if (err) |
615 | goto out_free_oe; | 636 | goto out_free_oe; |
616 | 637 | ||
617 | err = ovl_mount_dir(config.lowerdir, &lowerpath); | 638 | err = ovl_mount_dir(ufs->config.lowerdir, &lowerpath); |
618 | if (err) | 639 | if (err) |
619 | goto out_put_upperpath; | 640 | goto out_put_upperpath; |
620 | 641 | ||
621 | err = ovl_mount_dir(config.workdir, &workpath); | 642 | err = ovl_mount_dir(ufs->config.workdir, &workpath); |
622 | if (err) | 643 | if (err) |
623 | goto out_put_lowerpath; | 644 | goto out_put_lowerpath; |
624 | 645 | ||
@@ -674,7 +695,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
674 | err = PTR_ERR(ufs->workdir); | 695 | err = PTR_ERR(ufs->workdir); |
675 | if (IS_ERR(ufs->workdir)) { | 696 | if (IS_ERR(ufs->workdir)) { |
676 | pr_err("overlayfs: failed to create directory %s/%s\n", | 697 | pr_err("overlayfs: failed to create directory %s/%s\n", |
677 | config.workdir, OVL_WORKDIR_NAME); | 698 | ufs->config.workdir, OVL_WORKDIR_NAME); |
678 | goto out_put_lower_mnt; | 699 | goto out_put_lower_mnt; |
679 | } | 700 | } |
680 | 701 | ||
@@ -729,12 +750,11 @@ out_put_upperpath: | |||
729 | path_put(&upperpath); | 750 | path_put(&upperpath); |
730 | out_free_oe: | 751 | out_free_oe: |
731 | kfree(oe); | 752 | kfree(oe); |
732 | out_free_ufs: | ||
733 | kfree(ufs); | ||
734 | out_free_config: | 753 | out_free_config: |
735 | kfree(config.lowerdir); | 754 | kfree(ufs->config.lowerdir); |
736 | kfree(config.upperdir); | 755 | kfree(ufs->config.upperdir); |
737 | kfree(config.workdir); | 756 | kfree(ufs->config.workdir); |
757 | kfree(ufs); | ||
738 | out: | 758 | out: |
739 | return err; | 759 | return err; |
740 | } | 760 | } |