← Sistemas de Archivos y E/S Bloque 1 · Archivos y sistemas de archivos · Tema 2 de 50

Tema 2

Archivos, tipos, atributos y rutas

En el tema anterior convertimos bloques numerados en archivos con nombre. Ahora vamos a precisar qué es un archivo para Linux: qué diferencia un documento de un directorio o de un dispositivo, dónde están sus atributos y cómo una ruta permite encontrarlo. Crearás un pequeño laboratorio de archivos, distinguirás su nombre de su identidad, observarás sus fechas y sus enlaces, escribirás un inspector de metadatos en C y recorrerás un árbol de directorios con un simulador. Son las ideas que necesitarás para entender después qué ocurre cuando un programa llama a open.

1. Qué es un archivo para el sistema operativo

Un editor habla de documentos, un reproductor de canciones y un compilador de programas fuente. El sistema operativo necesita una abstracción común que permita almacenarlos sin conocer cada formato. Para un archivo regular, esa abstracción es una secuencia de bytes identificados por su posición: byte 0, byte 1, byte 2… hasta el último byte anterior a su tamaño.

Un archivo regular es una secuencia de bytes con una longitud y un conjunto de metadatos. Un nombre, registrado en un directorio, permite encontrarlo. El significado de sus bytes corresponde a los programas que los interpretan.

Si un archivo contiene 41 42 43 0a en hexadecimal, un editor puede mostrar ABC y un salto de línea. Un visor hexadecimal mostrará cuatro valores y otro programa podría interpretar esos mismos bytes como parte de un número. El sistema de archivos no necesita decidir cuál de esas interpretaciones es la correcta: conserva la secuencia y ofrece operaciones para acceder a ella.

Esto permite separar tres preguntas que a menudo se confunden:

La extensión .txt es una convención útil para las personas y las aplicaciones. Renombrar foto.png como foto.txt no convierte una imagen en texto. En Linux, tampoco existe un tipo de inodo «PDF» o «programa C»: ambos se almacenan como archivos regulares.

Un archivo no es una variable en memoria

Una variable pertenece a un proceso y su dirección solo tiene sentido en su espacio de memoria. Un archivo tiene una existencia independiente de la ejecución que lo creó: otros procesos pueden abrirlo, el creador puede terminar y el archivo puede seguir disponible. En un sistema de archivos persistente, los datos y los metadatos se conservan en el almacenamiento.

La misma interfaz también se aplica a objetos que no almacenan un documento persistente. Bajo /proc hay archivos cuyo contenido genera el núcleo al leerlos; bajo /dev hay nombres que dan acceso a dispositivos. Por eso «todo es un archivo» expresa una idea de interfaz común, pero no significa que todo sea una secuencia de bytes guardada en un disco. Un socket y un archivo regular admiten operaciones distintas y tienen comportamientos distintos.

2. Los tipos de archivo de Linux

El primer carácter de una línea de ls -l indica el tipo de objeto. Los nueve siguientes describen permisos, que estudiaremos a fondo en el bloque 8. Por ahora nos interesa poder leer el comienzo de una línea como -rw-r--r-- o drwxr-xr-x.

CarácterTipoQué representaEjemplo
-Archivo regularBytes de un documento, imagen, ejecutable o cualquier otro contenido.notas.txt
dDirectorioUn conjunto de entradas que relacionan nombres con objetos del sistema de archivos./home
lEnlace simbólicoUna ruta que se utiliza para localizar otro objeto.actual → versiones/v2
cDispositivo de caracteresUna interfaz a un controlador, como una terminal o un dispositivo virtual./dev/null
bDispositivo de bloquesUna interfaz a un dispositivo direccionable por bloques./dev/sda, si existe
pFIFO o tubería con nombreUn canal de comunicación entre procesos con una entrada en el árbol de directorios.canal creado con mkfifo
sSocket UNIXUn punto de comunicación local que puede tener un nombre en el sistema de archivos.Un socket de un servicio bajo /run

La expresión archivo especial suele referirse a los nodos de dispositivo. Al abrir uno, el núcleo dirige las operaciones al controlador correspondiente. Leer /dev/zero produce bytes cero y escribir en /dev/null descarta los bytes. El tamaño que muestre su ficha no mide cuántos datos puedes leer o escribir en ellos.

Una FIFO tampoco es un archivo regular vacío que irá creciendo. Sus datos circulan entre procesos; si no hay otro extremo abierto, una apertura puede quedar esperando. No uses cat canal en el laboratorio esperando encontrar un documento: podría quedarse bloqueado hasta que otro proceso escriba. Los mecanismos de E/S y comunicación explicarán estos comportamientos en otros temas.

Dos significados de «tipo»

stat informa del tipo de objeto reconocido por el sistema. file intenta identificar el formato del contenido mediante patrones y otras reglas. Un archivo puede ser «regular» según stat y «PNG image data» según file; ambas respuestas son compatibles.

3. Laboratorio: nombres, contenido y tipos

Los ejemplos de este tema están pensados para Bash en Linux o Ubuntu sobre WSL. Usan herramientas habituales de GNU y no requieren permisos de administrador. Para observar permisos y enlaces de Linux con menos diferencias, en WSL conviene trabajar dentro del directorio personal de la distribución. Cada ejecución comienza en una carpeta temporal nueva:

