コード例 #1
0
ファイル: unlink.c プロジェクト: rickgaiser/kernelloader
int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct unionfs_dir_state *namelist = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_rmdir: ", dentry);

	/* check if this unionfs directory is empty or not */
	err = check_empty(dentry, &namelist);
	if (err) {
		goto out;
	}

	if (IS_SET(dir->i_sb, DELETE_WHITEOUT)) {
		/* Delete the first directory. */
		err = unionfs_rmdir_first(dir, dentry, namelist);
		/* create whiteout */
		if (!err) {
			err = create_whiteout(dentry, dbstart(dentry));
		} else {
			int new_err;

			if (dbstart(dentry) == 0)
				goto out;

			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;

			new_err = create_whiteout(dentry, dbstart(dentry) - 1);
			if (new_err != -EEXIST)
				err = new_err;
		}
	} else {
		/* delete all. */
		err = unionfs_rmdir_all(dir, dentry, namelist);
	}

      out:
	/* call d_drop so the system "forgets" about us */
	if (!err)
		d_drop(dentry);

	if (namelist)
		free_rdstate(namelist);

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
コード例 #2
0
int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct unionfs_dir_state *namelist = NULL;

	unionfs_lock_dentry(dentry);

	/* check if this unionfs directory is empty or not */
	err = check_empty(dentry, &namelist);
	if (err)
		goto out;

	err = unionfs_rmdir_first(dir, dentry, namelist);
	/* create whiteout */
	if (!err)
		err = create_whiteout(dentry, dbstart(dentry));
	else {
		int new_err;

		if (dbstart(dentry) == 0)
			goto out;

		/* exit if the error returned was NOT -EROFS */
		if (!IS_COPYUP_ERR(err))
			goto out;

		new_err = create_whiteout(dentry, dbstart(dentry) - 1);
		if (new_err != -EEXIST)
			err = new_err;
	}

out:
	/* call d_drop so the system "forgets" about us */
	if (!err)
		d_drop(dentry);

	if (namelist)
		free_rdstate(namelist);

	unionfs_unlock_dentry(dentry);
	return err;
}
コード例 #3
0
ファイル: dirfops.c プロジェクト: mayli/unionfs-2.6.32.y
static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir)
{
	int err = 0;
	struct file *lower_file = NULL;
	struct dentry *dentry = file->f_path.dentry;
	struct dentry *parent;
	struct inode *inode = NULL;
	struct unionfs_getdents_callback buf;
	struct unionfs_dir_state *uds;
	int bend;
	loff_t offset;

	unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
	parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
	unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);

	err = unionfs_file_revalidate(file, parent, false);
	if (unlikely(err))
		goto out;

	inode = dentry->d_inode;

	uds = UNIONFS_F(file)->rdstate;
	if (!uds) {
		if (file->f_pos == DIREOF) {
			goto out;
		} else if (file->f_pos > 0) {
			uds = find_rdstate(inode, file->f_pos);
			if (unlikely(!uds)) {
				err = -ESTALE;
				goto out;
			}
			UNIONFS_F(file)->rdstate = uds;
		} else {
			init_rdstate(file);
			uds = UNIONFS_F(file)->rdstate;
		}
	}
	bend = fbend(file);

	while (uds->bindex <= bend) {
		lower_file = unionfs_lower_file_idx(file, uds->bindex);
		if (!lower_file) {
			uds->bindex++;
			uds->dirpos = 0;
			continue;
		}

		/* prepare callback buffer */
		buf.filldir_called = 0;
		buf.filldir_error = 0;
		buf.entries_written = 0;
		buf.dirent = dirent;
		buf.filldir = filldir;
		buf.rdstate = uds;
		buf.sb = inode->i_sb;

		/* Read starting from where we last left off. */
		offset = vfs_llseek(lower_file, uds->dirpos, SEEK_SET);
		if (offset < 0) {
			err = offset;
			goto out;
		}
		err = vfs_readdir(lower_file, unionfs_filldir, &buf);

		/* Save the position for when we continue. */
		offset = vfs_llseek(lower_file, 0, SEEK_CUR);
		if (offset < 0) {
			err = offset;
			goto out;
		}
		uds->dirpos = offset;

		/* Copy the atime. */
		fsstack_copy_attr_atime(inode,
					lower_file->f_path.dentry->d_inode);

		if (err < 0)
			goto out;

		if (buf.filldir_error)
			break;

		if (!buf.entries_written) {
			uds->bindex++;
			uds->dirpos = 0;
		}
	}

	if (!buf.filldir_error && uds->bindex >= bend) {
		/* Save the number of hash entries for next time. */
		UNIONFS_I(inode)->hashsize = uds->hashentries;
		free_rdstate(uds);
		UNIONFS_F(file)->rdstate = NULL;
		file->f_pos = DIREOF;
	} else {
		file->f_pos = rdstate2offset(uds);
	}

