Esempio n. 1
0
/*
 * our custom d_alloc_root work-alike
 *
 * we can't use d_alloc_root if we want to use our own interpose function
 * unchanged, so we simply call our own "fake" d_alloc_root
 */
static struct dentry *unionfs_d_alloc_root(struct super_block *sb)
{
    struct dentry *ret = NULL;

    if (sb) {
        static const struct qstr name = {
            .name = "/",
            .len = 1
        };

        ret = d_alloc(NULL, &name);
        if (likely(ret)) {
            ret->d_op = &unionfs_dops;
            ret->d_sb = sb;
            ret->d_parent = ret;
        }
    }
    return ret;
}

/*
 * There is no need to lock the unionfs_super_info's rwsem as there is no
 * way anyone can have a reference to the superblock at this point in time.
 */
static int unionfs_read_super(struct super_block *sb, void *raw_data,
                              int silent)
{
    int err = 0;
    struct unionfs_dentry_info *lower_root_info = NULL;
    int bindex, bstart, bend;

    if (!raw_data) {
        printk(KERN_ERR
               "unionfs: read_super: missing data argument\n");
        err = -EINVAL;
        goto out;
    }

    /* Allocate superblock private data */
    sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL);
    if (unlikely(!UNIONFS_SB(sb))) {
        printk(KERN_CRIT "unionfs: read_super: out of memory\n");
        err = -ENOMEM;
        goto out;
    }

    UNIONFS_SB(sb)->bend = -1;
    atomic_set(&UNIONFS_SB(sb)->generation, 1);
    init_rwsem(&UNIONFS_SB(sb)->rwsem);
    UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */

    lower_root_info = unionfs_parse_options(sb, raw_data);
    if (IS_ERR(lower_root_info)) {
        printk(KERN_ERR
               "unionfs: read_super: error while parsing options "
               "(err = %ld)\n", PTR_ERR(lower_root_info));
        err = PTR_ERR(lower_root_info);
        lower_root_info = NULL;
        goto out_free;
    }
    if (lower_root_info->bstart == -1) {
        err = -ENOENT;
        goto out_free;
    }

    /* set the lower superblock field of upper superblock */
    bstart = lower_root_info->bstart;
    BUG_ON(bstart != 0);
    sbend(sb) = bend = lower_root_info->bend;
    for (bindex = bstart; bindex <= bend; bindex++) {
        struct dentry *d = lower_root_info->lower_paths[bindex].dentry;
        atomic_inc(&d->d_sb->s_active);
        unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
    }

    /* max Bytes is the maximum bytes from highest priority branch */
    sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;

    /*
     * Our c/m/atime granularity is 1 ns because we may stack on file
     * systems whose granularity is as good.  This is important for our
     * time-based cache coherency.
     */
    sb->s_time_gran = 1;

    sb->s_op = &unionfs_sops;

    /* See comment next to the definition of unionfs_d_alloc_root */
    sb->s_root = unionfs_d_alloc_root(sb);
    if (unlikely(!sb->s_root)) {
        err = -ENOMEM;
        goto out_dput;
    }

    /* link the upper and lower dentries */
    sb->s_root->d_fsdata = NULL;
    err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);
    if (unlikely(err))
        goto out_freedpd;

    /* Set the lower dentries for s_root */
    for (bindex = bstart; bindex <= bend; bindex++) {
        struct dentry *d;
        struct vfsmount *m;

        d = lower_root_info->lower_paths[bindex].dentry;
        m = lower_root_info->lower_paths[bindex].mnt;

        unionfs_set_lower_dentry_idx(sb->s_root, bindex, d);
        unionfs_set_lower_mnt_idx(sb->s_root, bindex, m);
    }
    dbstart(sb->s_root) = bstart;
    dbend(sb->s_root) = bend;

    /* Set the generation number to one, since this is for the mount. */
    atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);

    /*
     * Call interpose to create the upper level inode.  Only
     * INTERPOSE_LOOKUP can return a value other than 0 on err.
     */
    err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0));
    unionfs_unlock_dentry(sb->s_root);
    if (!err)
        goto out;
    /* else fall through */

out_freedpd:
    if (UNIONFS_D(sb->s_root)) {
        kfree(UNIONFS_D(sb->s_root)->lower_paths);
        free_dentry_private_data(sb->s_root);
    }
    dput(sb->s_root);

out_dput:
    if (lower_root_info && !IS_ERR(lower_root_info)) {
        for (bindex = lower_root_info->bstart;
                bindex <= lower_root_info->bend; bindex++) {
            struct dentry *d;
            struct vfsmount *m;

            d = lower_root_info->lower_paths[bindex].dentry;
            m = lower_root_info->lower_paths[bindex].mnt;

            dput(d);
            /* initializing: can't use unionfs_mntput here */
            mntput(m);
            /* drop refs we took earlier */
            atomic_dec(&d->d_sb->s_active);
        }
        kfree(lower_root_info->lower_paths);
        kfree(lower_root_info);
        lower_root_info = NULL;
    }

out_free:
    kfree(UNIONFS_SB(sb)->data);
    kfree(UNIONFS_SB(sb));
    sb->s_fs_info = NULL;

