Exemplo n.º 1
0
/* unionfs_open helper function: open a directory */
static int __open_dir(struct inode *inode, struct file *file)
{
	struct dentry *lower_dentry;
	struct file *lower_file;
	int bindex, bstart, bend;
	struct vfsmount *mnt;

	bstart = fbstart(file) = dbstart(file->f_path.dentry);
	bend = fbend(file) = dbend(file->f_path.dentry);

	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_dentry =
			unionfs_lower_dentry_idx(file->f_path.dentry, bindex);
		if (!lower_dentry)
			continue;

		dget(lower_dentry);
		unionfs_mntget(file->f_path.dentry, bindex);
		mnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex);
		lower_file = dentry_open(lower_dentry, mnt, file->f_flags,
					 current_cred());
		if (IS_ERR(lower_file))
			return PTR_ERR(lower_file);

		unionfs_set_lower_file_idx(file, bindex, lower_file);

		/*
		 * The branchget goes after the open, because otherwise
		 * we would miss the reference on release.
		 */
		branchget(inode->i_sb, bindex);
	}

	return 0;
}
Exemplo n.º 2
0
/* open all lower files for a given file */
static int open_all_files(struct file *file)
{
	int bindex, bstart, bend, err = 0;
	struct file *lower_file;
	struct dentry *lower_dentry;
	struct dentry *dentry = file->f_path.dentry;
	struct super_block *sb = dentry->d_sb;

	bstart = dbstart(dentry);
	bend = dbend(dentry);

	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry)
			continue;

		dget(lower_dentry);
		unionfs_mntget(dentry, bindex);
		branchget(sb, bindex);

		lower_file =
			dentry_open(lower_dentry,
				    unionfs_lower_mnt_idx(dentry, bindex),
				    file->f_flags, current_cred());
		if (IS_ERR(lower_file)) {
			branchput(sb, bindex);
			err = PTR_ERR(lower_file);
			goto out;
		} else {
			unionfs_set_lower_file_idx(file, bindex, lower_file);
		}
	}
out:
	return err;
}
Exemplo n.º 3
0
/* perform a delayed copyup of a read-write file on a read-only branch */
static int do_delayed_copyup(struct file *file, struct dentry *parent)
{
	int bindex, bstart, bend, err = 0;
	struct dentry *dentry = file->f_path.dentry;
	struct inode *parent_inode = parent->d_inode;

	bstart = fbstart(file);
	bend = fbend(file);

	BUG_ON(!S_ISREG(dentry->d_inode->i_mode));

	unionfs_check_file(file);
	for (bindex = bstart - 1; bindex >= 0; bindex--) {
		if (!d_deleted(dentry))
			err = copyup_file(parent_inode, file, bstart,
					  bindex,
					  i_size_read(dentry->d_inode));
		else
			err = copyup_deleted_file(file, dentry, parent,
						  bstart, bindex);
		/* if succeeded, set lower open-file flags and break */
		if (!err) {
			struct file *lower_file;
			lower_file = unionfs_lower_file_idx(file, bindex);
			lower_file->f_flags = file->f_flags;
			break;
		}
	}
	if (err || (bstart <= fbstart(file)))
		goto out;
	bend = fbend(file);
	for (bindex = bstart; bindex <= bend; bindex++) {
		if (unionfs_lower_file_idx(file, bindex)) {
			branchput(dentry->d_sb, bindex);
			fput(unionfs_lower_file_idx(file, bindex));
			unionfs_set_lower_file_idx(file, bindex, NULL);
		}
	}
	path_put_lowers(dentry, bstart, bend, false);
	iput_lowers(dentry->d_inode, bstart, bend, false);
	/* for reg file, we only open it "once" */
	fbend(file) = fbstart(file);
	dbend(dentry) = dbstart(dentry);
	ibend(dentry->d_inode) = ibstart(dentry->d_inode);

out:
	unionfs_check_file(file);
	return err;
}
Exemplo n.º 4
0
/*
 * This function creates a copy of a file represented by 'file' which
 * currently resides in branch 'bstart' to branch 'new_bindex.'  The copy
 * will be named "name".
 */