terminal · crear el laboratorio
$ LAB=$(mktemp -d "$HOME/tema2.XXXXXX")
$ cd "$LAB"
$ mkdir -p curso/datos documentos
$ printf 'ABC\n' > curso/datos/notas.txt
$ printf 'Documento de prueba\n' > documentos/nota.txt
$ touch curso/vacio
$ printf 'configuracion=1\n' > curso/.config
$ ln -s datos/notas.txt curso/actual
$ ln -s ../documentos curso/acceso
$ mkfifo curso/canal
$ printf '%s\n' "$LAB"
/home/ana/tema2.A1b2C3

La última línea es ilustrativa: tu usuario y el sufijo generado serán distintos. La variable LAB conserva la ubicación para volver a ella durante el tema. mktemp -d crea la carpeta y evita mezclar la práctica con archivos de trabajo anteriores. Mantén la misma terminal para conservar esa variable.

Observar objetos de clases diferentes

terminal · el tipo no se deduce del nombre
$ stat -c '%F | %n' curso/datos/notas.txt curso curso/actual curso/canal /dev/null
regular file | curso/datos/notas.txt
directory | curso
symbolic link | curso/actual
fifo | curso/canal
character special file | /dev/null
$ od -An -tx1 curso/datos/notas.txt
 41 42 43 0a
$ cp curso/datos/notas.txt curso/imagen.png
$ file curso/imagen.png
curso/imagen.png: ASCII text
$ stat -c '%F | %s bytes' curso/imagen.png
regular file | 4 bytes

Las descripciones de las herramientas pueden aparecer traducidas según la configuración regional. Lo que importa es la clase de objeto. La copia llamada imagen.png sigue teniendo los mismos cuatro bytes que notas.txt; el nombre no añadió una cabecera PNG ni cambió la interpretación de file.

Los nombres ocultos y los espacios

El punto inicial de .config es una convención de las herramientas: ls lo omite por defecto y ls -a lo muestra. No es un tipo de archivo adicional ni un mecanismo de protección. Cualquier proceso con los permisos adecuados puede acceder a él por su nombre.

terminal · las comillas pertenecen al shell
$ printf 'total: 1200\n' > 'curso/informe final.txt'
$ cat 'curso/informe final.txt'
total: 1200
$ ls -a curso
.  ..  .config  acceso  actual  canal  datos  imagen.png  informe final.txt  vacio

El orden y la presentación de ls pueden variar. Las comillas no forman parte del nombre: hacen que Bash entregue el texto con espacios como un solo argumento. Sin ellas, cat curso/informe final.txt intenta abrir dos rutas distintas. La misma precaución explica por qué usamos cd "$LAB" y entrecomillamos las expansiones de variables.

En un componente de nombre de Linux no pueden aparecer la barra /, que separa componentes, ni el byte nulo, que termina las cadenas usadas por las interfaces del sistema. Otros caracteres, incluidos espacios o saltos de línea, pueden existir, aunque algunos dificultan el trabajo en la terminal. Usa nombres sencillos en los experimentos.

4. Los atributos: leer una ficha de stat

El contenido del archivo responde «¿qué bytes hay?». Sus metadatos responden «¿qué objeto es, cuánto mide, quién es su dueño y qué operaciones están permitidas?». La orden stat permite consultar esa ficha sin leer el documento completo.

terminal · consultar atributos sin depender del formato de ls
$ stat -c 'tipo=%F tamaño=%s enlaces=%h modo=%a uid=%u gid=%g' curso/datos/notas.txt
tipo=regular file tamaño=4 enlaces=1 modo=644 uid=1000 gid=1000
$ stat -c 'dispositivo=%d inodo=%i bloques=%b unidad=%B' curso/datos/notas.txt
dispositivo=2049 inodo=393220 bloques=8 unidad=512

Estas salidas son ejemplos de un sistema de archivos Linux con bloques de 4096 bytes. Los identificadores, permisos iniciales y bloques asignados dependen del equipo, el sistema de archivos y la máscara de creación. No necesitas obtener esos números exactos para completar la práctica.

AtributoQué significaQué no debes confundir
TipoLa clase de objeto: regular, directorio, enlace…No es la extensión ni el formato de los bytes.
TamañoEn un archivo regular, la longitud lógica en bytes.No es necesariamente el espacio reservado en el dispositivo.
Bloques asignadosLa contabilidad de almacenamiento que comunica el sistema de archivos.No siempre coincide con redondear el tamaño a bloques: hay archivos dispersos, compresión y otras particularidades.
UID y GIDIdentificadores numéricos del propietario y del grupo.Los nombres de usuario y grupo se obtienen mediante servicios de resolución de identidades.
ModoIncluye el tipo de objeto y los bits de permisos.El número octal mostrado por stat %a representa los bits de permisos, incluidos los especiales si están presentes.
EnlacesCuántos enlaces duros tiene el objeto.No cuenta los enlaces simbólicos que contienen una ruta hacia él.
Dispositivo e inodoPermiten distinguir objetos dentro de los sistemas de archivos montados.El número de inodo por sí solo no identifica un archivo entre volúmenes distintos.
FechasAcceso, modificación de contenido y cambio de estado; a veces también nacimiento.«Cambio» no significa «creación».

Tamaño lógico y almacenamiento asignado

En la salida anterior, el archivo tiene 4 bytes lógicos y 8 unidades de 512 bytes asignadas: 8 × 512 = 4096. El tamaño describe hasta dónde llega el contenido; los bloques describen su almacenamiento. Un archivo vacío puede tener longitud cero y no necesitar bloques de datos, aunque su nombre y sus metadatos sí consumen recursos.

En GNU stat, %b muestra el número de bloques asignados y %B la unidad de esa cuenta. No confundas ese valor con %o, que indica un tamaño de transferencia aconsejado. En C veremos la misma separación entre st_size, st_blocks y st_blksize. Una unidad de contabilidad, una unidad de asignación y una recomendación de E/S pueden tener valores distintos.

Para un directorio, el tamaño describe su propia representación, según el sistema de archivos; no suma el contenido de sus descendientes. Una carpeta con un video de 2 GiB no tiene por qué mostrar 2 GiB en stat. Para sumar una jerarquía se necesita recorrerla, que es lo que hace du y estudiaremos en el tema 3.

Una primera lectura de los permisos

El modo 644 corresponde a rw-r--r--: el dueño puede leer y escribir; el grupo y los demás pueden leer. Cada dígito suma r=4, w=2 y x=1. En un directorio, x permite buscar y atravesar sus entradas: necesitarlo para llegar a un archivo no convierte al directorio en un programa ejecutable.

No deduzcas todos los accesos posibles a partir de esos nueve bits. También intervienen credenciales, ACL, opciones de montaje y otras reglas. Aquí necesitamos reconocer los atributos; en los temas 26 a 29 estudiaremos cómo se toman las decisiones de acceso.

5. Las fechas y lo que realmente significan

Linux distingue tres marcas temporales tradicionales, y algunos sistemas de archivos ofrecen una cuarta. Sus nombres en inglés aparecen en la documentación y en los campos de las estructuras de C:

ctime no es la fecha de creación

La «c» significa change. Si cambias los permisos de un archivo antiguo, su ctime puede pasar a ser la de hoy sin que el archivo haya nacido hoy ni se haya modificado el documento.

terminal · separar modificación de contenido y cambio de atributos
$ cd "$LAB"
$ printf 'uno\n' > fechas.txt
$ chmod 600 fechas.txt
$ stat -c 'mtime=%y | ctime=%z' fechas.txt
$ sleep 1
$ chmod 640 fechas.txt
$ stat -c 'mtime=%y | ctime=%z' fechas.txt
$ sleep 1
$ printf 'dos\n' >> fechas.txt
$ stat -c 'mtime=%y | ctime=%z' fechas.txt
$ stat -c 'atime=%x | nacimiento=%w' fechas.txt

Compara las tres consultas de mtime y ctime: al cambiar de 600 a 640 debe cambiar ctime y mantenerse mtime; al añadir dos, deben cambiar ambas. Los segundos de espera facilitan la observación incluso cuando una herramienta presenta tiempos con poca precisión. En un sistema de archivos remoto o con otras resoluciones de reloj los detalles pueden variar.

No esperes necesariamente un cambio de atime tras cada cat. Actualizar metadatos por toda lectura genera escrituras adicionales; opciones como relatime o noatime limitan o evitan esas actualizaciones. Del mismo modo, %w puede imprimir - si no hay una fecha de nacimiento disponible. La documentación de inode(7) detalla estos atributos y sus condiciones de actualización.

Las fechas del directorio son otra ficha

Añadir, quitar o renombrar una entrada modifica el contenido del directorio. Por eso puede cambiar la mtime de la carpeta aunque los documentos que ya tenía permanezcan intactos. A la inversa, editar bytes de curso/datos/notas.txt no implica añadir o quitar una entrada en curso/datos: no tienes por qué observar una nueva mtime en esa carpeta.

Esta diferencia será útil al interpretar herramientas de copia, sincronización y búsqueda. «Cambió un archivo de esta carpeta» y «cambió el directorio» son hechos distintos.

6. Nombre, inodo y enlaces

En el pequeño sistema de archivos del tema 1, una misma entrada guardaba el nombre y la información del archivo. En los sistemas de la familia UNIX separamos esas funciones: el directorio relaciona un nombre con un número de inodo, y el inodo representa el objeto y sus atributos. El nombre no es uno de los campos del inodo.

Dos nombres y un enlace simbólico notas.txt y copia.txt apuntan al mismo inodo 42. actual tiene su propio inodo 73 y contiene la ruta datos/notas.txt. Entradas de directorio datos/notas.txt → 42 datos/copia.txt → 42 Inodo 42 · archivo regular tamaño: 4 · enlaces: 2 datos: 41 42 43 0a Entrada de directorio actual → 73 Inodo 73 · enlace simbólico ruta guardada: datos/notas.txt
Números de inodo ilustrativos. Los dos primeros nombres comparten el objeto; el enlace simbólico contiene una ruta.

Un segundo nombre para el mismo archivo

terminal · un enlace duro no es una copia
$ cd "$LAB"
$ ln curso/datos/notas.txt curso/datos/copia.txt
$ stat -c '%n | dispositivo=%d inodo=%i enlaces=%h' curso/datos/{notas,copia}.txt
curso/datos/notas.txt | dispositivo=2049 inodo=393220 enlaces=2
curso/datos/copia.txt | dispositivo=2049 inodo=393220 enlaces=2
$ printf 'otra linea\n' >> curso/datos/copia.txt
$ cat curso/datos/notas.txt
ABC
otra linea

ln sin -s crea un enlace duro: otra entrada para el mismo objeto. No hay una copia que deba sincronizarse. Ambos nombres muestran la misma pareja dispositivo-inodo y sus escrituras afectan al mismo contenido. El nombre copia.txt fue elegido a propósito: llamarlo «copia» no hace que lo sea.

En cambio, el cp del laboratorio creó otro objeto. Sus bytes iniciales eran iguales, pero editar uno no modifica al otro. También se puede renombrar una entrada con mv dentro del mismo sistema de archivos sin cambiar la identidad del objeto. En los temas 23 a 25 estudiaremos los límites de los enlaces duros y qué sucede al borrar nombres mientras hay archivos abiertos.

Un enlace simbólico guarda una ruta

terminal · observar el enlace o seguirlo
$ readlink curso/actual
datos/notas.txt
$ stat -c 'tipo=%F tamaño=%s inodo=%i' curso/actual
tipo=symbolic link tamaño=15 inodo=393224
$ stat -L -c 'tipo=%F tamaño=%s inodo=%i' curso/actual
tipo=regular file tamaño=15 inodo=393220

El tamaño 15 del enlace es la longitud de datos/notas.txt, la ruta almacenada; no incluye un byte nulo final. El tamaño 15 del destino coincide por casualidad: resulta de los cuatro bytes originales más los once de otra linea\n añadidos en el paso anterior. Si omitiste ese paso, el destino seguirá midiendo 4 bytes y el enlace medirá igualmente 15.

GNU stat observa por defecto el enlace; stat -L lo sigue. Ten cuidado al pasar de la terminal a C: la función stat() de C sí sigue el enlace final; lstat() observa ese enlace. El programa de la sección 10 mostrará ambas fichas.

Un enlace simbólico puede existir aunque su destino no exista. Se llama enlace roto. No almacena una copia de los datos ni mantiene por sí solo el objeto de destino con vida. Si cambias el nombre del destino y la ruta guardada deja de encontrarlo, el enlace no se corrige automáticamente.

7. Rutas absolutas y relativas

Un nombre como notas.txt solo es suficiente si sabemos en qué directorio buscarlo. Una ruta indica un recorrido mediante componentes separados por /. En /home/ana/curso/datos/notas.txt, cada componente intermedio permite llegar al siguiente.

Una ruta absoluta comienza con / y se interpreta desde la raíz visible para el proceso. Una ruta relativa se interpreta desde un directorio de referencia: normalmente el directorio de trabajo actual del proceso.

árbol de ejemplo · independiente del sufijo temporal del laboratorio
/
├── etc/
│   └── hosts
├── home/
│   └── ana/
│       ├── curso/
│       │   ├── datos/
│       │   │   └── notas.txt
│       │   ├── acceso -> ../documentos
│       │   └── actual -> datos/notas.txt
│       └── documentos/
│           └── nota.txt
└── tmp/

Si el directorio actual es /home/ana/curso, las rutas siguientes alcanzan el mismo archivo en este árbol:

RutaPunto de partida y recorrido
/home/ana/curso/datos/notas.txtDesde la raíz, con todos los componentes.
datos/notas.txtDesde el directorio actual, entrar en datos.
./datos/notas.txtEl punto conserva el directorio actual.
../curso/datos/notas.txtSubir a ana y volver a entrar en curso.
actualSeguir el enlace que contiene datos/notas.txt.

El directorio de trabajo pertenece al proceso

pwd muestra el directorio de trabajo del shell y cd lo cambia. Un programa ejecutado desde ese shell hereda inicialmente su directorio de trabajo. Si abres dos terminales y ejecutas cd en una, la otra no cambia. Tampoco existe una única «carpeta actual del equipo».

terminal · la misma ruta relativa desde dos lugares
$ cd "$LAB/curso"
$ cat datos/notas.txt
ABC
otra linea
$ cd datos
$ cat notas.txt
ABC
otra linea
$ cat datos/notas.txt
cat: datos/notas.txt: No such file or directory
$ cat ../actual
ABC
otra linea

Desde curso/datos, la ruta datos/notas.txt pediría otro directorio datos dentro del actual. Las rutas relativas no se interpretan desde la carpeta que contiene el ejecutable ni desde la carpeta del archivo fuente. Un programa que abre config.ini debe tener en cuenta desde dónde lo lanzan.

Punto, punto-punto, raíz y directorio personal

Por ejemplo, Bash expande cat ~/notas.txt antes de ejecutar cat. Pero cat '~/notas.txt' conserva la virgulilla literal por las comillas simples. Del mismo modo, pasar "~/notas.txt" directamente a stat() desde C no solicita esa expansión: pide un componente llamado ~ bajo el directorio actual.

