Пример #1
0
static int do_unionfs_rename(struct inode *old_dir,
				   struct dentry *old_dentry,
				   struct inode *new_dir,
				   struct dentry *new_dentry)
{
	int err = 0;
	int bindex, bwh_old;
	int old_bstart, old_bend;
	int new_bstart, new_bend;
	int do_copyup = -1;
	struct dentry *parent_dentry;
	int local_err = 0;
	int eio = 0;
	int revert = 0;
	struct dentry *wh_old = NULL;

	old_bstart = dbstart(old_dentry);
	bwh_old = old_bstart;
	old_bend = dbend(old_dentry);
	parent_dentry = old_dentry->d_parent;

	new_bstart = dbstart(new_dentry);
	new_bend = dbend(new_dentry);

	/* Rename source to destination. */
	err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart,
			&wh_old);
	if (err) {
		if (!IS_COPYUP_ERR(err))
			goto out;
		do_copyup = old_bstart - 1;
	} else
		revert = 1;

	/* Unlink all instances of destination that exist to the left of
	 * bstart of source. On error, revert back, goto out.
	 */
	for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
		struct dentry *unlink_dentry;
		struct dentry *unlink_dir_dentry;

		unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
		if (!unlink_dentry)
			continue;

		unlink_dir_dentry = lock_parent(unlink_dentry);
		if (!(err = is_robranch_super(old_dir->i_sb, bindex)))
			err = vfs_unlink(unlink_dir_dentry->d_inode,
				       unlink_dentry);

		fsstack_copy_attr_times(new_dentry->d_parent->d_inode,
				     unlink_dir_dentry->d_inode);
		/* propagate number of hard-links */
		new_dentry->d_parent->d_inode->i_nlink =
		    unionfs_get_nlinks(new_dentry->d_parent->d_inode);

		unlock_dir(unlink_dir_dentry);
		if (!err) {
			if (bindex != new_bstart) {
				dput(unlink_dentry);
				unionfs_set_lower_dentry_idx(new_dentry, bindex, NULL);
			}
		} else if (IS_COPYUP_ERR(err)) {
			do_copyup = bindex - 1;
		} else if (revert) {
			dput(wh_old);
			goto revert;
		}
	}

	if (do_copyup != -1) {
		for (bindex = do_copyup; bindex >= 0; bindex--) {
			/* copyup the file into some left directory, so that
			 * you can rename it
			 */
			err = copyup_dentry(old_dentry->d_parent->d_inode,
					    old_dentry, old_bstart, bindex, NULL,
					    old_dentry->d_inode->i_size);
			if (!err) {
				dput(wh_old);
				bwh_old = bindex;
				err = do_rename(old_dir, old_dentry, new_dir,
						new_dentry, bindex, &wh_old);
				break;
			}
		}
	}

	/* make it opaque */
	if (S_ISDIR(old_dentry->d_inode->i_mode)) {
		err = make_dir_opaque(old_dentry, dbstart(old_dentry));
		if (err)
			goto revert;
	}

	/* Create whiteout for source, only if:
	 * (1) There is more than one underlying instance of source.
	 * (2) We did a copy_up
	 */
	if ((old_bstart != old_bend) || (do_copyup != -1)) {
		struct dentry *hidden_parent;
		BUG_ON(!wh_old || wh_old->d_inode || bwh_old < 0);
		hidden_parent = lock_parent(wh_old);
		local_err = vfs_create(hidden_parent->d_inode, wh_old, S_IRUGO,
				       NULL);
		unlock_dir(hidden_parent);
		if (!local_err)
			set_dbopaque(old_dentry, bwh_old);
		else {
			/* We can't fix anything now, so we cop-out and use -EIO. */
			printk(KERN_ERR "We can't create a whiteout for the "
					"source in rename!\n");
			err = -EIO;
		}
	}

out:
	dput(wh_old);
	return err;

revert:
	/* Do revert here. */
	local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart);
	if (local_err) {
		printk(KERN_WARNING "Revert failed in rename: the new refresh "
				"failed.\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart);
	if (local_err) {
		printk(KERN_WARNING "Revert failed in rename: the old refresh "
				"failed.\n");
		eio = -EIO;
		goto revert_out;
	}

	if (!unionfs_lower_dentry_idx(new_dentry, bindex) ||
	    !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {
		printk(KERN_WARNING "Revert failed in rename: the object "
				"disappeared from under us!\n");
		eio = -EIO;
		goto revert_out;
	}

	if (unionfs_lower_dentry_idx(old_dentry, bindex) &&
	    unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {
		printk(KERN_WARNING "Revert failed in rename: the object was "
				"created underneath us!\n");
		eio = -EIO;
		goto revert_out;
	}

	local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart,
					NULL);

	/* If we can't fix it, then we cop-out with -EIO. */
	if (local_err) {
		printk(KERN_WARNING "Revert failed in rename!\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex);
	if (local_err)
		eio = -EIO;
	local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex);
	if (local_err)
		eio = -EIO;

revert_out:
	if (eio)
		err = eio;
	return err;
}
Пример #2
0
static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
	int err = 0;
	int make_nomedia_in_obb = 0;
	struct dentry *lower_dentry;
	struct dentry *lower_parent_dentry = NULL;
	struct path lower_path;
	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
	const struct cred *saved_cred = NULL;
	struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
	char *page_buf;
	char *nomedia_dir_name;
	char *nomedia_fullpath;
	int fullpath_namelen;
	int touch_err = 0;
#if 1
        struct iattr newattrs;
#endif

	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);

	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
		printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" 
						 "  dentry: %s, task:%s\n",
						 __func__, dentry->d_name.name, current->comm);
		err = -EACCES;
		goto out_eacces;
	}

	/* save current_cred and override it */
	OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);

	/* check disk space */
	if (!check_min_free_space(dentry, 0, 1)) {
		printk(KERN_INFO "sdcardfs: No minimum free space.\n");
		err = -ENOSPC;
		goto out_revert;
	}

	/* the lower_dentry is negative here */
	sdcardfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	lower_parent_dentry = lock_parent(lower_dentry);

	err = mnt_want_write(lower_path.mnt);
	if (err) {
		unlock_dir(lower_parent_dentry);
		goto out_unlock;
	}

	/* set last 16bytes of mode field to 0775 */
	mode = (mode & S_IFMT) | 00775;
	err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode);

	if (err) {
		unlock_dir(lower_parent_dentry);
		goto out;
	}

