Exemple #1
0
/*
 * Connect a unionfs inode dentry/inode with several lower ones.  This is
 * the classic stackable file system "vnode interposition" action.
 *
 * @sb: unionfs's super_block
 */
struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb,
                                 int flag)
{
    int err = 0;
    struct inode *inode;
    int need_fill_inode = 1;
    struct dentry *spliced = NULL;

    verify_locked(dentry);

    /*
     * We allocate our new inode below, by calling iget.
     * iget will call our read_inode which will initialize some
     * of the new inode's fields
     */

    /*
     * On revalidate we've already got our own inode and just need
     * to fix it up.
     */
    if (flag == INTERPOSE_REVAL) {
        inode = dentry->d_inode;
        UNIONFS_I(inode)->bstart = -1;
        UNIONFS_I(inode)->bend = -1;
        atomic_set(&UNIONFS_I(inode)->generation,
                   atomic_read(&UNIONFS_SB(sb)->generation));

        UNIONFS_I(inode)->lower_inodes =
            kcalloc(sbmax(sb), sizeof(struct inode *), GFP_KERNEL);
        if (unlikely(!UNIONFS_I(inode)->lower_inodes)) {
            err = -ENOMEM;
            goto out;
        }
    } else {
        /* get unique inode number for unionfs */
        inode = iget(sb, iunique(sb, UNIONFS_ROOT_INO));
        if (!inode) {
            err = -EACCES;
            goto out;
        }
        if (atomic_read(&inode->i_count) > 1)
            goto skip;
    }

    need_fill_inode = 0;
    unionfs_fill_inode(dentry, inode);

skip:
    /* only (our) lookup wants to do a d_add */
    switch (flag) {
    case INTERPOSE_DEFAULT:
        /* for operations which create new inodes */
        d_add(dentry, inode);
        break;
    case INTERPOSE_REVAL_NEG:
        d_instantiate(dentry, inode);
        break;
    case INTERPOSE_LOOKUP:
        spliced = d_splice_alias(inode, dentry);
        if (spliced && spliced != dentry) {
            /*
             * d_splice can return a dentry if it was
             * disconnected and had to be moved.  We must ensure
             * that the private data of the new dentry is
             * correct and that the inode info was filled
             * properly.  Finally we must return this new
             * dentry.
             */
            spliced->d_op = &unionfs_dops;
            spliced->d_fsdata = dentry->d_fsdata;
            dentry->d_fsdata = NULL;
            dentry = spliced;
            if (need_fill_inode) {
                need_fill_inode = 0;
                unionfs_fill_inode(dentry, inode);
            }
            goto out_spliced;
        } else if (!spliced) {
            if (need_fill_inode) {
                need_fill_inode = 0;
                unionfs_fill_inode(dentry, inode);
                goto out_spliced;
            }
        }
        break;
    case INTERPOSE_REVAL:
        /* Do nothing. */
        break;
    default:
        printk(KERN_CRIT "unionfs: invalid interpose flag passed!\n");
        BUG();
    }
    goto out;

out_spliced:
    if (!err)
        return spliced;
out:
    return ERR_PTR(err);
}
Exemple #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;
}