out:
	if (!err)
		unionfs_check_file(file);
	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(dentry->d_sb);
	return err;
}
コード例 #4
0
ファイル: dirfops.c プロジェクト: mayli/unionfs-2.6.32.y
/*
 * This is not meant to be a generic repositioning function.  If you do
 * things that aren't supported, then we return EINVAL.
 *
 * What is allowed:
 *  (1) seeking to the same position that you are currently at
 *	This really has no effect, but returns where you are.
 *  (2) seeking to the beginning of the file
 *	This throws out all state, and lets you begin again.
 */
static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin)
{
	struct unionfs_dir_state *rdstate;
	struct dentry *dentry = file->f_path.dentry;
	struct dentry *parent;
	loff_t err;

	unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
	parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
	unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);

	err = unionfs_file_revalidate(file, parent, false);
	if (unlikely(err))
		goto out;

	rdstate = UNIONFS_F(file)->rdstate;

	/*
	 * we let users seek to their current position, but not anywhere
	 * else.
	 */
	if (!offset) {
		switch (origin) {
		case SEEK_SET:
			if (rdstate) {
				free_rdstate(rdstate);
				UNIONFS_F(file)->rdstate = NULL;
			}
			init_rdstate(file);
			err = 0;
			break;
		case SEEK_CUR:
			err = file->f_pos;
			break;
		case SEEK_END:
			/* Unsupported, because we would break everything.  */
			err = -EINVAL;
			break;
		}
	} else {
		switch (origin) {
		case SEEK_SET:
			if (rdstate) {
				if (offset == rdstate2offset(rdstate))
					err = offset;
				else if (file->f_pos == DIREOF)
					err = DIREOF;
				else
					err = -EINVAL;
			} else {
				struct inode *inode;
				inode = dentry->d_inode;
				rdstate = find_rdstate(inode, offset);
				if (rdstate) {
					UNIONFS_F(file)->rdstate = rdstate;
					err = rdstate->offset;
				} else {
					err = -EINVAL;
				}
			}
			break;
		case SEEK_CUR:
		case SEEK_END:
			/* Unsupported, because we would break everything.  */
			err = -EINVAL;
			break;
		}
	}