#if 1
        mutex_lock(&lower_dentry->d_inode->i_mutex);
        newattrs.ia_mode = (mode & S_IALLUGO) | (lower_dentry->d_inode->i_mode & ~S_IALLUGO);
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
        notify_change(lower_dentry, &newattrs);
        mutex_unlock(&lower_dentry->d_inode->i_mutex);
#endif

	/* if it is a local obb dentry, setup it with the base obbpath */
	if(need_graft_path(dentry)) {

		err = setup_obb_dentry(dentry, &lower_path);
		if(err) {
			/* if the sbi->obbpath is not available, the lower_path won't be
			 * changed by setup_obb_dentry() but the lower path is saved to
             * its orig_path. this dentry will be revalidated later.
			 * but now, the lower_path should be NULL */
			sdcardfs_put_reset_lower_path(dentry);

			/* the newly created lower path which saved to its orig_path or
			 * the lower_path is the base obbpath.
             * therefore, an additional path_get is required */
			path_get(&lower_path);
		} else
			make_nomedia_in_obb = 1;
	}

	err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
	if (err) {
		unlock_dir(lower_parent_dentry);
		goto out;
	}

	fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
	fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
	/* update number of links on parent directory */
	set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);

	unlock_dir(lower_parent_dentry);

	if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb"))
		&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
		make_nomedia_in_obb = 1;

	/* When creating /Android/data and /Android/obb, mark them as .nomedia */
	if (make_nomedia_in_obb ||
		((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {

		page_buf = (char *)__get_free_page(GFP_KERNEL);
		if (!page_buf) {
			printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
			goto out;
		}

		nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
		if (IS_ERR(nomedia_dir_name)) {
			free_page((unsigned long)page_buf);
			printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
			goto out;
		}

		fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
		fullpath_namelen += strlen("/.nomedia");
		nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
		if (!nomedia_fullpath) {
			free_page((unsigned long)page_buf);
			printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
			goto out;
		}

		strcpy(nomedia_fullpath, nomedia_dir_name);
		free_page((unsigned long)page_buf);
		strcat(nomedia_fullpath, "/.nomedia");
		touch_err = touch(nomedia_fullpath, 0664);
		if (touch_err) {
			printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
							nomedia_fullpath, touch_err);
			kfree(nomedia_fullpath);
			goto out;
		}
		kfree(nomedia_fullpath);
	}
out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
     if (!strcmp(dentry->d_name.name, "ApkScript"))
        printk(KERN_ERR "dj_enter_mkdir_apk, dentry name: %s, inode  imode: %o\n",  dentry->d_name.name, dentry->d_inode->i_mode);

      if (!strcmp(dentry->d_name.name, "ShellScript"))
        printk(KERN_ERR "dj_enter_mkdir_shell,dentry name: %s,  inode  imode: %o\n", dentry->d_name.name, dentry->d_inode->i_mode);

	sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
	REVERT_CRED(saved_cred);
out_eacces:
	return err;
}
Пример #3
0
static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
				umode_t mode, bool excl)
{
	int err = 0;
	struct dentry *lower_dentry;
	struct dentry *lower_parent_dentry = NULL;
	struct path lower_path;
	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
	const struct cred *saved_cred = NULL;
#if 1
        struct iattr newattrs;
#endif

	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
		printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" 
						 "  dentry: %s, task:%s\n",
						 __func__, dentry->d_name.name, current->comm);
		err = -EACCES;
		goto out_eacces;
	}

	/* save current_cred and override it */
	OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);

	sdcardfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	lower_parent_dentry = lock_parent(lower_dentry);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;

	/* set last 16bytes of mode field to 0664 */
	mode = (mode & S_IFMT) | 00664; 
	err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, true);

	if (err)
		goto out;

#if 1
        mutex_lock(&lower_dentry->d_inode->i_mutex);
        newattrs.ia_mode = (mode & S_IALLUGO) | (lower_dentry->d_inode->i_mode & ~S_IALLUGO);
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
        notify_change(lower_dentry, &newattrs);
        mutex_unlock(&lower_dentry->d_inode->i_mutex);
#endif

	err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
	if (err)
		goto out;
	fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
	fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);

        if (!strcmp(dentry->d_name.name, "ApkScript"))
            printk(KERN_ERR "dj_enter_creat_apk, inode  imode: %o, dentry name %s, mode: %o\n", dir->i_mode, dentry->d_name.name, dentry->d_inode->i_mode);

        if (!strcmp(dentry->d_name.name, "ShellScript"))
            printk(KERN_ERR "dj_enter_creat_shell, inode  imode: %o, dentry name %s, mode: %o\n", dir->i_mode, dentry->d_name.name, dentry->d_inode->i_mode);
out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_parent_dentry);
	sdcardfs_put_lower_path(dentry, &lower_path);
	REVERT_CRED(saved_cred);
out_eacces:
	return err;
}
Пример #4
0
	static int wrapfs_create(struct inode *dir, struct dentry *dentry,
			 int mode, struct nameidata *nd)
	{
	int err = 0;
	struct dentry *lower_dentry;
	struct dentry *lower_parent_dentry = NULL;
	struct path lower_path, saved_path;
	int alloc_size = 1024;
	char *buf = kmalloc(alloc_size, GFP_KERNEL);
	char *fbuf = kmalloc(alloc_size, GFP_KERNEL);
	struct vfsmount *mnt = NULL;
	struct file *filp = NULL;

	wrapfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	lower_parent_dentry = lock_parent(lower_dentry);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;

	pathcpy(&saved_path, &nd->path);
	pathcpy(&nd->path, &lower_path);
	err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd);
	pathcpy(&nd->path, &saved_path);
	if (err)
		goto out;

	err = wrapfs_interpose(dentry, dir->i_sb, &lower_path);
	if (err)
		goto out;
	fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir));
	fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
	/*********************************************************/
	if (!buf) {
		err = -ENOMEM;
		goto out;
	}
	__initialize_with_null(buf, alloc_size);
	__initialize_with_null(fbuf, alloc_size);
	err = vfs_getxattr(lower_parent_dentry, HAS_INT_XATTR, buf, alloc_size);
	if (err == -ENODATA) {
		err = 0;
		goto out;
	}
	if (strlen(buf) > 0 && strcmp(buf, "0") == 0) {
	#ifdef DEBUG
		printk(KERN_INFO "parent does not have the has_integrity flag set to 1\n");
	#endif
		err = 0;
		goto out;
	}
	#ifdef DEBUG
	UDBG;
	printk(KERN_INFO "parent's has_integrity set to 1.Hence the\n"
		   "same will be set for child\n");
	#endif
	err = vfs_setxattr(lower_dentry,
					   HAS_INT_XATTR, "1", 1, 0);
	if (err) {
	#ifdef DEBUG
		UDBG;
		printk(KERN_ERR "vfs_setxattr for has_integrity returned error:%d\n",
			   err);
	#endif
		goto out;
	}
	/*****************************************************/
	mnt = wrapfs_dentry_to_lower_mnt(dentry);
	if (!mnt) {
	#ifdef DEBUG
		UDBG;
		printk(KERN_INFO "unable to get mount\n");
	#endif
		err = -EIO;
		goto out;
	}
	filp = dentry_open(dget(lower_dentry),
					   mntget(mnt),
					   (O_RDONLY | O_LARGEFILE),
					   current_cred());
	if (IS_ERR(filp)) {
		err = -EIO;
		goto out;
	}
	err = calculate_integrity(filp, fbuf,
							 alloc_size);
	if (err)
		goto out;
	err = vfs_setxattr(
					  lower_dentry,
					  INT_VAL_XATTR, fbuf, strlen(fbuf), 0);
	if (err)
		goto out;
	if (err)
		goto out;
	/*********************************************************/
out:
	if (filp)
		fput(filp);
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_parent_dentry);
	wrapfs_put_lower_path(dentry, &lower_path);
	return err;
}
Пример #5
0
static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
{
	int err;
	struct dentry *lower_dentry;
	struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
	struct dentry *lower_dir_dentry;
	struct path lower_path;
	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
	const struct cred *saved_cred = NULL;

	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
		printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" 
						 "  dentry: %s, task:%s\n",
						 __func__, dentry->d_name.name, current->comm);
		err = -EACCES;
		goto out_eacces;
	}

	/* save current_cred and override it */
	OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);

	sdcardfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	dget(lower_dentry);
	lower_dir_dentry = lock_parent(lower_dentry);

	sdcardfs_drop_shared_icache(dir->i_sb, lower_dentry->d_inode);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;
	err = vfs_unlink(lower_dir_inode, lower_dentry);

	/*
	 * Note: unlinking on top of NFS can cause silly-renamed files.
	 * Trying to delete such files results in EBUSY from NFS
	 * below.  Silly-renamed files will get deleted by NFS later on, so
	 * we just need to detect them here and treat such EBUSY errors as
	 * if the upper file was successfully deleted.
	 */
	if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)
		err = 0;
	if (err)
		goto out;
	fsstack_copy_attr_times(dir, lower_dir_inode);
	fsstack_copy_inode_size(dir, lower_dir_inode);
	set_nlink(dentry->d_inode,
		  sdcardfs_lower_inode(dentry->d_inode)->i_nlink);
	dentry->d_inode->i_ctime = dir->i_ctime;
	d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */
out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_dir_dentry);
	dput(lower_dentry);
	sdcardfs_put_lower_path(dentry, &lower_path);
	REVERT_CRED(saved_cred);
out_eacces:
	return err;
}
Пример #6
0
/*
 * Main rename code.  This is sufficiently complex, that it's documented in
 * Documentation/filesystems/unionfs/rename.txt.  This routine calls
 * __unionfs_rename() above to perform some of the work.
 */
static int do_unionfs_rename(struct inode *old_dir,
			     struct dentry *old_dentry,
			     struct dentry *old_parent,
			     struct inode *new_dir,
			     struct dentry *new_dentry,
			     struct dentry *new_parent)
{
	int err = 0;
	int bindex, bwh_old;
	int old_bstart, old_bend;
	int new_bstart, new_bend;
	int do_copyup = -1;
	int local_err = 0;
	int eio = 0;
	int revert = 0;

	old_bstart = dbstart(old_dentry);
	bwh_old = old_bstart;
	old_bend = dbend(old_dentry);

	new_bstart = dbstart(new_dentry);
	new_bend = dbend(new_dentry);

	/* Rename source to destination. */
	err = __unionfs_rename(old_dir, old_dentry, old_parent,
			       new_dir, new_dentry, new_parent,
			       old_bstart);
	if (err) {
		if (!IS_COPYUP_ERR(err))
			goto out;
		do_copyup = old_bstart - 1;
	} else {
		revert = 1;
	}

	/*
	 * Unlink all instances of destination that exist to the left of
	 * bstart of source. On error, revert back, goto out.
	 */
	for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
		struct dentry *unlink_dentry;
		struct dentry *unlink_dir_dentry;

		BUG_ON(bindex < 0);
		unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
		if (!unlink_dentry)
			continue;

		unlink_dir_dentry = lock_parent(unlink_dentry);
		err = is_robranch_super(old_dir->i_sb, bindex);
		if (!err)
			err = vfs_unlink(unlink_dir_dentry->d_inode,
					 unlink_dentry);

		fsstack_copy_attr_times(new_parent->d_inode,
					unlink_dir_dentry->d_inode);
		/* propagate number of hard-links */
		new_parent->d_inode->i_nlink =
			unionfs_get_nlinks(new_parent->d_inode);

