lab5 added.
[mit-jos.git] / fs / fsformat.c
blobd6b1b77b37d8d5ee41b7fc6b884f8253fa0f78c3
1 /*
2 * JOS file system format
3 */
5 #define _BSD_EXTENSION
7 // We don't actually want to define off_t!
8 #define off_t xxx_off_t
9 #define bool xxx_bool
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <inttypes.h>
15 #include <sys/stat.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <errno.h>
19 #include <sys/types.h>
20 #include <dirent.h>
21 #include <limits.h>
22 #undef off_t
23 #undef bool
25 // Prevent inc/types.h, included from inc/fs.h,
26 // from attempting to redefine types defined in the host's inttypes.h.
27 #define JOS_INC_TYPES_H
28 // Typedef the types that inc/mmu.h needs.
29 typedef uint32_t physaddr_t;
30 typedef uint32_t off_t;
31 typedef int bool;
33 #include <inc/mmu.h>
34 #include <inc/fs.h>
36 #define nelem(x) (sizeof(x) / sizeof((x)[0]))
37 typedef struct Super Super;
38 typedef struct File File;
40 struct Super super;
41 int diskfd;
42 uint32_t nblocks;
43 uint32_t nbitblock;
44 uint32_t nextb;
46 enum {
47 BLOCK_SUPER,
48 BLOCK_DIR,
49 BLOCK_FILE,
50 BLOCK_BITS
53 struct Block
55 uint32_t busy;
56 uint32_t bno;
57 uint32_t used;
58 uint8_t buf[BLKSIZE];
59 uint32_t type;
62 struct Block cache[16];
64 ssize_t
65 readn(int f, void *av, size_t n)
67 uint8_t *a;
68 size_t t;
70 a = av;
71 t = 0;
72 while (t < n) {
73 size_t m = read(f, a + t, n - t);
74 if (m <= 0) {
75 if (t == 0)
76 return m;
77 break;
79 t += m;
81 return t;
84 // make little-endian
85 void
86 swizzle(uint32_t *x)
88 uint32_t y;
89 uint8_t *z;
91 z = (uint8_t*) x;
92 y = *x;
93 z[0] = y & 0xFF;
94 z[1] = (y >> 8) & 0xFF;
95 z[2] = (y >> 16) & 0xFF;
96 z[3] = (y >> 24) & 0xFF;
99 void
100 swizzlefile(struct File *f)
102 int i;
104 if (f->f_name[0] == 0)
105 return;
106 swizzle((uint32_t*) &f->f_size);
107 swizzle(&f->f_type);
108 for (i = 0; i < NDIRECT; i++)
109 swizzle(&f->f_direct[i]);
110 swizzle(&f->f_indirect);
113 void
114 swizzleblock(struct Block *b)
116 int i;
117 struct Super *s;
118 struct File *f;
119 uint32_t *u;
121 switch (b->type) {
122 case BLOCK_SUPER:
123 s = (struct Super*) b->buf;
124 swizzle(&s->s_magic);
125 swizzle(&s->s_nblocks);
126 swizzlefile(&s->s_root);
127 break;
128 case BLOCK_DIR:
129 f = (struct File*) b->buf;
130 for (i = 0; i < BLKFILES; i++)
131 swizzlefile(f + i);
132 break;
133 case BLOCK_BITS:
134 u = (uint32_t*) b->buf;
135 for (i = 0; i < BLKSIZE / 4; i++)
136 swizzle(u + i);
137 break;
141 void
142 flushb(struct Block *b)
144 swizzleblock(b);
145 if (lseek(diskfd, b->bno * BLKSIZE, 0) < 0
146 || write(diskfd, b->buf, BLKSIZE) != BLKSIZE) {
147 perror("flushb");
148 fprintf(stderr, "\n");
149 abort();
151 swizzleblock(b);
154 struct Block*
155 getblk(uint32_t bno, int clr, uint32_t type)
157 int i, least;
158 static int t = 1;
159 struct Block *b;
161 if (bno >= nblocks) {
162 fprintf(stderr, "attempt to access past end of disk bno=%d\n", bno);
163 abort();
166 least = -1;
167 for (i = 0; i < nelem(cache); i++) {
168 if (cache[i].bno == bno) {
169 b = &cache[i];
170 goto out;
172 if (!cache[i].busy
173 && (least == -1 || cache[i].used < cache[least].used))
174 least = i;
177 if (least == -1) {
178 fprintf(stderr, "panic: block cache full\n");
179 abort();
182 b = &cache[least];
183 if (b->used)
184 flushb(b);
186 if (lseek(diskfd, bno*BLKSIZE, 0) < 0
187 || readn(diskfd, b->buf, BLKSIZE) != BLKSIZE) {
188 fprintf(stderr, "read block %d: ", bno);
189 perror("");
190 fprintf(stderr, "\n");
191 abort();
193 b->bno = bno;
194 if (!clr)
195 swizzleblock(b);
197 out:
198 if (clr)
199 memset(b->buf, 0, sizeof(b->buf));
200 b->used = ++t;
201 if (b->busy) {
202 fprintf(stderr, "panic: block #%d is busy\n", b->bno);
203 abort();
205 /* it is important to reset b->type in case we reuse a block for a
206 * different purpose while it is still in the cache - this can happen
207 * for example if a file ends exactly on a block boundary */
208 b->type = type;
209 b->busy = 1;
210 return b;
213 void
214 putblk(struct Block *b)
216 b->busy = 0;
219 void
220 opendisk(const char *name)
222 int i, r;
223 struct Block *b;
225 if ((diskfd = open(name, O_RDWR | O_CREAT, 0666)) < 0) {
226 fprintf(stderr, "open %s: ", name);
227 perror("");
228 fprintf(stderr, "\n");
229 abort();
232 if ((r = ftruncate(diskfd, 0)) < 0
233 || (r = ftruncate(diskfd, nblocks * BLKSIZE)) < 0) {
234 fprintf(stderr, "truncate %s: ", name);
235 perror("");
236 abort();
239 nbitblock = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE;
240 for (i = 0; i < nbitblock; i++){
241 b = getblk(2 + i, 0, BLOCK_BITS);
242 memset(b->buf, 0xFF, BLKSIZE);
243 putblk(b);
246 nextb = 2 + nbitblock;
248 super.s_magic = FS_MAGIC;
249 super.s_nblocks = nblocks;
250 super.s_root.f_type = FTYPE_DIR;
251 strcpy(super.s_root.f_name, "/");
254 void
255 storeblk(struct File *f, struct Block *b, int nblk)
257 if (nblk < NDIRECT)
258 f->f_direct[nblk] = b->bno;
259 else if (nblk < NINDIRECT) {
260 struct Block *bindir;
261 if (f->f_indirect == 0) {
262 bindir = getblk(nextb++, 1, BLOCK_BITS);
263 f->f_indirect = bindir->bno;
264 } else
265 bindir = getblk(f->f_indirect, 0, BLOCK_BITS);
266 ((uint32_t*)bindir->buf)[nblk] = b->bno;
267 putblk(bindir);
268 } else {
269 fprintf(stderr, "file too large\n");
270 abort();
274 struct File *
275 allocfile(struct File *dirf, const char *name, struct Block **dirb)
277 struct File *ino;
278 int nblk, i;
280 nblk = (int)((dirf->f_size + BLKSIZE - 1) / BLKSIZE) - 1;
281 if (nblk >= NDIRECT) {
282 struct Block *idirb = getblk(dirf->f_indirect, 0, BLOCK_BITS);
283 *dirb = getblk(((uint32_t*)idirb->buf) [nblk], 0, BLOCK_DIR);
284 putblk(idirb);
285 } else if (nblk >= 0)
286 *dirb = getblk(dirf->f_direct[nblk], 0, BLOCK_DIR);
287 else
288 goto new_dirb;
290 ino = (struct File *) (*dirb)->buf;
291 for (i = 0; i < BLKFILES; i++)
292 if (ino[i].f_name[0] == '\0') {
293 ino = &ino[i];
294 goto gotit;
297 putblk(*dirb);
299 new_dirb:
300 *dirb = getblk(nextb++, 1, BLOCK_DIR);
301 storeblk(dirf, *dirb, ++nblk);
302 dirf->f_size += BLKSIZE;
303 assert((nblk + 1) * BLKSIZE == dirf->f_size);
305 ino = (struct File *) (*dirb)->buf;
307 gotit:
308 strcpy(ino->f_name, name);
309 return ino;
312 void
313 writefile(struct File *dirf, const char *name)
315 int fd;
316 const char *last;
317 File *f;
318 int n, nblk;
319 struct Block *dirb, *b;
321 if ((fd = open(name, O_RDONLY)) < 0) {
322 fprintf(stderr, "open %s:", name);
323 perror("");
324 abort();
327 last = strrchr(name, '/');
328 if (last)
329 last++;
330 else
331 last = name;
333 f = allocfile(dirf, last, &dirb);
334 f->f_type = FTYPE_REG;
336 n = 0;
337 for (nblk = 0; ; nblk++) {
338 b = getblk(nextb, 1, BLOCK_FILE);
339 n = readn(fd, b->buf, BLKSIZE);
340 if (n < 0) {
341 fprintf(stderr, "reading %s: ", name);
342 perror("");
343 abort();
345 if (n == 0) {
346 putblk(b);
347 break;
349 nextb++;
350 storeblk(f, b, nblk);
351 putblk(b);
352 if (n < BLKSIZE)
353 break;
355 f->f_size = nblk*BLKSIZE + n;
356 putblk(dirb);
359 void
360 writedirectory(struct File *parentdirf, char *name, int root)
362 struct File *dirf;
363 DIR *dir;
364 struct dirent *ent;
365 struct stat s;
366 char pathbuf[PATH_MAX];
367 int namelen;
368 struct Block *dirb = NULL;
370 if ((dir = opendir(name)) == NULL) {
371 fprintf(stderr, "open %s:", name);
372 perror("");
373 abort();
376 if (!root) {
377 const char *last = strrchr(name, '/');
378 if (last)
379 last++;
380 else
381 last = name;
383 dirf = allocfile(parentdirf, last, &dirb);
384 dirf->f_type = FTYPE_DIR;
385 dirf->f_size = 0;
386 } else
387 dirf = parentdirf;
389 strcpy(pathbuf, name);
390 namelen = strlen(pathbuf);
391 if (pathbuf[namelen - 1] != '/') {
392 pathbuf[namelen++] = '/';
393 pathbuf[namelen] = 0;
396 while ((ent = readdir(dir)) != NULL) {
397 int ent_namlen = strlen(ent->d_name);
398 strcpy(pathbuf + namelen, ent->d_name);
400 // don't depend on unreliable parts of the dirent structure
401 if (stat(pathbuf, &s) < 0)
402 continue;
404 if (S_ISREG(s.st_mode))
405 writefile(dirf, pathbuf);
406 else if (S_ISDIR(s.st_mode)
407 && (ent_namlen > 1 || ent->d_name[0] != '.')
408 && (ent_namlen > 2 || ent->d_name[0] != '.' || ent->d_name[1] != '.')
409 && (ent_namlen > 3 || ent->d_name[0] != 'C' || ent->d_name[1] != 'V' || ent->d_name[2] != 'S'))
410 writedirectory(dirf, pathbuf, 0);
413 closedir(dir);
414 if (dirb)
415 putblk(dirb);
418 void
419 finishfs(void)
421 int i;
422 struct Block *b;
424 for (i = 0; i < nextb; i++) {
425 b = getblk(2 + i/BLKBITSIZE, 0, BLOCK_BITS);
426 ((uint32_t*)b->buf)[(i%BLKBITSIZE)/32] &= ~(1<<(i%32));
427 putblk(b);
430 // this is slow but not too slow. i do not care
431 if (nblocks != nbitblock*BLKBITSIZE) {
432 b = getblk(2+nbitblock-1, 0, BLOCK_BITS);
433 for (i = nblocks % BLKBITSIZE; i < BLKBITSIZE; i++)
434 ((uint32_t*)b->buf)[i/32] &= ~(1<<(i%32));
435 putblk(b);
438 b = getblk(1, 1, BLOCK_SUPER);
439 memmove(b->buf, &super, sizeof(Super));
440 putblk(b);
443 void
444 flushdisk(void)
446 int i;
448 for (i = 0; i < nelem(cache); i++)
449 if (cache[i].used)
450 flushb(&cache[i]);
453 void
454 usage(void)
456 fprintf(stderr, "Usage: fsformat kern/fs.img NBLOCKS files...\n\
457 fsformat kern/fs.img NBLOCKS -r DIR\n");
458 abort();
462 main(int argc, char **argv)
464 int i;
465 char *s;
467 assert(BLKSIZE % sizeof(struct File) == 0);
469 if (argc < 3)
470 usage();
472 nblocks = strtol(argv[2], &s, 0);
473 if (*s || s == argv[2] || nblocks < 2 || nblocks > 1024)
474 usage();
476 opendisk(argv[1]);
478 if (strcmp(argv[3], "-r") == 0) {
479 if (argc != 5)
480 usage();
481 writedirectory(&super.s_root, argv[4], 1);
482 } else {
483 for (i = 3; i < argc; i++)
484 writefile(&super.s_root, argv[i]);
487 finishfs();
488 flushdisk();
489 exit(0);
490 return 0;