1 
2 #include <assert.h>
3 #include <stdbool.h>
4 #include <stdint.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 
9 #include <unistd.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <time.h>
14 #include <getopt.h>
15 
16 #include <o2fs.h>
17 
18 #define ROUND_UP(_a, _b) (((_a) + (_b) - 1)/(_b))
19 
20 #define MAXBLOCKSIZE (64*1024*1024)
21 
22 char tempbuf[MAXBLOCKSIZE];
23 char zerobuf[MAXBLOCKSIZE];
24 
25 bool verbose = false;
26 bool hasManifest = false;
27 uint64_t diskSize = 0;
28 uint64_t diskOffset = 0;
29 uint64_t blockSize = 16*1024;
30 uint64_t bitmapSize;
31 int diskfd;
32 struct stat diskstat;
33 
34 #define TOKEN_EOF	0
35 #define TOKEN_DIR	1
36 #define TOKEN_END	2
37 #define TOKEN_FILE	3
38 #define TOKEN_STRING	4
39 
40 char *tokenBuf;
41 char *tokenCur;
42 char tokenString[512];
43 
LoadManifest(const char * manifest)44 void LoadManifest(const char *manifest)
45 {
46     int fd = open(manifest, O_RDONLY);
47     struct stat manifeststat;
48 
49     if (fd < 0) {
50 	perror("Cannot open manifest");
51 	exit(1);
52     }
53 
54     fstat(fd, &manifeststat);
55     tokenBuf = malloc(manifeststat.st_size + 1);
56     read(fd, tokenBuf, manifeststat.st_size);
57     tokenBuf[manifeststat.st_size] = '\0';
58 
59     tokenCur = tokenBuf;
60     tokenString[0] = '\0';
61 }
62 
GetToken()63 int GetToken()
64 {
65     int i;
66 
67     while (*tokenCur == ' ' || *tokenCur == '\t' ||
68 	   *tokenCur == '\n' || *tokenCur == '\r')
69 	tokenCur++;
70 
71     for (i = 0; i < 512; i++)
72     {
73 	tokenString[i] = tokenCur[i];
74 	if (tokenCur[i] == ' ' || tokenCur[i] == '\t' ||
75 	    tokenCur[i] == '\n' || tokenCur[i] == '\r' ||
76 	    tokenCur[i] == '\0')
77 	{
78 	    tokenString[i] = '\0';
79 	    tokenCur += i;
80 	    break;
81 	}
82     }
83 
84     if (strcmp(tokenString, "") == 0)
85 	return TOKEN_EOF;
86     if (strcmp(tokenString, "DIR") == 0)
87 	return TOKEN_DIR;
88     if (strcmp(tokenString, "END") == 0)
89 	return TOKEN_END;
90     if (strcmp(tokenString, "FILE") == 0)
91 	return TOKEN_FILE;
92     return TOKEN_STRING;
93 }
94 
95 void
FlushBlock(uint64_t offset,const void * buf,size_t len)96 FlushBlock(uint64_t offset, const void *buf, size_t len)
97 {
98     assert(offset % blockSize == 0);
99     assert(len <= blockSize);
100 
101     pwrite(diskfd, buf, len, offset);
102     if (len != blockSize) {
103 	pwrite(diskfd, zerobuf, blockSize - len, offset + len);
104     }
105 }
106 
107 
108 uint64_t
AppendBlock(const void * buf,size_t len)109 AppendBlock(const void *buf, size_t len)
110 {
111     uint64_t offset = lseek(diskfd, 0, SEEK_CUR);
112 
113     FlushBlock(offset, buf, len);
114     lseek(diskfd, blockSize, SEEK_CUR);
115 
116     return offset;
117 }
118 
119 uint64_t
AppendEmpty(void)120 AppendEmpty(void)
121 {
122 	return AppendBlock(NULL, 0);
123 }
124 
AddFile(const char * file)125 ObjID *AddFile(const char *file)
126 {
127     int i = 0;
128     int fd;
129     ObjID *id = malloc(sizeof(ObjID));
130     BNode node;
131 
132     memset(id, 0, sizeof(*id));
133     memset(&node, 0, sizeof(node));
134     memcpy(node.magic, BNODE_MAGIC, 8);
135     node.versionMajor = O2FS_VERSION_MAJOR;
136     node.versionMinor = O2FS_VERSION_MINOR;
137 
138     // Copy file
139     fd = open(file, O_RDONLY);
140     if (fd < 0) {
141 	perror("Cannot open file");
142 	exit(1);
143     }
144     while (1) {
145 	int len = read(fd, tempbuf, blockSize);
146 	if (len < 0) {
147 	    perror("File read error");
148 	    exit(1);
149 	}
150 	if (len == 0) {
151 	    break;
152 	}
153 
154 	node.direct[i].device = 0;
155 	node.direct[i].offset = AppendBlock(tempbuf, len);
156 	node.size += (uint64_t)len;
157 	i += 1;
158     }
159     close(fd);
160 
161     // Construct BNode
162     uint64_t offset = AppendBlock(&node, sizeof(node));
163 
164     // Construct ObjID
165     id->device = 0;
166     id->offset = offset;
167 
168     return id;
169 }
170 
AddDirectory()171 ObjID *AddDirectory()
172 {
173     int tok;
174     BDirEntry *entries = malloc(128 * sizeof(BDirEntry));
175     int entry = 0;
176     ObjID *id = malloc(sizeof(ObjID));
177     BNode node;
178 
179     while (1)
180     {
181 	memset(&entries[entry], 0, sizeof(BDirEntry));
182 	tok = GetToken();
183 	if (tok == TOKEN_FILE) {
184 	    tok = GetToken();
185 	    printf("FILE %s\n", tokenString);
186 	    strncpy((char *)entries[entry].name, tokenString, MAXNAMELEN);
187 
188 	    tok = GetToken();
189 	    ObjID *fobj = AddFile(tokenString);
190 	    memcpy(&entries[entry].objId, fobj, sizeof(ObjID));
191 	    free(fobj);
192 	} else if (tok == TOKEN_DIR) {
193 	    tok = GetToken();
194 	    printf("DIR %s\n", tokenString);
195 	    strncpy((char *)entries[entry].name, tokenString, MAXNAMELEN);
196 	    ObjID *dobj = AddDirectory();
197 	    memcpy(&entries[entry].objId, dobj, sizeof(ObjID));
198 	    free(dobj);
199 	} else if (tok == TOKEN_END) {
200 	    printf("END\n");
201 	    break;
202 	} else if (tok == TOKEN_EOF) {
203 	    printf("Unexpected end of file\n");
204 	    exit(1);
205 	} else {
206 	    printf("Unknown token '%s'\n", tokenString);
207 	    exit(1);
208 	}
209 
210 	memcpy(entries[entry].magic, BDIR_MAGIC, 8);
211 	// entries[entry].size ...
212 	entries[entry].flags = 0;
213 	entries[entry].ctime = (uint64_t)time(NULL);
214 	entries[entry].mtime = (uint64_t)time(NULL);
215 	strncpy((char *)entries[entry].user, "root", MAXUSERNAMELEN);
216 	strncpy((char *)entries[entry].group, "root", MAXUSERNAMELEN);
217 
218 	entry++;
219     }
220 
221     // Write Directory
222 
223     // Make sure we fit into a single indirect
224     // block to simplify the logic below.
225     uint64_t size = entry * sizeof(BDirEntry);
226     assert(size < blockSize);
227     uint64_t offset = AppendBlock(entries, size);
228 
229     // Write Inode
230     memset(&node, 0, sizeof(node));
231     memcpy(node.magic, BNODE_MAGIC, 8);
232     node.versionMajor = O2FS_VERSION_MAJOR;
233     node.versionMinor = O2FS_VERSION_MINOR;
234     node.size = size;
235     node.direct[0].device = 0;
236     node.direct[0].offset = offset;
237     uint64_t nodeoff = AppendBlock(&node, sizeof(node));
238 
239     memset(id, 0, sizeof(*id));
240     id->device = 0;
241     id->offset = nodeoff;
242 
243     return id;
244 }
245 
BlockBitmap()246 void BlockBitmap()
247 {
248     off_t off = lseek(diskfd, 0, SEEK_CUR) / blockSize;
249 
250     /* Code below only supports using the first 16K blocks */
251     assert(off < blockSize);
252 
253     memset(tempbuf, 0, MAXBLOCKSIZE);
254 
255     /* Mark the blocks in use up to the current offset */
256     assert(off > 8);
257     for (off_t i = 0; i < (off / 8); i++) {
258 	tempbuf[i] = 0xFF;
259     }
260     for (off_t i = 0; i < (off % 8); i++) {
261 	tempbuf[off / 8] |= 1 << i;
262     }
263 
264     for (int i = 0; i < bitmapSize; i++)
265 	FlushBlock(blockSize + (blockSize * i), tempbuf + (blockSize * i), blockSize);
266 }
267 
Superblock(ObjID * objid)268 void Superblock(ObjID *objid)
269 {
270     SuperBlock sb;
271 
272     memset(&sb, 0, sizeof(sb));
273     memcpy(sb.magic, SUPERBLOCK_MAGIC, 8);
274     sb.versionMajor = O2FS_VERSION_MAJOR;
275     sb.versionMinor = O2FS_VERSION_MINOR;
276     sb.blockCount = diskSize / blockSize;
277     sb.blockSize = blockSize;
278     sb.bitmapSize = bitmapSize;
279     sb.bitmapOffset = blockSize;
280 
281     if (objid)
282 	memcpy(&sb.root, objid, sizeof(ObjID));
283 
284     FlushBlock(0, &sb, sizeof(sb));
285 }
286 
usage()287 void usage()
288 {
289     printf("Usage: newfs_o2fs [OPTIONS] special-device\n");
290     printf("Options:\n");
291     printf("    -m, --manifest  Manifest of files to copy to file system\n");
292     printf("    -s, --size      Size in megabytes of device or disk image\n");
293     printf("    -v, --verbose   Verbose logging\n");
294     printf("    -h, --help      Print help message\n");
295 }
296 
main(int argc,char * const * argv)297 int main(int argc, char * const *argv)
298 {
299     int ch;
300     int status;
301 
302     // Sanity check
303     assert(sizeof(BDirEntry) == 512);
304 
305     struct option longopts[] = {
306 	{ "manifest",		required_argument,	NULL,	'm' },
307 	{ "size",		required_argument,	NULL,	's' },
308 	{ "verbose",		no_argument,		NULL,	'v' },
309 	{ "help",		no_argument,		NULL,	'h' },
310 	{ NULL,			0,			NULL,	0   }
311     };
312 
313     while ((ch = getopt_long(argc, argv, "m:s:vh", longopts, NULL)) != -1)
314     {
315 	switch (ch) {
316 	    case 'm':
317 		hasManifest = true;
318 		LoadManifest(optarg);
319 		break;
320 	    case 's':
321 		diskSize = atol(optarg) * 1024 * 1024;
322 		break;
323 	    case 'v':
324 		verbose = true;
325 		break;
326 	    case 'h':
327 		usage();
328 		return 0;
329 	    default:
330 		usage();
331 		return 1;
332 	}
333     }
334 
335     argc -= optind;
336     argv += optind;
337 
338     if (argc != 1) {
339 	usage();
340 	return 1;
341     }
342 
343     diskfd = open(argv[0], O_RDWR | O_CREAT, 0660);
344     if (diskfd < 0) {
345 	perror("Cannot open special device or disk image");
346 	return 1;
347     }
348 
349     status = fstat(diskfd, &diskstat);
350     if (status < 0) {
351 	perror("Cannot fstat special device or disk image");
352 	return 1;
353     }
354 
355     if (diskstat.st_size == 0 && diskSize == 0) {
356 	printf("Error: Must specify size for disk images\n");
357 	usage();
358 	return 1;
359     }
360 
361     if (diskstat.st_size == 0)
362 	FlushBlock(diskSize - blockSize, zerobuf, blockSize);
363 
364     /* Skip superblock */
365     diskOffset = blockSize;
366     lseek(diskfd, diskOffset, SEEK_SET);
367     memset(zerobuf, 0, MAXBLOCKSIZE);
368 
369     /* Zero the bitmap (and skip past it) */
370     bitmapSize = ROUND_UP(diskSize / (blockSize * 8), blockSize);
371     for (int i = 0; i < bitmapSize; i++)
372 	AppendBlock(zerobuf, blockSize);
373 
374     ObjID *root = NULL;
375     if (hasManifest) {
376 	int tok;
377 
378 	tok = GetToken();
379 	if (tok != TOKEN_DIR) {
380 	    printf("Expected 'DIR' token, but found '%s'\n", tokenString);
381 	    exit(1);
382 	}
383 	tok = GetToken();
384 	if (tok != TOKEN_STRING || strcmp(tokenString, "/") != 0) {
385 	    printf("Expected '/' token\n");
386 	    exit(1);
387 	}
388 
389 	root = AddDirectory();
390 	tok = GetToken();
391 	if (tok != TOKEN_EOF) {
392 	    printf("Expected end-of-file, but found '%s'\n", tokenString);
393 	    exit(1);
394 	}
395     }
396 
397     /* Write bitmap */
398     BlockBitmap();
399 
400     Superblock(root);
401     free(root);
402 
403     close(diskfd);
404 }
405 
406