From 7fee08e53f5146b7ef8a48759c930ab29f8efc4e Mon Sep 17 00:00:00 2001 From: Anders Roxell Date: Fri, 16 Jan 2026 10:17:05 +0100 Subject: [PATCH] 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 --- daemon/tar.c | 12 ++++++++---- generator/actions_core.ml | 14 ++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/daemon/tar.c b/daemon/tar.c index 1d6a4b7eb..063e41ba3 100644 --- a/daemon/tar.c +++ b/daemon/tar.c @@ -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. */ diff --git a/generator/actions_core.ml b/generator/actions_core.ml index b4ec6db87..96cf63135 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -2631,21 +2631,21 @@ To get the checksums for many files, use C.|} }; { 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 + +If set to true, existing symlinks to directories are followed when +extracting from the tar file. This is important for usrmerge systems +where F is a symlink to F. + =back|} }; { defaults with