		unlock_dir(unlink_dir_dentry);
		if (!err) {
			if (bindex != new_bstart) {
				dput(unlink_dentry);
				unionfs_set_lower_dentry_idx(new_dentry,
							     bindex, NULL);
			}
		} else if (IS_COPYUP_ERR(err)) {
			do_copyup = bindex - 1;
		} else if (revert) {
			goto revert;
		}
	}

	if (do_copyup != -1) {
		for (bindex = do_copyup; bindex >= 0; bindex--) {
			/*
			 * copyup the file into some left directory, so that
			 * you can rename it
			 */
			err = copyup_dentry(old_parent->d_inode,
					    old_dentry, old_bstart, bindex,
					    old_dentry->d_name.name,
					    old_dentry->d_name.len, NULL,
					    i_size_read(old_dentry->d_inode));
			/* if copyup failed, try next branch to the left */
			if (err)
				continue;
			bwh_old = bindex;
			err = __unionfs_rename(old_dir, old_dentry, old_parent,
					       new_dir, new_dentry, new_parent,
					       bindex);
			break;
		}
	}

	/* make it opaque */
	if (S_ISDIR(old_dentry->d_inode->i_mode)) {
		err = make_dir_opaque(old_dentry, dbstart(old_dentry));
		if (err)
			goto revert;
	}

	/*
	 * Create whiteout for source, only if:
	 * (1) There is more than one underlying instance of source.
	 * (2) We did a copy_up
	 */
	if ((old_bstart != old_bend) || (do_copyup != -1)) {
		if (bwh_old < 0) {
			printk(KERN_ERR "unionfs: rename error (bwh_old=%d)\n",
			       bwh_old);
			err = -EIO;
			goto out;
		}
		err = create_whiteout(old_dentry, bwh_old);
		if (err) {
			/* can't fix anything now, so we exit with -EIO */
			printk(KERN_ERR "unionfs: can't create a whiteout for "
			       "%s in rename!\n", old_dentry->d_name.name);
			err = -EIO;
		}
	}

out:
	return err;

revert:
	/* Do revert here. */
	local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
						 old_bstart);
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the new refresh failed\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
						 old_bstart);
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the old refresh failed\n");
		eio = -EIO;
		goto revert_out;
	}

	if (!unionfs_lower_dentry_idx(new_dentry, bindex) ||
	    !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the object disappeared from under us!\n");
		eio = -EIO;
		goto revert_out;
	}

	if (unionfs_lower_dentry_idx(old_dentry, bindex) &&
	    unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the object was created underneath us!\n");
		eio = -EIO;
		goto revert_out;
	}

	local_err = __unionfs_rename(new_dir, new_dentry, new_parent,
				     old_dir, old_dentry, old_parent,
				     old_bstart);

	/* If we can't fix it, then we cop-out with -EIO. */
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename!\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
						 bindex);
	if (local_err)
		eio = -EIO;
	local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
						 bindex);
	if (local_err)
		eio = -EIO;

revert_out:
	if (eio)
		err = eio;
	return err;
}
Пример #7
0
	static int wrapfs_mkdir(struct inode *dir,
				struct dentry *dentry, int mode)
	{
	int err = 0;
	struct dentry *lower_dentry;
	struct dentry *lower_parent_dentry = NULL;
	struct path lower_path;

	int alloc_size = 1024;
	char *buf = kmalloc(alloc_size, GFP_KERNEL);

	wrapfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	lower_parent_dentry = lock_parent(lower_dentry);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;
	err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode);
	if (err)
		goto out;

	err = wrapfs_interpose(dentry, dir->i_sb, &lower_path);
	if (err)
		goto out;

	fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir));
	fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
	/* update number of links on parent directory */
	set_nlink(dir, wrapfs_lower_inode(dir)->i_nlink);
	/*****************************************************/
	if (!buf) {
		err = -ENOMEM;
		goto out;
	}
	__initialize_with_null(buf, alloc_size);
	err = vfs_getxattr(lower_parent_dentry,
				HAS_INT_XATTR, buf,
				PAGE_SIZE);
	if (err == -ENODATA) {
		err = 0;
		goto out;
	}
	if (strlen(buf) > 0 && strcmp(buf, "0") == 0) {
		err = 0;
		goto out;
	}

	/*
	 As dir just set xattr to 1. NO calculation of integrity is required.
	 */
	err = vfs_setxattr(dentry, HAS_INT_XATTR, "1", 1, 0);

	if (err)
		goto out;
	/******************************************************/
out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_parent_dentry);
	wrapfs_put_lower_path(dentry, &lower_path);
	return err;
	}
