0fabbeceecc6572b3a6f231a90a0e29066604c19
[reactos.git] / reactos / base / system / format / format.c
1 //======================================================================
2 //
3 // Formatx
4 //
5 // Copyright (c) 1998 Mark Russinovich
6 // Systems Internals
7 // http://www.sysinternals.com
8 //
9 // Format clone that demonstrates the use of the FMIFS file system
10 // utility library.
11 //
12 // --------------------------------------------------------------------
13 //
14 // This software is free software; you can redistribute it and/or
15 // modify it under the terms of the GNU Library General Public License as
16 // published by the Free Software Foundation; either version 2 of the
17 // License, or (at your option) any later version.
18 //
19 // This software is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 // Library General Public License for more details.
23 //
24 // You should have received a copy of the GNU Library General Public
25 // License along with this software; see the file COPYING.LIB. If
26 // not, write to the Free Software Foundation, Inc., 675 Mass Ave,
27 // Cambridge, MA 02139, USA.
28 //
29 // --------------------------------------------------------------------
30 //
31 // 1999 February (Emanuele Aliberti)
32 // Adapted for ReactOS and lcc-win32.
33 //
34 // 1999 April (Emanuele Aliberti)
35 // Adapted for ReactOS and egcs.
36 //
37 // 2003 April (Casper S. Hornstrup)
38 // Reintegration.
39 //
40 //======================================================================
41
42 #include <stdio.h>
43 #include <tchar.h>
44
45 /* PSDK/NDK Headers */
46 #define WIN32_NO_STATUS
47 #include <windef.h>
48 #include <winbase.h>
49
50 #include <conutils.h>
51
52 #define NTOS_MODE_USER
53 #include <ndk/rtlfuncs.h>
54
55 /* FMIFS Public Header */
56 #include <fmifs/fmifs.h>
57
58 #include "resource.h"
59
60 #define FMIFS_IMPORT_DLL
61
62 // Globals
63 BOOL Error = FALSE;
64
65 // Switches
66 BOOL QuickFormat = FALSE;
67 DWORD ClusterSize = 0;
68 BOOL CompressDrive = FALSE;
69 BOOL GotALabel = FALSE;
70 PWCHAR Label = L"";
71 PWCHAR Drive = NULL;
72 PWCHAR FileSystem = L"FAT";
73
74 WCHAR RootDirectory[MAX_PATH];
75 WCHAR LabelString[12];
76
77 #ifndef FMIFS_IMPORT_DLL
78 //
79 // Functions in FMIFS.DLL
80 //
81 PFORMATEX FormatEx;
82 PENABLEVOLUMECOMPRESSION EnableVolumeCompression;
83 PQUERYAVAILABLEFILESYSTEMFORMAT QueryAvailableFileSystemFormat;
84 #endif
85
86
87 //
88 // Size array
89 //
90 typedef struct {
91 WCHAR SizeString[16];
92 DWORD ClusterSize;
93 } SIZEDEFINITION, *PSIZEDEFINITION;
94
95 SIZEDEFINITION LegalSizes[] = {
96 { L"512", 512 },
97 { L"1024", 1024 },
98 { L"2048", 2048 },
99 { L"4096", 4096 },
100 { L"8192", 8192 },
101 { L"16K", 16384 },
102 { L"32K", 32768 },
103 { L"64K", 65536 },
104 { L"128K", 65536 * 2 },
105 { L"256K", 65536 * 4 },
106 { L"", 0 },
107 };
108
109
110 //----------------------------------------------------------------------
111 //
112 // PrintWin32Error
113 //
114 // Takes the win32 error code and prints the text version.
115 //
116 //----------------------------------------------------------------------
117 static VOID PrintWin32Error(LPWSTR Message, DWORD ErrorCode)
118 {
119 ConPrintf(StdErr, L"%s: ", Message);
120 ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM,
121 NULL, ErrorCode, LANG_USER_DEFAULT);
122 ConPuts(StdErr, L"\n");
123 }
124
125
126 //----------------------------------------------------------------------
127 //
128 // ParseCommandLine
129 //
130 // Get the switches.
131 //
132 //----------------------------------------------------------------------
133 static int ParseCommandLine(int argc, WCHAR *argv[])
134 {
135 int i, j;
136 BOOLEAN gotFormat = FALSE;
137 BOOLEAN gotQuick = FALSE;
138 BOOLEAN gotSize = FALSE;
139 BOOLEAN gotLabel = FALSE;
140 BOOLEAN gotCompressed = FALSE;
141
142 for (i = 1; i < argc; i++)
143 {
144 switch (argv[i][0])
145 {
146 case L'-': case L'/':
147
148 if (!_wcsnicmp(&argv[i][1], L"FS:", 3))
149 {
150 if (gotFormat) return -1;
151 FileSystem = &argv[i][4];
152 gotFormat = TRUE;
153 }
154 else if (!_wcsnicmp(&argv[i][1], L"A:", 2))
155 {
156 if (gotSize) return -1;
157 j = 0;
158 while (LegalSizes[j].ClusterSize &&
159 wcsicmp(LegalSizes[j].SizeString, &argv[i][3]))
160 {
161 j++;
162 }
163
164 if (!LegalSizes[j].ClusterSize) return i;
165 ClusterSize = LegalSizes[j].ClusterSize;
166 gotSize = TRUE;
167 }
168 else if (!_wcsnicmp(&argv[i][1], L"V:", 2))
169 {
170 if (gotLabel) return -1;
171 Label = &argv[i][3];
172 gotLabel = TRUE;
173 GotALabel = TRUE;
174 }
175 else if (!wcsicmp(&argv[i][1], L"Q"))
176 {
177 if (gotQuick) return -1;
178 QuickFormat = TRUE;
179 gotQuick = TRUE;
180 }
181 else if (!wcsicmp(&argv[i][1], L"C"))
182 {
183 if (gotCompressed) return -1;
184 CompressDrive = TRUE;
185 gotCompressed = TRUE;
186 }
187 else
188 {
189 return i;
190 }
191 break;
192
193 default:
194 {
195 if (Drive) return i;
196 if (argv[i][1] != L':') return i;
197
198 Drive = argv[i];
199 break;
200 }
201 }
202 }
203 return 0;
204 }
205
206 //----------------------------------------------------------------------
207 //
208 // FormatExCallback
209 //
210 // The file system library will call us back with commands that we
211 // can interpret. If we wanted to halt the chkdsk we could return FALSE.
212 //
213 //----------------------------------------------------------------------
214 BOOLEAN WINAPI
215 FormatExCallback(
216 CALLBACKCOMMAND Command,
217 ULONG Modifier,
218 PVOID Argument)
219 {
220 PDWORD percent;
221 PTEXTOUTPUT output;
222 PBOOLEAN status;
223
224 //
225 // We get other types of commands, but we don't have to pay attention to them
226 //
227 switch (Command)
228 {
229 case PROGRESS:
230 percent = (PDWORD)Argument;
231 ConResPrintf(StdOut, STRING_COMPLETE, *percent);
232 break;
233
234 case OUTPUT:
235 output = (PTEXTOUTPUT)Argument;
236 ConPrintf(StdOut, L"%S\n", output->Output);
237 break;
238
239 case DONE:
240 status = (PBOOLEAN)Argument;
241 if (*status == FALSE)
242 {
243 ConResPuts(StdOut, STRING_FORMAT_FAIL);
244 Error = TRUE;
245 }
246 break;
247
248 case DONEWITHSTRUCTURE:
249 case UNKNOWN2:
250 case UNKNOWN3:
251 case UNKNOWN4:
252 case UNKNOWN5:
253 case INSUFFICIENTRIGHTS:
254 case FSNOTSUPPORTED:
255 case VOLUMEINUSE:
256 case UNKNOWN9:
257 case UNKNOWNA:
258 case UNKNOWNC:
259 case UNKNOWND:
260 case STRUCTUREPROGRESS:
261 case CLUSTERSIZETOOSMALL:
262 ConResPuts(StdOut, STRING_NO_SUPPORT);
263 return FALSE;
264 }
265 return TRUE;
266 }
267
268 #ifndef FMIFS_IMPORT_DLL
269 //----------------------------------------------------------------------
270 //
271 // LoadFMIFSEntryPoints
272 //
273 // Loads FMIFS.DLL and locates the entry point(s) we are going to use
274 //
275 //----------------------------------------------------------------------
276 static BOOLEAN LoadFMIFSEntryPoints(VOID)
277 {
278 HMODULE hFmifs = LoadLibraryW( L"fmifs.dll");
279 if (hFmifs == NULL)
280 return FALSE;
281
282 FormatEx = (PFORMATEX)GetProcAddress(hFmifs, "FormatEx");
283 if (!FormatEx)
284 {
285 FreeLibrary(hFmifs);
286 return FALSE;
287 }
288
289 EnableVolumeCompression = (PENABLEVOLUMECOMPRESSION)GetProcAddress(hFmifs, "EnableVolumeCompression");
290 if (!EnableVolumeCompression)
291 {
292 FreeLibrary(hFmifs);
293 return FALSE;
294 }
295
296 QueryAvailableFileSystemFormat = (PQUERYAVAILABLEFILESYSTEMFORMAT)GetProcAddress(hFmifs, "QueryAvailableFileSystemFormat");
297 if (!QueryAvailableFileSystemFormat)
298 {
299 FreeLibrary(hFmifs);
300 return FALSE;
301 }
302
303 return TRUE;
304 }
305 #endif
306
307
308 //----------------------------------------------------------------------
309 //
310 // Usage
311 //
312 // Tell the user how to use the program
313 //
314 //----------------------------------------------------------------------
315 static VOID Usage(LPWSTR ProgramName)
316 {
317 WCHAR szMsg[RC_STRING_MAX_SIZE];
318 WCHAR szFormats[MAX_PATH];
319 WCHAR szFormatW[MAX_PATH];
320 DWORD Index = 0;
321 BYTE dummy;
322 BOOLEAN latestVersion;
323
324 K32LoadStringW(GetModuleHandle(NULL), STRING_HELP, szMsg, ARRAYSIZE(szMsg));
325
326 #ifndef FMIFS_IMPORT_DLL
327 if (!LoadFMIFSEntryPoints())
328 {
329 ConPrintf(StdOut, szMsg, ProgramName, L"");
330 return;
331 }
332 #endif
333
334 szFormats[0] = 0;
335 while (QueryAvailableFileSystemFormat(Index++, szFormatW, &dummy, &dummy, &latestVersion))
336 {
337 if (!latestVersion)
338 continue;
339 if (szFormats[0])
340 wcscat(szFormats, L", ");
341
342 wcscat(szFormats, szFormatW);
343 }
344 ConPrintf(StdOut, szMsg, ProgramName, szFormats);
345 }
346
347
348 //----------------------------------------------------------------------
349 //
350 // WMain
351 //
352 // Engine. Just get command line switches and fire off a format. This
353 // could also be done in a GUI like Explorer does when you select a
354 // drive and run a check on it.
355 //
356 // We do this in UNICODE because the chkdsk command expects PWCHAR
357 // arguments.
358 //
359 //----------------------------------------------------------------------
360 int wmain(int argc, WCHAR *argv[])
361 {
362 int badArg;
363 DWORD media = FMIFS_HARDDISK;
364 DWORD driveType;
365 WCHAR fileSystem[1024];
366 WCHAR volumeName[1024];
367 WCHAR input[1024];
368 DWORD serialNumber;
369 DWORD flags, maxComponent;
370 ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes;
371 WCHAR szMsg[RC_STRING_MAX_SIZE];
372
373 /* Initialize the Console Standard Streams */
374 ConInitStdStreams();
375
376 ConPuts(StdOut,
377 L"\n"
378 L"Formatx v1.0 by Mark Russinovich\n"
379 L"Systems Internals - http://www.sysinternals.com\n"
380 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n");
381
382 #ifndef FMIFS_IMPORT_DLL
383 //
384 // Get function pointers
385 //
386 if (!LoadFMIFSEntryPoints())
387 {
388 ConResPuts(StdErr, STRING_FMIFS_FAIL);
389 return -1;
390 }
391 #endif
392
393 //
394 // Parse command line
395 //
396 badArg = ParseCommandLine(argc, argv);
397 if (badArg)
398 {
399 ConResPrintf(StdErr, STRING_UNKNOW_ARG, argv[badArg]);
400 Usage(argv[0]);
401 return -1;
402 }
403
404 //
405 // Get the drive's format
406 //
407 if (!Drive)
408 {
409 ConResPuts(StdErr, STRING_DRIVE_PARM);
410 Usage(argv[0]);
411 return -1;
412 }
413 else
414 {
415 wcscpy(RootDirectory, Drive);
416 }
417 RootDirectory[2] = L'\\';
418 RootDirectory[3] = L'\0';
419
420 //
421 // See if the drive is removable or not
422 //
423 driveType = GetDriveTypeW(RootDirectory);
424 switch (driveType)
425 {
426 case DRIVE_UNKNOWN :
427 K32LoadStringW(GetModuleHandle(NULL), STRING_ERROR_DRIVE_TYPE, szMsg, ARRAYSIZE(szMsg));
428 PrintWin32Error(szMsg, GetLastError());
429 return -1;
430
431 case DRIVE_REMOTE:
432 case DRIVE_CDROM:
433 ConResPuts(StdOut, STRING_NO_SUPPORT);
434 return -1;
435
436 case DRIVE_NO_ROOT_DIR:
437 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
438 PrintWin32Error(szMsg, GetLastError());
439 return -1;
440
441 case DRIVE_REMOVABLE:
442 ConResPrintf(StdOut, STRING_INSERT_DISK, RootDirectory[0]);
443 fgetws(input, ARRAYSIZE(input), stdin);
444 media = FMIFS_FLOPPY;
445 break;
446
447 case DRIVE_FIXED:
448 case DRIVE_RAMDISK:
449 media = FMIFS_HARDDISK;
450 break;
451 }
452
453 // Reject attempts to format the system drive
454 {
455 WCHAR path[MAX_PATH + 1];
456 UINT rc;
457 rc = GetWindowsDirectoryW(path, MAX_PATH);
458 if (rc == 0 || rc > MAX_PATH)
459 // todo: Report "Unable to query system directory"
460 return -1;
461 if (towlower(path[0]) == towlower(Drive[0]))
462 {
463 // todo: report "Cannot format system drive"
464 ConResPuts(StdOut, STRING_NO_SUPPORT);
465 return -1;
466 }
467 }
468
469 //
470 // Determine the drive's file system format
471 //
472 if (!GetVolumeInformationW(RootDirectory,
473 volumeName, ARRAYSIZE(volumeName),
474 &serialNumber, &maxComponent, &flags,
475 fileSystem, ARRAYSIZE(fileSystem)))
476 {
477 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
478 PrintWin32Error(szMsg, GetLastError());
479 return -1;
480 }
481
482 if (!GetDiskFreeSpaceExW(RootDirectory,
483 &freeBytesAvailableToCaller,
484 &totalNumberOfBytes,
485 &totalNumberOfFreeBytes))
486 {
487 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
488 PrintWin32Error(szMsg, GetLastError());
489 return -1;
490 }
491 ConResPrintf(StdOut, STRING_FILESYSTEM, fileSystem);
492
493 //
494 // Make sure they want to do this
495 //
496 if (driveType == DRIVE_FIXED)
497 {
498 if (volumeName[0])
499 {
500 while (TRUE)
501 {
502 ConResPrintf(StdOut, STRING_LABEL_NAME_EDIT, RootDirectory[0]);
503 fgetws(input, ARRAYSIZE(input), stdin);
504 input[wcslen(input) - 1] = 0;
505
506 if (!wcsicmp(input, volumeName))
507 break;
508
509 ConResPuts(StdOut, STRING_ERROR_LABEL);
510 }
511 }
512
513 ConResPrintf(StdOut, STRING_YN_FORMAT, RootDirectory[0]);
514
515 K32LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg));
516 while (TRUE)
517 {
518 fgetws(input, ARRAYSIZE(input), stdin);
519 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break;
520 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0)
521 {
522 ConPuts(StdOut, L"\n");
523 return 0;
524 }
525 }
526 }
527
528 //
529 // Tell the user we're doing a long format if appropriate
530 //
531 if (!QuickFormat)
532 {
533 K32LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg));
534 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
535 {
536 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
537 }
538 else
539 {
540 ConPrintf(StdOut, L"%s %.1fM\n", szMsg,
541 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
542 }
543 }
544 else
545 {
546 K32LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg));
547 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
548 {
549 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
550 }
551 else
552 {
553 ConPrintf(StdOut, L"%s %.2fM\n", szMsg,
554 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
555 }
556 ConResPuts(StdOut, STRING_CREATE_FSYS);
557 }
558
559 //
560 // Format away!
561 //
562 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat,
563 ClusterSize, FormatExCallback);
564 if (Error) return -1;
565 ConResPuts(StdOut, STRING_FMT_COMPLETE);
566
567 //
568 // Enable compression if desired
569 //
570 if (CompressDrive)
571 {
572 if (!EnableVolumeCompression(RootDirectory, TRUE))
573 ConResPuts(StdOut, STRING_VOL_COMPRESS);
574 }
575
576 //
577 // Get the label if we don't have it
578 //
579 if (!GotALabel)
580 {
581 ConResPuts(StdOut, STRING_ENTER_LABEL);
582 fgetws(input, ARRAYSIZE(LabelString), stdin);
583
584 input[wcslen(input) - 1] = 0;
585 if (!SetVolumeLabelW(RootDirectory, input))
586 {
587 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg));
588 PrintWin32Error(szMsg, GetLastError());
589 return -1;
590 }
591 }
592
593 if (!GetVolumeInformationW(RootDirectory,
594 volumeName, ARRAYSIZE(volumeName),
595 &serialNumber, &maxComponent, &flags,
596 fileSystem, ARRAYSIZE(fileSystem)))
597 {
598 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
599 PrintWin32Error(szMsg, GetLastError());
600 return -1;
601 }
602
603 //
604 // Print out some stuff including the formatted size
605 //
606 if (!GetDiskFreeSpaceExW(RootDirectory,
607 &freeBytesAvailableToCaller,
608 &totalNumberOfBytes,
609 &totalNumberOfFreeBytes))
610 {
611 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
612 PrintWin32Error(szMsg, GetLastError());
613 return -1;
614 }
615
616 ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart,
617 totalNumberOfFreeBytes.QuadPart);
618
619 //
620 // Get the drive's serial number
621 //
622 if (!GetVolumeInformationW(RootDirectory,
623 volumeName, ARRAYSIZE(volumeName),
624 &serialNumber, &maxComponent, &flags,
625 fileSystem, ARRAYSIZE(fileSystem)))
626 {
627 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
628 PrintWin32Error(szMsg, GetLastError());
629 return -1;
630 }
631 ConResPrintf(StdOut, STRING_SERIAL_NUMBER,
632 (unsigned int)(serialNumber >> 16),
633 (unsigned int)(serialNumber & 0xFFFF));
634
635 return 0;
636 }
637
638 /* EOF */