xv6, line by line
mkfs/mkfs.c

mkfs/mkfs.c

C · 307 lines · annotated 100% · host tool (runs on your computer, not in xv6) · upstream

About this file

mkfs (“make file system”) builds fs.img, the disk image (fs.img) that xv6 boots with. It is the only C program in the repository that runs on your computer (macOS or Linux), not inside xv6. The Makefile compiles it with your ordinary gcc (Makefile:124) and runs it as mkfs/mkfs fs.img README user/_cat user/_echo ... (Makefile:154). It uses the host’s C library (open, lseek, read, printf), and none of the kernel’s code.

What it shares with the kernel is the on-disk format: it includes kernel/fs.h and kernel/param.h, so the sizes and structs it writes are exactly the ones kernel/fs.c will read. The job, in order:

  1. compute the size of each region of the disk layout (xv6 file system) and fill in the superblock;
  2. zero the whole 2000-block image and write the superblock into block 1;
  3. create the root directory (inode 1) with its . and .. entries;
  4. for each file named on the command line, allocate an inode, add a directory entry for it in /, and copy its bytes in, using direct and indirect blocks;
  5. write the free bitmap, marking every block used so far.

mkfs never reuses or frees anything: inodes and blocks are handed out in increasing order, which keeps it short. Almost every number it writes goes through xint or xshort, which are meant to give the image RISC-V’s little-endian byte order (endianness); the conversion is incomplete (see xint), so in practice the image is right only when the host is little-endian, as nearly all are.

Read before: kernel/fs.h. Read next: kernel/fs.c, which reads this image at boot (fsinit) and from then on maintains it.

1#include <stdio.h>
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5#include <fcntl.h>
6#include <assert.h>
8#define stat xv6_stat // avoid clash with host struct stat
14#ifndef static_assert
15#define static_assert(a, b) \
16 do { \
17 switch (0) \
18 case 0: \
19 case (a):; \
20 } while (0)
21#endif
23#define NINODES 200
25// Disk layout:
26// [ boot block | sb block | log | inode blocks | free bit map | data blocks ]
28int nbitmap = FSSIZE / BPB + 1;
30int nlog = LOGBLOCKS + 1; // Header followed by LOGBLOCKS data blocks.
31int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap)
32int nblocks; // Number of data blocks
34int fsfd;
40void balloc(int);
41void wsect(uint, void *);
42void winode(uint, struct dinode *);
43void rinode(uint inum, struct dinode *ip);
44void rsect(uint sec, void *buf);
46void iappend(uint inum, void *p, int n);
47void die(const char *);
49// convert to riscv byte order
54 uchar *a = (uchar *)&y;
55 a[0] = x;
56 a[1] = x >> 8;
57 return y;
64 uchar *a = (uchar *)&y;
65 a[0] = x;
66 a[1] = x >> 8;
67 a[2] = x >> 16;
68 a[3] = x >> 24;
69 return y;
72int
73main(int argc, char *argv[])
75 int i, cc, fd;
77 struct dirent de;
78 char buf[BSIZE];
79 struct dinode din;
81 static_assert(sizeof(int) == 4, "Integers must be 4 bytes!");
83 if (argc < 2) {
84 fprintf(stderr, "Usage: mkfs fs.img files...\n");
85 exit(1);
86 }
88 assert((BSIZE % sizeof(struct dinode)) == 0);
89 assert((BSIZE % sizeof(struct dirent)) == 0);
91 fsfd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666);
92 if (fsfd < 0)
93 die(argv[1]);
95 // 1 fs block = 1 disk sector
108 printf(
109 "nmeta %d (boot, super, log blocks %u, inode blocks %u, bitmap blocks %u) blocks %d total %d\n",
112 freeblock = nmeta; // the first free block that we can allocate
114 for (i = 0; i < FSSIZE; i++)
117 memset(buf, 0, sizeof(buf));
118 memmove(buf, &sb, sizeof(sb));
122 assert(rootino == ROOTINO);
124 bzero(&de, sizeof(de));
126 strcpy(de.name, ".");
127 iappend(rootino, &de, sizeof(de));
129 bzero(&de, sizeof(de));
131 strcpy(de.name, "..");
132 iappend(rootino, &de, sizeof(de));
134 for (i = 2; i < argc; i++) {
135 // get rid of "user/"
136 char *shortname;
137 if (strncmp(argv[i], "user/", 5) == 0)
139 else
142 assert(index(shortname, '/') == 0);
144 if ((fd = open(argv[i], 0)) < 0)
147 // Skip leading _ in name when writing to file system.
148 // The binaries are named _rm, _cat, etc. to keep the
149 // build operating system from trying to execute them
150 // in place of system binaries like rm and cat.
151 if (shortname[0] == '_')
154 assert(strlen(shortname) <= DIRSIZ);
158 bzero(&de, sizeof(de));
160 strncpy(de.name, shortname, DIRSIZ);
161 iappend(rootino, &de, sizeof(de));
163 while ((cc = read(fd, buf, sizeof(buf))) > 0)
166 close(fd);
167 }
169 // fix size of root inode dir
172 off = ((off / BSIZE) + 1) * BSIZE;
178 exit(0);
181void
184 if (lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE)
185 die("lseek");
186 if (write(fsfd, buf, BSIZE) != BSIZE)
187 die("write");
190void
193 char buf[BSIZE];
195 struct dinode *dip;
199 dip = ((struct dinode *)buf) + (inum % IPB);
200 *dip = *ip;
204void
207 char buf[BSIZE];
209 struct dinode *dip;
213 dip = ((struct dinode *)buf) + (inum % IPB);
214 *ip = *dip;
217void
220 if (lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE)
221 die("lseek");
222 if (read(fsfd, buf, BSIZE) != BSIZE)
223 die("read");
230 struct dinode din;
232 bzero(&din, sizeof(din));
237 return inum;
240void
244 int i;
246 printf("balloc: first %d blocks have been allocated\n", used);
247 assert(used < BPB);
248 bzero(buf, BSIZE);
249 for (i = 0; i < used; i++) {
250 buf[i / 8] = buf[i / 8] | (0x1 << (i % 8));
251 }
252 printf("balloc: write bitmap block at sector %d\n", sb.bmapstart);
256#define min(a, b) ((a) < (b) ? (a) : (b))
258void
259iappend(uint inum, void *xp, int n)
261 char *p = (char *)xp;
263 struct dinode din;
264 char buf[BSIZE];
270 // printf("append inum %d at off %d sz %d\n", inum, off, n);
271 while (n > 0) {
273 assert(fbn < MAXFILE);
274 if (fbn < NDIRECT) {
275 if (xint(din.addrs[fbn]) == 0) {
277 }
279 } else {
280 if (xint(din.addrs[NDIRECT]) == 0) {
282 }
284 if (indirect[fbn - NDIRECT] == 0) {
287 }
289 }
290 n1 = min(n, (fbn + 1) * BSIZE - off);
292 bcopy(p, buf + off - (fbn * BSIZE), n1);
294 n -= n1;
295 off += n1;
296 p += n1;
297 }
302void
303die(const char *s)
305 perror(s);
306 exit(1);