out:
	if (!err)
		unionfs_check_file(file);
	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(dentry->d_sb);
	return err;
}
コード例 #5
0
ファイル: dirhelper.c プロジェクト: kizukukoto/WDN900_GPL
/* Is a directory logically empty? */
int check_empty(struct dentry *dentry, struct dentry *parent,
		struct unionfs_dir_state **namelist)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct vfsmount *mnt;
	struct super_block *sb;
	struct file *lower_file;
	struct unionfs_rdutil_callback *buf = NULL;
	int bindex, bstart, bend, bopaque;

	sb = dentry->d_sb;


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

	err = unionfs_partial_lookup(dentry, parent);
	if (err)
		goto out;

	bstart = dbstart(dentry);
	bend = dbend(dentry);
	bopaque = dbopaque(dentry);
	if (0 <= bopaque && bopaque < bend)
		bend = bopaque;

	buf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL);
	if (unlikely(!buf)) {
		err = -ENOMEM;
		goto out;
	}
	buf->err = 0;
	buf->mode = RD_CHECK_EMPTY;
	buf->rdstate = alloc_rdstate(dentry->d_inode, bstart);
	if (unlikely(!buf->rdstate)) {
		err = -ENOMEM;
		goto out;
	}

	/* Process the lower directories with rdutil_callback as a filldir. */
	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry)
			continue;
		if (!lower_dentry->d_inode)
			continue;
		if (!S_ISDIR(lower_dentry->d_inode->i_mode))
			continue;

		dget(lower_dentry);
		mnt = unionfs_mntget(dentry, bindex);
		branchget(sb, bindex);
		lower_file = dentry_open(lower_dentry, mnt, O_RDONLY);
		if (IS_ERR(lower_file)) {
			err = PTR_ERR(lower_file);
			branchput(sb, bindex);
			goto out;
		}

		do {
			buf->filldir_called = 0;
			buf->rdstate->bindex = bindex;
			err = vfs_readdir(lower_file,
					  readdir_util_callback, buf);
			if (buf->err)
				err = buf->err;
		} while ((err >= 0) && buf->filldir_called);

		/* fput calls dput for lower_dentry */
		fput(lower_file);
		branchput(sb, bindex);

		if (err < 0)
			goto out;
	}

out:
	if (buf) {
		if (namelist && !err)
			*namelist = buf->rdstate;
		else if (buf->rdstate)
			free_rdstate(buf->rdstate);
		kfree(buf);
	}


	return err;
}
コード例 #6
0
int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct unionfs_dir_state *namelist = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_rmdir: ", dentry);

	/* check if this unionfs directory is empty or not */
	err = check_empty(dentry, &namelist);
	if (err) {
#if 0
		/* vfs_rmdir(our caller) unhashed the dentry.  This will recover
		 * the Unionfs inode number for the directory itself, but the 
		 * children are already lost.  It seems that tmpfs manages its
		 * way around this by upping the refcount on everything.
		 *
		 * Even if we do this, we still lose the inode numbers of the
		 * children.  The best way to fix this is to fix the VFS (or
		 * use persistent inode maps). */
		if (d_unhashed(dentry))
			d_rehash(dentry);
#endif
		goto out;
	}
#ifdef UNIONFS_DELETE_ALL
	if (IS_SET(dir->i_sb, DELETE_ALL)) {
		/* delete all. */
		err = unionfs_rmdir_all(dir, dentry, namelist);
	} else {		/* Delete the first directory. */
#endif
		err = unionfs_rmdir_first(dir, dentry, namelist);
		/* create whiteout */
		if (!err) {
			err = create_whiteout(dentry, dbstart(dentry));
		} else {
			int new_err;

			if (dbstart(dentry) == 0)
				goto out;

			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;

			new_err = create_whiteout(dentry, dbstart(dentry) - 1);
			if (new_err != -EEXIST)
				err = new_err;
		}

#ifdef UNIONFS_DELETE_ALL
	}
#endif
      out:
	/* call d_drop so the system "forgets" about us */
	if (!err)
		d_drop(dentry);

	if (namelist)
		free_rdstate(namelist);

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
コード例 #7
0
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		   struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *wh_dentry;

	double_lock_dentry(old_dentry, new_dentry);

	if (!S_ISDIR(old_dentry->d_inode->i_mode))
		err = unionfs_partial_lookup(old_dentry);
	else
		err = may_rename_dir(old_dentry);

	if (err)
		goto out;

	err = unionfs_partial_lookup(new_dentry);
	if (err)
		goto out;

	/*
	 * if new_dentry is already hidden because of whiteout,
	 * simply override it even if the whiteouted dir is not empty.
	 */
	wh_dentry = lookup_whiteout(new_dentry);
	if (!IS_ERR(wh_dentry))
		dput(wh_dentry);
	else if (new_dentry->d_inode) {
		if (S_ISDIR(old_dentry->d_inode->i_mode) !=
		    S_ISDIR(new_dentry->d_inode->i_mode)) {
			err = S_ISDIR(old_dentry->d_inode->i_mode) ?
					-ENOTDIR : -EISDIR;
			goto out;
		}

		if (S_ISDIR(new_dentry->d_inode->i_mode)) {
			struct unionfs_dir_state *namelist;
			/* check if this unionfs directory is empty or not */
			err = check_empty(new_dentry, &namelist);
			if (err)
				goto out;

			if (!is_robranch(new_dentry))
				err = delete_whiteouts(new_dentry,
						       dbstart(new_dentry),
						       namelist);

			free_rdstate(namelist);

			if (err)
				goto out;
		}
	}
	err = do_unionfs_rename(old_dir, old_dentry, new_dir, new_dentry);

