ae50aeaf9662e24327184ed0486da8855a8b8072
[reactos.git] / reactos / sdk / tools / fatten / fatten.c
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 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_) { printf("Too few args for command %s.\n" , argv[-1]); PRINT_HELP_AND_QUIT(); } \
107 if (nargs > _max_) { printf("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 printf("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 PRINT_HELP_AND_QUIT();
142 }
143
144 if (is_command(argv[0]))
145 {
146 printf("Error: first parameter must be a filename, found '%s' instead.\n", argv[0]);
147 PRINT_HELP_AND_QUIT();
148 }
149
150 if (disk_openimage(0, argv[0]))
151 {
152 printf("Error: could not open image file '%s'.\n", argv[0]);
153 ret = 1;
154 goto exit;
155 }
156
157 argc--;
158 argv++;
159
160 while (argc > 0)
161 {
162 char* parg = *argv;
163 int nargs = 0;
164 int i = 0;
165
166 if (!is_command(parg))
167 {
168 printf("Error: Expected a command, found '%s' instead.\n", parg);
169 PRINT_HELP_AND_QUIT();
170 }
171
172 parg++;
173 argv++;
174 argc--;
175
176 // find next command, to calculare number of args
177 while ((argv[i] != NULL) && !is_command(argv[i++]))
178 nargs++;
179
180 if (strcmp(parg, "format") == 0)
181 {
182 // NOTE: The fs driver detects which FAT format fits best based on size
183 int sectors;
184
185 NEED_PARAMS(1, 2);
186
187 // Arg 1: number of sectors
188 sectors = atoi(argv[0]);
189
190 if (sectors <= 0)
191 {
192 printf("Error: Sectors must be > 0\n");
193 ret = 1;
194 goto exit;
195 }
196
197 disk_ioctl(0, SET_SECTOR_COUNT, &sectors);
198
199 NEED_MOUNT();
200
201 ret = f_mkfs("0:", 1, sectors < 4096 ? 1 : 8);
202 if (ret)
203 {
204 printf("ERROR: Formatting drive: %d.\n", ret);
205 goto exit;
206 }
207
208 // Arg 2: custom header label (optional)
209 if (nargs > 1)
210 {
211 #define FAT_VOL_LABEL_LEN 11
212 char vol_label[2 + FAT_VOL_LABEL_LEN + 1]; // Null-terminated buffer
213 char* label = vol_label + 2; // The first two characters are reserved for the drive number "0:"
214 char ch;
215
216 int i, invalid = 0;
217 int len = strlen(argv[1]);
218
219 if (len <= FAT_VOL_LABEL_LEN)
220 {
221 // Verify each character (should be printable ASCII)
222 // and copy it in uppercase.
223 for (i = 0; i < len; i++)
224 {
225 ch = toupper(argv[1][i]);
226 if ((ch < 0x20) || !isprint(ch))
227 {
228 invalid = 1;
229 break;
230 }
231
232 label[i] = ch;
233 }
234
235 if (!invalid)
236 {
237 // Pad the label with spaces
238 while (len < FAT_VOL_LABEL_LEN)
239 {
240 label[len++] = ' ';
241 }
242 }
243 }
244 else
245 {
246 invalid = 1;
247 }
248
249 if (invalid)
250 {
251 printf("Error: header label is limited to 11 printable uppercase ASCII symbols.");
252 ret = 1;
253 goto exit;
254 }
255
256 if (disk_read(0, buff, 0, 1))
257 {
258 printf("Error: unable to read existing boot sector from image.");
259 ret = 1;
260 goto exit;
261 }
262
263 if (g_Filesystem.fs_type == FS_FAT32)
264 {
265 memcpy(buff + 71, label, FAT_VOL_LABEL_LEN);
266 }
267 else
268 {
269 memcpy(buff + 43, label, FAT_VOL_LABEL_LEN);
270 }
271
272 if (disk_write(0, buff, 0, 1))
273 {
274 printf("Error: unable to write new boot sector to image.");
275 ret = 1;
276 goto exit;
277 }
278
279 // Set also the directory volume label
280 memcpy(vol_label, "0:", 2);
281 vol_label[2 + FAT_VOL_LABEL_LEN] = '\0';
282 f_setlabel(vol_label);
283 }
284 }
285 else if (strcmp(parg, "boot") == 0)
286 {
287 FILE* fe;
288 BYTE* temp = buff + 1024;
289
290 NEED_PARAMS(1, 1);
291
292 // Arg 1: boot file
293
294 fe = fopen(argv[0], "rb");
295 if (!fe)
296 {
297 printf("Error: unable to open external file '%s' for reading.", argv[0]);
298 ret = 1;
299 goto exit;
300 }
301
302 if (!fread(buff, 512, 1, fe))
303 {
304 printf("Error: unable to read boot sector from file '%s'.", argv[0]);
305 fclose(fe);
306 ret = 1;
307 goto exit;
308 }
309
310 fclose(fe);
311
312 NEED_MOUNT();
313
314 if (disk_read(0, temp, 0, 1))
315 {
316 printf("Error: unable to read existing boot sector from image.");
317 ret = 1;
318 goto exit;
319 }
320
321 if (g_Filesystem.fs_type == FS_FAT32)
322 {
323 printf("TODO: writing boot sectors for FAT32 images not yet supported.");
324 ret = 1;
325 goto exit;
326 }
327 else
328 {
329 #define FAT16_HEADER_START 3
330 #define FAT16_HEADER_END 62
331
332 memcpy(buff + FAT16_HEADER_START, temp + FAT16_HEADER_START, FAT16_HEADER_END - FAT16_HEADER_START);
333 }
334
335 if (disk_write(0, buff, 0, 1))
336 {
337 printf("Error: unable to write new boot sector to image.");
338 ret = 1;
339 goto exit;
340 }
341 }
342 else if (strcmp(parg, "add") == 0)
343 {
344 FILE* fe;
345 FIL fv = { 0 };
346 UINT rdlen = 0;
347 UINT wrlen = 0;
348
349 NEED_PARAMS(2, 2);
350
351 NEED_MOUNT();
352
353 // Arg 1: external file to add
354 // Arg 2: virtual filename
355
356 fe = fopen(argv[0], "rb");
357 if (!fe)
358 {
359 printf("Error: unable to open external file '%s' for reading.", argv[0]);
360 ret = 1;
361 goto exit;
362 }
363
364 if (f_open(&fv, argv[1], FA_WRITE | FA_CREATE_ALWAYS))
365 {
366 printf("Error: unable to open file '%s' for writing.", argv[1]);
367 fclose(fe);
368 ret = 1;
369 goto exit;
370 }
371
372 while ((rdlen = fread(buff, 1, sizeof(buff), fe)) > 0)
373 {
374 f_write(&fv, buff, rdlen, &wrlen);
375 }
376
377 fclose(fe);
378 f_close(&fv);
379 }
380 else if (strcmp(parg, "extract") == 0)
381 {
382 FIL fe = { 0 };
383 FILE* fv;
384 UINT rdlen = 0;
385 UINT wrlen = 0;
386
387 NEED_PARAMS(2, 2);
388
389 NEED_MOUNT();
390
391 // Arg 1: virtual file to extract
392 // Arg 2: external filename
393
394 if (f_open(&fe, argv[0], FA_READ))
395 {
396 printf("Error: unable to open file '%s' for reading.", argv[0]);
397 ret = 1;
398 goto exit;
399 }
400
401 fv = fopen(argv[1], "wb");
402 if (!fv)
403 {
404 printf("Error: unable to open external file '%s' for writing.", argv[1]);
405 f_close(&fe);
406 ret = 1;
407 goto exit;
408 }
409
410 while ((f_read(&fe, buff, sizeof(buff), &rdlen) == 0) && (rdlen > 0))
411 {
412 fwrite(buff, 1, rdlen, fv);
413 }
414
415 f_close(&fe);
416 fclose(fv);
417 }
418 else if (strcmp(parg, "move") == 0)
419 {
420 NEED_PARAMS(2, 2);
421
422 NEED_MOUNT();
423 // Arg 1: src path & filename
424 // Arg 2: new path & filename
425
426 if (f_rename(argv[0], argv[1]))
427 printf("Error moving/renaming '%s' to '%s'", argv[0], argv[1]);
428 }
429 else if (strcmp(parg, "copy") == 0)
430 {
431 FIL fe = { 0 };
432 FIL fv = { 0 };
433 UINT rdlen = 0;
434 UINT wrlen = 0;
435
436 NEED_PARAMS(2, 2);
437
438 NEED_MOUNT();
439 // Arg 1: src path & filename
440 // Arg 2: new path & filename
441
442 if (f_open(&fe, argv[0], FA_READ))
443 {
444 printf("Error: unable to open file '%s' for reading.", argv[0]);
445 ret = 1;
446 goto exit;
447 }
448 if (f_open(&fv, argv[1], FA_WRITE | FA_CREATE_ALWAYS))
449 {
450 printf("Error: unable to open file '%s' for writing.", argv[1]);
451 f_close(&fe);
452 ret = 1;
453 goto exit;
454 }
455
456 while ((f_read(&fe, buff, sizeof(buff), &rdlen) == 0) && (rdlen > 0))
457 {
458 f_write(&fv, buff, rdlen, &wrlen);
459 }
460
461 f_close(&fe);
462 f_close(&fv);
463 }
464 else if (strcmp(parg, "mkdir") == 0)
465 {
466 NEED_PARAMS(1, 1);
467
468 NEED_MOUNT();
469
470 // Arg 1: folder path
471 f_mkdir(argv[0]);
472 }
473 else if (strcmp(parg, "delete") == 0)
474 {
475 NEED_PARAMS(1, 1);
476
477 NEED_MOUNT();
478
479 // Arg 1: file/folder path (cannot delete non-empty folders)
480 f_unlink(argv[0]);
481 }
482 else if (strcmp(parg, "list") == 0)
483 {
484 char* root = "/";
485 DIR dir = { 0 };
486 FILINFO info = { 0 };
487 char lfname[257];
488
489 NEED_PARAMS(0, 1);
490
491 // Arg 1: folder path (optional)
492
493 if (nargs == 1)
494 {
495 root = argv[0];
496 }
497
498 if (f_opendir(&dir, root))
499 {
500 printf("Error opening directory '%s'.\n", root);
501 ret = 1;
502 goto exit;
503 }
504
505 printf("Listing directory contents of: %s\n", root);
506
507 info.lfname = lfname;
508 info.lfsize = sizeof(lfname)-1;
509 while ((!f_readdir(&dir, &info)) && (strlen(info.fname) > 0))
510 {
511 if (strlen(info.lfname) > 0)
512 printf(" - %s (%s)\n", info.lfname, info.fname);
513 else
514 printf(" - %s\n", info.fname);
515 }
516 }
517 else
518 {
519 printf("Error: Unknown or invalid command: %s\n", argv[-1]);
520 PRINT_HELP_AND_QUIT();
521 }
522 argv += nargs;
523 argc -= nargs;
524 }
525
526 ret = 0;
527
528 exit:
529
530 disk_cleanup(0);
531
532 return ret;
533 }