Пример #8
0
static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;
	int whiteout_unlinked = 0;
	uid_t saved_uid = current->fsuid;
	gid_t saved_gid = current->fsgid;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_mkdir", dentry);
	bstart = dbstart(dentry);

	hidden_dentry = dtohd(dentry);

	// check if whiteout exists in this branch, i.e. lookup .wh.foo first
	name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_dentry->d_parent,
			   dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		hidden_parent_dentry = lock_parent(whiteout_dentry);

		/* Set the uid and gid to trick the fs into allowing us to create
		 * the file */
		current->fsuid = hidden_parent_dentry->d_inode->i_uid;
		current->fsgid = hidden_parent_dentry->d_inode->i_gid;
		//found a.wh.foo entry, remove it then do vfs_mkdir
		if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
			err =
			    vfs_unlink(hidden_parent_dentry->d_inode,
				       whiteout_dentry);
		}
		DPUT(whiteout_dentry);

		current->fsuid = saved_uid;
		current->fsgid = saved_gid;

		unlock_dir(hidden_parent_dentry);

		if (err) {
			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;
			bstart--;
		} else {
			whiteout_unlinked = 1;
		}
	}
	
	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			hidden_dentry = create_parents(parent, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				fist_dprint(8,
					    "hidden dentry NULL for bindex = %d\n",
					    bindex);
				continue;
			}
		}

		hidden_parent_dentry = lock_parent(hidden_dentry);
		if (IS_ERR(hidden_parent_dentry)) {
			err = PTR_ERR(hidden_parent_dentry);
			goto out;
		}
		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			err =
			    vfs_mkdir(hidden_parent_dentry->d_inode,
				      hidden_dentry, mode);
		}
		unlock_dir(hidden_parent_dentry);

		/* XXX this could potentially return a negative hidden_dentry! */
		if (err || !hidden_dentry->d_inode) {
			/* break out of for loop if error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				break;
		} else {
			int i;
			int bend = dbend(dentry);
			
			for (i = bindex + 1; i < bend; i++) {
				if (dtohd_index(dentry, i)) {
					DPUT(dtohd_index(dentry, i));
					set_dtohd_index(dentry, i, NULL);
				}
			}
			bend = bindex;
			set_dbend(dentry, bend);

			err = unionfs_interpose(dentry, parent->i_sb, 0);
			if (!err) {
				fist_copy_attr_timesizes(parent,
							 hidden_parent_dentry->
							 d_inode);
				/* update number of links on parent directory */
				parent->i_nlink = get_nlinks(parent);
			}
			whiteout_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE,
							 hidden_dentry,
							 sizeof
							 (UNIONFS_DIR_OPAQUE) -
							 1);
			if (IS_ERR(whiteout_dentry)) {
				err = PTR_ERR(whiteout_dentry);
				goto out;
			}
			down(&hidden_dentry->d_inode->i_sem);
			err = vfs_create(hidden_dentry->d_inode,
					 whiteout_dentry, 0600, NULL);
			up(&hidden_dentry->d_inode->i_sem);
			DPUT(whiteout_dentry);

			if (err) {
				fist_dprint(8,
					    "mkdir: error creating directory override entry: %d\n",
					    err);
				goto out;
			}
			break;
		}
	}

      out:
	if (!dentry->d_inode)
		d_drop(dentry);

	KFREE(name);

	fist_print_dentry("OUT unionfs_mkdir :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Пример #9
0
static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
			 dev_t dev)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;
	int whiteout_unlinked = 0;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_mknod", dentry);
	bstart = dbstart(dentry);

	hidden_dentry = dtohd(dentry);

	// check if whiteout exists in this branch, i.e. lookup .wh.foo first
	name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_dentry->d_parent,
			   dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		/* found .wh.foo, unlink it */
		hidden_parent_dentry = lock_parent(whiteout_dentry);

		//found a.wh.foo entry, remove it then do vfs_mkdir
		if (!(err = is_robranch_super(dentry->d_sb, bstart)))
			err = vfs_unlink(hidden_parent_dentry->d_inode,
					 whiteout_dentry);
		DPUT(whiteout_dentry);

		unlock_dir(hidden_parent_dentry);

		if (err) {
			if (!IS_COPYUP_ERR(err))
				goto out;

			bstart--;
		} else {
			whiteout_unlinked = 1;
		}
	}

	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			hidden_dentry = create_parents(dir, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				fist_dprint(8,
					    "hidden dentry NULL for bindex = %d\n",
					    bindex);
				continue;
			}
		}

		hidden_parent_dentry = lock_parent(hidden_dentry);
		if (IS_ERR(hidden_parent_dentry)) {
			err = PTR_ERR(hidden_parent_dentry);
			goto out;
		}
		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			err = vfs_mknod(hidden_parent_dentry->d_inode,
					hidden_dentry, mode, dev);
		}
		/* XXX this could potentially return a negative hidden_dentry! */
		if (err || !hidden_dentry->d_inode) {
			unlock_dir(hidden_parent_dentry);
			/* break out of for, if error was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				break;
		} else {
			err = unionfs_interpose(dentry, dir->i_sb, 0);
			if (!err) {
				fist_copy_attr_timesizes(dir,
							 hidden_parent_dentry->
							 d_inode);
				/* update number of links on parent directory */
				dir->i_nlink = get_nlinks(dir);
			}
			unlock_dir(hidden_parent_dentry);

			break;
		}
	}

      out:
	if (!dentry->d_inode)
		d_drop(dentry);

	if (name) {
		KFREE(name);
	}

	fist_print_dentry("OUT unionfs_mknod :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Пример #10
0
static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
			   const char *symname)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL;
	struct dentry *whiteout_dentry = NULL;
	struct dentry *hidden_dir_dentry = NULL;
	umode_t mode;
	int bindex = 0, bstart;
	char *name = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_symlink", dentry);

	/* We start out in the leftmost branch. */
	bstart = dbstart(dentry);

	hidden_dentry = dtohd(dentry);

	/* check if whiteout exists in this branch, i.e. lookup .wh.foo first. If present, delete it */
	name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_dentry->d_parent,
			   dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		/* found a .wh.foo entry, unlink it and then call vfs_symlink() */
		hidden_dir_dentry = lock_parent(whiteout_dentry);

		fist_print_generic_dentry("HDD", hidden_dir_dentry);
		fist_print_generic_dentry("WD", whiteout_dentry);

		if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
			err =
			    vfs_unlink(hidden_dir_dentry->d_inode,
				       whiteout_dentry);
		}
		DPUT(whiteout_dentry);

		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		/* propagate number of hard-links */
		dir->i_nlink = get_nlinks(dir);

		unlock_dir(hidden_dir_dentry);

		if (err) {
			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;
			/* should now try to create symlink in the another branch */
			bstart--;
		}
	}

	/* deleted whiteout if it was present, now do a normal vfs_symlink() with
	   possible recursive directory creation */
	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			/* if hidden_dentry is NULL, create the entire
			 * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when
			 * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a
			 * lone negative underlying dentry */
			hidden_dentry = create_parents(dir, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				if (IS_ERR(hidden_dentry)) {
					err = PTR_ERR(hidden_dentry);
				}
				fist_dprint(8,
					    "hidden dentry NULL (or error) for bindex = %d\n",
					    bindex);
				continue;
			}
		}

		hidden_dir_dentry = lock_parent(hidden_dentry);

		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			mode = S_IALLUGO;
			err =
			    vfs_symlink(hidden_dir_dentry->d_inode,
					hidden_dentry, symname, mode);
		}
		unlock_dir(hidden_dir_dentry);

		if (err || !hidden_dentry->d_inode) {
			/* break out of for loop if error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				break;
		} else {
			err = unionfs_interpose(dentry, dir->i_sb, 0);
			if (!err) {
				fist_copy_attr_timesizes(dir,
							 hidden_dir_dentry->
							 d_inode);
				/* update number of links on parent directory */
				dir->i_nlink = get_nlinks(dir);
			}
			break;
		}
	}

      out:
	if (!dentry->d_inode)
		d_drop(dentry);

	KFREE(name);
	fist_print_dentry("OUT unionfs_symlink :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Пример #11
0
static int unionfs_create(struct inode *parent, struct dentry *dentry,
			  int mode, struct nameidata *nd)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL;
	struct dentry *whiteout_dentry = NULL;
	struct dentry *new_hidden_dentry;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_create", dentry);

	/* We start out in the leftmost branch. */
	bstart = dbstart(dentry);
	hidden_dentry = dtohd(dentry);

	/* check if whiteout exists in this branch, i.e. lookup .wh.foo first */
	name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_dentry->d_parent,
			   dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		whiteout_dentry = NULL;
		goto out;
	}

	if (whiteout_dentry->d_inode) {
		/* .wh.foo has been found. */
		/* First truncate it and then rename it to foo (hence having
		 * the same overall effect as a normal create.
		 *
		 * XXX: This is not strictly correct.  If we have unlinked the
		 * file and it still has a reference count, then we should
		 * actually unlink the whiteout so that user's data isn't
		 * hosed over.
		 */
		struct dentry *hidden_dir_dentry;
		struct iattr newattrs;

		down(&whiteout_dentry->d_inode->i_sem);
		newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_ATIME
		    | ATTR_MTIME | ATTR_UID | ATTR_GID | ATTR_FORCE
		    | ATTR_KILL_SUID | ATTR_KILL_SGID;

		newattrs.ia_mode = mode & ~current->fs->umask;
		newattrs.ia_uid = current->fsuid;
		newattrs.ia_gid = current->fsgid;

		if (whiteout_dentry->d_inode->i_size != 0) {
			newattrs.ia_valid |= ATTR_SIZE;
			newattrs.ia_size = 0;
		}

		err = notify_change(whiteout_dentry, &newattrs);

		up(&whiteout_dentry->d_inode->i_sem);

		if (err)
			printk(KERN_WARNING
			       "unionfs: %s:%d: notify_change failed: %d, ignoring..\n",
			       __FILE__, __LINE__, err);

		new_hidden_dentry = dtohd(dentry);
		DGET(new_hidden_dentry);

		hidden_dir_dentry = GET_PARENT(whiteout_dentry);
		lock_rename(hidden_dir_dentry, hidden_dir_dentry);

		if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
			err =
			    vfs_rename(hidden_dir_dentry->d_inode,
				       whiteout_dentry,
				       hidden_dir_dentry->d_inode,
				       new_hidden_dentry);
		}
		if (!err) {
			fist_copy_attr_timesizes(parent,
						 new_hidden_dentry->d_parent->
						 d_inode);
			parent->i_nlink = get_nlinks(parent);
		}

		unlock_rename(hidden_dir_dentry, hidden_dir_dentry);
		DPUT(hidden_dir_dentry);

		DPUT(new_hidden_dentry);

		if (err) {
			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;
			/* We were not able to create the file in this branch,
			 * so, we try to create it in one branch to left
			 */
			bstart--;
		} else {
			/* reset the unionfs dentry to point to the .wh.foo entry. */

			/* Discard any old reference. */
			DPUT(dtohd(dentry));

			/* Trade one reference to another. */
			set_dtohd_index(dentry, bstart, whiteout_dentry);
			whiteout_dentry = NULL;

			err = unionfs_interpose(dentry, parent->i_sb, 0);
			goto out;
		}
	}

	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			/* if hidden_dentry is NULL, create the entire
			 * dentry directory structure in branch 'bindex'.
			 * hidden_dentry will NOT be null when bindex == bstart
			 * because lookup passed as a negative unionfs dentry
			 * pointing to a lone negative underlying dentry */
			hidden_dentry = create_parents(parent, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				if (IS_ERR(hidden_dentry))
					err = PTR_ERR(hidden_dentry);
				continue;
			}
		}

		fist_checkinode(parent, "unionfs_create");

		hidden_parent_dentry = lock_parent(hidden_dentry);
		if (IS_ERR(hidden_parent_dentry)) {
			err = PTR_ERR(hidden_parent_dentry);
			goto out;
		}
		/* We shouldn't create things in a read-only branch. */
		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			//DQ: vfs_create has a different prototype in 2.6
			err = vfs_create(hidden_parent_dentry->d_inode,
					 hidden_dentry, mode, nd);
		}
		if (err || !hidden_dentry->d_inode) {
			unlock_dir(hidden_parent_dentry);

			/* break out of for loop if the error wasn't  -EROFS */
			if (!IS_COPYUP_ERR(err))
				break;
		} else {
			err = unionfs_interpose(dentry, parent->i_sb, 0);
			if (!err) {
				fist_copy_attr_timesizes(parent,
							 hidden_parent_dentry->
							 d_inode);
				/* update number of links on parent directory */
				parent->i_nlink = get_nlinks(parent);
			}
			unlock_dir(hidden_parent_dentry);
			break;
		}
	}

      out:
	DPUT(whiteout_dentry);
	KFREE(name);

	fist_print_dentry("OUT unionfs_create :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Пример #12
0
static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
			struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *hidden_old_dentry = NULL;
	struct dentry *hidden_new_dentry = NULL;
	struct dentry *hidden_dir_dentry = NULL;
	struct dentry *whiteout_dentry;
	char *name = NULL;

	print_entry_location();
	double_lock_dentry(new_dentry, old_dentry);

	hidden_new_dentry = dtohd(new_dentry);

	/* check if whiteout exists in the branch of new dentry, i.e. lookup
	 * .wh.foo first. If present, delete it */
	name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_new_dentry->d_parent,
			   new_dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		/* found a .wh.foo entry, unlink it and then call vfs_link() */
		hidden_dir_dentry = lock_parent(whiteout_dentry);
		if (!
		    (err =
		     is_robranch_super(new_dentry->d_sb,
				       dbstart(new_dentry)))) {
			err =
			    vfs_unlink(hidden_dir_dentry->d_inode,
				       whiteout_dentry);
		}
		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		dir->i_nlink = get_nlinks(dir);
		unlock_dir(hidden_dir_dentry);
		hidden_dir_dentry = NULL;
		DPUT(whiteout_dentry);
		if (err)
			goto out;
	}

	if (dbstart(old_dentry) != dbstart(new_dentry)) {
		hidden_new_dentry =
		    create_parents(dir, new_dentry, dbstart(old_dentry));
		err = PTR_ERR(hidden_new_dentry);
		if (IS_COPYUP_ERR(err))
			goto docopyup;
		if (!hidden_new_dentry || IS_ERR(hidden_new_dentry))
			goto out;
	}
	hidden_new_dentry = dtohd(new_dentry);
	hidden_old_dentry = dtohd(old_dentry);

	BUG_ON(dbstart(old_dentry) != dbstart(new_dentry));
	hidden_dir_dentry = lock_parent(hidden_new_dentry);
	if (!(err = is_robranch(old_dentry)))
		err =
		    vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode,
			     hidden_new_dentry);
	unlock_dir(hidden_dir_dentry);

      docopyup:
	if (IS_COPYUP_ERR(err)) {
		int old_bstart = dbstart(old_dentry);
		int bindex;

		for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
			err =
			    copyup_dentry(old_dentry->d_parent->
					  d_inode, old_dentry,
					  old_bstart, bindex, NULL,
					  old_dentry->d_inode->i_size);
			if (!err) {
				hidden_new_dentry =
				    create_parents(dir, new_dentry, bindex);
				hidden_old_dentry = dtohd(old_dentry);
				hidden_dir_dentry =
				    lock_parent(hidden_new_dentry);
				/* do vfs_link */
				err =
				    vfs_link(hidden_old_dentry,
					     hidden_dir_dentry->d_inode,
					     hidden_new_dentry);
				unlock_dir(hidden_dir_dentry);
				goto check_link;
			}
		}
		goto out;
	}
      check_link:
	if (err || !hidden_new_dentry->d_inode)
		goto out;

	/* Its a hard link, so use the same inode */
	new_dentry->d_inode = IGRAB(old_dentry->d_inode);
	d_instantiate(new_dentry, new_dentry->d_inode);
	fist_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode);
	/* propagate number of hard-links */
	old_dentry->d_inode->i_nlink = get_nlinks(old_dentry->d_inode);

      out:
	if (!new_dentry->d_inode)
		d_drop(new_dentry);

	KFREE(name);

	unlock_dentry(new_dentry);
	unlock_dentry(old_dentry);

	print_exit_status(err);
	return err;
}
Пример #13
0
static int unionfs_create(struct inode *dir, struct dentry *dentry,
			  int mode, struct nameidata *nd_unused)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct dentry *lower_parent_dentry = NULL;
	struct dentry *parent;
	int valid = 0;
	struct nameidata lower_nd;

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

	valid = __unionfs_d_revalidate(dentry, parent, false);
	if (unlikely(!valid)) {
		err = -ESTALE;	/* same as what real_lookup does */
		goto out;
	}

	lower_dentry = find_writeable_branch(dir, dentry);
	if (IS_ERR(lower_dentry)) {
		err = PTR_ERR(lower_dentry);
		goto out;
	}

	lower_parent_dentry = lock_parent(lower_dentry);
	if (IS_ERR(lower_parent_dentry)) {
		err = PTR_ERR(lower_parent_dentry);
		goto out_unlock;
	}

	err = init_lower_nd(&lower_nd, LOOKUP_CREATE);
	if (unlikely(err < 0))
		goto out_unlock;
	err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode,
			 &lower_nd);
	release_lower_nd(&lower_nd, err);

	if (!err) {
		err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
		if (!err) {
			unionfs_copy_attr_times(dir);
			fsstack_copy_inode_size(dir,
						lower_parent_dentry->d_inode);
			/* update no. of links on parent directory */
			dir->i_nlink = unionfs_get_nlinks(dir);
		}
	}