out:
	if (err)
		/* clear the new_dentry stuff created */
		d_drop(new_dentry);
	else
		/* force re-lookup since the dir on ro branch is not renamed,
		   and hidden dentries still indicate the un-renamed ones. */
		if (S_ISDIR(old_dentry->d_inode->i_mode))
			atomic_dec(&UNIONFS_D(old_dentry)->generation);

	unionfs_unlock_dentry(new_dentry);
	unionfs_unlock_dentry(old_dentry);
	return err;
}
コード例 #8
0
/* Is a directory logically empty? */
int check_empty(struct dentry *dentry, struct dentry *parent,
		struct unionfs_dir_state **namelist)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct vfsmount *mnt;
	struct super_block *sb;
	struct file *lower_file;
	struct unionfs_rdutil_callback buf = {
		.ctx.actor = readdir_util_callback,
	};
	int bindex, bstart, bend, bopaque;
	struct path path;

	sb = dentry->d_sb;


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

	err = unionfs_partial_lookup(dentry, parent);
	if (err)
		goto out;

	bstart = dbstart(dentry);
	bend = dbend(dentry);
	bopaque = dbopaque(dentry);
	if (0 <= bopaque && bopaque < bend)
		bend = bopaque;

	buf.err = 0;
	buf.filldir_called = 0;
	buf.mode = RD_CHECK_EMPTY;
	buf.ctx.pos = 0; /* XXX: needed?! */
	buf.rdstate = alloc_rdstate(dentry->d_inode, bstart);
	if (unlikely(!buf.rdstate)) {
		err = -ENOMEM;
		goto out;
	}

	/* Process the lower directories with rdutil_callback as a filldir. */
	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry)
			continue;
		if (!lower_dentry->d_inode)
			continue;
		if (!S_ISDIR(lower_dentry->d_inode->i_mode))
			continue;

		dget(lower_dentry);
		mnt = unionfs_mntget(dentry, bindex);
		branchget(sb, bindex);
		path.dentry = lower_dentry;
		path.mnt = mnt;
		lower_file = dentry_open(&path, O_RDONLY, current_cred());
		path_put(&path);
		if (IS_ERR(lower_file)) {
			err = PTR_ERR(lower_file);
			branchput(sb, bindex);
			goto out;
		}

		do {
			buf.filldir_called = 0;
			buf.rdstate->bindex = bindex;
			err = iterate_dir(lower_file, &buf.ctx);
			if (buf.err)
				err = buf.err;
		} while ((err >= 0) && buf.filldir_called);

		/* fput calls dput for lower_dentry */
		fput(lower_file);
		branchput(sb, bindex);

		if (err < 0)
			goto out;
	}