int copyup_named_file(struct inode *dir, struct file *file, char *name,
		      int bstart, int new_bindex, loff_t len)
{
	int err = 0;
	struct file *output_file = NULL;

	err = copyup_dentry(dir, file->f_path.dentry, bstart, new_bindex,
			    name, strlen(name), &output_file, len);
	if (!err) {
		fbstart(file) = new_bindex;
		unionfs_set_lower_file_idx(file, new_bindex, output_file);
	}

	return err;
}
Exemplo n.º 5
0
/*
 * This function creates a copy of a file represented by 'file' which
 * currently resides in branch 'bstart' to branch 'new_bindex'.
 */
int copyup_file(struct inode *dir, struct file *file, int bstart,
		int new_bindex, loff_t len)
{
	int err = 0;
	struct file *output_file = NULL;
	struct dentry *dentry = file->f_path.dentry;

	err = copyup_dentry(dir, dentry, bstart, new_bindex,
			    dentry->d_name.name, dentry->d_name.len,
			    &output_file, len);
	if (!err) {
		fbstart(file) = new_bindex;
		unionfs_set_lower_file_idx(file, new_bindex, output_file);
	}

	return err;
}
Exemplo n.º 6
0
/*
 * release all lower object references & free the file info structure
 *
 * No need to grab sb info's rwsem.
 */
int unionfs_file_release(struct inode *inode, struct file *file)
{
	struct file *lower_file = NULL;
	struct unionfs_file_info *fileinfo;
	struct unionfs_inode_info *inodeinfo;
	struct super_block *sb = inode->i_sb;
	struct dentry *dentry = file->f_path.dentry;
	struct dentry *parent;
	int bindex, bstart, bend;
	int fgen, err = 0;

	/*
	 * Since mm/memory.c:might_fault() (under PROVE_LOCKING) was
	 * modified in 2.6.29-rc1 to call might_lock_read on mmap_sem, this
	 * has been causing false positives in file system stacking layers.
	 * In particular, our ->mmap is called after sys_mmap2 already holds
	 * mmap_sem, then we lock our own mutexes; but earlier, it's
	 * possible for lockdep to have locked our mutexes first, and then
	 * we call a lower ->readdir which could call might_fault.  The
	 * different ordering of the locks is what lockdep complains about
	 * -- unnecessarily.  Therefore, we have no choice but to tell
	 * lockdep to temporarily turn off lockdep here.  Note: the comments
	 * inside might_sleep also suggest that it would have been
	 * nicer to only annotate paths that needs that might_lock_read.
	 */
	lockdep_off();
	unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT);
	parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
	unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);

	/*
	 * We try to revalidate, but the VFS ignores return return values
	 * from file->release, so we must always try to succeed here,
	 * including to do the kfree and dput below.  So if revalidation
	 * failed, all we can do is print some message and keep going.
	 */
	err = unionfs_file_revalidate(file, parent,
				      UNIONFS_F(file)->wrote_to_file);
	if (!err)
		unionfs_check_file(file);
	fileinfo = UNIONFS_F(file);
	BUG_ON(file->f_path.dentry->d_inode != inode);
	inodeinfo = UNIONFS_I(inode);

	/* fput all the lower files */
	fgen = atomic_read(&fileinfo->generation);
	bstart = fbstart(file);
	bend = fbend(file);

	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_file = unionfs_lower_file_idx(file, bindex);

		if (lower_file) {
			unionfs_set_lower_file_idx(file, bindex, NULL);
			fput(lower_file);
			branchput(sb, bindex);
		}

		/* if there are no more refs to the dentry, dput it */
		if (d_deleted(dentry)) {
			dput(unionfs_lower_dentry_idx(dentry, bindex));
			unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
		}
	}

	kfree(fileinfo->lower_files);
	kfree(fileinfo->saved_branch_ids);

	if (fileinfo->rdstate) {
		fileinfo->rdstate->access = jiffies;
		spin_lock(&inodeinfo->rdlock);
		inodeinfo->rdcount++;
		list_add_tail(&fileinfo->rdstate->cache,
			      &inodeinfo->readdircache);
		mark_inode_dirty(inode);
		spin_unlock(&inodeinfo->rdlock);
		fileinfo->rdstate = NULL;
	}
	kfree(fileinfo);

	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(sb);
	lockdep_on();
	return err;
}