← Sistemas de Archivos y E/SBloque 10 · Consistencia y journaling · Tema 34 de 50

Tema 34

Copy-on-write y snapshots

Conservar una versión anterior no exige copiar inmediatamente todos sus datos. Estudiaremos cómo compartir bloques y separar las modificaciones, qué representan las instantáneas de Btrfs y ZFS y cuándo una suma de verificación permite detectar o reparar un error. Dos programas en C, una imagen de práctica y dos simuladores conectarán estas ideas con operaciones observables.

1. Qué significa copiar al escribir

En el tema 33 estudiamos cómo un diario ayuda a recuperar cambios comprometidos. Otra técnica importante consiste en escribir una versión modificada en espacio nuevo antes de hacerla accesible desde los metadatos que la referencian.

Copy-on-write (COW): técnica que conserva una versión existente mientras prepara otra y cambia las referencias necesarias para publicar la modificación. Cuando varias versiones comparten datos, escribir en una de ellas debe preservar los bytes que todavía pertenecen a las demás.

Una analogía útil es corregir una página de un manual conservando la edición anterior: se prepara la página nueva y se actualiza el índice de la edición actual. Las páginas que no cambiaron pueden seguir siendo las mismas para ambas ediciones.

La frase «no sobrescribir nunca» resume una idea, pero no describe todas las escrituras de Btrfs o ZFS. Hay estructuras de control, reutilización de espacio y opciones particulares. En Btrfs, además, existen archivos con NOCOW. En este tema trabajaremos con archivos regulares en su configuración COW habitual, sin activar esas excepciones. Véase la introducción de Btrfs.

2. Bloques, árboles y publicación de una raíz

No basta con copiar un bloque de datos: también hay que registrar dónde está. Si la referencia reside en un nodo compartido del árbol de metadatos, actualizarla puede exigir una nueva versión de ese nodo y de sus antecesores. Al final, una raíz permite alcanzar el estado publicado.

Dos versiones comparten bloques sin modificarLa raíz de la instantánea apunta a A, B y C. La raíz actual apunta a A, B nuevo y C. A y C están compartidos; B anterior sigue retenido por la instantánea. Raíz de la instantánea Raíz actual A B anterior B nuevo C La modificación separa B; A y C siguen compartidos.
Modelo conceptual: omite nodos intermedios, agrupación de escrituras y estructuras de recuperación.

La recuperación necesita distinguir raíces válidas y respetar el orden de persistencia. COW no permite publicar referencias a datos que todavía no se han escrito correctamente. Los detalles de transacciones, logs y confirmaciones son propios de cada implementación; el dibujo no sustituye ese protocolo.

3. Referencias compartidas y liberación

modelo de tres bloques de datos · sin metadatos ni compresión
Actual:      [A, B, C]       3 bloques únicos
 snapshot:  [A, B, C]       siguen siendo 3 bloques de datos únicos
 cambiar B en actual:
Actual:      [A, D, C]
Snapshot:    [A, B, C]       ahora hay 4 bloques de datos únicos
 borrar snapshot:
Actual:      [A, D, C]       B ya no tiene referencias; puede liberarse

Contar versiones no equivale a multiplicar el tamaño del archivo. Se cuenta la unión de los bloques alcanzables desde las referencias vivas. En el ejemplo, sumar tres bloques por versión daría seis, aunque solo existen cuatro bloques de datos distintos.

En un sistema real también consumen espacio los metadatos, las reservas y la redundancia. La retirada de referencias puede procesarse de forma diferida. «Ya no es necesario para esta versión» no significa necesariamente «el contador de espacio disponible cambió en este instante».

4. Qué conserva una instantánea

Una instantánea o snapshot permite acceder a un estado de una unidad del sistema de archivos en un momento determinado. No requiere recorrer y duplicar todos los bytes como una copia convencional; mantiene referencias a datos existentes y conserva los que dejan de pertenecer a la versión activa.

Imagina un proyecto con configuración, fuentes y resultados. Si capturas su subvolumen antes de una modificación, podrás leer la versión capturada aunque los nombres activos hayan cambiado. Eso no incluye automáticamente un directorio que sea otro subvolumen, otro dataset o un montaje ajeno.

La instantánea representa el estado del sistema de archivos dentro de su alcance. No captura por sí sola buffers privados de un proceso, una respuesta pendiente de un servidor ni un servicio remoto. Su utilidad depende tanto del momento elegido como de qué objetos incluye.

5. Hard link, copia, reflink y snapshot

OperaciónIdentidad y alcanceEscritura posterior
Hard linkOtro nombre del mismo inodo.Modificar el objeto se observa desde ambos nombres.
Copia de bytesArchivo distinto con contenido copiado.Las modificaciones son independientes; no exige compartir extents.
ReflinkArchivo distinto que comparte inicialmente extents de datos.COW mantiene privados los cambios de cada archivo.
SnapshotEstado de un subvolumen o dataset.La versión capturada conserva sus datos frente a cambios de la activa.

Dos hashes iguales solo prueban igualdad del contenido observado según ese hash; no demuestran que los archivos compartan almacenamiento. Tampoco dos valores iguales de st_blocks permiten deducirlo: el espacio compartido puede contabilizarse en más de un archivo.

GNU cp · exigir reflink o deshabilitar su uso explícito
$ cp --reflink=always -- original.bin clon.bin
$ cp --reflink=never -- original.bin copia.bin
$ ln -- original.bin enlace.bin

Estos nombres deben ser nuevos dentro del ensayo. Con always, un reflink no disponible debe producir error, en lugar de sustituirse silenciosamente por una copia de bytes. Un hard link se limita al mismo sistema de archivos; un reflink también tiene restricciones de soporte y ubicación. Consulta FICLONE(2const) para el contrato de Linux.

6. Btrfs: subvolúmenes y límites de la captura

Btrfs permite organizar jerarquías como subvolúmenes. Parecen directorios al recorrerlos, pero tienen operaciones e identidad propias. Un snapshot de Btrfs es otro subvolumen con contenido inicial derivado del origen; puede ser escribible o crearse de solo lectura con -r.

esquema · capturar una unidad identificada
btrfs subvolume create PUNTO/activo
btrfs subvolume snapshot -r PUNTO/activo PUNTO/antes
btrfs subvolume show PUNTO/antes

La captura no es recursiva sobre subvolúmenes anidados: el contenido de un subvolumen hijo no queda incluido como si fuera un directorio ordinario. Hay que planificar su captura por separado; varias órdenes independientes tampoco forman automáticamente un único instante común.

Solo lectura protege esa vista de escrituras ordinarias, pero no la vuelve inmutable frente al administrador. El alcance y los atributos se documentan en btrfs-subvolume(8).

7. ZFS: pools, datasets, snapshots y clones

En ZFS, un pool aporta almacenamiento a datasets. Un dataset de tipo filesystem puede tener su propio punto de montaje y propiedades. Un snapshot es de solo lectura; un clone proporciona una rama escribible a partir de una instantánea. No debe confundirse ese clone de dataset con el ioctl FICLONE de Linux para archivos individuales.

Esta es una secuencia ilustrativa para un dataset de laboratorio ya preparado. labpool es un nombre de ejemplo: no se crea ni se altera un pool del equipo para seguir el laboratorio Btrfs de este tema.

ZFS · ejemplo sobre un dataset de práctica existente
$ zfs list labpool/curso34
$ sudo zfs snapshot labpool/curso34@antes
$ zfs list -t snapshot -o name,used,refer
$ sudo zfs clone labpool/curso34@antes labpool/rama34
$ zfs get origin labpool/rama34
$ zpool status labpool

La rama depende de su instantánea de origen; esa relación importa al administrar su ciclo de vida. Véase zfsconcepts(7). Con zfs snapshot -r se pueden capturar de forma conjunta los datasets descendientes; es una diferencia respecto a las capturas independientes de subvolúmenes Btrfs. Véase zfs-snapshot(8).

Recuperar un archivo copiándolo desde una instantánea permite seleccionar qué se restaura. Un rollback cambia el estado activo de una unidad más amplia y puede descartar modificaciones posteriores; no es necesario para las prácticas de este tema.

8. Checksums: detectar no siempre es reparar

Una suma de verificación se calcula a partir de datos y se compara al comprobarlos. Si los bytes almacenados cambian de forma accidental, una discrepancia permite detectar que no coinciden con lo esperado. Hay una probabilidad de colisión; un checksum tampoco certifica por sí solo autenticidad frente a un atacante.

ejemplo conceptual · referencia de integridad protegida
Esperado: checksum del bloque original
Lectura A: checksum distinto → copia no válida
Lectura B: checksum esperado → posible fuente de recuperación

Sin copia válida: detectar el daño no reconstruye los bytes perdidos.

La referencia esperada también debe protegerse. Conservar un checksum junto a datos que se pueden alterar arbitrariamente no constituye un protocolo completo de integridad. Las estructuras del formato relacionan las referencias con las verificaciones.

Una instantánea y la versión activa pueden apuntar al mismo bloque físico dañado: dos nombres lógicos no aportan dos réplicas independientes. Además, si una aplicación escribe por error un valor válido, el sistema calcula el checksum de ese valor equivocado. No sabe cuál era la intención del usuario.

9. Scrub, revisión estructural y copias externas

Un scrub recorre datos almacenados para verificar su integridad sin esperar a que una aplicación lea cada archivo. La reparación requiere una fuente válida y las condiciones que admite la implementación. En Btrfs, los datos de archivos NOCOW no tienen la protección de checksums de datos habitual; sus metadatos son otro asunto. Véase btrfs-scrub(8).

En ZFS se consulta el resultado con zpool status; la redundancia puede permitir corregir daños detectados. Scrub y resilver tienen objetivos distintos: el segundo reconstruye lo que un dispositivo necesita poner al día. Véase zpool-scrub(8).

MecanismoPregunta que ayuda a resolver
Snapshot¿Cómo accedo a una versión anterior capturada?
Scrub¿Los bloques verificables siguen concordando con sus checksums?
Comprobador estructural¿Son coherentes las relaciones internas que examina?
Copia externa y restauración probada¿Puedo recuperar los datos si pierdo el almacenamiento original?

Una instantánea local no resuelve la pérdida del dispositivo, del pool completo o de las credenciales que permiten borrar todas las versiones. Btrfs send/receive y ZFS send/receive permiten transferir estados a otro almacenamiento, pero necesitan una política de conservación y pruebas de restauración. Tener una herramienta de transferencia no asegura por sí solo una copia recuperable.

10. Consistencia de aplicación y persistencia

Supongamos que una aplicación actualiza inventario.txt y luego pedidos.txt. Una captura entre ambos cambios puede conservar un sistema de archivos correcto y una operación de negocio incompleta. Capturar todos los nombres de una unidad no convierte varias llamadas en una transacción de aplicación.

Para una base de datos se sigue su procedimiento de backup: puede requerir coordinación, un punto de control o recuperación mediante su propio log. Si los datos están repartidos entre unidades, además hay que coordinar el alcance temporal de las capturas.

Los programas siguen comprobando write, pwrite, fsync y close. COW no elimina los errores de E/S, el espacio insuficiente ni la diferencia entre memoria del proceso y datos entregados al kernel. La llamada fsync del ejemplo confirma el archivo según el contrato del sistema; no convierte varias modificaciones lógicas en una transacción conjunta.

El tema 35 construirá el protocolo para publicar una nueva versión de un archivo desde C mediante temporal, fsync, rename y sincronización del directorio. Un reflink exitoso por sí solo no sustituye ese protocolo.

11. Espacio retenido y coste de las modificaciones

Un snapshot puede ser barato al crearse y retener mucho espacio después. Si reemplazas un archivo de 10 GiB por otro de igual tamaño, la instantánea puede seguir necesitando los datos antiguos. Borrar el nombre activo tampoco libera los bloques que aún referencia otra versión.

Los informes de tamaño lógico, asignación y espacio exclusivo responden preguntas diferentes. No sumes du de todas las versiones para calcular el consumo físico único. En Btrfs se pueden consultar filesystem usage y filesystem du; el primero distingue asignación y uso, y el segundo ayuda a observar extents compartidos. Véase btrfs-filesystem(8).

consultas · usar el punto de montaje del laboratorio
$ sudo btrfs filesystem usage "$MNT34"
$ sudo btrfs filesystem du -s "$MNT34/activo" "$MNT34/antes"
$ df -B1 -- "$MNT34"

Una escritura pequeña puede implicar nuevos extents y metadatos. Compresión, tamaño de registro, fragmentación y agrupación de operaciones cambian el coste real. No se deduce el número de bytes físicos escritos a partir de un pwrite de un byte. Conviene dejar margen de capacidad y definir cuánto tiempo se conservan las versiones.

12. Programa en C: solicitar un reflink

Guarda clonar-cow.c. El programa exige un destino nuevo y solicita FICLONE, sin recurrir a una copia convencional si falla. Es específico de Linux y requiere cabeceras y un sistema de archivos compatibles. No copia permisos, propietario, atributos extendidos ni fechas del origen.

clonar-cow.c · clonar datos entre dos archivos del mismo sistema
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

static int confirmar(int fd)
{
    int r;
    do { r = fsync(fd); } while (r == -1 && errno == EINTR);
    return r;
}

int main(int argc, char **argv)
{
    if (argc != 3) {
        fprintf(stderr, "Uso: %s origen destino-nuevo\n", argv[0]);
        return 2;
    }
    int origen = open(argv[1], O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
    if (origen == -1) { perror("open origen"); return 1; }
    struct stat st;
    if (fstat(origen, &st) == -1) {
        perror("fstat"); close(origen); return 1;
    }
    if (!S_ISREG(st.st_mode)) {
        fprintf(stderr, "El origen debe ser regular.\n");
        close(origen); return 1;
    }
    int destino = open(argv[2], O_WRONLY | O_CREAT | O_EXCL |
                       O_CLOEXEC | O_NOFOLLOW, 0600);
    if (destino == -1) { perror("open destino"); close(origen); return 1; }
    int estado = 0;
    if (ioctl(destino, FICLONE, origen) == -1) {
        perror("FICLONE (sin copia alternativa)"); estado = 1;
    } else if (confirmar(destino) == -1) {
        perror("fsync destino"); estado = 1;
    }
    if (close(destino) == -1) { perror("close destino"); estado = 1; }
    if (close(origen) == -1) { perror("close origen"); estado = 1; }
    if (estado) {
        fprintf(stderr, "Destino conservado para inspeccion; no asumir copia valida.\n");
        return 1;
    }
    puts("Reflink confirmado para el archivo; falta persistir el nombre del directorio.");
    return 0;
}

O_EXCL evita truncar un nombre existente; O_NOFOLLOW rechaza un enlace simbólico final del origen. El ensayo se ejecuta en un directorio propio, sin cambios concurrentes de rutas ni del contenido. Estas opciones no resuelven por sí solas todos los problemas de rutas no confiables.

El destino creado se conserva si falla la clonación; no se intenta borrarlo por una ruta que podría haber cambiado. Lee el mensaje y revisa ese archivo antes de repetir con otro nombre. EXDEV, EOPNOTSUPP o EINVAL pueden señalar restricciones diferentes; no todo fallo significa disco lleno.

13. Programa en C: modificar una posición

Guarda cambiar-byte.c. Cambia únicamente el primer byte de un regular no vacío por el carácter ASCII suministrado. No crea archivos, no los trunca y conserva el resto del contenido. Ejecutarlo sobre un hard link cambia el mismo objeto; sobre un reflink debe preservar la otra versión.

cambiar-byte.c · pwrite acotado con comprobación de errores
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    if (argc != 3 || strlen(argv[2]) != 1 ||
        (unsigned char)argv[2][0] > 127) {
        fprintf(stderr, "Uso: %s archivo caracter-ASCII\n", argv[0]);
        return 2;
    }
    int fd = open(argv[1], O_RDWR | O_CLOEXEC | O_NOFOLLOW);
    if (fd == -1) { perror("open"); return 1; }
    struct stat st;
    if (fstat(fd, &st) == -1) { perror("fstat"); close(fd); return 1; }
    if (!S_ISREG(st.st_mode) || st.st_size < 1) {
        fprintf(stderr, "Se requiere un regular no vacio.\n");
        close(fd); return 1;
    }
    ssize_t n;
    do { n = pwrite(fd, argv[2], 1, 0); } while (n == -1 && errno == EINTR);
    int estado = 0;
    if (n != 1) {
        if (n == -1) perror("pwrite");
        else fprintf(stderr, "pwrite no completo el byte.\n");
        estado = 1;
    } else {
        int r;
        do { r = fsync(fd); } while (r == -1 && errno == EINTR);
        if (r == -1) { perror("fsync"); estado = 1; }
    }
    if (close(fd) == -1) { perror("close"); estado = 1; }
    if (!estado) puts("Primer byte modificado y archivo sincronizado.");
    return estado;
}

