Semana 14 — Implementación de sistemas de archivos, FFS y journaling
Objetivos
- Diseñar un sistema de archivos simple (VSFS): superbloque, bitmaps, tabla de inodos, bloques de datos.
- Explicar cómo FFS aprovecha la geometría del disco.
- Explicar el problema de consistencia ante fallos y sus soluciones:
fscky journaling.
Sesión A — Implementación de un FS
Lecturas:
- Cap. 40 File System Implementation
Temas:
- Estructuras en disco: superbloque (superblock), bitmaps, inodos, bloques de datos.
- Punteros directos, indirectos y multinivel; extents.
- Recorrido de una ruta (path traversal) al abrir, leer y escribir un archivo.
- Caché de páginas y búfer de escritura (write buffering).
Sesión B — FFS · Journaling
Lecturas:
Temas:
- Grupos de cilindros (cylinder groups), política de ubicación, excepción de archivos grandes.
- Escenarios de fallo durante una escritura.
fsck. - Journaling de datos y de metadatos (ordered mode), checkpoint, revocación (ext3/ext4).
- Alternativas: soft updates, copy-on-write, backpointers.
Laboratorio — Simuladores de FS y el sistema de archivos de xv6
vsfs.py: preguntas 1–4 del cap. 40.ffs.pyyfsck.py: preguntas seleccionadas.- Para P5: leer
kernel/fs.h,kernel/fs.c(bmap()) ymkfs/mkfs.cde xv6-riscv; dibujar el formato del disco e identificar dónde se agregaría un bloque doblemente indirecto.
Preguntas de repaso
- ¿Cuántas lecturas de disco necesita
open("/foo/bar")en VSFS sin caché? - Con bloques de 4 KB, punteros de 4 bytes, 12 directos y uno indirecto, ¿cuál es el tamaño máximo de archivo?
- ¿Por qué el journaling de metadatos escribe los datos antes que la transacción del journal?
Código de la semana
Compilar dentro del contenedor del curso con make o gcc -Wall.
Ejemplos
Los programas que usan los apuntes y la clase.
// bmapcalc.c: el formato del disco de xv6-riscv y qué bloque guarda un byte.
//
// Las constantes son las de kernel/fs.h, kernel/param.h y mkfs/mkfs.c.
// bmapcalc muestra el formato del disco
// bmapcalc off [off...] para cada offset: bloque lógico y dónde está
// bmapcalc -i inum en qué bloque y byte está el inodo inum
// bmapcalc -max bs ptr nd tamaño máximo con nd directos y un indirecto
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// kernel/fs.h
#define BSIZE 1024
#define NDIRECT 12
#define NINDIRECT (BSIZE / sizeof(unsigned int))
#define MAXFILE (NDIRECT + NINDIRECT)
#define DINODE 64 // sizeof(struct dinode)
#define IPB (BSIZE / DINODE)
#define BPB (BSIZE * 8)
// kernel/param.h y mkfs/mkfs.c
#define FSSIZE 2000
#define LOGBLOCKS 30
#define NINODES 200
static int nlog, ninodeblocks, nbitmap, nmeta;
static int logstart, inodestart, bmapstart;
static void layout(void) {
// Mismas fórmulas que mkfs/mkfs.c
nbitmap = FSSIZE / BPB + 1;
ninodeblocks = NINODES / IPB + 1;
nlog = LOGBLOCKS + 1; // cabecera + bloques del log
nmeta = 2 + nlog + ninodeblocks + nbitmap;
logstart = 2;
inodestart = 2 + nlog;
bmapstart = 2 + nlog + ninodeblocks;
}
static void print_layout(void) {
printf("xv6: %d bloques de %d B\n", FSSIZE, BSIZE);
printf(" %4d arranque (boot)\n", 0);
printf(" %4d superbloque\n", 1);
printf(" %4d - %4d log (cabecera + %d bloques)\n", logstart,
logstart + nlog - 1, LOGBLOCKS);
printf(" %4d - %4d inodos (%d por bloque)\n", inodestart,
inodestart + ninodeblocks - 1, IPB);
printf(" %4d bitmap (%d bits por bloque)\n", bmapstart, BPB);
printf(" %4d - %4d datos (%d bloques)\n", nmeta, FSSIZE - 1, FSSIZE - nmeta);
printf("archivo máximo: %d + %d = %d bloques = %d KB\n", NDIRECT,
(int)NINDIRECT, (int)MAXFILE, (int)MAXFILE * BSIZE / 1024);
}
static void where(unsigned long off) {
unsigned long bn = off / BSIZE;
printf("offset %lu: bloque lógico %lu, byte %lu dentro del bloque\n",
off, bn, off % BSIZE);
if (bn < NDIRECT) {
printf(" directo: addrs[%lu]; lecturas = inodo + datos = 2\n", bn);
} else if (bn < MAXFILE) {
unsigned long k = bn - NDIRECT;
printf(" indirecto: addrs[%d] -> entrada %lu (bytes %lu-%lu del bloque)\n",
NDIRECT, k, k * 4, k * 4 + 3);
printf(" lecturas = inodo + indirecto + datos = 3\n");
} else {
printf(" fuera de rango: MAXFILE = %d bloques (esto es P5)\n", (int)MAXFILE);
}
}
int main(int argc, char *argv[]) {
layout();
if (argc == 1) {
print_layout();
return 0;
}
if (strcmp(argv[1], "-i") == 0 && argc == 3) {
int inum = atoi(argv[2]);
// IBLOCK(i, sb) de kernel/fs.h
printf("inodo %d: bloque %d, byte %d\n", inum, inum / IPB + inodestart,
(inum % IPB) * DINODE);
return 0;
}
if (strcmp(argv[1], "-max") == 0 && argc == 5) {
long bs = atol(argv[2]), ptr = atol(argv[3]), nd = atol(argv[4]);
long nptr = bs / ptr;
long blocks = nd + nptr;
printf("%ld punteros por bloque\n", nptr);
printf("%ld + %ld = %ld bloques = %ld KB\n", nd, nptr, blocks, blocks * bs / 1024);
return 0;
}
for (int i = 1; i < argc; i++)
where(strtoul(argv[i], NULL, 0));
return 0;
}// fsyncbench.c: cuánto cuesta pedir que cada escritura llegue al disco.
//
// fsyncbench archivo n
// Escribe n registros de 512 B tres veces: sin fsync, con fdatasync y con
// fsync después de cada uno. Los números dependen mucho del disco y, en el
// contenedor, de la capa de virtualización: compárenlos entre sí.
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static double now(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_sec + t.tv_nsec / 1e9;
}
// modo 0: solo write; 1: write + fdatasync; 2: write + fsync
static double run(const char *path, int n, int mode) {
char rec[512];
memset(rec, 'x', sizeof rec);
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { perror(path); exit(1); }
double t0 = now();
for (int i = 0; i < n; i++) {
if (write(fd, rec, sizeof rec) != (ssize_t)sizeof rec) { perror("write"); exit(1); }
if (mode == 1 && fdatasync(fd) < 0) { perror("fdatasync"); exit(1); }
if (mode == 2 && fsync(fd) < 0) { perror("fsync"); exit(1); }
}
double t = now() - t0;
close(fd);
return t;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "uso: %s archivo n\n", argv[0]);
return 1;
}
int n = atoi(argv[2]);
if (n <= 0) n = 100;
const char *label[] = { "write", "write + fdatasync", "write + fsync" };
for (int m = 0; m < 3; m++) {
double t = run(argv[1], n, m);
printf("%-18s %5d registros: %8.1f us por registro\n", label[m], n, t / n * 1e6);
}
unlink(argv[1]);
return 0;
}// journal.c: simula una actualización con y sin journaling, y un fallo
// (crash) después de cada escritura al disco.
//
// La actualización es la de OSTEP cap. 42: agregar un bloque a un archivo.
// Cambian tres bloques: el inodo I, el bitmap de datos B y el bloque Db.
//
// journal none escribir I, B y Db en su lugar, sin protección
// journal data journaling de datos: TxB I B Db | TxE | checkpoint
// journal ordered journaling de metadatos: Db | TxB I B | TxE | checkpoint
// journal reorder como data, pero el disco reordena y TxE llega primero
#include <stdio.h>
#include <string.h>
enum { I, B, DB, TXB, JI, JB, JDB, TXE, NBLK };
static const char *name[NBLK] = { "I", "B", "Db", "TxB", "j:I", "j:B", "j:Db", "TxE" };
// 0 = viejo (o TxB/TxE ausentes), 1 = nuevo, 2 = basura de una transacción anterior
struct disk { int v[NBLK]; };
// Recuperación: si el journal tiene TxB y TxE, se reproduce (replay).
// Sin checksum, no se puede saber si los bloques del medio llegaron.
static void recover(struct disk *d, int ordered) {
if (d->v[TXB] && d->v[TXE]) {
d->v[I] = d->v[JI];
d->v[B] = d->v[JB];
if (!ordered) d->v[DB] = d->v[JDB];
}
d->v[TXB] = d->v[TXE] = 0; // vaciar el journal
}
static const char *verdict(const struct disk *d) {
if (d->v[I] == 2 || d->v[B] == 2 || d->v[DB] == 2)
return "CORRUPTO: replay de basura";
if (d->v[I] != d->v[B]) return "INCONSISTENTE: I y B no coinciden";
if (d->v[I] == 0) return "ok: estado viejo";
if (d->v[DB] == 0) return "metadatos ok, Db es basura";
return "ok: estado nuevo";
}
static void run(const char *mode, const int *order, int n, int ordered) {
int w = 18; // ancho de la primera columna
int len = 0;
for (int i = 0; i < n; i++) len += strlen(name[order[i]]) + 1;
if (len > w) w = len;
printf("== %s ==\n", mode);
printf("%-*s %s\n", w, "en disco al fallar", "tras recuperar");
for (int k = 0; k <= n; k++) { // k escrituras llegaron al disco
struct disk d = {{ 0, 0, 0, 0, 2, 2, 2, 0 }};
char done[64] = "";
for (int i = 0; i < k; i++) {
d.v[order[i]] = 1;
strcat(done, name[order[i]]);
strcat(done, " ");
}
if (k == 0) strcpy(done, "(nada)");
recover(&d, ordered);
printf("%-*s %s\n", w, done, verdict(&d));
}
}
int main(int argc, char *argv[]) {
const char *m = argc > 1 ? argv[1] : "data";
if (strcmp(m, "none") == 0) {
int o[] = { DB, I, B };
run("sin journal: Db, I, B", o, 3, 0);
} else if (strcmp(m, "data") == 0) {
int o[] = { TXB, JI, JB, JDB, TXE, I, B, DB };
run("journaling de datos", o, 8, 0);
} else if (strcmp(m, "ordered") == 0) {
int o[] = { DB, TXB, JI, JB, TXE, I, B };
run("journaling de metadatos (ordered)", o, 7, 1);
} else if (strcmp(m, "reorder") == 0) {
int o[] = { TXB, TXE, JI, JB, JDB }; // el disco reordenó TxE
run("sin barrera: TxE llega antes", o, 5, 0);
} else {
fprintf(stderr, "uso: %s none|data|ordered|reorder\n", argv[0]);
return 1;
}
return 0;
}// vsfs.c: un sistema de archivos muy simple (VSFS) sobre un archivo imagen.
//
// Formato: 64 bloques de 4 KB.
// 0 superbloque | 1 bitmap de inodos | 2 bitmap de datos |
// 3-7 tabla de inodos (80 inodos de 256 B) | 8-63 bloques de datos
// Sin caché: cada lectura o escritura de bloque es una E/S y se cuenta.
//
// Uso:
// vsfs img mkfs
// vsfs img mkdir /ruta
// vsfs img create /ruta nbytes (crea y escribe nbytes)
// vsfs img read /ruta (abre y lee todo el archivo)
// vsfs img ls /ruta
// vsfs img stat inum
// Con -v antes de img se imprime cada E/S.
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BSIZE 4096
#define NBLOCKS 64
#define IBMAP 1
#define DBMAP 2
#define INODESTART 3
#define DATASTART 8
#define ISIZE 256
#define IPB (BSIZE / ISIZE) // 16 inodos por bloque
#define NINODES ((DATASTART - INODESTART) * IPB)
#define NDATA (NBLOCKS - DATASTART)
#define NDIRECT 12
#define ROOTINO 2 // como en Unix
#define DIRSIZ 12
#define MAGIC 0x56534653
enum { T_FREE = 0, T_FILE = 1, T_DIR = 2 };
struct superblock {
uint32_t magic, nblocks, ninodes, inodestart, datastart;
};
struct dinode { // 256 bytes en disco
uint16_t type, nlink;
uint32_t size;
uint32_t addrs[NDIRECT]; // 0 = sin bloque
uint8_t pad[ISIZE - 8 - 4 * NDIRECT];
};
struct dirent { // 16 bytes
uint32_t inum;
char name[DIRSIZ];
};
static int fd, verbose, nreads, nwrites;
static const char *region(uint32_t b) {
static char s[32];
if (b == 0) return "superbloque";
if (b == IBMAP) return "bitmap de inodos";
if (b == DBMAP) return "bitmap de datos";
if (b < DATASTART) {
snprintf(s, sizeof s, "inodos %u-%u", (b - INODESTART) * IPB,
(b - INODESTART) * IPB + IPB - 1);
return s;
}
return "datos";
}
static void bread(uint32_t b, void *buf) {
if (pread(fd, buf, BSIZE, (off_t)b * BSIZE) != BSIZE) { perror("pread"); exit(1); }
nreads++;
if (verbose) printf(" leer bloque %2u (%s)\n", b, region(b));
}
static void bwrite(uint32_t b, const void *buf) {
if (pwrite(fd, buf, BSIZE, (off_t)b * BSIZE) != BSIZE) { perror("pwrite"); exit(1); }
nwrites++;
if (verbose) printf(" escribir bloque %2u (%s)\n", b, region(b));
}
// Inodo inum: está en el bloque INODESTART + inum/IPB, posición inum%IPB.
static void iread(uint32_t inum, struct dinode *ip) {
uint8_t buf[BSIZE];
bread(INODESTART + inum / IPB, buf);
memcpy(ip, buf + (inum % IPB) * ISIZE, ISIZE);
}
static void iwrite(uint32_t inum, const struct dinode *ip) {
uint8_t buf[BSIZE];
bread(INODESTART + inum / IPB, buf); // leer-modificar-escribir
memcpy(buf + (inum % IPB) * ISIZE, ip, ISIZE);
bwrite(INODESTART + inum / IPB, buf);
}
// Busca un bit en 0 en el bitmap, lo pone en 1 y devuelve su índice.
static int bitmap_alloc(uint32_t bmap, int n) {
uint8_t buf[BSIZE];
bread(bmap, buf);
for (int i = 0; i < n; i++) {
if ((buf[i / 8] & (1 << (i % 8))) == 0) {
buf[i / 8] |= 1 << (i % 8);
bwrite(bmap, buf);
return i;
}
}
return -1;
}
static int balloc(void) {
int i = bitmap_alloc(DBMAP, NDATA);
return i < 0 ? -1 : DATASTART + i;
}
// Busca name en el directorio dp; devuelve el inum o 0.
static uint32_t dirlookup(struct dinode *dp, const char *name) {
struct dirent de[BSIZE / sizeof(struct dirent)];
uint32_t nent = dp->size / sizeof(struct dirent);
for (uint32_t i = 0; i < nent; i++) {
uint32_t k = i % (BSIZE / sizeof(struct dirent));
if (k == 0) bread(dp->addrs[i / (BSIZE / sizeof(struct dirent))], de);
if (de[k].inum && strncmp(de[k].name, name, DIRSIZ) == 0)
return de[k].inum;
}
return 0;
}
// Recorre la ruta desde la raíz. Si last != NULL, se detiene en el
// directorio padre y copia el último componente en last.
static uint32_t namei(const char *path, char *last) {
char copy[256], *save, *tok, *next;
uint32_t inum = ROOTINO;
struct dinode d;
snprintf(copy, sizeof copy, "%s", path);
tok = strtok_r(copy, "/", &save);
while (tok) {
next = strtok_r(NULL, "/", &save);
if (last && next == NULL) { // último componente
snprintf(last, DIRSIZ + 1, "%s", tok);
return inum;
}
iread(inum, &d);
if (d.type != T_DIR) return 0;
uint32_t child = dirlookup(&d, tok);
if (verbose) printf(" en el inodo %u: \"%s\" -> inodo %u\n", inum, tok, child);
if (child == 0) return 0;
inum = child;
tok = next;
}
return inum;
}
// Agrega (name, inum) al final del directorio dinum, cuyo inodo es *dp.
static int diradd(uint32_t dinum, struct dinode *dp, const char *name, uint32_t inum) {
struct dirent de[BSIZE / sizeof(struct dirent)];
uint32_t bn = dp->size / BSIZE;
if (bn >= NDIRECT) return -1;
if (dp->size % BSIZE == 0) { // hace falta un bloque nuevo
int b = balloc();
if (b < 0) return -1;
dp->addrs[bn] = b;
memset(de, 0, sizeof de);
} else {
bread(dp->addrs[bn], de);
}
struct dirent *e = &de[(dp->size % BSIZE) / sizeof(struct dirent)];
e->inum = inum;
strncpy(e->name, name, DIRSIZ);
bwrite(dp->addrs[bn], de);
dp->size += sizeof(struct dirent);
iwrite(dinum, dp);
return 0;
}
// Crea un archivo o directorio vacío en path; devuelve su inum.
static uint32_t create(const char *path, int type) {
char name[DIRSIZ + 1];
uint32_t parent = namei(path, name);
struct dinode dp;
if (parent == 0) { fprintf(stderr, "no existe el directorio padre\n"); exit(1); }
iread(parent, &dp);
if (dirlookup(&dp, name)) { fprintf(stderr, "%s ya existe\n", path); exit(1); }
int inum = bitmap_alloc(IBMAP, NINODES);
if (inum < 0) { fprintf(stderr, "no hay inodos libres\n"); exit(1); }
struct dinode ip = { .type = type, .nlink = 1 };
if (type == T_DIR) { // un bloque con "." y ".."
struct dirent de[BSIZE / sizeof(struct dirent)] = {{ inum, "." }, { parent, ".." }};
int b = balloc();
if (b < 0) { fprintf(stderr, "disco lleno\n"); exit(1); }
bwrite(b, de);
ip.addrs[0] = b;
ip.size = 2 * sizeof(struct dirent);
ip.nlink = 2;
}
iwrite(inum, &ip);
if (type == T_DIR) dp.nlink++; // por el ".." del hijo
if (diradd(parent, &dp, name, inum) < 0) { fprintf(stderr, "directorio lleno\n"); exit(1); }
return inum;
}
static void phase(const char *what) {
static int r0, w0;
if (verbose && what == NULL)
printf(" -> %d lecturas, %d escrituras\n", nreads - r0, nwrites - w0);
if (what && verbose) printf("%s\n", what);
r0 = nreads;
w0 = nwrites;
}
static void do_mkfs(void) {
uint8_t buf[BSIZE] = {0};
for (int b = 0; b < NBLOCKS; b++) bwrite(b, buf);
struct superblock sb = { MAGIC, NBLOCKS, NINODES, INODESTART, DATASTART };
memcpy(buf, &sb, sizeof sb);
bwrite(0, buf);
memset(buf, 0, BSIZE);
buf[0] = 0x07; // inodos 0, 1 (reservados) y 2 (raíz)
bwrite(IBMAP, buf);
buf[0] = 0x01; // bloque de datos 0 = bloque 8
bwrite(DBMAP, buf);
struct dinode root = { .type = T_DIR, .nlink = 2, .size = 2 * sizeof(struct dirent) };
root.addrs[0] = DATASTART;
iwrite(ROOTINO, &root);
struct dirent de[BSIZE / sizeof(struct dirent)] = {{ ROOTINO, "." }, { ROOTINO, ".." }};
bwrite(DATASTART, de);
printf("mkfs: %d bloques de %d B, %d inodos de %d B, %d bloques de datos\n",
NBLOCKS, BSIZE, NINODES, ISIZE, NDATA);
}
static void do_write(uint32_t inum, int nbytes) {
uint8_t buf[BSIZE], iblk[BSIZE];
uint32_t ib = INODESTART + inum / IPB, io = (inum % IPB) * ISIZE;
struct dinode ip;
for (int off = 0, i = 0; off < nbytes; off += BSIZE, i++) {
char what[64];
snprintf(what, sizeof what, "write() del bloque %d del archivo", i);
phase(what);
if (i >= NDIRECT) { fprintf(stderr, "archivo demasiado grande\n"); exit(1); }
bread(ib, iblk); // leer el inodo
memcpy(&ip, iblk + io, ISIZE);
int b = balloc(); // leer y escribir el bitmap
if (b < 0) { fprintf(stderr, "disco lleno\n"); exit(1); }
memset(buf, 'a' + i % 26, BSIZE);
bwrite(b, buf); // escribir los datos
ip.addrs[i] = b;
ip.size = (uint32_t)(off + BSIZE < nbytes ? off + BSIZE : nbytes);
memcpy(iblk + io, &ip, ISIZE);
bwrite(ib, iblk); // escribir el inodo
phase(NULL);
}
}
static void do_read(const char *path) {
uint8_t buf[BSIZE];
struct dinode ip;
phase("open()");
uint32_t inum = namei(path, NULL);
if (inum == 0) { fprintf(stderr, "%s: no existe\n", path); exit(1); }
iread(inum, &ip);
phase(NULL);
for (uint32_t i = 0; i * BSIZE < ip.size; i++) {
phase("read()");
bread(ip.addrs[i], buf); // el inodo ya está en memoria
phase(NULL);
}
printf("%s: inodo %u, %u bytes\n", path, inum, ip.size);
}
static void do_ls(const char *path) {
struct dinode dp, ip;
struct dirent de[BSIZE / sizeof(struct dirent)];
uint32_t inum = namei(path, NULL);
if (inum == 0) { fprintf(stderr, "%s: no existe\n", path); exit(1); }
iread(inum, &dp);
for (uint32_t i = 0; i < dp.size / sizeof(struct dirent); i++) {
uint32_t k = i % (BSIZE / sizeof(struct dirent));
if (k == 0) bread(dp.addrs[i / (BSIZE / sizeof(struct dirent))], de);
iread(de[k].inum, &ip);
printf("%-12.12s inodo %2u %-4s %6u B\n", de[k].name, de[k].inum,
ip.type == T_DIR ? "dir" : "arch", ip.size);
}
}
static void do_stat(uint32_t inum) {
struct dinode ip;
if (inum >= NINODES) { fprintf(stderr, "inodo fuera de rango\n"); exit(1); }
iread(inum, &ip);
printf("inodo %u: bloque %u, byte %u del bloque\n", inum,
INODESTART + inum / IPB, (inum % IPB) * ISIZE);
printf(" tipo %s, nlink %u, tamaño %u B\n",
ip.type == T_DIR ? "dir" : ip.type == T_FILE ? "arch" : "libre",
ip.nlink, ip.size);
printf(" bloques:");
for (int i = 0; i < NDIRECT && ip.addrs[i]; i++) printf(" %u", ip.addrs[i]);
printf("\n");
}
int main(int argc, char *argv[]) {
int a = 1;
if (a < argc && strcmp(argv[a], "-v") == 0) { verbose = 1; a++; }
if (argc - a < 2) {
fprintf(stderr, "uso: %s [-v] img mkfs|mkdir|create|read|ls|stat ...\n", argv[0]);
return 1;
}
const char *img = argv[a], *cmd = argv[a + 1], *arg = argc - a > 2 ? argv[a + 2] : "/";
int mk = strcmp(cmd, "mkfs") == 0;
fd = open(img, mk ? O_RDWR | O_CREAT | O_TRUNC : O_RDWR, 0644);
if (fd < 0) { perror(img); return 1; }
if (!mk) {
struct superblock sb;
if (pread(fd, &sb, sizeof sb, 0) != sizeof sb || sb.magic != MAGIC) {
fprintf(stderr, "%s: no es una imagen vsfs\n", img);
return 1;
}
}
if (mk) {
do_mkfs();
} else if (strcmp(cmd, "mkdir") == 0) {
phase("mkdir()");
create(arg, T_DIR);
phase(NULL);
} else if (strcmp(cmd, "create") == 0 && argc - a > 3) {
phase("create()");
uint32_t inum = create(arg, T_FILE);
phase(NULL);
do_write(inum, atoi(argv[a + 3]));
printf("%s: inodo %u\n", arg, inum);
} else if (strcmp(cmd, "read") == 0) {
do_read(arg);
} else if (strcmp(cmd, "ls") == 0) {
do_ls(arg);
} else if (strcmp(cmd, "stat") == 0) {
do_stat(atoi(arg));
} else {
fprintf(stderr, "comando desconocido: %s\n", cmd);
return 1;
}
if (verbose) printf("total: %d lecturas, %d escrituras\n", nreads, nwrites);
close(fd);
return 0;
}Compilación
Cómo se compilan y ejecutan los ejemplos.
# Semana 14: implementación de sistemas de archivos y journaling.
# make compila todo
# make test corre cada programa (la imagen vsfs va a /tmp)
# make clean borra los binarios y la imagen
CFLAGS = -Wall -Wextra -g
PROGS = vsfs bmapcalc journal fsyncbench
IMG = /tmp/vsfs.img
all: $(PROGS)
$(PROGS): %: %.c
$(CC) $(CFLAGS) -o $@ $<
test: $(PROGS)
./vsfs $(IMG) mkfs
./vsfs -v $(IMG) mkdir /foo
./vsfs -v $(IMG) create /foo/bar 12288
./vsfs -v $(IMG) read /foo/bar
./vsfs $(IMG) ls /foo
./vsfs $(IMG) stat 4
./vsfs $(IMG) stat 37
./bmapcalc
./bmapcalc 0 5000 13000 70000 300000
./bmapcalc -i 25
./bmapcalc -max 4096 4 12
./journal none
./journal data
./journal ordered
./journal reorder
./fsyncbench /tmp/fsb.dat 200
clean:
rm -f $(PROGS) $(IMG) /tmp/fsb.dat
.PHONY: all test clean