Моя ситуация была другой. Я устанавливал на новый ноут. На моей настроенной виртуальной машине было много памяти и диска, но эта проблема все еще оставалась у меня.
В конце концов я обнаружил, что, поскольку это был новый ноутбук, в конфигурации BIOS не была включена виртуализация.
Как только я включил это в BIOS, мои виртуальные машины работали должным образом.
Puede ver la diferencia comparando la salida de stat my-symlink
y stat my-symlink/
. my-symlink
, sin la barra inclinada, es el enlace simbólico en sí mismo; my-symlink/
, con la barra oblicua, es el directorio al que apunta el enlace simbólico, que puede verificar por separado comparando los inodos de my-symlink/
y del directorio al que apunta.
Con esa información en la mano, el comportamiento que está viendo coincide con el descrito en la especificación derm
:al procesar un enlace simbólico, rm
elimina el enlace sin descender "a" él si apunta a un directorio; al procesar un directorio (con la opción -r
), borra recursivamente su contenido. En el caso my-symlink/
, rm
intenta eliminar el "directorio", pero falla ya que no es un directorio sino un enlace simbólico; sin embargo, debido a la bandera -f
, esto no provoca un error.
Pensé en investigar el comportamiento un poco más, así que aquí hay otra respuesta.
Internamente, rm
usa FTS para recursivamente en jerarquías de archivos. fts_open
toma una matriz de rutas como parámetro y crea una estructura de árbol para cada ruta. Esto le permite al programador explorar varias ubicaciones sin problemas como si fueran parte de una jerarquía unificada.
Aquí hay un programa de prueba que puedes usar para jugar con FTS tú mismo.
#include <stdio.h>
#include <stdlib.h>
#include <fts.h>
int main(int argc, char* argv[])
{
if(argc < 2) return EXIT_FAILURE;
char* const* arr = argv + 1;
FTS* hier = fts_open(arr, FTS_NOSTAT | FTS_PHYSICAL, NULL);
FTSENT* ent;
while((ent = fts_read(hier))) {
printf("%s info=%d (D=%d DP=%d F=%d SL=%d)\n",
ent->fts_accpath, ent->fts_info,
ent->fts_info == FTS_D, ent->fts_info == FTS_DP,
ent->fts_info == FTS_F || ent->fts_info == FTS_NSOK,
ent->fts_info == FTS_SL);
}
fts_close(hier);
return EXIT_SUCCESS;
}
Supongamos que hemos creado la siguiente estructura de directorios:
$ mkdir dir
$ touch dir/file
$ ln -s dir sym
Ahora, consideremos su primer caso y veamos cómo FTS lidera la exploración.
$ gcc fts.c
$./a.out sym
sym info=12 (D=0 DP=0 F=0 SL=1)
Como puede ver, en este caso, sym
se ve como un archivo. Un enlace simbólico, para ser más exactos. Con esta información, rm
lo trataría como un archivo y llamaría a unlinkat(AT_FDCWD, "sym", 0)
. El último parámetro (0 )hace que unlinkat
se comporte como unlink
. En otras palabras :simplemente borra un archivo. Como resultado, su enlace desaparece.
Ahora, echemos un vistazo a lo que sucede con sym/
.
$./a.out sym/
sym/ info=1 (D=1 DP=0 F=0 SL=0)
file info=11 (D=0 DP=0 F=1 SL=0)
sym/ info=6 (D=0 DP=1 F=0 SL=0)
En este caso, sym
fue tratado como su directorio de destino. Primero iteramos a sym
, luego sym/file
y luego sym
nuevamente. Este último se debe a cómo funciona FTS :primero, itera sobre el contenido y luego regresa al nodo raíz. Esto es bastante conveniente para rm
. En la primera pasada (D
), puede borrar archivos, y en la segunda(DP
)eliminar los directorios vacíos.
Como puede ver, en este caso, FTS informa sym/
como un directorio en ambos casos. Esto se debe a que le dimos la ruta con una barra al final, lo que obliga al núcleo a interpretarla como un directorio. En el caso de un enlace, esto significa que lo seguirá sin importar nada. En términos más técnicos, las especificaciones dicen:
A pathname that contains at least one non-slash character and that ends with one or more trailing slashes shall be resolved as if a single dot character ( '.' ) were appended to the pathname.
Debido a que FTS informa sym/
como un directorio, rm
se comporta como si estuviera eliminando un directorio vacío. En consecuencia, llama unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
.Esto hace que unlinkat
se comporte como rmdir
.
Sin embargo, al resolver la ruta sym/
, la llamada al sistema unlinkat
se dará cuenta de que, de hecho, no se le está dando un directorio. Por lo tanto, informará ENOTDIR
, lo que desencadena:
$ rm: cannot remove ‘sym/’: Not a directory
Y, de hecho, si elimina la marca -f
de sus llamadas... Eso es exactamente lo que verá. Ahora, si esto es un error o una característica... No tengo idea.