Commit d41bb034 authored by Dave Chinner's avatar Dave Chinner

Merge branch 'xfs-rename-whiteout' into for-next

Conflicts:
	fs/xfs/xfs_inode.c
parents 88e8fda9 7dcf5c3e
...@@ -391,15 +391,14 @@ xfs_lock_inumorder(int lock_mode, int subclass) ...@@ -391,15 +391,14 @@ xfs_lock_inumorder(int lock_mode, int subclass)
} }
/* /*
* The following routine will lock n inodes in exclusive mode. * The following routine will lock n inodes in exclusive mode. We assume the
* We assume the caller calls us with the inodes in i_ino order. * caller calls us with the inodes in i_ino order.
* *
* We need to detect deadlock where an inode that we lock * We need to detect deadlock where an inode that we lock is in the AIL and we
* is in the AIL and we start waiting for another inode that is locked * start waiting for another inode that is locked by a thread in a long running
* by a thread in a long running transaction (such as truncate). This can * transaction (such as truncate). This can result in deadlock since the long
* result in deadlock since the long running trans might need to wait * running trans might need to wait for the inode we just locked in order to
* for the inode we just locked in order to push the tail and free space * push the tail and free space in the log.
* in the log.
*/ */
void void
xfs_lock_inodes( xfs_lock_inodes(
...@@ -410,30 +409,27 @@ xfs_lock_inodes( ...@@ -410,30 +409,27 @@ xfs_lock_inodes(
int attempts = 0, i, j, try_lock; int attempts = 0, i, j, try_lock;
xfs_log_item_t *lp; xfs_log_item_t *lp;
ASSERT(ips && (inodes >= 2)); /* we need at least two */ /* currently supports between 2 and 5 inodes */
ASSERT(ips && inodes >= 2 && inodes <= 5);
try_lock = 0; try_lock = 0;
i = 0; i = 0;
again: again:
for (; i < inodes; i++) { for (; i < inodes; i++) {
ASSERT(ips[i]); ASSERT(ips[i]);
if (i && (ips[i] == ips[i-1])) /* Already locked */ if (i && (ips[i] == ips[i - 1])) /* Already locked */
continue; continue;
/* /*
* If try_lock is not set yet, make sure all locked inodes * If try_lock is not set yet, make sure all locked inodes are
* are not in the AIL. * not in the AIL. If any are, set try_lock to be used later.
* If any are, set try_lock to be used later.
*/ */
if (!try_lock) { if (!try_lock) {
for (j = (i - 1); j >= 0 && !try_lock; j--) { for (j = (i - 1); j >= 0 && !try_lock; j--) {
lp = (xfs_log_item_t *)ips[j]->i_itemp; lp = (xfs_log_item_t *)ips[j]->i_itemp;
if (lp && (lp->li_flags & XFS_LI_IN_AIL)) { if (lp && (lp->li_flags & XFS_LI_IN_AIL))
try_lock++; try_lock++;
}
} }
} }
...@@ -443,51 +439,42 @@ xfs_lock_inodes( ...@@ -443,51 +439,42 @@ xfs_lock_inodes(
* we can't get any, we must release all we have * we can't get any, we must release all we have
* and try again. * and try again.
*/ */
if (!try_lock) {
xfs_ilock(ips[i], xfs_lock_inumorder(lock_mode, i));
continue;
}
/* try_lock means we have an inode locked that is in the AIL. */
ASSERT(i != 0);
if (xfs_ilock_nowait(ips[i], xfs_lock_inumorder(lock_mode, i)))
continue;
if (try_lock) { /*
/* try_lock must be 0 if i is 0. */ * Unlock all previous guys and try again. xfs_iunlock will try
* to push the tail if the inode is in the AIL.
*/
attempts++;
for (j = i - 1; j >= 0; j--) {
/* /*
* try_lock means we have an inode locked * Check to see if we've already unlocked this one. Not
* that is in the AIL. * the first one going back, and the inode ptr is the
* same.
*/ */
ASSERT(i != 0); if (j != (i - 1) && ips[j] == ips[j + 1])
if (!xfs_ilock_nowait(ips[i], xfs_lock_inumorder(lock_mode, i))) { continue;
attempts++;
/*
* Unlock all previous guys and try again.
* xfs_iunlock will try to push the tail
* if the inode is in the AIL.
*/
for(j = i - 1; j >= 0; j--) {
/*
* Check to see if we've already
* unlocked this one.
* Not the first one going back,
* and the inode ptr is the same.
*/
if ((j != (i - 1)) && ips[j] ==
ips[j+1])
continue;
xfs_iunlock(ips[j], lock_mode);
}
if ((attempts % 5) == 0) { xfs_iunlock(ips[j], lock_mode);
delay(1); /* Don't just spin the CPU */ }
if ((attempts % 5) == 0) {
delay(1); /* Don't just spin the CPU */
#ifdef DEBUG #ifdef DEBUG
xfs_lock_delays++; xfs_lock_delays++;
#endif #endif
}
i = 0;
try_lock = 0;
goto again;
}
} else {
xfs_ilock(ips[i], xfs_lock_inumorder(lock_mode, i));
} }
i = 0;
try_lock = 0;
goto again;
} }
#ifdef DEBUG #ifdef DEBUG
...@@ -2681,19 +2668,22 @@ xfs_remove( ...@@ -2681,19 +2668,22 @@ xfs_remove(
/* /*
* Enter all inodes for a rename transaction into a sorted array. * Enter all inodes for a rename transaction into a sorted array.
*/ */
#define __XFS_SORT_INODES 5
STATIC void STATIC void
xfs_sort_for_rename( xfs_sort_for_rename(
xfs_inode_t *dp1, /* in: old (source) directory inode */ struct xfs_inode *dp1, /* in: old (source) directory inode */
xfs_inode_t *dp2, /* in: new (target) directory inode */ struct xfs_inode *dp2, /* in: new (target) directory inode */
xfs_inode_t *ip1, /* in: inode of old entry */ struct xfs_inode *ip1, /* in: inode of old entry */
xfs_inode_t *ip2, /* in: inode of new entry, if it struct xfs_inode *ip2, /* in: inode of new entry */
already exists, NULL otherwise. */ struct xfs_inode *wip, /* in: whiteout inode */
xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ struct xfs_inode **i_tab,/* out: sorted array of inodes */
int *num_inodes) /* out: number of inodes in array */ int *num_inodes) /* in/out: inodes in array */
{ {
xfs_inode_t *temp;
int i, j; int i, j;
ASSERT(*num_inodes == __XFS_SORT_INODES);
memset(i_tab, 0, *num_inodes * sizeof(struct xfs_inode *));
/* /*
* i_tab contains a list of pointers to inodes. We initialize * i_tab contains a list of pointers to inodes. We initialize
* the table here & we'll sort it. We will then use it to * the table here & we'll sort it. We will then use it to
...@@ -2701,25 +2691,24 @@ xfs_sort_for_rename( ...@@ -2701,25 +2691,24 @@ xfs_sort_for_rename(
* *
* Note that the table may contain duplicates. e.g., dp1 == dp2. * Note that the table may contain duplicates. e.g., dp1 == dp2.
*/ */
i_tab[0] = dp1; i = 0;
i_tab[1] = dp2; i_tab[i++] = dp1;
i_tab[2] = ip1; i_tab[i++] = dp2;
if (ip2) { i_tab[i++] = ip1;
*num_inodes = 4; if (ip2)
i_tab[3] = ip2; i_tab[i++] = ip2;
} else { if (wip)
*num_inodes = 3; i_tab[i++] = wip;
i_tab[3] = NULL; *num_inodes = i;
}
/* /*
* Sort the elements via bubble sort. (Remember, there are at * Sort the elements via bubble sort. (Remember, there are at
* most 4 elements to sort, so this is adequate.) * most 5 elements to sort, so this is adequate.)
*/ */
for (i = 0; i < *num_inodes; i++) { for (i = 0; i < *num_inodes; i++) {
for (j = 1; j < *num_inodes; j++) { for (j = 1; j < *num_inodes; j++) {
if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) {
temp = i_tab[j]; struct xfs_inode *temp = i_tab[j];
i_tab[j] = i_tab[j-1]; i_tab[j] = i_tab[j-1];
i_tab[j-1] = temp; i_tab[j-1] = temp;
} }
...@@ -2727,6 +2716,31 @@ xfs_sort_for_rename( ...@@ -2727,6 +2716,31 @@ xfs_sort_for_rename(
} }
} }
static int
xfs_finish_rename(
struct xfs_trans *tp,
struct xfs_bmap_free *free_list)
{
int committed = 0;
int error;
/*
* If this is a synchronous mount, make sure that the rename transaction
* goes to disk before returning to the user.
*/
if (tp->t_mountp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC))
xfs_trans_set_sync(tp);
error = xfs_bmap_finish(&tp, free_list, &committed);
if (error) {
xfs_bmap_cancel(free_list);
xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
return error;
}
return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
}
/* /*
* xfs_cross_rename() * xfs_cross_rename()
* *
...@@ -2755,14 +2769,14 @@ xfs_cross_rename( ...@@ -2755,14 +2769,14 @@ xfs_cross_rename(
ip2->i_ino, ip2->i_ino,
first_block, free_list, spaceres); first_block, free_list, spaceres);
if (error) if (error)
goto out; goto out_trans_abort;
/* Swap inode number for dirent in second parent */ /* Swap inode number for dirent in second parent */
error = xfs_dir_replace(tp, dp2, name2, error = xfs_dir_replace(tp, dp2, name2,
ip1->i_ino, ip1->i_ino,
first_block, free_list, spaceres); first_block, free_list, spaceres);
if (error) if (error)
goto out; goto out_trans_abort;
/* /*
* If we're renaming one or more directories across different parents, * If we're renaming one or more directories across different parents,
...@@ -2777,16 +2791,16 @@ xfs_cross_rename( ...@@ -2777,16 +2791,16 @@ xfs_cross_rename(
dp1->i_ino, first_block, dp1->i_ino, first_block,
free_list, spaceres); free_list, spaceres);
if (error) if (error)
goto out; goto out_trans_abort;
/* transfer ip2 ".." reference to dp1 */ /* transfer ip2 ".." reference to dp1 */
if (!S_ISDIR(ip1->i_d.di_mode)) { if (!S_ISDIR(ip1->i_d.di_mode)) {
error = xfs_droplink(tp, dp2); error = xfs_droplink(tp, dp2);
if (error) if (error)
goto out; goto out_trans_abort;
error = xfs_bumplink(tp, dp1); error = xfs_bumplink(tp, dp1);
if (error) if (error)
goto out; goto out_trans_abort;
} }
/* /*
...@@ -2804,16 +2818,16 @@ xfs_cross_rename( ...@@ -2804,16 +2818,16 @@ xfs_cross_rename(
dp2->i_ino, first_block, dp2->i_ino, first_block,
free_list, spaceres); free_list, spaceres);
if (error) if (error)
goto out; goto out_trans_abort;
/* transfer ip1 ".." reference to dp2 */ /* transfer ip1 ".." reference to dp2 */
if (!S_ISDIR(ip2->i_d.di_mode)) { if (!S_ISDIR(ip2->i_d.di_mode)) {
error = xfs_droplink(tp, dp1); error = xfs_droplink(tp, dp1);
if (error) if (error)
goto out; goto out_trans_abort;
error = xfs_bumplink(tp, dp2); error = xfs_bumplink(tp, dp2);
if (error) if (error)
goto out; goto out_trans_abort;
} }
/* /*
...@@ -2841,66 +2855,108 @@ xfs_cross_rename( ...@@ -2841,66 +2855,108 @@ xfs_cross_rename(
} }
xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
out: return xfs_finish_rename(tp, free_list);
out_trans_abort:
xfs_bmap_cancel(free_list);
xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
return error; return error;
} }
/*
* xfs_rename_alloc_whiteout()
*
* Return a referenced, unlinked, unlocked inode that that can be used as a
* whiteout in a rename transaction. We use a tmpfile inode here so that if we
* crash between allocating the inode and linking it into the rename transaction
* recovery will free the inode and we won't leak it.
*/
static int
xfs_rename_alloc_whiteout(
struct xfs_inode *dp,
struct xfs_inode **wip)
{
struct xfs_inode *tmpfile;
int error;
error = xfs_create_tmpfile(dp, NULL, S_IFCHR | WHITEOUT_MODE, &tmpfile);
if (error)
return error;
/* Satisfy xfs_bumplink that this is a real tmpfile */
xfs_finish_inode_setup(tmpfile);
VFS_I(tmpfile)->i_state |= I_LINKABLE;
*wip = tmpfile;
return 0;
}
/* /*
* xfs_rename * xfs_rename
*/ */
int int
xfs_rename( xfs_rename(
xfs_inode_t *src_dp, struct xfs_inode *src_dp,
struct xfs_name *src_name, struct xfs_name *src_name,
xfs_inode_t *src_ip, struct xfs_inode *src_ip,
xfs_inode_t *target_dp, struct xfs_inode *target_dp,
struct xfs_name *target_name, struct xfs_name *target_name,
xfs_inode_t *target_ip, struct xfs_inode *target_ip,
unsigned int flags) unsigned int flags)
{ {
xfs_trans_t *tp = NULL; struct xfs_mount *mp = src_dp->i_mount;
xfs_mount_t *mp = src_dp->i_mount; struct xfs_trans *tp;
int new_parent; /* moving to a new dir */ struct xfs_bmap_free free_list;
int src_is_directory; /* src_name is a directory */ xfs_fsblock_t first_block;
int error; struct xfs_inode *wip = NULL; /* whiteout inode */
xfs_bmap_free_t free_list; struct xfs_inode *inodes[__XFS_SORT_INODES];
xfs_fsblock_t first_block; int num_inodes = __XFS_SORT_INODES;
int cancel_flags; int new_parent = (src_dp != target_dp);
int committed; int src_is_directory = S_ISDIR(src_ip->i_d.di_mode);
xfs_inode_t *inodes[4]; int cancel_flags = 0;
int spaceres; int spaceres;
int num_inodes; int error;
trace_xfs_rename(src_dp, target_dp, src_name, target_name); trace_xfs_rename(src_dp, target_dp, src_name, target_name);
new_parent = (src_dp != target_dp); if ((flags & RENAME_EXCHANGE) && !target_ip)
src_is_directory = S_ISDIR(src_ip->i_d.di_mode); return -EINVAL;
/*
* If we are doing a whiteout operation, allocate the whiteout inode
* we will be placing at the target and ensure the type is set
* appropriately.
*/
if (flags & RENAME_WHITEOUT) {
ASSERT(!(flags & (RENAME_NOREPLACE | RENAME_EXCHANGE)));
error = xfs_rename_alloc_whiteout(target_dp, &wip);
if (error)
return error;
/* setup target dirent info as whiteout */
src_name->type = XFS_DIR3_FT_CHRDEV;
}
xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
inodes, &num_inodes); inodes, &num_inodes);
xfs_bmap_init(&free_list, &first_block);
tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME);
cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len); spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, spaceres, 0); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, spaceres, 0);
if (error == -ENOSPC) { if (error == -ENOSPC) {
spaceres = 0; spaceres = 0;
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, 0, 0); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, 0, 0);
} }
if (error) { if (error)
xfs_trans_cancel(tp, 0); goto out_trans_cancel;
goto std_return; cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
}
/* /*
* Attach the dquots to the inodes * Attach the dquots to the inodes
*/ */
error = xfs_qm_vop_rename_dqattach(inodes); error = xfs_qm_vop_rename_dqattach(inodes);
if (error) { if (error)
xfs_trans_cancel(tp, cancel_flags); goto out_trans_cancel;
goto std_return;
}
/* /*
* Lock all the participating inodes. Depending upon whether * Lock all the participating inodes. Depending upon whether
...@@ -2921,6 +2977,8 @@ xfs_rename( ...@@ -2921,6 +2977,8 @@ xfs_rename(
xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);
if (target_ip) if (target_ip)
xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);
if (wip)
xfs_trans_ijoin(tp, wip, XFS_ILOCK_EXCL);
/* /*
* If we are using project inheritance, we only allow renames * If we are using project inheritance, we only allow renames
...@@ -2930,24 +2988,16 @@ xfs_rename( ...@@ -2930,24 +2988,16 @@ xfs_rename(
if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
(xfs_get_projid(target_dp) != xfs_get_projid(src_ip)))) { (xfs_get_projid(target_dp) != xfs_get_projid(src_ip)))) {
error = -EXDEV; error = -EXDEV;
goto error_return; goto out_trans_cancel;
} }
/* xfs_bmap_init(&free_list, &first_block);
* Handle RENAME_EXCHANGE flags
*/ /* RENAME_EXCHANGE is unique from here on. */
if (flags & RENAME_EXCHANGE) { if (flags & RENAME_EXCHANGE)
if (target_ip == NULL) { return xfs_cross_rename(tp, src_dp, src_name, src_ip,
error = -EINVAL; target_dp, target_name, target_ip,
goto error_return; &free_list, &first_block, spaceres);
}
error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
target_dp, target_name, target_ip,
&free_list, &first_block, spaceres);
if (error)
goto abort_return;
goto finish_rename;
}
/* /*
* Set up the target. * Set up the target.
...@@ -2960,7 +3010,7 @@ xfs_rename( ...@@ -2960,7 +3010,7 @@ xfs_rename(
if (!spaceres) { if (!spaceres) {
error = xfs_dir_canenter(tp, target_dp, target_name); error = xfs_dir_canenter(tp, target_dp, target_name);
if (error) if (error)
goto error_return; goto out_trans_cancel;
} }
/* /*
* If target does not exist and the rename crosses * If target does not exist and the rename crosses
...@@ -2971,9 +3021,9 @@ xfs_rename( ...@@ -2971,9 +3021,9 @@ xfs_rename(
src_ip->i_ino, &first_block, src_ip->i_ino, &first_block,
&free_list, spaceres); &free_list, spaceres);
if (error == -ENOSPC) if (error == -ENOSPC)
goto error_return; goto out_bmap_cancel;
if (error) if (error)
goto abort_return; goto out_trans_abort;
xfs_trans_ichgtime(tp, target_dp, xfs_trans_ichgtime(tp, target_dp,
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
...@@ -2981,7 +3031,7 @@ xfs_rename( ...@@ -2981,7 +3031,7 @@ xfs_rename(
if (new_parent && src_is_directory) { if (new_parent && src_is_directory) {
error = xfs_bumplink(tp, target_dp); error = xfs_bumplink(tp, target_dp);
if (error) if (error)
goto abort_return; goto out_trans_abort;
} }
} else { /* target_ip != NULL */ } else { /* target_ip != NULL */
/* /*
...@@ -2996,7 +3046,7 @@ xfs_rename( ...@@ -2996,7 +3046,7 @@ xfs_rename(
if (!(xfs_dir_isempty(target_ip)) || if (!(xfs_dir_isempty(target_ip)) ||
(target_ip->i_d.di_nlink > 2)) { (target_ip->i_d.di_nlink > 2)) {
error = -EEXIST; error = -EEXIST;
goto error_return; goto out_trans_cancel;
} }
} }
...@@ -3013,7 +3063,7 @@ xfs_rename( ...@@ -3013,7 +3063,7 @@ xfs_rename(
src_ip->i_ino, src_ip->i_ino,
&first_block, &free_list, spaceres); &first_block, &free_list, spaceres);
if (error) if (error)
goto abort_return; goto out_trans_abort;
xfs_trans_ichgtime(tp, target_dp, xfs_trans_ichgtime(tp, target_dp,
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
...@@ -3024,7 +3074,7 @@ xfs_rename( ...@@ -3024,7 +3074,7 @@ xfs_rename(
*/ */
error = xfs_droplink(tp, target_ip); error = xfs_droplink(tp, target_ip);
if (error) if (error)
goto abort_return; goto out_trans_abort;
if (src_is_directory) { if (src_is_directory) {
/* /*
...@@ -3032,7 +3082,7 @@ xfs_rename( ...@@ -3032,7 +3082,7 @@ xfs_rename(
*/ */
error = xfs_droplink(tp, target_ip); error = xfs_droplink(tp, target_ip);
if (error) if (error)
goto abort_return; goto out_trans_abort;
} }
} /* target_ip != NULL */ } /* target_ip != NULL */
...@@ -3049,7 +3099,7 @@ xfs_rename( ...@@ -3049,7 +3099,7 @@ xfs_rename(
&first_block, &free_list, spaceres); &first_block, &free_list, spaceres);
ASSERT(error != -EEXIST); ASSERT(error != -EEXIST);
if (error) if (error)
goto abort_return; goto out_trans_abort;
} }
/* /*
...@@ -3075,49 +3125,67 @@ xfs_rename( ...@@ -3075,49 +3125,67 @@ xfs_rename(
*/ */
error = xfs_droplink(tp, src_dp); error = xfs_droplink(tp, src_dp);
if (error) if (error)
goto abort_return; goto out_trans_abort;
} }
error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino, /*
* For whiteouts, we only need to update the source dirent with the
* inode number of the whiteout inode rather than removing it
* altogether.
*/
if (wip) {
error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino,
&first_block, &free_list, spaceres); &first_block, &free_list, spaceres);
} else
error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
&first_block, &free_list, spaceres);
if (error) if (error)
goto abort_return; goto out_trans_abort;
xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
if (new_parent)
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
finish_rename:
/* /*
* If this is a synchronous mount, make sure that the * For whiteouts, we need to bump the link count on the whiteout inode.
* rename transaction goes to disk before returning to * This means that failures all the way up to this point leave the inode
* the user. * on the unlinked list and so cleanup is a simple matter of dropping
* the remaining reference to it. If we fail here after bumping the link
* count, we're shutting down the filesystem so we'll never see the
* intermediate state on disk.
*/ */
if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) { if (wip) {
xfs_trans_set_sync(tp); ASSERT(wip->i_d.di_nlink == 0);
} error = xfs_bumplink(tp, wip);
if (error)
goto out_trans_abort;
error = xfs_iunlink_remove(tp, wip);
if (error)
goto out_trans_abort;
xfs_trans_log_inode(tp, wip, XFS_ILOG_CORE);
error = xfs_bmap_finish(&tp, &free_list, &committed); /*
if (error) { * Now we have a real link, clear the "I'm a tmpfile" state
xfs_bmap_cancel(&free_list); * flag from the inode so it doesn't accidentally get misused in
xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | * future.
XFS_TRANS_ABORT)); */
goto std_return; VFS_I(wip)->i_state &= ~I_LINKABLE;
} }
/* xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
* trans_commit will unlock src_ip, target_ip & decrement xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
* the vnode references. if (new_parent)
*/ xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
abort_return: error = xfs_finish_rename(tp, &free_list);
if (wip)
IRELE(wip);
return error;
out_trans_abort:
cancel_flags |= XFS_TRANS_ABORT; cancel_flags |= XFS_TRANS_ABORT;
error_return: out_bmap_cancel:
xfs_bmap_cancel(&free_list); xfs_bmap_cancel(&free_list);
out_trans_cancel:
xfs_trans_cancel(tp, cancel_flags); xfs_trans_cancel(tp, cancel_flags);
std_return: if (wip)
IRELE(wip);
return error; return error;
} }
......
...@@ -394,7 +394,7 @@ xfs_vn_rename( ...@@ -394,7 +394,7 @@ xfs_vn_rename(
struct xfs_name oname; struct xfs_name oname;
struct xfs_name nname; struct xfs_name nname;
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
return -EINVAL; return -EINVAL;
/* if we are exchanging files, we need to set i_mode of both files */ /* if we are exchanging files, we need to set i_mode of both files */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment