Beispiel #1
0
static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
{
	int err;
	struct path workpath = { };

	err = ovl_mount_dir(ofs->config.workdir, &workpath);
	if (err)
		goto out;

	err = -EINVAL;
	if (upperpath->mnt != workpath.mnt) {
		pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
		goto out;
	}
	if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) {
		pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
		goto out;
	}

	ofs->workbasedir = dget(workpath.dentry);

	err = -EBUSY;
	if (ovl_inuse_trylock(ofs->workbasedir)) {
		ofs->workdir_locked = true;
	} else if (ofs->config.index) {
		pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
		goto out;
	} else {
		pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
	}

	err = ovl_make_workdir(ofs, &workpath);
	if (err)
		goto out;

	err = 0;
out:
	path_put(&workpath);

	return err;
}
Beispiel #2
0
static int ovl_fill_super(struct super_block *sb, void *data, int silent)
{
    struct path lowerpath;
    struct path upperpath;
    struct path workpath;
    struct inode *root_inode;
    struct dentry *root_dentry;
    struct ovl_entry *oe;
    struct ovl_fs *ufs;
    struct kstatfs statfs;
    int err;

    err = -ENOMEM;
    ufs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
    if (!ufs)
        goto out;

    err = ovl_parse_opt((char *) data, &ufs->config);
    if (err)
        goto out_free_config;

    /* FIXME: workdir is not needed for a R/O mount */
    err = -EINVAL;
    if (!ufs->config.upperdir || !ufs->config.lowerdir ||
            !ufs->config.workdir) {
        pr_err("overlayfs: missing upperdir or lowerdir or workdir\n");
        goto out_free_config;
    }

    err = -ENOMEM;
    oe = ovl_alloc_entry();
    if (oe == NULL)
        goto out_free_config;

    err = ovl_mount_dir(ufs->config.upperdir, &upperpath);
    if (err)
        goto out_free_oe;

    err = ovl_mount_dir(ufs->config.lowerdir, &lowerpath);
    if (err)
        goto out_put_upperpath;

    err = ovl_mount_dir(ufs->config.workdir, &workpath);
    if (err)
        goto out_put_lowerpath;

    err = -EINVAL;
    if (!S_ISDIR(upperpath.dentry->d_inode->i_mode) ||
            !S_ISDIR(lowerpath.dentry->d_inode->i_mode) ||
            !S_ISDIR(workpath.dentry->d_inode->i_mode)) {
        pr_err("overlayfs: upperdir or lowerdir or workdir not a directory\n");
        goto out_put_workpath;
    }

    if (upperpath.mnt != workpath.mnt) {
        pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
        goto out_put_workpath;
    }
    if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
        pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
        goto out_put_workpath;
    }

    if (!ovl_is_allowed_fs_type(upperpath.dentry)) {
        pr_err("overlayfs: filesystem of upperdir is not supported\n");
        goto out_put_workpath;
    }

    if (!ovl_is_allowed_fs_type(lowerpath.dentry)) {
        pr_err("overlayfs: filesystem of lowerdir is not supported\n");
        goto out_put_workpath;
    }

    err = vfs_statfs(&lowerpath, &statfs);
    if (err) {
        pr_err("overlayfs: statfs failed on lowerpath\n");
        goto out_put_workpath;
    }
    ufs->lower_namelen = statfs.f_namelen;

    sb->s_stack_depth = max(upperpath.mnt->mnt_sb->s_stack_depth,
                            lowerpath.mnt->mnt_sb->s_stack_depth) + 1;

    err = -EINVAL;
    if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
        pr_err("overlayfs: maximum fs stacking depth exceeded\n");
        goto out_put_workpath;
    }

    ufs->upper_mnt = clone_private_mount(&upperpath);
    err = PTR_ERR(ufs->upper_mnt);
    if (IS_ERR(ufs->upper_mnt)) {
        pr_err("overlayfs: failed to clone upperpath\n");
        goto out_put_workpath;
    }

    ufs->lower_mnt = clone_private_mount(&lowerpath);
    err = PTR_ERR(ufs->lower_mnt);
    if (IS_ERR(ufs->lower_mnt)) {
        pr_err("overlayfs: failed to clone lowerpath\n");
        goto out_put_upper_mnt;
    }

    ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
    err = PTR_ERR(ufs->workdir);
    if (IS_ERR(ufs->workdir)) {
        if (err == -ENOSPC || err == -EROFS) {
            pr_warning("overlayfs: failed to create work directory (%s), mounting read-only\n", err == ENOSPC ? "ENOSPC" : "EROFS");
            sb->s_flags |= MS_RDONLY;
            ufs->workdir = NULL;
        } else {
            pr_err("overlayfs: failed to create directory %s/%s\n",
                   ufs->config.workdir, OVL_WORKDIR_NAME);
            goto out_put_lower_mnt;
        }
    }

    /*
     * Make lower_mnt R/O.  That way fchmod/fchown on lower file
     * will fail instead of modifying lower fs.
     */
    ufs->lower_mnt->mnt_flags |= MNT_READONLY;

    /* If the upper fs is r/o, we mark overlayfs r/o too */
    if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY)
        sb->s_flags |= MS_RDONLY;

    sb->s_d_op = &ovl_dentry_operations;

    err = -ENOMEM;
    root_inode = ovl_new_inode(sb, S_IFDIR, oe);
    if (!root_inode)
        goto out_put_workdir;

    root_dentry = d_make_root(root_inode);
    if (!root_dentry)
        goto out_put_workdir;

    mntput(upperpath.mnt);
    mntput(lowerpath.mnt);
    path_put(&workpath);

    oe->__upperdentry = upperpath.dentry;
    oe->lowerdentry = lowerpath.dentry;

    root_dentry->d_fsdata = oe;

    sb->s_magic = OVERLAYFS_SUPER_MAGIC;
    sb->s_op = &ovl_super_operations;
    sb->s_root = root_dentry;
    sb->s_fs_info = ufs;

    return 0;

out_put_workdir:
    dput(ufs->workdir);
out_put_lower_mnt:
    mntput(ufs->lower_mnt);
out_put_upper_mnt:
    mntput(ufs->upper_mnt);
out_put_workpath:
    path_put(&workpath);
out_put_lowerpath:
    path_put(&lowerpath);
out_put_upperpath:
    path_put(&upperpath);
out_free_oe:
    kfree(oe);
out_free_config:
    kfree(ufs->config.lowerdir);
    kfree(ufs->config.upperdir);
    kfree(ufs->config.workdir);
    kfree(ufs);
out:
    return err;
}