Una barra final también comunica una condición: datos/ debe resolver a un directorio. notas.txt/ no convierte un archivo regular en carpeta y normalmente produce ENOTDIR. Veremos esa comprobación en el simulador.

8. Cómo se resuelve una ruta

Podemos imaginar la búsqueda como un recorrido, sin entrar todavía en la caché de nombres ni en la implementación de los directorios. Para una operación que sigue el enlace final, como leer un archivo, el modelo básico es:

  1. Elegir dónde empezar. Si la ruta empieza por /, usar la raíz; en otro caso, usar el directorio de referencia.
  2. Buscar el siguiente componente. Consultar su nombre en el directorio actual. Para atravesarlo hace falta permiso de búsqueda.
  3. Resolver enlaces simbólicos. Si aparece uno, continuar con la ruta que guarda, seguida de los componentes todavía pendientes.
  4. Continuar o terminar. Cada objeto que aún deba atravesarse tiene que ser un directorio. Al agotar los componentes se obtiene el objeto de destino.

Un enlace simbólico con destino relativo se interpreta desde el directorio que contiene al enlace. El enlace /home/ana/curso/acceso, que guarda ../documentos, lleva a /home/ana/documentos con independencia del directorio de trabajo de quien lo use. Si la ruta guardada empieza por /, en cambio, la búsqueda del destino vuelve a la raíz.

No basta con borrar texto entre barras

Sin enlaces, es tentador simplificar algo/.. tachando ambos componentes. Con enlaces simbólicos puede cambiar el resultado. En nuestro árbol, desde /home/ana/curso:

resolución física de acceso/../curso/datos/notas.txt
inicio                 /home/ana/curso
acceso -> ../documentos /home/ana/documentos
..                     /home/ana
curso                  /home/ana/curso
datos                  /home/ana/curso/datos
notas.txt              /home/ana/curso/datos/notas.txt

Si hubieras borrado acceso/.. antes de mirar el enlace, habrías obtenido curso/datos/notas.txt desde /home/ana/curso, que pide un segundo curso y no existe en el ejemplo. Además, aunque no haya enlaces, inexistente/../datos no se puede resolver si el primer componente no existe. El núcleo comprueba el recorrido, no solo su aspecto final.

Bash mantiene por defecto una interpretación lógica para cd y pwd, que puede conservar nombres de enlaces en PWD. Para observar la ubicación física puedes usar pwd -P y el comportamiento físico de cd -P:

terminal · ubicación lógica y física
$ cd "$LAB/curso/acceso"
$ pwd
/home/ana/tema2.A1b2C3/curso/acceso
$ pwd -P
/home/ana/tema2.A1b2C3/documentos
$ cd -P ..
$ pwd -P
/home/ana/tema2.A1b2C3

Errores que explican qué parte falló

ErrorCausa típicaEjemplo desde curso
ENOENTFalta un componente o el destino de un enlace.datos/ausente.txt
ENOTDIRSe intenta atravesar un objeto que no es directorio.datos/notas.txt/otra
EACCESNo hay permiso de búsqueda en un directorio del recorrido, o falta un permiso requerido por la operación.Un directorio intermedio sin permiso de búsqueda para el proceso.
ELOOPLa búsqueda sigue demasiados enlaces simbólicos, por ejemplo por un ciclo.Un enlace bucle → bucle.

Que una ruta no se pueda abrir no demuestra que sus bytes no existan. Puede faltar un permiso en una carpeta anterior al destino o fallar un enlace intermedio. La descripción de path_resolution(7) explica el recorrido y las condiciones de error. En el tema 22 estudiaremos cómo lo implementa el núcleo.

9. La jerarquía de directorios de Linux

Linux presenta un árbol que parte de /. Sus carpetas principales siguen convenciones que ayudan a distinguir programas, configuración, datos de usuarios y estado del sistema. No hace falta memorizar cada subdirectorio: interesa saber dónde buscar y por qué no todo debería guardarse en la misma carpeta.

RutaUso habitualEjemplo de lo que buscarías
/homeDirectorios personales de usuarios normales.Tus proyectos bajo /home/ana.
/rootDirectorio personal del administrador root.Archivos personales de esa cuenta.
/etcConfiguración del sistema.Configuración de servicios y resolución local en /etc/hosts.
/usrProgramas, bibliotecas y recursos instalados para el sistema.Ejecutables en /usr/bin, recursos en /usr/share.
/bin, /sbin, /libUbicaciones tradicionales de programas y bibliotecas esenciales; pueden ser enlaces a ubicaciones bajo /usr.Comprobar con ls -ld /bin /sbin /lib.
/varDatos variables del sistema y los servicios.Registros en /var/log, estado persistente bajo /var/lib.
/tmpArchivos temporales, sujetos a políticas de limpieza.Un área de trabajo transitoria.
/runEstado de ejecución desde el arranque.PID y sockets de servicios.
/devNodos de dispositivo y otros objetos relacionados./dev/null, terminales y discos.
/procInformación de procesos y del núcleo mediante un sistema de archivos virtual./proc/self/status.
/sysVista de dispositivos y objetos del núcleo mediante sysfs.Información de colas de dispositivos de bloque.
/bootArchivos relacionados con el arranque.Núcleo e imágenes de arranque, según la instalación.
/media, /mntPuntos convencionales para montar otros sistemas de archivos.Una memoria USB o un montaje temporal.
/optPaquetes adicionales con su propia estructura.Una aplicación instalada bajo su propio directorio.

