aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2024-01-09 09:40:38 -0800
committerDarrick J. Wong <djwong@kernel.org>2024-05-10 17:22:25 -0700
commita7b13e5a0b68c0238406a259927627885e2df37c (patch)
treefaa91510fc59b04e171ba0c93d0157e93c3dc300
parent448305e06f5f42d69d15417e61e047987df80c19 (diff)
downloadxfsprogs-dev-defrag-freespace.tar.gz
spaceman: move inodes with hardlinksdefrag-freespace_2024-05-10defrag-freespace
When a inode to be moved to a different AG has multiple hard links, we need to "move" all the hard links, too. To do this, we need to create temporary hardlinks to the new file, and then use rename exchange to swap all the hardlinks that point to the old inode with new hardlinks that point to the new inode. We already know that an inode has hard links via the path discovery, and we can check it against the link count that is reported for the inode before we start building the link farm. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--spaceman/find_owner.c13
-rw-r--r--spaceman/move_inode.c119
-rw-r--r--spaceman/relocation.c35
-rw-r--r--spaceman/relocation.h6
4 files changed, 140 insertions, 33 deletions
diff --git a/spaceman/find_owner.c b/spaceman/find_owner.c
index 8e93145539..1984d0ee7c 100644
--- a/spaceman/find_owner.c
+++ b/spaceman/find_owner.c
@@ -240,7 +240,6 @@ resolve_owner_cb(
struct FTW *data)
{
struct inode_path *ipath, *slot_ipath;
- int pathlen;
struct inode_path **slot;
/*
@@ -260,17 +259,9 @@ _("Failed to obtain stat(2) information from path %s. Aborting\n"),
}
/* Allocate a new inode path and record the path in it. */
- pathlen = strlen(path);
- ipath = calloc(1, sizeof(*ipath) + pathlen + 1);
- if (!ipath) {
- fprintf(stderr,
-_("Aborting: Storing path %s for inode 0x%lx failed: %s\n"),
- path, stat->st_ino, strerror(ENOMEM));
+ ipath = ipath_alloc(path, stat);
+ if (!ipath)
return -ENOMEM;
- }
- INIT_LIST_HEAD(&ipath->path_list);
- memcpy(&ipath->path[0], path, pathlen);
- ipath->ino = stat->st_ino;
/*
* If the slot contains the inode number we just looked up, then we
diff --git a/spaceman/move_inode.c b/spaceman/move_inode.c
index ab3c12f5de..3a18292957 100644
--- a/spaceman/move_inode.c
+++ b/spaceman/move_inode.c
@@ -36,12 +36,14 @@ create_tmpfile(
struct xfs_fd *xfd,
xfs_agnumber_t agno,
char **tmpfile,
- int *tmpfd)
+ int *tmpfd,
+ int link_count)
{
char name[PATH_MAX + 1];
+ char linkname[PATH_MAX + 1];
mode_t mask;
int fd;
- int i;
+ int i, j;
int ret;
/* construct tmpdir */
@@ -105,14 +107,36 @@ create_tmpfile(
fprintf(stderr, _("cannot create tmpfile: %s: %s\n"),
name, strerror(errno));
ret = -errno;
+ goto out_cleanup_dir;
+ }
+
+ /* Create hard links to temporary file. */
+ for (j = link_count; j > 1; i--) {
+ snprintf(linkname, PATH_MAX, "%s/.spaceman/dir%d/tmpfile.%d.hardlink.%d", mnt, i, getpid(), j);
+ ret = link(name, linkname);
+ if (ret < 0) {
+ fprintf(stderr, _("cannot create hardlink: %s: %s\n"),
+ linkname, strerror(errno));
+ ret = -errno;
+ goto out_cleanup_links;
+ }
}
+
/* return name and fd */
(void)umask(mask);
*tmpfd = fd;
*tmpfile = strdup(name);
return 0;
+
+out_cleanup_links:
+ for (; j <= link_count; j++) {
+ snprintf(linkname, PATH_MAX, "%s/.spaceman/dir%d/tmpfile.%d.hardlink.%d", mnt, i, getpid(), j);
+ unlink(linkname);
+ }
+ close(fd);
+ unlink(name);
out_cleanup_dir:
snprintf(name, PATH_MAX, "%s/.spaceman", mnt);
rmdir(name);
@@ -405,21 +429,53 @@ exchange_inodes(
return 0;
}
+static int
+exchange_hardlinks(
+ struct inode_path *ipath,
+ const char *tmpfile)
+{
+ char linkname[PATH_MAX];
+ struct inode_path *linkpath;
+ int i = 2;
+ int ret;
+
+ list_for_each_entry(linkpath, &ipath->path_list, path_list) {
+ if (i++ > ipath->link_count) {
+ fprintf(stderr, "ipath link count mismatch!\n");
+ return 0;
+ }
+
+ snprintf(linkname, PATH_MAX, "%s.hardlink.%d", tmpfile, i);
+ ret = renameat2(AT_FDCWD, linkname,
+ AT_FDCWD, linkpath->path, RENAME_EXCHANGE);
+ if (ret) {
+ fprintf(stderr,
+ "failed to exchange hard link %s with %s: %s\n",
+ linkname, linkpath->path, strerror(errno));
+ return -errno;
+ }
+ }
+ return 0;
+}
+
int
relocate_file_to_ag(
const char *mnt,
- const char *path,
+ struct inode_path *ipath,
struct xfs_fd *xfd,
xfs_agnumber_t agno)
{
int ret;
int tmpfd = -1;
char *tmpfile = NULL;
+ int i;
- fprintf(stderr, "move mnt %s, path %s, agno %d\n", mnt, path, agno);
+ fprintf(stderr, "move mnt %s, path %s, agno %d\n",
+ mnt, ipath->path, agno);
/* create temporary file in agno */
- ret = create_tmpfile(mnt, xfd, agno, &tmpfile, &tmpfd);
+ ret = create_tmpfile(mnt, xfd, agno, &tmpfile, &tmpfd,
+ ipath->link_count);
if (ret)
return ret;
@@ -444,12 +500,28 @@ relocate_file_to_ag(
goto out_cleanup;
/* swap the inodes over */
- ret = exchange_inodes(xfd, tmpfd, tmpfile, path);
+ ret = exchange_inodes(xfd, tmpfd, tmpfile, ipath->path);
+ if (ret)
+ goto out_cleanup;
+
+ /* swap the hard links over */
+ ret = exchange_hardlinks(ipath, tmpfile);
+ if (ret)
+ goto out_cleanup;
out_cleanup:
if (ret == -1)
ret = -errno;
+ /* remove old hard links */
+ for (i = 2; i <= ipath->link_count; i++) {
+ char linkname[PATH_MAX + 256]; // anti-warning-crap
+
+ snprintf(linkname, PATH_MAX + 256, "%s.hardlink.%d", tmpfile, i);
+ unlink(linkname);
+ }
+
+ /* remove tmpfile */
close(tmpfd);
if (tmpfile)
unlink(tmpfile);
@@ -459,10 +531,31 @@ out_cleanup:
}
static int
+build_ipath(
+ const char *path,
+ struct stat *st,
+ struct inode_path **ipathp)
+{
+ struct inode_path *ipath;
+
+ *ipathp = NULL;
+
+ ipath = ipath_alloc(path, st);
+ if (!ipath)
+ return -ENOMEM;
+
+ /* we only move a single path with move_inode */
+ ipath->link_count = 1;
+ *ipathp = ipath;
+ return 0;
+}
+
+static int
move_inode_f(
int argc,
char **argv)
{
+ struct inode_path *ipath = NULL;
void *fshandle;
size_t fshdlen;
xfs_agnumber_t agno = 0;
@@ -511,24 +604,30 @@ _("Destination AG %d does not exist. Filesystem only has %d AGs\n"),
goto exit_fail;
}
- if (S_ISREG(st.st_mode)) {
- ret = relocate_file_to_ag(file->fs_path.fs_dir, file->name,
- &file->xfd, agno);
- } else {
+ if (!S_ISREG(st.st_mode)) {
fprintf(stderr, _("Unsupported: %s is not a regular file.\n"),
file->name);
goto exit_fail;
}
+ ret = build_ipath(file->name, &st, &ipath);
+ if (ret)
+ goto exit_fail;
+
+ ret = relocate_file_to_ag(file->fs_path.fs_dir, ipath,
+ &file->xfd, agno);
if (ret) {
fprintf(stderr, _("Failed to move inode to AG %d: %s\n"),
agno, strerror(-ret));
goto exit_fail;
}
+ free(ipath);
fshandle_destroy();
return 0;
exit_fail:
+ if (ipath)
+ free(ipath);
fshandle_destroy();
exitcode = 1;
return 0;
diff --git a/spaceman/relocation.c b/spaceman/relocation.c
index 7b125cc0ae..b096027216 100644
--- a/spaceman/relocation.c
+++ b/spaceman/relocation.c
@@ -318,6 +318,30 @@ forget_reloc_ino(
static struct cmdinfo relocate_cmd;
+struct inode_path *
+ipath_alloc(
+ const char *path,
+ const struct stat *stat)
+{
+ struct inode_path *ipath;
+ int pathlen = strlen(path);
+
+ /* Allocate a new inode path and record the path in it. */
+ ipath = calloc(1, sizeof(*ipath) + pathlen + 1);
+ if (!ipath) {
+ fprintf(stderr,
+_("Failed to allocate ipath %s for inode 0x%llx failed: %s\n"),
+ path, (unsigned long long)stat->st_ino,
+ strerror(-errno));
+ return NULL;
+ }
+ INIT_LIST_HEAD(&ipath->path_list);
+ memcpy(&ipath->path[0], path, pathlen);
+ ipath->ino = stat->st_ino;
+
+ return ipath;
+}
+
static int
relocate_targets_to_ag(
const char *mnt,
@@ -336,15 +360,6 @@ relocate_targets_to_ag(
if (!ipath)
break;
- /* XXX: don't handle hard link cases yet */
- if (ipath->link_count > 1) {
- fprintf(stderr,
- "FIXME! Skipping hardlinked inode at path %s\n",
- ipath->path);
- goto next;
- }
-
-
ret = stat(ipath->path, &st);
if (ret) {
fprintf(stderr, _("stat(%s) failed: %s\n"),
@@ -367,7 +382,7 @@ relocate_targets_to_ag(
}
/* move to destination AG */
- ret = relocate_file_to_ag(mnt, ipath->path, &xfd, dst_agno);
+ ret = relocate_file_to_ag(mnt, ipath, &xfd, dst_agno);
xfd_close(&xfd);
/*
diff --git a/spaceman/relocation.h b/spaceman/relocation.h
index d4c71b7bb7..2c807aa678 100644
--- a/spaceman/relocation.h
+++ b/spaceman/relocation.h
@@ -43,9 +43,11 @@ struct inode_path {
*/
#define UNLINKED_IPATH ((struct inode_path *)1)
+struct inode_path *ipath_alloc(const char *path, const struct stat *st);
+
int find_relocation_targets(xfs_agnumber_t agno);
-int relocate_file_to_ag(const char *mnt, const char *path, struct xfs_fd *xfd,
- xfs_agnumber_t agno);
+int relocate_file_to_ag(const char *mnt, struct inode_path *ipath,
+ struct xfs_fd *xfd, xfs_agnumber_t agno);
int resolve_target_paths(const char *mntpt);
#endif /* XFS_SPACEMAN_RELOCATION_H_ */