diff options
Diffstat (limited to 'fs/gfs2/inode.c')
| -rw-r--r-- | fs/gfs2/inode.c | 221 | 
1 files changed, 197 insertions, 24 deletions
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 1b3ca7a2e3fc..063fdfcf8275 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1227,8 +1227,8 @@ static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,   */  static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry, -                            struct file *file, unsigned flags, -                            umode_t mode, int *opened) +			    struct file *file, unsigned flags, +			    umode_t mode, int *opened)  {  	struct dentry *d;  	bool excl = !!(flags & O_EXCL); @@ -1307,6 +1307,35 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)  }  /** + * update_moved_ino - Update an inode that's being moved + * @ip: The inode being moved + * @ndip: The parent directory of the new filename + * @dir_rename: True of ip is a directory + * + * Returns: errno + */ + +static int update_moved_ino(struct gfs2_inode *ip, struct gfs2_inode *ndip, +			    int dir_rename) +{ +	int error; +	struct buffer_head *dibh; + +	if (dir_rename) +		return gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); + +	error = gfs2_meta_inode_buffer(ip, &dibh); +	if (error) +		return error; +	ip->i_inode.i_ctime = CURRENT_TIME; +	gfs2_trans_add_meta(ip->i_gl, dibh); +	gfs2_dinode_out(ip, dibh->b_data); +	brelse(dibh); +	return 0; +} + + +/**   * gfs2_rename - Rename a file   * @odir: Parent directory of old file name   * @odentry: The old dentry of the file @@ -1354,7 +1383,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,  		if (S_ISDIR(ip->i_inode.i_mode)) {  			dir_rename = 1; -			/* don't move a dirctory into it's subdir */ +			/* don't move a directory into its subdir */  			error = gfs2_ok_to_move(ip, ndip);  			if (error)  				goto out_gunlock_r; @@ -1494,20 +1523,9 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,  	if (nip)  		error = gfs2_unlink_inode(ndip, ndentry); -	if (dir_rename) { -		error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); -		if (error) -			goto out_end_trans; -	} else { -		struct buffer_head *dibh; -		error = gfs2_meta_inode_buffer(ip, &dibh); -		if (error) -			goto out_end_trans; -		ip->i_inode.i_ctime = CURRENT_TIME; -		gfs2_trans_add_meta(ip->i_gl, dibh); -		gfs2_dinode_out(ip, dibh->b_data); -		brelse(dibh); -	} +	error = update_moved_ino(ip, ndip, dir_rename); +	if (error) +		goto out_end_trans;  	error = gfs2_dir_del(odip, odentry);  	if (error) @@ -1539,6 +1557,161 @@ out:  }  /** + * gfs2_exchange - exchange two files + * @odir: Parent directory of old file name + * @odentry: The old dentry of the file + * @ndir: Parent directory of new file name + * @ndentry: The new dentry of the file + * @flags: The rename flags + * + * Returns: errno + */ + +static int gfs2_exchange(struct inode *odir, struct dentry *odentry, +			 struct inode *ndir, struct dentry *ndentry, +			 unsigned int flags) +{ +	struct gfs2_inode *odip = GFS2_I(odir); +	struct gfs2_inode *ndip = GFS2_I(ndir); +	struct gfs2_inode *oip = GFS2_I(odentry->d_inode); +	struct gfs2_inode *nip = GFS2_I(ndentry->d_inode); +	struct gfs2_sbd *sdp = GFS2_SB(odir); +	struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }; +	unsigned int num_gh; +	unsigned int x; +	umode_t old_mode = oip->i_inode.i_mode; +	umode_t new_mode = nip->i_inode.i_mode; +	int error; + +	error = gfs2_rindex_update(sdp); +	if (error) +		return error; + +	if (odip != ndip) { +		error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, +					   0, &r_gh); +		if (error) +			goto out; + +		if (S_ISDIR(old_mode)) { +			/* don't move a directory into its subdir */ +			error = gfs2_ok_to_move(oip, ndip); +			if (error) +				goto out_gunlock_r; +		} + +		if (S_ISDIR(new_mode)) { +			/* don't move a directory into its subdir */ +			error = gfs2_ok_to_move(nip, odip); +			if (error) +				goto out_gunlock_r; +		} +	} + +	num_gh = 1; +	gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); +	if (odip != ndip) { +		gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); +		num_gh++; +	} +	gfs2_holder_init(oip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); +	num_gh++; + +	gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); +	num_gh++; + +	for (x = 0; x < num_gh; x++) { +		error = gfs2_glock_nq(ghs + x); +		if (error) +			goto out_gunlock; +	} + +	error = -ENOENT; +	if (oip->i_inode.i_nlink == 0 || nip->i_inode.i_nlink == 0) +		goto out_gunlock; + +	error = gfs2_unlink_ok(odip, &odentry->d_name, oip); +	if (error) +		goto out_gunlock; +	error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip); +	if (error) +		goto out_gunlock; + +	if (S_ISDIR(old_mode)) { +		error = gfs2_permission(odentry->d_inode, MAY_WRITE); +		if (error) +			goto out_gunlock; +	} +	if (S_ISDIR(new_mode)) { +		error = gfs2_permission(ndentry->d_inode, MAY_WRITE); +		if (error) +			goto out_gunlock; +	} +	error = gfs2_trans_begin(sdp, 4 * RES_DINODE + 4 * RES_LEAF, 0); +	if (error) +		goto out_gunlock; + +	error = update_moved_ino(oip, ndip, S_ISDIR(old_mode)); +	if (error) +		goto out_end_trans; + +	error = update_moved_ino(nip, odip, S_ISDIR(new_mode)); +	if (error) +		goto out_end_trans; + +	error = gfs2_dir_mvino(ndip, &ndentry->d_name, oip, +			       IF2DT(old_mode)); +	if (error) +		goto out_end_trans; + +	error = gfs2_dir_mvino(odip, &odentry->d_name, nip, +			       IF2DT(new_mode)); +	if (error) +		goto out_end_trans; + +	if (odip != ndip) { +		if (S_ISDIR(new_mode) && !S_ISDIR(old_mode)) { +			inc_nlink(&odip->i_inode); +			drop_nlink(&ndip->i_inode); +		} else if (S_ISDIR(old_mode) && !S_ISDIR(new_mode)) { +			inc_nlink(&ndip->i_inode); +			drop_nlink(&odip->i_inode); +		} +	} +	mark_inode_dirty(&ndip->i_inode); +	if (odip != ndip) +		mark_inode_dirty(&odip->i_inode); + +out_end_trans: +	gfs2_trans_end(sdp); +out_gunlock: +	while (x--) { +		gfs2_glock_dq(ghs + x); +		gfs2_holder_uninit(ghs + x); +	} +out_gunlock_r: +	if (r_gh.gh_gl) +		gfs2_glock_dq_uninit(&r_gh); +out: +	return error; +} + +static int gfs2_rename2(struct inode *odir, struct dentry *odentry, +			struct inode *ndir, struct dentry *ndentry, +			unsigned int flags) +{ +	flags &= ~RENAME_NOREPLACE; + +	if (flags & ~RENAME_EXCHANGE) +		return -EINVAL; + +	if (flags & RENAME_EXCHANGE) +		return gfs2_exchange(odir, odentry, ndir, ndentry, flags); + +	return gfs2_rename(odir, odentry, ndir, ndentry); +} + +/**   * gfs2_follow_link - Follow a symbolic link   * @dentry: The dentry of the link   * @nd: Data that we pass to vfs_follow_link() @@ -1548,7 +1721,7 @@ out:   * Returns: 0 on success or error code   */ -static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *gfs2_follow_link(struct dentry *dentry, void **cookie)  {  	struct gfs2_inode *ip = GFS2_I(d_inode(dentry));  	struct gfs2_holder i_gh; @@ -1561,8 +1734,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)  	error = gfs2_glock_nq(&i_gh);  	if (error) {  		gfs2_holder_uninit(&i_gh); -		nd_set_link(nd, ERR_PTR(error)); -		return NULL; +		return ERR_PTR(error);  	}  	size = (unsigned int)i_size_read(&ip->i_inode); @@ -1586,8 +1758,9 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)  	brelse(dibh);  out:  	gfs2_glock_dq_uninit(&i_gh); -	nd_set_link(nd, buf); -	return NULL; +	if (!IS_ERR(buf)) +		*cookie = buf; +	return buf;  }  /** @@ -1716,7 +1889,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)  	if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||  	    !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { -		gfs2_quota_change(ip, -ap.target, ouid, ogid); +		gfs2_quota_change(ip, -(s64)ap.target, ouid, ogid);  		gfs2_quota_change(ip, ap.target, nuid, ngid);  	} @@ -1943,7 +2116,7 @@ const struct inode_operations gfs2_dir_iops = {  	.mkdir = gfs2_mkdir,  	.rmdir = gfs2_unlink,  	.mknod = gfs2_mknod, -	.rename = gfs2_rename, +	.rename2 = gfs2_rename2,  	.permission = gfs2_permission,  	.setattr = gfs2_setattr,  	.getattr = gfs2_getattr,  |