// atomic.c — reemplazar el contenido de un archivo de forma atómica:
// escribir un temporal, fsync, rename, fsync del directorio.
//
// Uso: ./atomic ARCHIVO TEXTO          reemplaza ARCHIVO por TEXTO
//      ./atomic ARCHIVO TEXTO crash    se "cae" antes del rename
//
// Después de un fallo, ARCHIVO tiene el contenido viejo o el nuevo completo,
// nunca una mezcla ni un archivo vacío.
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static void die(const char *msg) { perror(msg); exit(1); }

int main(int argc, char *argv[]) {
    if (argc < 3) { fprintf(stderr, "uso: %s ARCHIVO TEXTO [crash]\n", argv[0]); return 1; }
    char tmp[4096], dir[4096];
    snprintf(tmp, sizeof tmp, "%s.tmp", argv[1]);
    snprintf(dir, sizeof dir, "%s", argv[1]);

    // 1. Escribir la versión nueva completa en un archivo temporal
    int fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) die("open");
    size_t n = strlen(argv[2]);
    if (write(fd, argv[2], n) != (ssize_t)n || write(fd, "\n", 1) != 1)
        die("write");
    // 2. Forzarla al disco: sin esto, el rename podría
    //    llegar al disco antes que los datos
    if (fsync(fd) < 0) die("fsync");
    close(fd);
    printf("escrito y sincronizado: %s\n", tmp);

    if (argc > 3 && strcmp(argv[3], "crash") == 0) {
        printf("*** caída simulada antes de rename ***\n");
        fflush(stdout);
        _exit(1);                     // termina sin limpiar nada
    }

    // 3. rename es atómico: el nombre apunta al inodo
    //    viejo o al nuevo, nunca a una mezcla
    if (rename(tmp, argv[1]) < 0) die("rename");
    // 4. El cambio de nombre vive en el directorio:
    //    sincronizarlo también
    int dfd = open(dirname(dir), O_RDONLY | O_DIRECTORY);
    if (dfd < 0) die("open dir");
    if (fsync(dfd) < 0) die("fsync dir");
    close(dfd);
    printf("rename %s -> %s listo\n", tmp, argv[1]);
    return 0;
}
