tar_in: add keepdirlink option for --keep-directory-symlink

Add a new optional boolean argument 'keepdirlink' to tar_in that passes
--keep-directory-symlink to tar. This preserves existing symlinks to
directories when extracting, which is important for usrmerge systems
where /lib is a symlink to /usr/lib.

Without this option, extracting a tarball containing lib/modules/...
to / would replace the /lib symlink with a real directory, breaking
the system.

Signed-off-by: Anders Roxell <anders.roxell@linaro.org>
This commit is contained in:
Anders Roxell
2026-01-16 10:17:05 +01:00
committed by rwmjones
parent 5da8102f5f
commit 7fee08e53f
2 changed files with 18 additions and 8 deletions

View File

@@ -134,7 +134,7 @@ write_cb (void *fd_ptr, const void *buf, size_t len)
/* Has one FileIn parameter. */
/* Takes optional arguments, consult optargs_bitmask. */
int
do_tar_in (const char *dir, const char *compress, int xattrs, int selinux, int acls)
do_tar_in (const char *dir, const char *compress, int xattrs, int selinux, int acls, int keepdirlink)
{
const char *filter;
int err, r;
@@ -179,6 +179,9 @@ do_tar_in (const char *dir, const char *compress, int xattrs, int selinux, int a
if (!(optargs_bitmask & GUESTFS_TAR_IN_ACLS_BITMASK))
acls = 0;
if (!(optargs_bitmask & GUESTFS_TAR_IN_KEEPDIRLINK_BITMASK))
keepdirlink = 0;
fd = mkstemp (error_file);
if (fd == -1) {
err = errno;
@@ -203,7 +206,7 @@ do_tar_in (const char *dir, const char *compress, int xattrs, int selinux, int a
}
fprintf (fp, "tar -C ");
sysroot_shell_quote (dir, fp);
fprintf (fp, "%s -xf - %s%s%s%s2> %s",
fprintf (fp, "%s -xf - %s%s%s%s%s2> %s",
filter,
chown_supported ? "" : "--no-same-owner ",
/* --xattrs-include=* is a workaround for a bug
@@ -213,6 +216,7 @@ do_tar_in (const char *dir, const char *compress, int xattrs, int selinux, int a
xattrs ? "--xattrs --xattrs-include='*' " : "",
selinux ? "--selinux " : "",
acls ? "--acls " : "",
keepdirlink ? "--keep-directory-symlink " : "",
error_file);
if (fclose (fp) == EOF)
goto cmd_error;
@@ -272,7 +276,7 @@ int
do_tgz_in (const char *dir)
{
optargs_bitmask = GUESTFS_TAR_IN_COMPRESS_BITMASK;
return do_tar_in (dir, "gzip", 0, 0, 0);
return do_tar_in (dir, "gzip", 0, 0, 0, 0);
}
/* Has one FileIn parameter. */
@@ -280,7 +284,7 @@ int
do_txz_in (const char *dir)
{
optargs_bitmask = GUESTFS_TAR_IN_COMPRESS_BITMASK;
return do_tar_in (dir, "xz", 0, 0, 0);
return do_tar_in (dir, "xz", 0, 0, 0, 0);
}
/* Has one FileOut parameter. */

View File

@@ -2631,21 +2631,21 @@ To get the checksums for many files, use C<guestfs_checksums_out>.|} };
{ defaults with
name = "tar_in"; added = (1, 0, 3);
style = RErr, [String (FileIn, "tarfile"); String (Pathname, "directory")], [OString "compress"; OBool "xattrs"; OBool "selinux"; OBool "acls"];
style = RErr, [String (FileIn, "tarfile"); String (Pathname, "directory")], [OString "compress"; OBool "xattrs"; OBool "selinux"; OBool "acls"; OBool "keepdirlink"];
once_had_no_optargs = true;
cancellable = true;
tests = [
InitScratchFS, Always, TestResultString (
[["mkdir"; "/tar_in"];
["tar_in"; "$srcdir/../test-data/files/helloworld.tar"; "/tar_in"; "NOARG"; ""; ""; ""];
["tar_in"; "$srcdir/../test-data/files/helloworld.tar"; "/tar_in"; "NOARG"; ""; ""; ""; ""];
["cat"; "/tar_in/hello"]], "hello\n"), [];
InitScratchFS, Always, TestResultString (
[["mkdir"; "/tar_in_gz"];
["tar_in"; "$srcdir/../test-data/files/helloworld.tar.gz"; "/tar_in_gz"; "gzip"; ""; ""; ""];
["tar_in"; "$srcdir/../test-data/files/helloworld.tar.gz"; "/tar_in_gz"; "gzip"; ""; ""; ""; ""];
["cat"; "/tar_in_gz/hello"]], "hello\n"), [];
InitScratchFS, IfAvailable "xz", TestResultString (
[["mkdir"; "/tar_in_xz"];
["tar_in"; "$srcdir/../test-data/files/helloworld.tar.xz"; "/tar_in_xz"; "xz"; ""; ""; ""];
["tar_in"; "$srcdir/../test-data/files/helloworld.tar.xz"; "/tar_in_xz"; "xz"; ""; ""; ""; ""];
["cat"; "/tar_in_xz/hello"]], "hello\n"), []
];
shortdesc = "unpack tarfile to directory";
@@ -2674,6 +2674,12 @@ If set to true, SELinux contexts are restored from the tar file.
If set to true, POSIX ACLs are restored from the tar file.
=item C<keepdirlink>
If set to true, existing symlinks to directories are followed when
extracting from the tar file. This is important for usrmerge systems
where F</lib> is a symlink to F</usr/lib>.
=back|} };
{ defaults with