Skip to content

Commit 28d0d81

Browse files
author
Darrick J. Wong
committed
xfs: create libxfs helper to rename two directory entries
Create a new libxfs function to rename two directory entries. The upcoming metadata directory feature will need this to replace a metadata inode directory entry. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent a55712b commit 28d0d81

File tree

3 files changed

+273
-215
lines changed

3 files changed

+273
-215
lines changed

fs/xfs/libxfs/xfs_dir2.c

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "xfs_trans_space.h"
2424
#include "xfs_parent.h"
2525
#include "xfs_ag.h"
26+
#include "xfs_ialloc.h"
2627

2728
const struct xfs_name xfs_name_dotdot = {
2829
.name = (const unsigned char *)"..",
@@ -1080,3 +1081,229 @@ xfs_dir_exchange_children(
10801081

10811082
return 0;
10821083
}
1084+
1085+
/*
1086+
* Given an entry (@src_name, @src_ip) in directory @src_dp, make the entry
1087+
* @target_name in directory @target_dp point to @src_ip and remove the
1088+
* original entry, cleaning up everything left behind.
1089+
*
1090+
* Cleanup involves dropping a link count on @target_ip, and either removing
1091+
* the (@src_name, @src_ip) entry from @src_dp or simply replacing the entry
1092+
* with (@src_name, @wip) if a whiteout inode @wip is supplied.
1093+
*
1094+
* All inodes must have the ILOCK held. We assume that if @src_ip is a
1095+
* directory then its '..' doesn't already point to @target_dp, and that @wip
1096+
* is a freshly allocated whiteout.
1097+
*/
1098+
int
1099+
xfs_dir_rename_children(
1100+
struct xfs_trans *tp,
1101+
struct xfs_dir_update *du_src,
1102+
struct xfs_dir_update *du_tgt,
1103+
unsigned int spaceres,
1104+
struct xfs_dir_update *du_wip)
1105+
{
1106+
struct xfs_mount *mp = tp->t_mountp;
1107+
struct xfs_inode *src_dp = du_src->dp;
1108+
const struct xfs_name *src_name = du_src->name;
1109+
struct xfs_inode *src_ip = du_src->ip;
1110+
struct xfs_inode *target_dp = du_tgt->dp;
1111+
const struct xfs_name *target_name = du_tgt->name;
1112+
struct xfs_inode *target_ip = du_tgt->ip;
1113+
bool new_parent = (src_dp != target_dp);
1114+
bool src_is_directory;
1115+
int error;
1116+
1117+
src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
1118+
1119+
/*
1120+
* Check for expected errors before we dirty the transaction
1121+
* so we can return an error without a transaction abort.
1122+
*/
1123+
if (target_ip == NULL) {
1124+
/*
1125+
* If there's no space reservation, check the entry will
1126+
* fit before actually inserting it.
1127+
*/
1128+
if (!spaceres) {
1129+
error = xfs_dir_canenter(tp, target_dp, target_name);
1130+
if (error)
1131+
return error;
1132+
}
1133+
} else {
1134+
/*
1135+
* If target exists and it's a directory, check that whether
1136+
* it can be destroyed.
1137+
*/
1138+
if (S_ISDIR(VFS_I(target_ip)->i_mode) &&
1139+
(!xfs_dir_isempty(target_ip) ||
1140+
(VFS_I(target_ip)->i_nlink > 2)))
1141+
return -EEXIST;
1142+
}
1143+
1144+
/*
1145+
* Directory entry creation below may acquire the AGF. Remove
1146+
* the whiteout from the unlinked list first to preserve correct
1147+
* AGI/AGF locking order. This dirties the transaction so failures
1148+
* after this point will abort and log recovery will clean up the
1149+
* mess.
1150+
*
1151+
* For whiteouts, we need to bump the link count on the whiteout
1152+
* inode. After this point, we have a real link, clear the tmpfile
1153+
* state flag from the inode so it doesn't accidentally get misused
1154+
* in future.
1155+
*/
1156+
if (du_wip->ip) {
1157+
struct xfs_perag *pag;
1158+
1159+
ASSERT(VFS_I(du_wip->ip)->i_nlink == 0);
1160+
1161+
pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, du_wip->ip->i_ino));
1162+
error = xfs_iunlink_remove(tp, pag, du_wip->ip);
1163+
xfs_perag_put(pag);
1164+
if (error)
1165+
return error;
1166+
1167+
xfs_bumplink(tp, du_wip->ip);
1168+
}
1169+
1170+
/*
1171+
* Set up the target.
1172+
*/
1173+
if (target_ip == NULL) {
1174+
/*
1175+
* If target does not exist and the rename crosses
1176+
* directories, adjust the target directory link count
1177+
* to account for the ".." reference from the new entry.
1178+
*/
1179+
error = xfs_dir_createname(tp, target_dp, target_name,
1180+
src_ip->i_ino, spaceres);
1181+
if (error)
1182+
return error;
1183+
1184+
xfs_trans_ichgtime(tp, target_dp,
1185+
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
1186+
1187+
if (new_parent && src_is_directory) {
1188+
xfs_bumplink(tp, target_dp);
1189+
}
1190+
} else { /* target_ip != NULL */
1191+
/*
1192+
* Link the source inode under the target name.
1193+
* If the source inode is a directory and we are moving
1194+
* it across directories, its ".." entry will be
1195+
* inconsistent until we replace that down below.
1196+
*
1197+
* In case there is already an entry with the same
1198+
* name at the destination directory, remove it first.
1199+
*/
1200+
error = xfs_dir_replace(tp, target_dp, target_name,
1201+
src_ip->i_ino, spaceres);
1202+
if (error)
1203+
return error;
1204+
1205+
xfs_trans_ichgtime(tp, target_dp,
1206+
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
1207+
1208+
/*
1209+
* Decrement the link count on the target since the target
1210+
* dir no longer points to it.
1211+
*/
1212+
error = xfs_droplink(tp, target_ip);
1213+
if (error)
1214+
return error;
1215+
1216+
if (src_is_directory) {
1217+
/*
1218+
* Drop the link from the old "." entry.
1219+
*/
1220+
error = xfs_droplink(tp, target_ip);
1221+
if (error)
1222+
return error;
1223+
}
1224+
} /* target_ip != NULL */
1225+
1226+
/*
1227+
* Remove the source.
1228+
*/
1229+
if (new_parent && src_is_directory) {
1230+
/*
1231+
* Rewrite the ".." entry to point to the new
1232+
* directory.
1233+
*/
1234+
error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
1235+
target_dp->i_ino, spaceres);
1236+
ASSERT(error != -EEXIST);
1237+
if (error)
1238+
return error;
1239+
}
1240+
1241+
/*
1242+
* We always want to hit the ctime on the source inode.
1243+
*
1244+
* This isn't strictly required by the standards since the source
1245+
* inode isn't really being changed, but old unix file systems did
1246+
* it and some incremental backup programs won't work without it.
1247+
*/
1248+
xfs_trans_ichgtime(tp, src_ip, XFS_ICHGTIME_CHG);
1249+
xfs_trans_log_inode(tp, src_ip, XFS_ILOG_CORE);
1250+
1251+
/*
1252+
* Adjust the link count on src_dp. This is necessary when
1253+
* renaming a directory, either within one parent when
1254+
* the target existed, or across two parent directories.
1255+
*/
1256+
if (src_is_directory && (new_parent || target_ip != NULL)) {
1257+
1258+
/*
1259+
* Decrement link count on src_directory since the
1260+
* entry that's moved no longer points to it.
1261+
*/
1262+
error = xfs_droplink(tp, src_dp);
1263+
if (error)
1264+
return error;
1265+
}
1266+
1267+
/*
1268+
* For whiteouts, we only need to update the source dirent with the
1269+
* inode number of the whiteout inode rather than removing it
1270+
* altogether.
1271+
*/
1272+
if (du_wip->ip)
1273+
error = xfs_dir_replace(tp, src_dp, src_name, du_wip->ip->i_ino,
1274+
spaceres);
1275+
else
1276+
error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
1277+
spaceres);
1278+
if (error)
1279+
return error;
1280+
1281+
xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
1282+
xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
1283+
if (new_parent)
1284+
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
1285+
1286+
/* Schedule parent pointer updates. */
1287+
if (du_wip->ppargs) {
1288+
error = xfs_parent_addname(tp, du_wip->ppargs, src_dp,
1289+
src_name, du_wip->ip);
1290+
if (error)
1291+
return error;
1292+
}
1293+
1294+
if (du_src->ppargs) {
1295+
error = xfs_parent_replacename(tp, du_src->ppargs, src_dp,
1296+
src_name, target_dp, target_name, src_ip);
1297+
if (error)
1298+
return error;
1299+
}
1300+
1301+
if (du_tgt->ppargs) {
1302+
error = xfs_parent_removename(tp, du_tgt->ppargs, target_dp,
1303+
target_name, target_ip);
1304+
if (error)
1305+
return error;
1306+
}
1307+
1308+
return 0;
1309+
}

fs/xfs/libxfs/xfs_dir2.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,5 +327,8 @@ int xfs_dir_remove_child(struct xfs_trans *tp, unsigned int resblks,
327327

328328
int xfs_dir_exchange_children(struct xfs_trans *tp, struct xfs_dir_update *du1,
329329
struct xfs_dir_update *du2, unsigned int spaceres);
330+
int xfs_dir_rename_children(struct xfs_trans *tp, struct xfs_dir_update *du_src,
331+
struct xfs_dir_update *du_tgt, unsigned int spaceres,
332+
struct xfs_dir_update *du_wip);
330333

331334
#endif /* __XFS_DIR2_H__ */

0 commit comments

Comments
 (0)