Add a separate symlink-creation phase when extracting an archive, by ignoring symlinks on the first pass, rewinding the archive, and then extracting only symlinks on the second pass.
This helps a lot with native symlinks (which require the destination to exist when created, so we can determine if it is a file or directory). Alternative implementations: We could collect symlinks, and defer making them until the end of extracting the archive. We'd also need to report errors if making those symlinks failed. We could close and re-open the archive, rather than rewinding it. Error handling if the archive isn't accessible the second time could be complex. --- install.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/install.cc b/install.cc index cec31a9..de98b99 100644 --- a/install.cc +++ b/install.cc @@ -99,7 +99,8 @@ class Installer HWND owner, io_stream *pkgfile, archive *tarstream, - io_stream *lst); + io_stream *lst, + bool symlink_phase); }; Installer::Installer() : errors(0) @@ -539,7 +540,12 @@ Installer::installOne (packagemeta &pkgm, const packageversion &ver, Log (LOG_PLAIN) << "Extracting from " << source.Cached () << endLog; bool error_in_this_package = _installOne(pkgm, prefixURL, prefixPath, owner, - pkgfile, tarstream, lst); + pkgfile, tarstream, lst, false); + if (tarstream->seek(0, IO_SEEK_SET) == -1) + Log (LOG_PLAIN) << "Error rewinding to extract symlinks" << source.Cached () << endLog; + + error_in_this_package |= _installOne(pkgm, prefixURL, prefixPath, owner, + pkgfile, tarstream, lst, true); if (lst) delete lst; @@ -562,7 +568,8 @@ Installer::_installOne (packagemeta &pkgm, HWND owner, io_stream *pkgfile, archive *tarstream, - io_stream *lst) + io_stream *lst, + bool symlink_phase) { bool error_in_this_package = false; bool ignoreInUseErrors = false; @@ -571,6 +578,12 @@ Installer::_installOne (packagemeta &pkgm, std::string fn; while ((fn = tarstream->next_file_name ()).size ()) { + if (symlink_phase != (tarstream->next_file_type () == ARCHIVE_FILE_SYMLINK)) + { + tarstream->skip_file(); + continue; + } + std::string canonicalfn = prefixPath + fn; // pathnames starting "." (i.e. dotfiles in the root directory) are -- 2.32.0