La disposición exacta depende de la distribución y de cómo se instaló el equipo. El manual hier(7) sirve como referencia para los directorios convencionales. En WSL encontrarás además rutas como /mnt/c, que permiten llegar a archivos de Windows.

Un árbol puede contener varios sistemas de archivos

La unidad del árbol no implica que todo viva en el mismo volumen. El sistema de archivos raíz puede estar en una partición, /home en otra y una memoria USB aparecer bajo /media/ana/USB. Montar conecta la raíz de otro sistema de archivos con un punto del árbol visible. Los componentes de la ruta siguen usando barras aunque al recorrerlos se cruce de un volumen a otro.

Tampoco todos los sistemas de archivos son persistentes. /proc y /sys exponen información generada por el núcleo, y /run suele estar en memoria mediante tmpfs. Poder abrir un nombre con la interfaz de archivos no garantiza que su contenido vaya a sobrevivir a un reinicio.

terminal · observar la organización sin modificar el sistema
$ ls -ld / /home /etc /usr /var /tmp /dev /proc /sys
$ ls -ld /bin /sbin /lib
$ cat /proc/self/status | head -5
$ findmnt -T /home
$ findmnt -T /proc

Las salidas dependen del equipo. findmnt -T muestra el montaje que contiene una ruta. En /proc/self/status, self se refiere al proceso que está accediendo a esa ruta: en este caso verás información de cat, no una ficha estática escrita en el disco. El montaje y la capa virtual se desarrollan en los temas 30 a 32.

10. Experimento: un inspector de metadatos en C

La orden stat es un programa. Nuestros programas pueden pedir al núcleo la misma clase de información mediante las funciones de la familia stat. Este inspector recibe una o varias rutas, consulta lstat para observar cada objeto y, si es un enlace simbólico, consulta también stat para mostrar el destino.

Guarda el código como inspeccionar.c dentro de $LAB. Trabaja con metadatos: no abre el contenido ni recorre el interior de directorios. Incluye todos los tipos de la tabla y comprueba los errores para que un enlace roto no se confunda con un archivo regular vacío.

inspeccionar.c · tipos, identidad, tamaño y fechas
#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
#include <sys/stat.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

static const char *tipo(mode_t modo)
{
    if (S_ISREG(modo))  return "archivo regular";
    if (S_ISDIR(modo))  return "directorio";
    if (S_ISLNK(modo))  return "enlace simbolico";
    if (S_ISCHR(modo))  return "dispositivo de caracteres";
    if (S_ISBLK(modo))  return "dispositivo de bloques";
    if (S_ISFIFO(modo)) return "FIFO";
    if (S_ISSOCK(modo)) return "socket UNIX";
    return "desconocido";
}

static void fecha(const char *nombre, const struct timespec *t)
{
    struct tm calendario;
    char texto[64];

    if (localtime_r(&t->tv_sec, &calendario) == NULL ||
        strftime(texto, sizeof texto, "%Y-%m-%d %H:%M:%S %z",
                 &calendario) == 0) {
        printf("  %s: no representable\n", nombre);
        return;
    }
    printf("  %s: %s (nanosegundos: %09ld)\n",
           nombre, texto, t->tv_nsec);
}

static void ficha(const struct stat *s)
{
    printf("  tipo: %s\n", tipo(s->st_mode));
    printf("  dispositivo: %ju | inodo: %ju | enlaces: %ju\n",
           (uintmax_t)s->st_dev, (uintmax_t)s->st_ino,
           (uintmax_t)s->st_nlink);
    printf("  permisos: %04jo | uid: %ju | gid: %ju\n",
           (uintmax_t)(s->st_mode & 07777),
           (uintmax_t)s->st_uid, (uintmax_t)s->st_gid);
    printf("  tamano logico: %jd bytes\n", (intmax_t)s->st_size);
    printf("  bloques de 512 bytes (Linux): %jd\n",
           (intmax_t)s->st_blocks);
    printf("  transferencia aconsejada: %jd bytes\n",
           (intmax_t)s->st_blksize);
    fecha("atime", &s->st_atim);
    fecha("mtime", &s->st_mtim);
    fecha("ctime", &s->st_ctim);
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "uso: %s RUTA [RUTA ...]\n", argv[0]);
        return 1;
    }

    int hubo_error = 0;
    for (int i = 1; i < argc; i++) {
        struct stat s;
        printf("\n%s [lstat]\n", argv[i]);
        if (lstat(argv[i], &s) == -1) {
            int error = errno;
            printf("  error: %s\n", strerror(error));
            hubo_error = 1;
            continue;
        }
        ficha(&s);

        if (S_ISLNK(s.st_mode)) {
            printf("  destino [stat]:\n");
            if (stat(argv[i], &s) == -1) {
                int error = errno;
                printf("  error: %s\n", strerror(error));
                hubo_error = 1;
            } else {
                ficha(&s);
            }
        }
    }
    return hubo_error;
}

Compilar y comparar resultados

