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 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 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 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 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 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