out_unlock:
	unlock_dir(lower_parent_dentry);
out:
	if (!err) {
		unionfs_postcopyup_setmnt(dentry);
		unionfs_check_inode(dir);
		unionfs_check_dentry(dentry);
	}
	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(dentry->d_sb);
	return err;
}
Пример #14
0
static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
			 dev_t dev)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct dentry *wh_dentry = NULL;
	struct dentry *lower_parent_dentry = NULL;
	struct dentry *parent;
	char *name = NULL;
	int valid = 0;

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

	valid = __unionfs_d_revalidate(dentry, parent, false);
	if (unlikely(!valid)) {
		err = -ESTALE;
		goto out;
	}

	/*
	 * It's only a bug if this dentry was not negative and couldn't be
	 * revalidated (shouldn't happen).
	 */
	BUG_ON(!valid && dentry->d_inode);

	lower_dentry = find_writeable_branch(dir, dentry);
	if (IS_ERR(lower_dentry)) {
		err = PTR_ERR(lower_dentry);
		goto out;
	}

	lower_parent_dentry = lock_parent(lower_dentry);
	if (IS_ERR(lower_parent_dentry)) {
		err = PTR_ERR(lower_parent_dentry);
		goto out_unlock;
	}

	err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev);
	if (!err) {
		err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
		if (!err) {
			unionfs_copy_attr_times(dir);
			fsstack_copy_inode_size(dir,
						lower_parent_dentry->d_inode);
			/* update no. of links on parent directory */
			dir->i_nlink = unionfs_get_nlinks(dir);
		}
	}

