Uma verificação rápida da fonte gzip (especificamente, o gzip 1.6 como incluído no Ubuntu 14.04), mostra que o comportamento observado vem da função open_and_stat , começando na linha 1037 do gzip.c:
static int
open_and_stat (char *name, int flags, mode_t mode, struct stat *st)
{
int fd;
/* Refuse to follow symbolic links unless -c or -f. */
if (!to_stdout && !force)
{
if (HAVE_WORKING_O_NOFOLLOW)
flags |= O_NOFOLLOW;
else
{
#if HAVE_LSTAT || defined lstat
if (lstat (name, st) != 0)
return -1;
else if (S_ISLNK (st->st_mode))
{
errno = ELOOP;
return -1;
}
#endif
}
}
fd = OPEN (name, flags, mode);
if (0 <= fd && fstat (fd, st) != 0)
{
int e = errno;
close (fd);
errno = e;
return -1;
}
return fd;
}
Note que a linha de comentário afirma que o gzip não seguirá links simbólicos a menos que seja chamado com os sinalizadores -c ou -f, e dentro do #if ... #endif a variável errno está configurada para ELOOP (muitos links simbólicos encontrado) se o arquivo a ser compactado é realmente um link simbólico.
Agora, na página man gzip (1), os sinalizadores -c e -f são:
-c --stdout --to-stdout Write output on standard output; keep original files unchanged. If there are several input files, the output consists of a sequence of independently com‐ pressed members. To obtain better compression, concatenate all input files before compressing them. -f --force Force compression or decompression even if the file has multiple links or the corresponding file already exists, or if the compressed data is read from or written to a terminal. If the input data is not in a format recognized by gzip, and if the option --stdout is also given, copy the input data without change to the standard output: let zcat behave as cat. If -f is not given, and when not running in the background, gzip prompts to verify whether an existing file should be overwritten.
Colocando todos juntos e voltando à pergunta original:
- O primeiro exemplo falha porque está tentando compactar o symlink real (mesmo que seja não um loop de link real)
- O segundo usa o sinalizador -c, portanto, ele está lendo o conteúdo do arquivo original e, em seguida, gravando a saída compactada para stdout, portanto, é bem-sucedido.
- Um terceiro cenário é usar -f em vez de -c. Neste caso, o gzip não reclama ao tentar compactar um symlink, mas na descompactação ele se torna um arquivo regular, como mostrado:
$ ls -l total 4 -rw-rw-r-- 1 x86tux x86tux 13 Jun 16 13:10 realfile.txt lrwxrwxrwx 1 x86tux x86tux 12 Jun 16 23:40 symlink.txt -> realfile.txt $ gzip symlink.txt gzip: symlink.txt: Too many levels of symbolic links $ gzip -f symlink.txt $ ls -l total 8 -rw-rw-r-- 1 x86tux x86tux 13 Jun 16 13:10 realfile.txt -rw-rw-r-- 1 x86tux x86tux 45 Jun 16 13:10 symlink.txt.gz $ gunzip symlink.txt.gz $ ls -l total 8 -rw-rw-r-- 1 x86tux x86tux 13 Jun 16 13:10 realfile.txt -rw-rw-r-- 1 x86tux x86tux 13 Jun 16 13:10 symlink.txt $ md5sum * 618f486e0225d305d16d0648ed44b1eb realfile.txt 618f486e0225d305d16d0648ed44b1eb symlink.txt