out:
	if (namelist && !err)
		*namelist = buf.rdstate;
	else if (buf.rdstate)
		free_rdstate(buf.rdstate);

	return err;
}
コード例 #9
0
ファイル: rename.c プロジェクト: robacklin/ts7800
/*
 * The locking rules in unionfs_rename are complex.  We could use a simpler
 * superblock-level name-space lock for renames and copy-ups.
 */
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		   struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *wh_dentry;
	struct dentry *old_parent, *new_parent;
	int valid = true;

	unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
	old_parent = dget_parent(old_dentry);
	new_parent = dget_parent(new_dentry);
	/* un/lock parent dentries only if they differ from old/new_dentry */
	if (old_parent != old_dentry &&
	    old_parent != new_dentry)
		unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT);
	if (new_parent != old_dentry &&
	    new_parent != new_dentry &&
	    new_parent != old_parent)
		unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD);
	unionfs_double_lock_dentry(old_dentry, new_dentry);

	valid = __unionfs_d_revalidate(old_dentry, old_parent, false);
	if (!valid) {
		err = -ESTALE;
		goto out;
	}
	if (!d_deleted(new_dentry) && new_dentry->d_inode) {
		valid = __unionfs_d_revalidate(new_dentry, new_parent, false);
		if (!valid) {
			err = -ESTALE;
			goto out;
		}
	}

	if (!S_ISDIR(old_dentry->d_inode->i_mode))
		err = unionfs_partial_lookup(old_dentry, old_parent);
	else
		err = may_rename_dir(old_dentry, old_parent);

	if (err)
		goto out;

	err = unionfs_partial_lookup(new_dentry, new_parent);
	if (err)
		goto out;

	/*
	 * if new_dentry is already lower because of whiteout,
	 * simply override it even if the whited-out dir is not empty.
	 */
	wh_dentry = find_first_whiteout(new_dentry);
	if (!IS_ERR(wh_dentry)) {
		dput(wh_dentry);
	} else if (new_dentry->d_inode) {
		if (S_ISDIR(old_dentry->d_inode->i_mode) !=
		    S_ISDIR(new_dentry->d_inode->i_mode)) {
			err = S_ISDIR(old_dentry->d_inode->i_mode) ?
				-ENOTDIR : -EISDIR;
			goto out;
		}

		if (S_ISDIR(new_dentry->d_inode->i_mode)) {
			struct unionfs_dir_state *namelist = NULL;
			/* check if this unionfs directory is empty or not */
			err = check_empty(new_dentry, new_parent, &namelist);
			if (err)
				goto out;

			if (!is_robranch(new_dentry))
				err = delete_whiteouts(new_dentry,
						       dbstart(new_dentry),
						       namelist);

			free_rdstate(namelist);

			if (err)
				goto out;
		}
	}

	err = do_unionfs_rename(old_dir, old_dentry, old_parent,
				new_dir, new_dentry, new_parent);
	if (err)
		goto out;

	/*
	 * force re-lookup since the dir on ro branch is not renamed, and
	 * lower dentries still indicate the un-renamed ones.
	 */
	if (S_ISDIR(old_dentry->d_inode->i_mode))
		atomic_dec(&UNIONFS_D(old_dentry)->generation);
	else
		unionfs_postcopyup_release(old_dentry);
	if (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) {
		unionfs_postcopyup_release(new_dentry);
		unionfs_postcopyup_setmnt(new_dentry);
		if (!unionfs_lower_inode(new_dentry->d_inode)) {
			/*
			 * If we get here, it means that no copyup was
			 * needed, and that a file by the old name already
			 * existing on the destination branch; that file got
			 * renamed earlier in this function, so all we need
			 * to do here is set the lower inode.
			 */
			struct inode *inode;
			inode = unionfs_lower_inode(old_dentry->d_inode);
			igrab(inode);
			unionfs_set_lower_inode_idx(new_dentry->d_inode,
						    dbstart(new_dentry),
						    inode);
		}
	}
	/* if all of this renaming succeeded, update our times */
	unionfs_copy_attr_times(old_dentry->d_inode);
	unionfs_copy_attr_times(new_dentry->d_inode);
	unionfs_check_inode(old_dir);
	unionfs_check_inode(new_dir);
	unionfs_check_dentry(old_dentry);
	unionfs_check_dentry(new_dentry);

out:
	if (err)		/* clear the new_dentry stuff created */
		d_drop(new_dentry);

	unionfs_double_unlock_dentry(old_dentry, new_dentry);
	if (new_parent != old_dentry &&
	    new_parent != new_dentry &&
	    new_parent != old_parent)
		unionfs_unlock_dentry(new_parent);
	if (old_parent != old_dentry &&
	    old_parent != new_dentry)
		unionfs_unlock_dentry(old_parent);
	dput(new_parent);
	dput(old_parent);
	unionfs_read_unlock(old_dentry->d_sb);

	return err;
}