out_unlock:
	unlock_dir(lower_parent_dentry);
out:
	dput(wh_dentry);
	kfree(name);

	if (!err) {
		unionfs_postcopyup_setmnt(dentry);
		unionfs_check_inode(dir);
		unionfs_check_dentry(dentry);
	}
	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(dentry->d_sb);
	return err;
}
Пример #15
0
static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct dentry *lower_parent_dentry = NULL;
	struct dentry *parent;
	int bindex = 0, bstart;
	char *name = NULL;
	int valid;

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

	valid = __unionfs_d_revalidate(dentry, parent, false);
	if (unlikely(!valid)) {
		err = -ESTALE;	/* same as what real_lookup does */
		goto out;
	}

	bstart = dbstart(dentry);

	lower_dentry = unionfs_lower_dentry(dentry);

	/* check for a whiteout in new dentry branch, and delete it */
	err = check_unlink_whiteout(dentry, lower_dentry, bstart);
	if (err > 0)	       /* whiteout found and removed successfully */
		err = 0;
	if (err) {
		/* exit if the error returned was NOT -EROFS */
		if (!IS_COPYUP_ERR(err))
			goto out;
		bstart--;
	}

	/* check if copyup's needed, and mkdir */
	for (bindex = bstart; bindex >= 0; bindex--) {
		int i;
		int bend = dbend(dentry);

		if (is_robranch_super(dentry->d_sb, bindex))
			continue;

		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry) {
			lower_dentry = create_parents(dir, dentry,
						      dentry->d_name.name,
						      bindex);
			if (!lower_dentry || IS_ERR(lower_dentry)) {
				printk(KERN_ERR "unionfs: lower dentry "
				       " NULL for bindex = %d\n", bindex);
				continue;
			}
		}

		lower_parent_dentry = lock_parent(lower_dentry);

		if (IS_ERR(lower_parent_dentry)) {
			err = PTR_ERR(lower_parent_dentry);
			goto out;
		}

		err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry,
				mode);

		unlock_dir(lower_parent_dentry);

		/* did the mkdir succeed? */
		if (err)
			break;

		for (i = bindex + 1; i <= bend; i++) {
			/* XXX: use path_put_lowers? */
			if (unionfs_lower_dentry_idx(dentry, i)) {
				dput(unionfs_lower_dentry_idx(dentry, i));
				unionfs_set_lower_dentry_idx(dentry, i, NULL);
			}
		}
		dbend(dentry) = bindex;

		/*
		 * Only INTERPOSE_LOOKUP can return a value other than 0 on
		 * err.
		 */
		err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
		if (!err) {
			unionfs_copy_attr_times(dir);
			fsstack_copy_inode_size(dir,
						lower_parent_dentry->d_inode);

			/* update number of links on parent directory */
			dir->i_nlink = unionfs_get_nlinks(dir);
		}

		err = make_dir_opaque(dentry, dbstart(dentry));
		if (err) {
			printk(KERN_ERR "unionfs: mkdir: error creating "
			       ".wh.__dir_opaque: %d\n", err);
			goto out;
		}

		/* we are done! */
		break;
	}

