@@ -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
@@ -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);
@@ -458,11 +530,32 @@ relocate_file_to_ag(
return ret;
}
+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;
@@ -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);
/*
@@ -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_ */