terminal · inspeccionar el laboratorio
$ cd "$LAB"
$ gcc -std=c11 -Wall -Wextra -Wpedantic -O2 inspeccionar.c -o inspeccionar
$ ./inspeccionar curso/datos/notas.txt curso/datos/copia.txt
$ ./inspeccionar curso curso/actual curso/canal /dev/null
$ ln -s datos/ausente.txt curso/roto
$ ./inspeccionar curso/roto
curso/roto [lstat]
  tipo: enlace simbolico
  ...
  destino [stat]:
  error: No such file or directory
$ echo $?
1

Los puntos suspensivos abrevian las demás líneas de la ficha. Comprueba que los dos nombres del enlace duro muestran el mismo dispositivo e inodo, mientras curso/actual ofrece dos fichas: la del enlace y la del archivo al que conduce. El enlace roto tiene metadatos propios que lstat puede consultar, pero stat falla al intentar llegar al destino. El programa devuelve 1 si alguna consulta falló y continúa con las rutas siguientes.

Qué decisiones hay en el programa

Las variantes que no siguen enlaces solo evitan seguir el componente final. Si consultas curso/acceso/nota.txt con lstat, es necesario seguir acceso para llegar al archivo. Por tanto, no es una operación que prohíba cualquier enlace en todo el recorrido.

El programa está orientado a Linux. No muestra la fecha de nacimiento porque no forma parte de la estructura tradicional que usamos. Tampoco demuestra que después vayas a poder leer el archivo: consultar metadatos y abrir para lectura son operaciones diferentes, y el estado podría cambiar entre ellas. Cuando estudiemos descriptores veremos fstat, que consulta el objeto asociado a uno ya abierto.

11. Simulador de resolución de rutas

Este simulador recorre un árbol fijo parecido al del ejemplo. Elige el directorio de trabajo y escribe una ruta. La tabla muestra cada componente, las sustituciones de enlaces simbólicos y el objeto obtenido o el primer error. Puedes contrastar tus predicciones sin crear ni modificar archivos en tu equipo.

árbol disponible en el simulador
/
├── etc/
│   └── hosts
├── home/ana/
│   ├── curso/
│   │   ├── datos/
│   │   │   └── notas.txt
│   │   ├── actual -> datos/notas.txt
│   │   ├── acceso -> ../documentos
│   │   ├── roto -> datos/ausente.txt
│   │   └── bucle -> bucle
│   └── documentos/
│       └── nota.txt
└── tmp/

Resolver una ruta componente a componente

Prueba acceso/nota.txt, actual/otra, roto, bucle o una ruta absoluta. No se expanden ~ ni variables del shell.

PasoComponenteResultado del paso

El modelo sigue el enlace final, como stat(), comprueba componentes inexistentes y el requisito de directorio de una barra final. Limita a 40 los enlaces seguidos. Para concentrarse en el recorrido supone que todos los directorios permiten la búsqueda y omite montajes, credenciales, concurrencia y cachés. No es una comprobación del sistema de archivos real de tu equipo.

Cuatro pruebas para pensar antes de escribir

  1. Desde /tmp, ¿funcionará datos/notas.txt? ¿Y /home/ana/curso/datos/notas.txt?
  2. Desde /home/ana/curso, compara actual con actual/. El enlace existe en ambos casos; cambia la exigencia sobre su destino.
  3. Prueba acceso/../curso/datos/notas.txt. Observa en qué lugar se aplica .. después de resolver el enlace.
  4. Prueba ausente/../datos/notas.txt. Aunque una simplificación de texto parezca dar una ruta válida, la búsqueda debe fallar al no encontrar el primer componente.

12. Resumen

13. Preguntas de repaso y práctica

Intenta responder antes de desplegar cada solución. Los ejercicios combinan observación de la terminal con predicciones sobre objetos y rutas.

1. Renombras foto.png como foto.txt. ¿Qué cambia para el sistema de archivos?

Cambia una entrada de directorio. Los bytes no se convierten a otro formato y el objeto sigue siendo un archivo regular. Si se trata de un renombrado dentro del mismo sistema de archivos, mantiene su identidad. Las aplicaciones pueden decidir tratarlo de otra manera por la extensión.

2. ¿Qué diferencia hay entre stat y file al informar el tipo?

stat informa del tipo de objeto del sistema de archivos, como regular o directorio. file intenta identificar el contenido, como texto ASCII o imagen PNG. Un documento PDF puede ser regular para stat y PDF para file.

3. Un archivo tiene tamaño 15 y st_blocks vale 8. ¿Cuánto almacenamiento cuenta Linux?

Ocho unidades de 512 bytes: 4096 bytes. La longitud lógica sigue siendo 15. No hay que multiplicar st_blocks por st_blksize: este último es una recomendación para las transferencias, no la unidad de la cuenta.

4. ¿Por qué un directorio de 4096 bytes puede contener un archivo de 2 GiB?

El tamaño del directorio describe su propia representación, que contiene entradas y referencias a otros objetos. No incorpora la suma de los bytes de sus archivos descendientes. Esa suma requiere recorrer la jerarquía.

5. Cambias permisos con chmod y avanza ctime. ¿Has cambiado la fecha de creación?

