summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-12-23 13:54:20 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-12-23 14:03:23 +0100
commit3d1f54abe4ef8f204b366c3eb107916f43845516 (patch)
treef85e4bcc654452478f7c9992a5a8638747232840
parentb4e5a24a3031c83ca51b50d34dea1a05aedf7b44 (diff)
Extend sqfs2tar to preserve hard links
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r--doc/sqfs2tar.16
-rw-r--r--tar/sqfs2tar.c80
2 files changed, 72 insertions, 14 deletions
diff --git a/doc/sqfs2tar.1 b/doc/sqfs2tar.1
index f42b266..35cc6b8 100644
--- a/doc/sqfs2tar.1
+++ b/doc/sqfs2tar.1
@@ -35,6 +35,12 @@ than once implies \fB\-\-keep\-as\-dir\fR.
Discard extended attributes from the SquashFS image. The default behavior is
to copy all xattrs attached to SquashFS inodes into the resulting tar archive.
.TP
+\fB\-\-no\-hard\-links\fR, \fB\-L\fR
+Normally, sqfs2tar runs hard link detection and generates hard links for
+entries that refer to the same inode. If this flag is set, hard link
+detection is not performed and duplicate data records are generated
+instead.
+.TP
\fB\-\-no\-skip\fR, \fB\-s\fR
Abort if a file cannot be stored in a tar record instead of skipping it.
.TP
diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c
index 58fb611..6e22621 100644
--- a/tar/sqfs2tar.c
+++ b/tar/sqfs2tar.c
@@ -22,11 +22,12 @@ static struct option long_opts[] = {
{ "root-becomes", required_argument, NULL, 'r' },
{ "no-skip", no_argument, NULL, 's' },
{ "no-xattr", no_argument, NULL, 'X' },
+ { "no-hard-links", no_argument, NULL, 'L' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
};
-static const char *short_opts = "d:kr:sXhV";
+static const char *short_opts = "d:kr:sXLhV";
static const char *usagestr =
"Usage: sqfs2tar [OPTIONS...] <sqfsfile>\n"
@@ -56,6 +57,8 @@ static const char *usagestr =
" Using --subdir more than once implies\n"
" --keep-as-dir.\n"
" --no-xattr, -X Do not copy extended attributes.\n"
+" --no-hard-links, -L Do not generate hard links. Produce duplicate\n"
+" entries instead.\n"
"\n"
" --no-skip, -s Abort if a file cannot be stored in a tar\n"
" archive. By default, it is simply skipped\n"
@@ -76,6 +79,7 @@ static unsigned int record_counter;
static bool dont_skip = false;
static bool keep_as_dir = false;
static bool no_xattr = false;
+static bool no_links = false;
static char *root_becomes = NULL;
static char **subdirs = NULL;
@@ -86,6 +90,7 @@ static sqfs_xattr_reader_t *xr;
static sqfs_data_reader_t *data;
static sqfs_file_t *file;
static sqfs_super_t super;
+static sqfs_hard_link_t *links = NULL;
static FILE *out_file = NULL;
@@ -153,6 +158,9 @@ static void process_args(int argc, char **argv)
case 'X':
no_xattr = true;
break;
+ case 'L':
+ no_links = true;
+ break;
case 'h':
fputs(usagestr, stdout);
goto out_success;
@@ -289,10 +297,28 @@ fail:
return -1;
}
+static char *prepend_prefix(char *name)
+{
+ size_t len = strlen(root_becomes);
+ char *temp = realloc(name, strlen(name) + len + 2);
+
+ if (temp == NULL) {
+ perror("assembling tar entry filename");
+ return NULL;
+ }
+
+ name = temp;
+ memmove(name + len + 1, name, strlen(name) + 1);
+ memcpy(name, root_becomes, len);
+ name[len] = '/';
+ return name;
+}
+
static int write_tree_dfs(const sqfs_tree_node_t *n)
{
tar_xattr_t *xattr = NULL, *xit;
- char *name, *target, *temp;
+ sqfs_hard_link_t *lnk = NULL;
+ char *name, *target;
struct stat sb;
size_t len;
int ret;
@@ -332,25 +358,30 @@ static int write_tree_dfs(const sqfs_tree_node_t *n)
if (canonicalize_name(name))
goto out_skip;
- if (root_becomes != NULL) {
- len = strlen(root_becomes);
- temp = realloc(name, strlen(name) + len + 2);
-
- if (temp == NULL) {
- perror("assembling tar entry filename");
- free(name);
- return -1;
+ for (lnk = links; lnk != NULL; lnk = lnk->next) {
+ if (lnk->inode_number == n->inode->base.inode_number) {
+ if (strcmp(name, lnk->target) == 0)
+ lnk = NULL;
+ break;
}
+ }
- name = temp;
- memmove(name + len + 1, name, strlen(name) + 1);
- memcpy(name, root_becomes, len);
- name[len] = '/';
+ if (root_becomes != NULL) {
+ name = prepend_prefix(name);
+ if (name == NULL)
+ return -1;
}
}
inode_stat(n, &sb);
+ if (lnk != NULL) {
+ ret = write_hard_link(out_file, &sb, name, lnk->target,
+ record_counter++);
+ free(name);
+ return ret;
+ }
+
if (!no_xattr) {
if (get_xattrs(name, n->inode, &xattr)) {
free(name);
@@ -455,6 +486,7 @@ int main(int argc, char **argv)
sqfs_compressor_t *cmp;
sqfs_id_table_t *idtbl;
sqfs_dir_reader_t *dr;
+ sqfs_hard_link_t *lnk;
size_t i;
process_args(argc, argv);
@@ -600,6 +632,19 @@ int main(int argc, char **argv)
}
}
+ if (!no_links) {
+ if (sqfs_tree_find_hard_links(root, &links))
+ goto out_tree;
+
+ if (root_becomes != NULL) {
+ for (lnk = links; lnk != NULL; lnk = lnk->next) {
+ lnk->target = prepend_prefix(lnk->target);
+ if (lnk->target == NULL)
+ goto out;
+ }
+ }
+ }
+
if (write_tree_dfs(root))
goto out;
@@ -609,6 +654,13 @@ int main(int argc, char **argv)
status = EXIT_SUCCESS;
fflush(out_file);
out:
+ while (links != NULL) {
+ lnk = links;
+ links = links->next;
+ free(lnk->target);
+ free(lnk);
+ }
+out_tree:
if (root != NULL)
sqfs_dir_tree_destroy(root);
out_xr: