1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS FAT Image Creator
4 * FILE: tools/fatten/fatten.c
5 * PURPOSE: FAT Image Creator (for EFI Boot)
6 * PROGRAMMERS: David Quintana
7 */
8 #include <stdio.h>
9 #include <string.h>
10 #include <time.h>
11 #include <ctype.h>
12 #include "fatfs/ff.h"
13 #include "fatfs/diskio.h"
14
15 static FATFS g_Filesystem;
16 static int isMounted = 0;
17 static unsigned char buff[32768];
18
19 // tool needed by fatfs
get_fattime(void)20 DWORD get_fattime(void)
21 {
22 /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
23 /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
24
25 time_t rawtime;
26 struct tm * timeinfo;
27
28 time(&rawtime);
29 timeinfo = localtime(&rawtime);
30
31 {
32 union FatTime {
33 struct {
34 DWORD Second : 5; // div 2
35 DWORD Minute : 6;
36 DWORD Hour : 5;
37 DWORD Day : 5;
38 DWORD Month : 4;
39 DWORD Year : 7; // year-1980
40 };
41 DWORD whole;
42 } myTime = {
43 {
44 timeinfo->tm_sec / 2,
45 timeinfo->tm_min,
46 timeinfo->tm_hour,
47 timeinfo->tm_mday,
48 timeinfo->tm_mon + 1,
49 timeinfo->tm_year - 80,
50 }
51 };
52
53 return myTime.whole;
54 }
55 }
56
print_help(const char * name)57 void print_help(const char* name)
58 {
59 printf("\n");
60 printf("Syntax: %s image_file [list of commands]\n\n", name);
61 #if _WIN32
62 printf("Commands: [Note: both '/' and '-' are accepted as command prefixes.]\n");
63 #else
64 printf("Commands:\n");
65 #endif
66 // printf(" -format <sectors> [<filesystem>] [<custom header label>]\n"
67 printf(" -format <sectors> [<custom header label>]\n"
68 " Formats the disk image.\n");
69 printf(" -boot <sector file>\n"
70 " Writes a new boot sector.\n");
71 printf(" -add <src path> <dst path>\n"
72 " Copies an external file or directory into the image.\n");
73 printf(" -extract <src path> <dst path>\n"
74 " Copies a file or directory from the image into an external file\n"
75 " or directory.\n");
76 printf(" -move <src path> <new path>\n"
77 " Moves/renames a file or directory.\n");
78 printf(" -copy <src path> <new path>\n"
79 " Copies a file or directory.\n");
80 printf(" -mkdir <src path> <new path>\n"
81 " Creates a directory.\n");
82 printf(" -rmdir <src path> <new path>\n"
83 " Creates a directory.\n");
84 printf(" -list [<pattern>]\n"
85 " Lists files a directory (defaults to root).\n");
86 }
87
88 #define PRINT_HELP_AND_QUIT() \
89 do { \
90 ret = 1; \
91 print_help(oargv[0]); \
92 goto exit; \
93 } while (0)
94
is_command(const char * parg)95 int is_command(const char* parg)
96 {
97 #if _WIN32
98 return (parg[0] == '/') || (parg[0] == '-');
99 #else
100 return (parg[0] == '-');
101 #endif
102 }
103
104 #define NEED_PARAMS(_min_, _max_) \
105 do {\
106 if (nargs < _min_) { fprintf(stderr, "Error: Too few args for command %s.\n" , argv[-1]); PRINT_HELP_AND_QUIT(); } \
107 if (nargs > _max_) { fprintf(stderr, "Error: Too many args for command %s.\n", argv[-1]); PRINT_HELP_AND_QUIT(); } \
108 } while(0)
109
need_mount(void)110 int need_mount(void)
111 {
112 int r;
113
114 if (isMounted)
115 return FR_OK;
116
117 r = f_mount(&g_Filesystem, "0:", 0);
118 if (r)
119 return r;
120
121 isMounted = 1;
122 return FR_OK;
123 }
124
125 #define NEED_MOUNT() \
126 do { ret = need_mount(); if(ret) \
127 {\
128 fprintf(stderr, "Error: Could not mount disk (%d).\n", ret); \
129 goto exit; \
130 } } while(0)
131
main(int oargc,char * oargv[])132 int main(int oargc, char* oargv[])
133 {
134 int ret;
135 int argc = oargc - 1;
136 char** argv = oargv + 1;
137
138 // first parameter must be the image file.
139 if (argc == 0)
140 {
141 fprintf(stderr, "Error: First parameter must be a filename.\n");
142 PRINT_HELP_AND_QUIT();
143 }
144
145 if (is_command(argv[0]))
146 {
147 fprintf(stderr, "Error: First parameter must be a filename, found '%s' instead.\n", argv[0]);
148 PRINT_HELP_AND_QUIT();
149 }
150
151 if (disk_openimage(0, argv[0]))
152 {
153 fprintf(stderr, "Error: Could not open image file '%s'.\n", argv[0]);
154 ret = 1;
155 goto exit;
156 }
157
158 argc--;
159 argv++;
160
161 while (argc > 0)
162 {
163 char* parg = *argv;
164 int nargs = 0;
165 int i = 0;
166
167 if (!is_command(parg))
168 {
169 fprintf(stderr, "Error: Expected a command, found '%s' instead.\n", parg);
170 PRINT_HELP_AND_QUIT();
171 }
172
173 parg++;
174 argv++;
175 argc--;
176
177 // find next command, to calculare number of args
178 while ((argv[i] != NULL) && !is_command(argv[i++]))
179 nargs++;
180
181 if (strcmp(parg, "format") == 0)
182 {
183 // NOTE: The fs driver detects which FAT format fits best based on size
184 int sectors;
185
186 NEED_PARAMS(1, 2);
187
188 // Arg 1: number of sectors
189 sectors = atoi(argv[0]);
190
191 if (sectors <= 0)
192 {
193 fprintf(stderr, "Error: Sectors must be > 0\n");
194 ret = 1;
195 goto exit;
196 }
197
198 if (disk_ioctl(0, SET_SECTOR_COUNT, §ors))
199 {
200 fprintf(stderr, "Error: Failed to set sector count to %d.\n", sectors);
201 ret = 1;
202 goto exit;
203 }
204
205 NEED_MOUNT();
206
207 ret = f_mkfs("0:", 1, sectors < 4096 ? 1 : 8);
208 if (ret)
209 {
210 fprintf(stderr, "Error: Formatting drive: %d.\n", ret);
211 goto exit;
212 }
213
214 // Arg 2: custom header label (optional)
215 if (nargs > 1)
216 {
217 #define FAT_VOL_LABEL_LEN 11
218 char vol_label[2 + FAT_VOL_LABEL_LEN + 1]; // Null-terminated buffer
219 char* label = vol_label + 2; // The first two characters are reserved for the drive number "0:"
220 char ch;
221
222 int i, invalid = 0;
223 int len = strlen(argv[1]);
224
225 if (len <= FAT_VOL_LABEL_LEN)
226 {
227 // Verify each character (should be printable ASCII)
228 // and copy it in uppercase.
229 for (i = 0; i < len; i++)
230 {
231 ch = toupper(argv[1][i]);
232 if ((ch < 0x20) || !isprint(ch))
233 {
234 invalid = 1;
235 break;
236 }
237
238 label[i] = ch;
239 }
240
241 if (!invalid)
242 {
243 // Pad the label with spaces
244 while (len < FAT_VOL_LABEL_LEN)
245 {
246 label[len++] = ' ';
247 }
248 }
249 }
250 else
251 {
252 invalid = 1;
253 }
254
255 if (invalid)
256 {
257 fprintf(stderr, "Error: Header label is limited to 11 printable uppercase ASCII symbols.");
258 ret = 1;
259 goto exit;
260 }
261
262 if (disk_read(0, buff, 0, 1))
263 {
264 fprintf(stderr, "Error: Unable to read existing boot sector from image.");
265 ret = 1;
266 goto exit;
267 }
268
269 if (g_Filesystem.fs_type == FS_FAT32)
270 {
271 memcpy(buff + 71, label, FAT_VOL_LABEL_LEN);
272 }
273 else
274 {
275 memcpy(buff + 43, label, FAT_VOL_LABEL_LEN);
276 }
277
278 if (disk_write(0, buff, 0, 1))
279 {
280 fprintf(stderr, "Error: Unable to write new boot sector to image.");
281 ret = 1;
282 goto exit;
283 }
284
285 // Set also the directory volume label
286 memcpy(vol_label, "0:", 2);
287 vol_label[2 + FAT_VOL_LABEL_LEN] = '\0';
288 if (f_setlabel(vol_label))
289 {
290 fprintf(stderr, "Error: Unable to set the volume label.");
291 ret = 1;
292 goto exit;
293 }
294 }
295 }
296 else if (strcmp(parg, "boot") == 0)
297 {
298 FILE* fe;
299 BYTE* temp = buff + 1024;
300
301 NEED_PARAMS(1, 1);
302
303 // Arg 1: boot file
304
305 fe = fopen(argv[0], "rb");
306 if (!fe)
307 {
308 fprintf(stderr, "Error: Unable to open external file '%s' for reading.", argv[0]);
309 ret = 1;
310 goto exit;
311 }
312
313 if (!fread(buff, 512, 1, fe))
314 {
315 fprintf(stderr, "Error: Unable to read boot sector from file '%s'.", argv[0]);
316 fclose(fe);
317 ret = 1;
318 goto exit;
319 }
320
321 fclose(fe);
322
323 NEED_MOUNT();
324
325 if (disk_read(0, temp, 0, 1))
326 {
327 fprintf(stderr, "Error: Unable to read existing boot sector from image.");
328 ret = 1;
329 goto exit;
330 }
331
332 if (g_Filesystem.fs_type == FS_FAT32)
333 {
334 printf("TODO: Writing boot sectors for FAT32 images not yet supported.");
335 ret = 1;
336 goto exit;
337 }
338 else
339 {
340 #define FAT16_HEADER_START 3
341 #define FAT16_HEADER_END 62
342
343 memcpy(buff + FAT16_HEADER_START, temp + FAT16_HEADER_START, FAT16_HEADER_END - FAT16_HEADER_START);
344 }
345
346 if (disk_write(0, buff, 0, 1))
347 {
348 fprintf(stderr, "Error: Unable to write new boot sector to image.");
349 ret = 1;
350 goto exit;
351 }
352 }
353 else if (strcmp(parg, "add") == 0)
354 {
355 FILE* fe;
356 FIL fv = { 0 };
357 UINT rdlen = 0;
358 UINT wrlen = 0;
359
360 NEED_PARAMS(2, 2);
361
362 NEED_MOUNT();
363
364 // Arg 1: external file to add
365 // Arg 2: virtual filename
366
367 fe = fopen(argv[0], "rb");
368 if (!fe)
369 {
370 fprintf(stderr, "Error: Unable to open external file '%s' for reading.", argv[0]);
371 ret = 1;
372 goto exit;
373 }
374
375 if (f_open(&fv, argv[1], FA_WRITE | FA_CREATE_ALWAYS))
376 {
377 fprintf(stderr, "Error: Unable to open file '%s' for writing.", argv[1]);
378 fclose(fe);
379 ret = 1;
380 goto exit;
381 }
382
383 while ((rdlen = fread(buff, 1, sizeof(buff), fe)) > 0)
384 {
385 if (f_write(&fv, buff, rdlen, &wrlen) || wrlen < rdlen)
386 {
387 fprintf(stderr, "Error: Unable to write '%d' bytes to disk.", wrlen);
388 ret = 1;
389 goto exit;
390 }
391 }
392
393 fclose(fe);
394 f_close(&fv);
395 }
396 else if (strcmp(parg, "extract") == 0)
397 {
398 FIL fe = { 0 };
399 FILE* fv;
400 UINT rdlen = 0;
401 UINT wrlen = 0;
402
403 NEED_PARAMS(2, 2);
404
405 NEED_MOUNT();
406
407 // Arg 1: virtual file to extract
408 // Arg 2: external filename
409
410 if (f_open(&fe, argv[0], FA_READ))
411 {
412 fprintf(stderr, "Error: Unable to open file '%s' for reading.", argv[0]);
413 ret = 1;
414 goto exit;
415 }
416
417 fv = fopen(argv[1], "wb");
418 if (!fv)
419 {
420 fprintf(stderr, "Error: Unable to open external file '%s' for writing.", argv[1]);
421 f_close(&fe);
422 ret = 1;
423 goto exit;
424 }
425
426 while ((f_read(&fe, buff, sizeof(buff), &rdlen) == 0) && (rdlen > 0))
427 {
428 if (fwrite(buff, 1, rdlen, fv) < rdlen)
429 {
430 fprintf(stderr, "Error: Unable to write '%d' bytes to file.", rdlen);
431 ret = 1;
432 goto exit;
433 }
434 }
435
436 f_close(&fe);
437 fclose(fv);
438 }
439 else if (strcmp(parg, "move") == 0)
440 {
441 NEED_PARAMS(2, 2);
442
443 NEED_MOUNT();
444 // Arg 1: src path & filename
445 // Arg 2: new path & filename
446
447 if (f_rename(argv[0], argv[1]))
448 {
449 fprintf(stderr, "Error: Unable to move/rename '%s' to '%s'", argv[0], argv[1]);
450 ret = 1;
451 goto exit;
452 }
453 }
454 else if (strcmp(parg, "copy") == 0)
455 {
456 FIL fe = { 0 };
457 FIL fv = { 0 };
458 UINT rdlen = 0;
459 UINT wrlen = 0;
460
461 NEED_PARAMS(2, 2);
462
463 NEED_MOUNT();
464 // Arg 1: src path & filename
465 // Arg 2: new path & filename
466
467 if (f_open(&fe, argv[0], FA_READ))
468 {
469 fprintf(stderr, "Error: Unable to open file '%s' for reading.", argv[0]);
470 ret = 1;
471 goto exit;
472 }
473 if (f_open(&fv, argv[1], FA_WRITE | FA_CREATE_ALWAYS))
474 {
475 fprintf(stderr, "Error: Unable to open file '%s' for writing.", argv[1]);
476 f_close(&fe);
477 ret = 1;
478 goto exit;
479 }
480
481 while ((f_read(&fe, buff, sizeof(buff), &rdlen) == 0) && (rdlen > 0))
482 {
483 if (f_write(&fv, buff, rdlen, &wrlen) || wrlen < rdlen)
484 {
485 fprintf(stderr, "Error: Unable to write '%d' bytes to disk.", wrlen);
486 ret = 1;
487 goto exit;
488 }
489 }
490
491 f_close(&fe);
492 f_close(&fv);
493 }
494 else if (strcmp(parg, "mkdir") == 0)
495 {
496 NEED_PARAMS(1, 1);
497
498 NEED_MOUNT();
499
500 // Arg 1: folder path
501 if (f_mkdir(argv[0]))
502 {
503 fprintf(stderr, "Error: Unable to create directory.");
504 ret = 1;
505 goto exit;
506 }
507 }
508 else if (strcmp(parg, "delete") == 0)
509 {
510 NEED_PARAMS(1, 1);
511
512 NEED_MOUNT();
513
514 // Arg 1: file/folder path (cannot delete non-empty folders)
515 if (f_unlink(argv[0]))
516 {
517 fprintf(stderr, "Error: Unable to delete file or directory.");
518 ret = 1;
519 goto exit;
520 }
521 }
522 else if (strcmp(parg, "list") == 0)
523 {
524 char* root = "/";
525 DIR dir = { 0 };
526 FILINFO info = { 0 };
527 char lfname[257];
528
529 NEED_PARAMS(0, 1);
530
531 // Arg 1: folder path (optional)
532
533 if (nargs == 1)
534 {
535 root = argv[0];
536 }
537
538 if (f_opendir(&dir, root))
539 {
540 fprintf(stderr, "Error: Unable to opening directory '%s' for listing.\n", root);
541 ret = 1;
542 goto exit;
543 }
544
545 printf("Listing directory contents of: %s\n", root);
546
547 info.lfname = lfname;
548 info.lfsize = sizeof(lfname)-1;
549 while ((!f_readdir(&dir, &info)) && (strlen(info.fname) > 0))
550 {
551 if (strlen(info.lfname) > 0)
552 printf(" - %s (%s)\n", info.lfname, info.fname);
553 else
554 printf(" - %s\n", info.fname);
555 }
556 }
557 else
558 {
559 fprintf(stderr, "Error: Unknown or invalid command: %s\n", argv[-1]);
560 PRINT_HELP_AND_QUIT();
561 }
562 argv += nargs;
563 argc -= nargs;
564 }
565
566 ret = 0;
567
568 exit:
569
570 disk_cleanup(0);
571
572 return ret;
573 }
574