Para un byte, un retorno positivo solo puede completar el byte solicitado; cero se trata como falta de progreso. El patrón general para buffers más grandes también debe avanzar por escrituras parciales. Véanse pread/pwrite(2) y fsync(2).

14. Laboratorio sobre una imagen Btrfs propia

Usa Linux con soporte Btrfs, btrfs-progs, GCC, Python 3 y autorización para montajes loop. Una VM sirve si dispone de esas capacidades. Trabaja en una misma sesión Bash y detén la secuencia si una orden falla. La imagen nueva tiene 512 MiB lógicos; comprueba antes el espacio del anfitrión y no uses un dispositivo real como destino de mkfs.

Preparar la imagen y las herramientas

Bash · guardar previamente los dos fuentes C en el directorio actual
$ cc -std=c11 -Wall -Wextra -Wpedantic -O2 clonar-cow.c -o clonar-cow
$ cc -std=c11 -Wall -Wextra -Wpedantic -O2 cambiar-byte.c -o cambiar-byte
$ BIN34="$PWD"
$ LAB34=$(mktemp -d "$HOME/cow34.XXXXXX")
$ IMG34="$LAB34/practica.img"
$ MNT34="$LAB34/mnt"
$ mkdir -- "$MNT34"
$ truncate -s 512M -- "$IMG34"
$ mkfs.btrfs -- "$IMG34"
$ sudo mount -o loop,nodev,nosuid,noexec "$IMG34" "$MNT34"
$ findmnt --mountpoint "$MNT34" --output TARGET,SOURCE,FSTYPE,OPTIONS
$ sudo btrfs subvolume create "$MNT34/activo"
$ sudo chown "$(id -u):$(id -g)" "$MNT34/activo"

mktemp crea una ruta exclusiva y mkfs se aplica solo a su archivo nuevo. No se usa -f para forzar el formateo. Los ejecutables permanecen fuera del montaje noexec; si tu directorio de compilación tampoco permite ejecutar, usa una ubicación de práctica que sí lo permita.

Crear datos y capturar la versión inicial

Bash y Python · 4 MiB deterministas, no un archivo disperso
$ python3 - "$MNT34/activo/original.bin" <<'PY'
import os
import sys
with open(sys.argv[1], 'xb') as f:
    for _ in range(1024):
        f.write(b'A' * 4096)
    f.flush()
    os.fsync(f.fileno())
PY
$ sudo btrfs subvolume snapshot -r "$MNT34/activo" "$MNT34/antes"
$ sudo btrfs subvolume show "$MNT34/antes"
$ "$BIN34/clonar-cow" "$MNT34/activo/original.bin" "$MNT34/activo/clon.bin"
$ cp --reflink=never -- "$MNT34/activo/original.bin" "$MNT34/activo/copia.bin"
$ ln -- "$MNT34/activo/original.bin" "$MNT34/activo/enlace.bin"
$ ls -li -- "$MNT34/activo/"*.bin
$ "$BIN34/cambiar-byte" "$MNT34/activo/clon.bin" X
$ "$BIN34/cambiar-byte" "$MNT34/activo/enlace.bin" Y

La instantánea se tomó antes de crear los otros nombres. Por tanto, antes solo contiene original.bin. El hard link activo y original.bin deben mostrar el mismo inodo dentro del subvolumen activo; no uses números de inodo de subvolúmenes distintos como prueba de identidad global.

Consultar uso y comprobar integridad

consultas del montaje de práctica · sin introducir corrupción
$ sudo btrfs filesystem sync "$MNT34"
$ sudo btrfs filesystem usage "$MNT34"
$ sudo btrfs filesystem du -s "$MNT34/activo" "$MNT34/antes"
$ sudo btrfs scrub start -B -r "$MNT34"
$ sudo btrfs scrub status "$MNT34"

-B espera el resultado del scrub y -r deshabilita su reparación. No se interpreta esa opción como garantía de cero escrituras sobre un montaje de lectura y escritura. El ensayo comprueba datos existentes; no daña la imagen para intentar demostrar una reparación.

15. Verificación reproducible y lectura de resultados

Ejecuta este verificador antes de desmontar. Comprueba todos los bytes, la independencia de las versiones y la identidad del hard link. Los ficheros son pequeños y propios del ensayo; no se aplica esta lectura completa indiscriminadamente a recursos especiales.

Python 3 · condiciones esperadas después de ambas modificaciones
$ python3 - "$MNT34" <<'PY'
from pathlib import Path
import sys

base = Path(sys.argv[1])
inicial = b'A' * (4 * 1024 * 1024)
esperados = {
    'antes/original.bin': inicial,
    'activo/original.bin': b'Y' + inicial[1:],
    'activo/enlace.bin': b'Y' + inicial[1:],
    'activo/clon.bin': b'X' + inicial[1:],
    'activo/copia.bin': inicial,
}
for nombre, esperado in esperados.items():
    if (base / nombre).read_bytes() != esperado:
        raise SystemExit('Contenido inesperado: ' + nombre)
    print('OK', nombre)
original = (base / 'activo/original.bin').stat()
enlace = (base / 'activo/enlace.bin').stat()
clon = (base / 'activo/clon.bin').stat()
if (original.st_dev, original.st_ino) != (enlace.st_dev, enlace.st_ino):
    raise SystemExit('El hard link no comparte identidad')
if (original.st_dev, original.st_ino) == (clon.st_dev, clon.st_ino):
    raise SystemExit('El clon no es un archivo distinto')
if sorted(p.name for p in (base / 'antes').iterdir()) != ['original.bin']:
    raise SystemExit('Contenido inesperado en el snapshot')
print('OK: aislamiento de versiones e identidad del hard link')
PY

El éxito de FICLONE acredita que se aceptó la operación de clonado; el verificador prueba el comportamiento del contenido. No mide el ahorro físico exacto. No esperes una diferencia de un byte en filesystem usage: la asignación trabaja con unidades mayores y también necesita metadatos.

cerrar la práctica · conservar la imagen para inspección posterior
$ cd -- "$LAB34"
$ sudo umount "$MNT34"
$ mountpoint -q "$MNT34" && printf 'El montaje sigue activo\n'
$ printf 'Imagen conservada: %s\n' "$IMG34"

mountpoint devuelve un estado distinto de cero cuando el directorio ya no es un punto de montaje; en ese caso no imprime el aviso. Si umount informa ocupado, cierra los procesos que usan el ensayo y vuelve a desmontar normalmente. La imagen y los fuentes se conservan; no hace falta borrarlos para completar la observación.

16. Simuladores de referencias e integridad

Bloques compartidos y versiones retenidas

El modelo tiene cuatro posiciones y bloques de datos de 4 KiB. Cada modificación crea un bloque nuevo, incluso si se repite una posición. Crear una instantánea de solo lectura comparte las referencias actuales; solo se mantiene una instantánea cada vez. Se omiten metadatos, compresión, caché y liberación diferida: los resultados no predicen df de un sistema real.

Una versión activa y una instantánea

referencias y espacio de datos único del modelo
Bloque vivoContenidoReferenciasVistas que lo necesitan

Captura, modifica la posición 1 y observa cinco bloques vivos. Modifica de nuevo la posición 1: el bloque intermedio ya no está referenciado, de modo que el total sigue siendo cinco. Elimina la instantánea: vuelven a quedar cuatro.

Detección, réplica y reparación

El checksum didáctico es la suma de cuatro bytes módulo 256. Es deliberadamente débil y no representa los algoritmos de Btrfs o ZFS. La referencia esperada permanece protegida en este modelo. La colisión altera dos bytes conservando la suma para mostrar el límite de la detección.

¿Hay una copia verificable para recuperar?

verificación ilustrativa · no se modifica ningún archivo

17. Diagnóstico y resumen

ObservaciónInterpretación a comprobar
Borré un archivo, pero sigue ocupado el espacio.Puede haber snapshots, reflinks o descriptores que retienen datos; también liberación pendiente.
Cambiar enlace.bin modifica original.bin.Son nombres del mismo inodo, no versiones independientes.
FICLONE falla.Revisar soporte, mismo sistema de archivos, opciones, permisos y errno.
El snapshot no contiene un subvolumen hijo.La captura Btrfs no recorre automáticamente ese límite.
Scrub detectó daño, pero no pudo corregirlo.Puede faltar una copia válida accesible para reparar.
Los checksums son correctos, pero el saldo es erróneo.La integridad de bloques no valida la lógica de la aplicación.
Sumar tamaños de versiones supera el uso del volumen.Puede haber almacenamiento compartido; distinguir tamaño lógico y consumo único.

18. Preguntas de repaso y práctica

1. ¿Qué evita COW al modificar un bloque compartido?

Que los cambios de una versión alteren los bytes que todavía necesita otra.

2. ¿Solo hay que escribir el bloque de datos nuevo?

No. Puede ser necesario crear metadatos nuevos y publicar referencias válidas respetando la persistencia.

3. ¿Crear un snapshot duplica todos los bytes?

No. Puede compartir datos existentes, aunque su gestión tiene coste y las modificaciones posteriores pueden retener espacio adicional.

4. ¿Un hard link conserva la versión anterior del archivo?

No. Es otro nombre del mismo objeto; escribir mediante cualquiera modifica ese objeto.

5. ¿Un reflink tiene el mismo inodo que el origen?

No en este ensayo dentro del mismo subvolumen. Es otro archivo, aunque pueda compartir extents de datos.

6. ¿Dos hashes iguales demuestran un reflink?

No. No indican cómo se almacenan los datos.

7. ¿Por qué el ejemplo exige FICLONE?

Para informar si no se puede clonar, sin ocultar ese resultado mediante una copia convencional.

8. ¿El programa clona permisos y atributos extendidos?

No. Crea el destino con modo solicitado 0600 y clona sus datos; no implementa una copia completa de metadatos.

9. ¿Los snapshots de Btrfs son siempre de solo lectura?

No. El laboratorio usa -r para crearlos de solo lectura.

10. ¿Capturar el padre Btrfs incluye el contenido de subvolúmenes hijos?

No. Es necesario considerar esos límites y capturar las unidades pertinentes.

11. ¿Qué diferencia hay entre snapshot y clone en ZFS?

El snapshot es de solo lectura; el clone es una rama escribible derivada de una instantánea.

12. ¿Un snapshot local protege contra perder todo el pool?

No. Comparte el dominio de pérdida de ese almacenamiento.

13. ¿Por qué un checksum no siempre permite reparar?

Detectar una discrepancia no aporta los bytes originales; hace falta una fuente válida de recuperación.

14. ¿Una colisión significa que dos contenidos son iguales?

No. Significa que contenidos distintos produjeron el mismo valor de checksum.

15. ¿Scrub es lo mismo que fsck?

No. Verificación de bloques y coherencia estructural tienen alcances distintos.

16. ¿Un checksum correcto valida una operación bancaria?

No. Puede proteger perfectamente un valor que la aplicación calculó mal.

17. ¿Por qué borrar el original puede no liberar los datos?

Porque otra versión o referencia todavía los necesita.

18. ¿Cambiar un byte implica escribir un byte físico?

No. El sistema trabaja con unidades de datos y metadatos mayores y puede agrupar operaciones.

19. ¿Un snapshot sustituye el backup de una base de datos?

Solo puede formar parte de un procedimiento que coordine la base, su recuperación y el almacenamiento de la copia.

20. ¿El ejemplo C confirma también la persistencia del nombre nuevo?

No. Sincroniza el archivo; el protocolo completo también debe considerar el directorio.

Para practicar

  1. Dibuja las referencias después de crear dos snapshots y modificar posiciones diferentes.
  2. Calcula los bloques únicos del dibujo y compáralos con la suma de los tamaños de las versiones.
  3. Ejecuta los programas sobre el montaje de práctica y verifica que las advertencias del compilador están resueltas.
  4. Repite la clonación usando el mismo destino: comprueba que O_EXCL rechaza la operación sin truncarlo.
  5. Prueba un archivo vacío con cambiar-byte y explica por qué lo rechaza.
  6. Comprueba que antes solo contiene el nombre que existía en el momento de la captura.
  7. Relaciona los inodos de original.bin, enlace.bin y clon.bin dentro del subvolumen activo.
  8. Compara filesystem usage, filesystem du y df sin exigir que informen la misma magnitud.
  9. En el simulador, modifica dos veces una posición después de capturar y explica por qué no se retienen todas las versiones intermedias.
  10. Combina daño, ausencia de réplica y colisión en el segundo simulador.
  11. Diseña una política que conserve versiones locales y una copia en otro almacenamiento.
  12. Explica qué coordinación necesitaría una aplicación cuyos datos se reparten entre dos subvolúmenes.

En el tema 35 desarrollaremos actualizaciones atómicas desde C: crear un temporal, escribir y comprobar, sincronizar, publicar con rename y confirmar el directorio.