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 //----------------------------------------------------------------------
PrintWin32Error(LPWSTR Message,DWORD ErrorCode)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 //----------------------------------------------------------------------
ParseCommandLine(int argc,WCHAR * argv[])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
FormatExCallback(CALLBACKCOMMAND Command,ULONG Modifier,PVOID Argument)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 //----------------------------------------------------------------------
LoadFMIFSEntryPoints(VOID)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 //----------------------------------------------------------------------
Usage(LPWSTR ProgramName)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 //----------------------------------------------------------------------
wmain(int argc,WCHAR * argv[])360 int wmain(int argc, WCHAR *argv[])
361 {
362 int badArg;
363 DEVICE_INFORMATION DeviceInformation = {0};
364 FMIFS_MEDIA_FLAG media = FMIFS_HARDDISK;
365 DWORD driveType;
366 WCHAR fileSystem[1024];
367 WCHAR volumeName[1024] = {0};
368 WCHAR input[1024];
369 DWORD serialNumber;
370 DWORD flags, maxComponent;
371 ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes;
372 WCHAR szMsg[RC_STRING_MAX_SIZE];
373
374 /* Initialize the Console Standard Streams */
375 ConInitStdStreams();
376
377 ConPuts(StdOut,
378 L"\n"
379 L"Formatx v1.0 by Mark Russinovich\n"
380 L"Systems Internals - http://www.sysinternals.com\n"
381 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n");
382
383 #ifndef FMIFS_IMPORT_DLL
384 //
385 // Get function pointers
386 //
387 if (!LoadFMIFSEntryPoints())
388 {
389 ConResPuts(StdErr, STRING_FMIFS_FAIL);
390 return -1;
391 }
392 #endif
393
394 //
395 // Parse command line
396 //
397 badArg = ParseCommandLine(argc, argv);
398 if (badArg)
399 {
400 ConResPrintf(StdErr, STRING_UNKNOW_ARG, argv[badArg]);
401 Usage(argv[0]);
402 return -1;
403 }
404
405 //
406 // Get the drive's format
407 //
408 if (!Drive)
409 {
410 ConResPuts(StdErr, STRING_DRIVE_PARM);
411 Usage(argv[0]);
412 return -1;
413 }
414 else
415 {
416 wcscpy(RootDirectory, Drive);
417 }
418 RootDirectory[2] = L'\\';
419 RootDirectory[3] = L'\0';
420
421 //
422 // See if the drive is removable or not
423 //
424 driveType = GetDriveTypeW(RootDirectory);
425 switch (driveType)
426 {
427 case DRIVE_UNKNOWN :
428 K32LoadStringW(GetModuleHandle(NULL), STRING_ERROR_DRIVE_TYPE, szMsg, ARRAYSIZE(szMsg));
429 PrintWin32Error(szMsg, GetLastError());
430 return -1;
431
432 case DRIVE_REMOTE:
433 case DRIVE_CDROM:
434 ConResPuts(StdOut, STRING_NO_SUPPORT);
435 return -1;
436
437 case DRIVE_NO_ROOT_DIR:
438 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
439 PrintWin32Error(szMsg, GetLastError());
440 return -1;
441
442 case DRIVE_REMOVABLE:
443 ConResPrintf(StdOut, STRING_INSERT_DISK, RootDirectory[0]);
444 fgetws(input, ARRAYSIZE(input), stdin);
445 media = FMIFS_FLOPPY;
446 break;
447
448 case DRIVE_FIXED:
449 case DRIVE_RAMDISK:
450 media = FMIFS_HARDDISK;
451 break;
452 }
453
454 // Reject attempts to format the system drive
455 {
456 WCHAR path[MAX_PATH + 1];
457 UINT rc;
458 rc = GetWindowsDirectoryW(path, MAX_PATH);
459 if (rc == 0 || rc > MAX_PATH)
460 // todo: Report "Unable to query system directory"
461 return -1;
462 if (towlower(path[0]) == towlower(Drive[0]))
463 {
464 // todo: report "Cannot format system drive"
465 ConResPuts(StdOut, STRING_NO_SUPPORT);
466 return -1;
467 }
468 }
469
470 //
471 // Determine the drive's file system format
472 //
473 if (!GetVolumeInformationW(RootDirectory,
474 volumeName, ARRAYSIZE(volumeName),
475 &serialNumber, &maxComponent, &flags,
476 fileSystem, ARRAYSIZE(fileSystem)))
477 {
478 if (GetLastError() == ERROR_UNRECOGNIZED_VOLUME)
479 {
480 wcscpy(fileSystem, L"RAW");
481 }
482 else
483 {
484 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
485 PrintWin32Error(szMsg, GetLastError());
486 return -1;
487 }
488 }
489
490 if (QueryDeviceInformation(RootDirectory,
491 &DeviceInformation,
492 sizeof(DeviceInformation)))
493 {
494 totalNumberOfBytes.QuadPart = DeviceInformation.SectorSize *
495 DeviceInformation.SectorCount.QuadPart;
496 }
497
498 /* QueryDeviceInformation returns more accurate volume length and works with
499 * unformatted volumes, however it will NOT return volume length on XP/2003.
500 * Fallback to GetFreeDiskSpaceExW if we did not get any volume length. */
501 if (totalNumberOfBytes.QuadPart == 0 &&
502 !GetDiskFreeSpaceExW(RootDirectory,
503 &freeBytesAvailableToCaller,
504 &totalNumberOfBytes,
505 &totalNumberOfFreeBytes))
506 {
507 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
508 PrintWin32Error(szMsg, GetLastError());
509 return -1;
510 }
511 ConResPrintf(StdOut, STRING_FILESYSTEM, fileSystem);
512
513 //
514 // Make sure they want to do this
515 //
516 if (driveType == DRIVE_FIXED)
517 {
518 if (volumeName[0])
519 {
520 while (TRUE)
521 {
522 ConResPrintf(StdOut, STRING_LABEL_NAME_EDIT, RootDirectory[0]);
523 fgetws(input, ARRAYSIZE(input), stdin);
524 input[wcslen(input) - 1] = 0;
525
526 if (!wcsicmp(input, volumeName))
527 break;
528
529 ConResPuts(StdOut, STRING_ERROR_LABEL);
530 }
531 }
532
533 ConResPrintf(StdOut, STRING_YN_FORMAT, RootDirectory[0]);
534
535 K32LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg));
536 while (TRUE)
537 {
538 fgetws(input, ARRAYSIZE(input), stdin);
539 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break;
540 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0)
541 {
542 ConPuts(StdOut, L"\n");
543 return 0;
544 }
545 }
546 }
547
548 //
549 // Tell the user we're doing a long format if appropriate
550 //
551 if (!QuickFormat)
552 {
553 K32LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg));
554 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
555 {
556 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
557 }
558 else
559 {
560 ConPrintf(StdOut, L"%s %.1fM\n", szMsg,
561 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
562 }
563 }
564 else
565 {
566 K32LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg));
567 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
568 {
569 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
570 }
571 else
572 {
573 ConPrintf(StdOut, L"%s %.2fM\n", szMsg,
574 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
575 }
576 ConResPuts(StdOut, STRING_CREATE_FSYS);
577 }
578
579 //
580 // Format away!
581 //
582 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat,
583 ClusterSize, FormatExCallback);
584 if (Error) return -1;
585 ConPuts(StdOut, L"\n");
586 ConResPuts(StdOut, STRING_FMT_COMPLETE);
587
588 //
589 // Enable compression if desired
590 //
591 if (CompressDrive)
592 {
593 if (!EnableVolumeCompression(RootDirectory, TRUE))
594 ConResPuts(StdOut, STRING_VOL_COMPRESS);
595 }
596
597 //
598 // Get the label if we don't have it
599 //
600 if (!GotALabel)
601 {
602 ConResPuts(StdOut, STRING_ENTER_LABEL);
603 fgetws(input, ARRAYSIZE(LabelString), stdin);
604
605 input[wcslen(input) - 1] = 0;
606 if (!SetVolumeLabelW(RootDirectory, input))
607 {
608 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg));
609 PrintWin32Error(szMsg, GetLastError());
610 return -1;
611 }
612 }
613
614 if (!GetVolumeInformationW(RootDirectory,
615 volumeName, ARRAYSIZE(volumeName),
616 &serialNumber, &maxComponent, &flags,
617 fileSystem, ARRAYSIZE(fileSystem)))
618 {
619 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
620 PrintWin32Error(szMsg, GetLastError());
621 return -1;
622 }
623
624 //
625 // Print out some stuff including the formatted size
626 //
627 if (!GetDiskFreeSpaceExW(RootDirectory,
628 &freeBytesAvailableToCaller,
629 &totalNumberOfBytes,
630 &totalNumberOfFreeBytes))
631 {
632 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
633 PrintWin32Error(szMsg, GetLastError());
634 return -1;
635 }
636
637 ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart,
638 totalNumberOfFreeBytes.QuadPart);
639
640 //
641 // Get the drive's serial number
642 //
643 if (!GetVolumeInformationW(RootDirectory,
644 volumeName, ARRAYSIZE(volumeName),
645 &serialNumber, &maxComponent, &flags,
646 fileSystem, ARRAYSIZE(fileSystem)))
647 {
648 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
649 PrintWin32Error(szMsg, GetLastError());
650 return -1;
651 }
652 ConResPrintf(StdOut, STRING_SERIAL_NUMBER,
653 (unsigned int)(serialNumber >> 16),
654 (unsigned int)(serialNumber & 0xFFFF));
655
656 return 0;
657 }
658
659 /* EOF */
660