No. ctime registra cambios de estado, incluidos los de permisos. La fecha de nacimiento, cuando existe y está disponible, es otro atributo. Un cambio exclusivo de permisos no tiene por qué cambiar mtime.

6. Dos nombres tienen igual inodo. ¿Basta para afirmar que son el mismo objeto?

Hay que comparar también el dispositivo o sistema de archivos al que pertenece el inodo. La pareja dispositivo-inodo distingue el objeto en esa observación. Los números pueden reutilizarse después de borrar objetos; no son identificadores eternos.

7. ¿Qué diferencia hay entre cp origen destino y ln origen destino?

cp crea normalmente un objeto distinto con una copia del contenido; editar uno no modifica el otro. ln crea otro enlace duro al mismo objeto, por lo que ambos nombres comparten contenido y metadatos. Compartir almacenamiento mediante optimizaciones de copia no convierte por sí solo dos archivos en un mismo inodo.

8. Un enlace guarda ../documentos. ¿Depende del directorio actual del lector?

Su destino relativo se interpreta desde el directorio que contiene al enlace. El lector puede usar rutas distintas para llegar al enlace, pero el directorio de trabajo no sustituye esa base al interpretar el texto guardado.

9. Desde /home/ana/curso/datos, ¿cómo llegas a documentos/nota.txt del árbol?

Con ../../documentos/nota.txt: subes de datos a curso y de curso a ana, y después entras en documentos. También sirve la absoluta /home/ana/documentos/nota.txt, que no depende del directorio actual.

10. ¿Por qué actual/otra produce ENOTDIR aunque actual sea un enlace válido?

actual conduce a datos/notas.txt, que es regular. Para buscar el componente otra habría que atravesar ese archivo como un directorio. La existencia del enlace y de su destino no satisface ese requisito.

11. lstat funciona sobre un enlace roto y stat falla. ¿Es contradictorio?

No. lstat obtiene la ficha del enlace, que existe y guarda una ruta. stat intenta llegar al destino de esa ruta y no lo encuentra. Son objetos y consultas diferentes.

12. ¿lstat evita seguir cualquier enlace que aparezca en una ruta?

No. No sigue el enlace cuando es el componente final que se está consultando. Los enlaces intermedios deben resolverse para alcanzar ese componente. En curso/acceso/nota.txt sigue acceso y consulta nota.txt.

13. ¿Por qué un archivo visible bajo /proc no demuestra persistencia en disco?

Porque el árbol puede incluir sistemas de archivos virtuales. El núcleo genera buena parte de esa información al consultarla. La interfaz de archivos unifica la forma de acceder, pero no impone que todos los objetos tengan datos persistentes en un dispositivo.

14. ¿Puede simplificarse ausente/../datos a datos sin consultar el árbol?

Como transformación de texto se puede escribir así, pero no describe necesariamente la resolución real. Si ausente no existe, el recorrido falla antes de llegar a los dos puntos. Los enlaces simbólicos también impiden asumir que nombre/.. siempre se cancela.

Para practicar

  1. Crea un archivo de texto con extensión .jpg y un directorio llamado documento.txt. Consulta ambos con stat y file. Explica por qué la extensión no permite predecir su tipo de objeto.
  2. Crea tres archivos de 0, 1 y 5000 bytes. Puedes generar el último con head -c 5000 /dev/zero > grande.bin. Compara %s, %b y %B con stat e interpreta cada valor.
  3. Consulta los tiempos de curso/datos, crea una entrada nueva dentro y vuelve a consultarlos. Después modifica solo el contenido del archivo nuevo. Distingue la ficha del directorio de la del documento.
  4. Crea un enlace duro y una copia de un archivo nuevo. Compara dispositivo, inodo y enlaces de los tres nombres. Añade texto a uno y comprueba cuáles muestran ese cambio.
  5. Crea un enlace simbólico relativo hacia un archivo. Accede a él desde dos directorios de trabajo usando rutas distintas y verifica que llega al mismo destino. Renombra después el destino y observa la diferencia entre stat y stat -L.
  6. Desde $LAB/curso/datos, escribe dos rutas relativas y una absoluta que lleguen a $LAB/documentos/nota.txt. Compruébalas con cat y explica cada componente.
  7. Ejecuta el inspector sobre un archivo regular, un directorio, un enlace válido, uno roto, la FIFO y /dev/null. No leas la FIFO: basta con consultar sus metadatos.
  8. Modifica el inspector para mostrar st_rdev únicamente en dispositivos de bloques o caracteres. Investiga por qué ese campo no es lo mismo que st_dev: uno identifica el dispositivo representado y el otro el sistema de archivos que contiene el nodo.
  9. Ejecuta el inspector con tres argumentos: uno válido, uno inexistente y otro válido. Comprueba que procesa el tercero y que el estado final indica un error.
  10. Predice el resultado de seis rutas en el simulador, incluyendo una absoluta, dos relativas, un enlace roto, una barra final y un ciclo. Anota el primer componente que explica cada error antes de mirar la tabla.

En el siguiente tema utilizaremos ls, stat, df y du de forma sistemática para observar archivos y espacio disponible. Ya tienes el vocabulario para distinguir las cifras que muestran y evitar confundir nombres, objetos y almacenamiento.