/* * parse the dirs= mount argument * * We don't need to lock the superblock private data's rwsem, as we get * called only by unionfs_read_super - it is still a long time before anyone * can even get a reference to us. */ static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info *lower_root_info, char *options) { struct nameidata nd; char *name; int err = 0; int branches = 1; int bindex = 0; int i = 0; int j = 0; struct dentry *dent1; struct dentry *dent2; if (options[0] == '\0') { printk(KERN_ERR "unionfs: no branches specified\n"); err = -EINVAL; goto out; } /* * Each colon means we have a separator, this is really just a rough * guess, since strsep will handle empty fields for us. */ for (i = 0; options[i]; i++) if (options[i] == ':') branches++; /* allocate space for underlying pointers to lower dentry */ UNIONFS_SB(sb)->data = kcalloc(branches, sizeof(struct unionfs_data), GFP_KERNEL); if (unlikely(!UNIONFS_SB(sb)->data)) { err = -ENOMEM; goto out; } lower_root_info->lower_paths = kcalloc(branches, sizeof(struct path), GFP_KERNEL); if (unlikely(!lower_root_info->lower_paths)) { err = -ENOMEM; goto out; } /* now parsing a string such as "b1:b2=rw:b3=ro:b4" */ branches = 0; while ((name = strsep(&options, ":")) != NULL) { int perms; char *mode = strchr(name, '='); if (!name) continue; if (!*name) { /* bad use of ':' (extra colons) */ err = -EINVAL; goto out; } branches++; /* strip off '=' if any */ if (mode) *mode++ = '\0'; err = parse_branch_mode(mode, &perms); if (err) { printk(KERN_ERR "unionfs: invalid mode \"%s\" for " "branch %d\n", mode, bindex); goto out; } /* ensure that leftmost branch is writeable */ if (!bindex && !(perms & MAY_WRITE)) { printk(KERN_ERR "unionfs: leftmost branch cannot be " "read-only (use \"-o ro\" to create a " "read-only union)\n"); err = -EINVAL; goto out; } err = path_lookup(name, LOOKUP_FOLLOW, &nd); if (err) { printk(KERN_ERR "unionfs: error accessing " "lower directory '%s' (error %d)\n", name, err); goto out; } err = check_branch(&nd); if (err) { printk(KERN_ERR "unionfs: lower directory " "'%s' is not a valid branch\n", name); path_release(&nd); goto out; } lower_root_info->lower_paths[bindex].dentry = nd.dentry; lower_root_info->lower_paths[bindex].mnt = nd.mnt; set_branchperms(sb, bindex, perms); set_branch_count(sb, bindex, 0); new_branch_id(sb, bindex); if (lower_root_info->bstart < 0) lower_root_info->bstart = bindex; lower_root_info->bend = bindex; bindex++; } if (branches == 0) { printk(KERN_ERR "unionfs: no branches specified\n"); err = -EINVAL; goto out; } BUG_ON(branches != (lower_root_info->bend + 1)); /* * Ensure that no overlaps exist in the branches. * * This test is required because the Linux kernel has no support * currently for ensuring coherency between stackable layers and * branches. If we were to allow overlapping branches, it would be * possible, for example, to delete a file via one branch, which * would not be reflected in another branch. Such incoherency could * lead to inconsistencies and even kernel oopses. Rather than * implement hacks to work around some of these cache-coherency * problems, we prevent branch overlapping, for now. A complete * solution will involve proper kernel/VFS support for cache * coherency, at which time we could safely remove this * branch-overlapping test. */ for (i = 0; i < branches; i++) { dent1 = lower_root_info->lower_paths[i].dentry; for (j = i + 1; j < branches; j++) { dent2 = lower_root_info->lower_paths[j].dentry; if (is_branch_overlap(dent1, dent2)) { printk(KERN_ERR "unionfs: branches %d and " "%d overlap\n", i, j); err = -EINVAL; goto out; } } } out: if (err) { for (i = 0; i < branches; i++) if (lower_root_info->lower_paths[i].dentry) { dput(lower_root_info->lower_paths[i].dentry); /* initialize: can't use unionfs_mntput here */ mntput(lower_root_info->lower_paths[i].mnt); } kfree(lower_root_info->lower_paths); kfree(UNIONFS_SB(sb)->data); /* * MUST clear the pointers to prevent potential double free if * the caller dies later on */ lower_root_info->lower_paths = NULL; UNIONFS_SB(sb)->data = NULL; } return err; }
static struct wrapfs_dentry_info *wrapfs_parse_options(struct super_block *sb, char *options) { struct wrapfs_dentry_info *lower_root_info; struct path path; struct dentry *dent1, *dent2; char *optname; int err = 0, branches = 2; int dirsfound = 0; int i = 0; bool is_ldir_present = false; bool is_rdir_present = false; lower_root_info = kzalloc(sizeof(struct wrapfs_dentry_info), GFP_KERNEL); if (unlikely(!lower_root_info)) goto out_error; WRAPFS_SB(sb)->data = kcalloc(branches, sizeof(struct wrapfs_data), GFP_KERNEL); if (unlikely(!WRAPFS_SB(sb)->data)) { err = -ENOMEM; goto out_return; } lower_root_info->lower_paths = kcalloc(branches, sizeof(struct path), GFP_KERNEL); if (unlikely(!lower_root_info->lower_paths)) { err = -ENOMEM; kfree(WRAPFS_SB(sb)->data); WRAPFS_SB(sb)->data = NULL; goto out_return; } while ((optname = strsep(&options, ",")) != NULL) { char *optarg; if (!optname || !*optname) continue; optarg = strchr(optname, '='); if (optarg) *optarg++ = '\0'; if (!optarg) { printk(KERN_ERR "u2fs: %s requires an argument\n", optname); err = -EINVAL; goto out_error; } if (!strcmp("ldir", optname)) { if (++dirsfound > 1) { printk(KERN_ERR "u2fs: multiple ldirs specified\n"); err = -EINVAL; goto out_error; } err = kern_path(optarg, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); if (err) { printk(KERN_ERR "u2fs: error accessing " "lower directory '%s'\n", optarg); goto out_error; } err = check_branch(&path); if (err) { printk(KERN_ERR "u2fs: lower directory " "'%s' is not a valid branch\n", optarg); path_put(&path); goto out_error; } lower_root_info->lower_paths[0].dentry = path.dentry; lower_root_info->lower_paths[0].mnt = path.mnt; WRAPFS_SB(sb)->data[0].branchperms = MAY_READ|MAY_WRITE; set_branch_count(sb, 0, 0); new_branch_id(sb, 0); is_ldir_present = true; continue; } if (!strcmp("rdir", optname)) { if (!is_ldir_present) printk(KERN_ERR "u2fs: ldir not specified\n"); if (++dirsfound > 2) { printk(KERN_ERR "u2fs: multiple rdirs specified\n"); err = -EINVAL; goto out_error; } err = kern_path(optarg, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); if (err) { printk(KERN_ERR "u2fs: error accessing " "lower directory '%s'\n", optarg); goto out_error; } lower_root_info->lower_paths[1].dentry = path.dentry; lower_root_info->lower_paths[1].mnt = path.mnt; WRAPFS_SB(sb)->data[1].branchperms = MAY_READ; set_branch_count(sb, 1, 0); new_branch_id(sb, 1); is_rdir_present = true; continue; } err = -EINVAL; printk(KERN_ERR "u2fs: unrecognized options '%s'\n", optname); goto out_error; } if (is_ldir_present && is_rdir_present) { /* Ensuring no overlaps */ dent1 = lower_root_info->lower_paths[0].dentry; dent2 = lower_root_info->lower_paths[1].dentry; if (is_branch_overlap(dent1, dent2)) { printk(KERN_ERR "u2fs:" "branches ldir and rdir overlap\n"); err = -EINVAL; goto out_error; } } else { printk(KERN_ERR "u2fs: no branches specified\n"); err = -EINVAL; goto out_error; } goto out_return; out_error: if (lower_root_info && lower_root_info->lower_paths) { for (i = 0; i < branches; i++) path_put(&lower_root_info->lower_paths[i]); kfree(lower_root_info->lower_paths); kfree(lower_root_info); kfree(WRAPFS_SB(sb)->data); lower_root_info->lower_paths = NULL; WRAPFS_SB(sb)->data = NULL; lower_root_info = ERR_PTR(err); } out_return: return lower_root_info; }