On 2020-12-28 Sebastian Andrzej Siewior wrote:
> xz refuses to decompress a file which has more than one hard link. It
> can be reproduced by (as per Vincent):
> |$ echo foo > file1
> |$ xz file1
> |$ ln file1.xz file2.xz
> |$ xz -dk file1.xz
> |xz: file1.xz: Input file has more than one hard link, skipping
> This behaviour is consistent with `gzip' and `bzip2' but it is not
> documented. The `--force' option would ignore this restriction.

The behavior is documented on the xz man page:

       Unless writing to standard output, xz will display a warning
       and skip the file if any of the following applies:

       o  File is not a regular file.  Symbolic links are not followed,
          and thus they are not considered to be regular files.

       o  File has more than one hard link.

       o  File has setuid, setgid, or sticky bit set.

The reason why it's done is indeed missing. I don't have any better
guesses than what you already listed in other messages.

> Ignore hard link count on input.

I'm fine with the patch since the suggested behavior makes sense to me
too. There's always a risk that some specific use case can break due to
this change but I guess that is acceptably low risk.

I wonder if it were more logical if --keep also allowed processing of
files with setuid, setgid, or sticky bit set. The same reasoning
applies to all these cases, I suppose, and they are all within the same
if-block in the code so it would still be a single-line change.

What about symlinks or files that aren't regular files (like block
devices)? I guess requiring --force for non-regular files makes sense
still but perhaps symlinks that point to regular files are OK to
process with --keep.

Any thoughts on this patch?

diff --git a/src/xz/file_io.c b/src/xz/file_io.c
index e1a37d5..80b56d3 100644
--- a/src/xz/file_io.c
+++ b/src/xz/file_io.c
@@ -536,8 +536,9 @@ io_open_src_real(file_pair *pair)
        // Symlinks are not followed unless writing to stdout or --force
-       // was used.
-       const bool follow_symlinks = opt_stdout || opt_force;
+       // or --keep was used.
+       const bool follow_symlinks
+                       = opt_stdout || opt_force || opt_keep_original;
        // We accept only regular files if we are writing the output
        // to disk too. bzip2 allows overriding this with --force but
@@ -674,7 +675,7 @@ io_open_src_real(file_pair *pair)
-       if (reg_files_only && !opt_force) {
+       if (reg_files_only && !opt_force && !opt_keep_original) {
                if (pair->src_st.st_mode & (S_ISUID | S_ISGID)) {
                        // gzip rejects setuid and setgid files even
                        // when --force was used. bzip2 doesn't check
@@ -683,7 +684,7 @@ io_open_src_real(file_pair *pair)
                        // and setgid bits there.
                        // We accept setuid and setgid files if
-                       // --force was used. We drop these bits
+                       // --force or --keep was used. We drop these bits
                        // explicitly in io_copy_attr().
                        message_warning(_("%s: File has setuid or "
                                        "setgid bit set, skipping"),
diff --git a/src/xz/xz.1 b/src/xz/xz.1
index db9de4f..e50e762 100644
--- a/src/xz/xz.1
+++ b/src/xz/xz.1
@@ -392,6 +392,20 @@ should be used.
 .BR \-k ", " \-\-keep
 Don't delete the input files.
+.IP ""
+.B xz
+this option also makes
+.B xz
+compress or decompress even if the input is
+a symbolic link to a regular file,
+has more than one hard link,
+or has the setuid, setgid, or sticky bit set.
+The setuid, setgid, and sticky bits are not copied
+to the target file.
+In earlier versions this was only done with
+.BR \-\-force .
 .BR \-f ", " \-\-force
 This option has several effects:

Lasse Collin  |  IRC: Larhzu @ IRCnet & Freenode

Reply via email to