Esempio n. 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 unionfs_iget,
	 * 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 = unionfs_iget(sb, iunique(sb, UNIONFS_ROOT_INO));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
			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);
}
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;
}