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