out:
    if (lower_root_info && !IS_ERR(lower_root_info)) {
        kfree(lower_root_info->lower_paths);
        kfree(lower_root_info);
    }
    return err;
}
Esempio n. 2
0
/*
 * There is no need to lock the unionfs_super_info's rwsem as there is no
 * way anyone can have a reference to the superblock at this point in time.
 */
static int unionfs_read_super(struct super_block *sb, void *raw_data,
			      int silent)
{
	int err = 0;
	struct unionfs_dentry_info *lower_root_info = NULL;
	int bindex, bstart, bend;
	struct inode *inode = NULL;

	if (!raw_data) {
		printk(KERN_ERR
		       "unionfs: read_super: missing data argument\n");
		err = -EINVAL;
		goto out;
	}

	/* Allocate superblock private data */
	sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL);
	if (unlikely(!UNIONFS_SB(sb))) {
		printk(KERN_CRIT "unionfs: read_super: out of memory\n");
		err = -ENOMEM;
		goto out;
	}

	UNIONFS_SB(sb)->bend = -1;
	atomic_set(&UNIONFS_SB(sb)->generation, 1);
	init_rwsem(&UNIONFS_SB(sb)->rwsem);
	UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */

	lower_root_info = unionfs_parse_options(sb, raw_data);
	if (IS_ERR(lower_root_info)) {
		printk(KERN_ERR
		       "unionfs: read_super: error while parsing options "
		       "(err = %ld)\n", PTR_ERR(lower_root_info));
		err = PTR_ERR(lower_root_info);
		lower_root_info = NULL;
		goto out_free;
	}
	if (lower_root_info->bstart == -1) {
		err = -ENOENT;
		goto out_free;
	}

	/* set the lower superblock field of upper superblock */
	bstart = lower_root_info->bstart;
	BUG_ON(bstart != 0);
	sbend(sb) = bend = lower_root_info->bend;
	for (bindex = bstart; bindex <= bend; bindex++) {
		struct dentry *d = lower_root_info->lower_paths[bindex].dentry;
		atomic_inc(&d->d_sb->s_active);
		unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
	}

	/* max Bytes is the maximum bytes from highest priority branch */
	sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;

	/*
	 * Our c/m/atime granularity is 1 ns because we may stack on file
	 * systems whose granularity is as good.  This is important for our
	 * time-based cache coherency.
	 */
	sb->s_time_gran = 1;

	sb->s_op = &unionfs_sops;

	/* get a new inode and allocate our root dentry */
	inode = unionfs_iget(sb, iunique(sb, UNIONFS_ROOT_INO));
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_dput;
	}
	sb->s_root = d_make_root(inode);
	if (unlikely(!sb->s_root)) {
		err = -ENOMEM;
		goto out_iput;
	}
	d_set_d_op(sb->s_root, &unionfs_dops);

	/* link the upper and lower dentries */
	sb->s_root->d_fsdata = NULL;
	err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);
	if (unlikely(err))
		goto out_freedpd;

	/* if get here: cannot have error */

	/* Set the lower dentries for s_root */
	for (bindex = bstart; bindex <= bend; bindex++) {
		struct dentry *d;
		struct vfsmount *m;

		d = lower_root_info->lower_paths[bindex].dentry;
		m = lower_root_info->lower_paths[bindex].mnt;

		unionfs_set_lower_dentry_idx(sb->s_root, bindex, d);
		unionfs_set_lower_mnt_idx(sb->s_root, bindex, m);
	}
	dbstart(sb->s_root) = bstart;
	dbend(sb->s_root) = bend;

	/* Set the generation number to one, since this is for the mount. */
	atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);

	if (atomic_read(&inode->i_count) <= 1)
		unionfs_fill_inode(sb->s_root, inode);

	/*
	 * No need to call interpose because we already have a positive
	 * dentry, which was instantiated by d_alloc_root.  Just need to
	 * d_rehash it.
	 */
	d_rehash(sb->s_root);

	unionfs_unlock_dentry(sb->s_root);
	goto out; /* all is well */

out_freedpd:
	if (UNIONFS_D(sb->s_root)) {
		kfree(UNIONFS_D(sb->s_root)->lower_paths);
		free_dentry_private_data(sb->s_root);
	}
	dput(sb->s_root);

out_iput:
	iput(inode);

out_dput:
	if (lower_root_info && !IS_ERR(lower_root_info)) {
		for (bindex = lower_root_info->bstart;
		     bindex <= lower_root_info->bend; bindex++) {
			struct dentry *d;
			d = lower_root_info->lower_paths[bindex].dentry;
			/* drop refs we took earlier */
			atomic_dec(&d->d_sb->s_active);
			path_put(&lower_root_info->lower_paths[bindex]);
		}
		kfree(lower_root_info->lower_paths);
		kfree(lower_root_info);
		lower_root_info = NULL;
	}

out_free:
	kfree(UNIONFS_SB(sb)->data);
	kfree(UNIONFS_SB(sb));
	sb->s_fs_info = NULL;

out:
	if (lower_root_info && !IS_ERR(lower_root_info)) {
		kfree(lower_root_info->lower_paths);
		kfree(lower_root_info);
	}
	return err;
}