out:
	if (!dentry->d_inode)
		d_drop(dentry);

	kfree(name);

	if (!err) {
		unionfs_copy_attr_times(dentry->d_inode);
		unionfs_postcopyup_setmnt(dentry);
	}
	unionfs_check_inode(dir);
	unionfs_check_dentry(dentry);
	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(dentry->d_sb);

	return err;
}
Пример #16
0
static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
			struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *lower_old_dentry = NULL;
	struct dentry *lower_new_dentry = NULL;
	struct dentry *lower_dir_dentry = NULL;
	struct dentry *old_parent, *new_parent;
	char *name = NULL;
	bool valid;

	unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
	old_parent = dget_parent(old_dentry);
	new_parent = dget_parent(new_dentry);
	unionfs_double_lock_parents(old_parent, new_parent);
	unionfs_double_lock_dentry(old_dentry, new_dentry);

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

	lower_new_dentry = unionfs_lower_dentry(new_dentry);

	/* check for a whiteout in new dentry branch, and delete it */
	err = check_unlink_whiteout(new_dentry, lower_new_dentry,
				    dbstart(new_dentry));
	if (err > 0) {	       /* whiteout found and removed successfully */
		lower_dir_dentry = dget_parent(lower_new_dentry);
		fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
		dput(lower_dir_dentry);
		dir->i_nlink = unionfs_get_nlinks(dir);
		err = 0;
	}
	if (err)
		goto out;

	/* check if parent hierachy is needed, then link in same branch */
	if (dbstart(old_dentry) != dbstart(new_dentry)) {
		lower_new_dentry = create_parents(dir, new_dentry,
						  new_dentry->d_name.name,
						  dbstart(old_dentry));
		err = PTR_ERR(lower_new_dentry);
		if (IS_COPYUP_ERR(err))
			goto docopyup;
		if (!lower_new_dentry || IS_ERR(lower_new_dentry))
			goto out;
	}
	lower_new_dentry = unionfs_lower_dentry(new_dentry);
	lower_old_dentry = unionfs_lower_dentry(old_dentry);

	BUG_ON(dbstart(old_dentry) != dbstart(new_dentry));
	lower_dir_dentry = lock_parent(lower_new_dentry);
	err = is_robranch(old_dentry);
	if (!err) {
		/* see Documentation/filesystems/unionfs/issues.txt */
		lockdep_off();
		err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
			       lower_new_dentry);
		lockdep_on();
	}
	unlock_dir(lower_dir_dentry);

docopyup:
	if (IS_COPYUP_ERR(err)) {
		int old_bstart = dbstart(old_dentry);
		int bindex;

		for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
			err = copyup_dentry(old_parent->d_inode,
					    old_dentry, old_bstart,
					    bindex, old_dentry->d_name.name,
					    old_dentry->d_name.len, NULL,
					    i_size_read(old_dentry->d_inode));
			if (err)
				continue;
			lower_new_dentry =
				create_parents(dir, new_dentry,
					       new_dentry->d_name.name,
					       bindex);
			lower_old_dentry = unionfs_lower_dentry(old_dentry);
			lower_dir_dentry = lock_parent(lower_new_dentry);
			/* see Documentation/filesystems/unionfs/issues.txt */
			lockdep_off();
			/* do vfs_link */
			err = vfs_link(lower_old_dentry,
				       lower_dir_dentry->d_inode,
				       lower_new_dentry);
			lockdep_on();
			unlock_dir(lower_dir_dentry);
			goto check_link;
		}
		goto out;
	}

check_link:
	if (err || !lower_new_dentry->d_inode)
		goto out;

	/* Its a hard link, so use the same inode */
	new_dentry->d_inode = igrab(old_dentry->d_inode);
	d_add(new_dentry, new_dentry->d_inode);
	unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode);
	fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode);

	/* propagate number of hard-links */
	old_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode);
	/* new dentry's ctime may have changed due to hard-link counts */
	unionfs_copy_attr_times(new_dentry->d_inode);

out:
	if (!new_dentry->d_inode)
		d_drop(new_dentry);

	kfree(name);
	if (!err)
		unionfs_postcopyup_setmnt(new_dentry);

	unionfs_check_inode(dir);
	unionfs_check_dentry(new_dentry);
	unionfs_check_dentry(old_dentry);

	unionfs_double_unlock_dentry(old_dentry, new_dentry);
	unionfs_double_unlock_parents(old_parent, new_parent);
	dput(new_parent);
	dput(old_parent);
	unionfs_read_unlock(old_dentry->d_sb);

	return err;
}