コード例 #1
0
ファイル: main.c プロジェクト: mrtos/Logitech-Revue
/*
 * 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;
}
コード例 #2
0
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;
}