GFS2: add support for rename2 and RENAME_EXCHANGE
gfs2 now uses the rename2 directory iop, and supports the RENAME_EXCHANGE flag (as well as RENAME_NOREPLACE, which the vfs takes care of). Signed-off-by: Benjamin Marzinski <bmarzins redhat com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
This commit is contained in:
parent
959b671717
commit
a63b7bbc21
205
fs/gfs2/inode.c
205
fs/gfs2/inode.c
|
@ -1306,6 +1306,35 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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)
|
||||
|
@ -1538,6 +1556,161 @@ out:
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue