Reactos
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
63BOOL Error = FALSE;
64
65// Switches
66BOOL QuickFormat = FALSE;
67DWORD ClusterSize = 0;
68BOOL CompressDrive = FALSE;
69BOOL GotALabel = FALSE;
70PWCHAR Label = L"";
71PWCHAR Drive = NULL;
72PWCHAR FileSystem = L"FAT";
73
74WCHAR RootDirectory[MAX_PATH];
75WCHAR LabelString[12];
76
77#ifndef FMIFS_IMPORT_DLL
78//
79// Functions in FMIFS.DLL
80//
81PFORMATEX FormatEx;
82PENABLEVOLUMECOMPRESSION EnableVolumeCompression;
83PQUERYAVAILABLEFILESYSTEMFORMAT QueryAvailableFileSystemFormat;
84#endif
85
86
87//
88// Size array
89//
90typedef struct {
91 WCHAR SizeString[16];
92 DWORD ClusterSize;
93} SIZEDEFINITION, *PSIZEDEFINITION;
94
95SIZEDEFINITION 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//----------------------------------------------------------------------
117static 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//----------------------------------------------------------------------
133static 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//----------------------------------------------------------------------
214BOOLEAN WINAPI
215FormatExCallback(
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//----------------------------------------------------------------------
276static 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//----------------------------------------------------------------------
315static 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 szFormats[0] = 0;
327 while (QueryAvailableFileSystemFormat(Index++, szFormatW, &dummy, &dummy, &latestVersion))
328 {
329 if (!latestVersion)
330 continue;
331 if (szFormats[0])
332 wcscat(szFormats, L", ");
333
334 wcscat(szFormats, szFormatW);
335 }
336 ConPrintf(StdOut, szMsg, ProgramName, szFormats);
337}
338
339
340//----------------------------------------------------------------------
341//
342// WMain
343//
344// Engine. Just get command line switches and fire off a format. This
345// could also be done in a GUI like Explorer does when you select a
346// drive and run a check on it.
347//
348// We do this in UNICODE because the chkdsk command expects PWCHAR
349// arguments.
350//
351//----------------------------------------------------------------------
352int wmain(int argc, WCHAR *argv[])
353{
354 int badArg;
355 DEVICE_INFORMATION DeviceInformation;
356 FMIFS_MEDIA_FLAG media = FMIFS_HARDDISK;
357 DWORD driveType;
358 WCHAR fileSystem[1024];
359 WCHAR volumeName[1024];
360 WCHAR input[1024];
361 DWORD serialNumber;
362 ULARGE_INTEGER totalNumberOfBytes, totalNumberOfFreeBytes;
363 WCHAR szMsg[RC_STRING_MAX_SIZE];
364 DWORD dwError;
365
366 /* Initialize the Console Standard Streams */
367 ConInitStdStreams();
368
369 ConPuts(StdOut,
370 L"\n"
371 L"Formatx v1.0 by Mark Russinovich\n"
372 L"Systems Internals - http://www.sysinternals.com\n"
373 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n");
374
375#ifndef FMIFS_IMPORT_DLL
376 //
377 // Get function pointers
378 //
379 if (!LoadFMIFSEntryPoints())
380 {
381 ConResPuts(StdErr, STRING_FMIFS_FAIL);
382 return -1;
383 }
384#endif
385
386 //
387 // Parse command line
388 //
389 badArg = ParseCommandLine(argc, argv);
390 if (badArg)
391 {
392 ConResPrintf(StdErr, STRING_UNKNOW_ARG, argv[badArg]);
393 Usage(argv[0]);
394 return -1;
395 }
396
397 //
398 // Get the drive's format
399 //
400 if (!Drive)
401 {
402 ConResPuts(StdErr, STRING_DRIVE_PARM);
403 Usage(argv[0]);
404 return -1;
405 }
406 else
407 {
408 wcscpy(RootDirectory, Drive);
409 }
410 RootDirectory[2] = L'\\';
411 RootDirectory[3] = L'\0';
412
413 //
414 // See if the drive is removable or not
415 //
416 driveType = GetDriveTypeW(RootDirectory);
417 switch (driveType)
418 {
419 case DRIVE_UNKNOWN:
420 case DRIVE_NO_ROOT_DIR: // This case used to report STRING_NO_VOLUME, which has no ".\n".
421 ConResPuts(StdErr, STRING_ERROR_DRIVE_TYPE);
422 return -1;
423
424 case DRIVE_REMOTE:
425 case DRIVE_CDROM:
426 ConResPuts(StdOut, STRING_NO_SUPPORT);
427 return -1;
428
429 case DRIVE_REMOVABLE:
430 ConResPrintf(StdOut, STRING_INSERT_DISK, RootDirectory[0]);
431 fgetws(input, ARRAYSIZE(input), stdin);
432 media = FMIFS_FLOPPY;
433 break;
434
435 case DRIVE_FIXED:
436 case DRIVE_RAMDISK:
437 media = FMIFS_HARDDISK;
438 break;
439 }
440
441 // Reject attempts to format the system drive
442 {
443 WCHAR path[MAX_PATH + 1];
444 UINT rc;
445 rc = GetWindowsDirectoryW(path, MAX_PATH);
446 if (rc == 0 || rc > MAX_PATH)
447 // todo: Report "Unable to query system directory"
448 return -1;
449 if (towlower(path[0]) == towlower(Drive[0]))
450 {
451 // todo: report "Cannot format system drive"
452 ConResPuts(StdOut, STRING_NO_SUPPORT);
453 return -1;
454 }
455 }
456
457 //
458 // Get the existing name and file system, and print out the latter
459 //
460 if (!GetVolumeInformationW(RootDirectory,
461 volumeName, ARRAYSIZE(volumeName),
462 NULL, NULL, NULL,
463 fileSystem, ARRAYSIZE(fileSystem)))
464 {
465 dwError = GetLastError();
466 if (dwError == ERROR_UNRECOGNIZED_VOLUME)
467 {
468 // Unformatted volume
469 volumeName[0] = UNICODE_NULL;
470 wcscpy(fileSystem, L"RAW");
471 }
472 else
473 {
474 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
475 PrintWin32Error(szMsg, dwError);
476 return -1;
477 }
478 }
479
480 ConResPrintf(StdOut, STRING_FILESYSTEM, fileSystem);
481
482 if (!QueryDeviceInformation(RootDirectory, &DeviceInformation, sizeof(DeviceInformation)))
483 {
484 totalNumberOfBytes.QuadPart = 0;
485 }
486 else
487 {
488 totalNumberOfBytes.QuadPart = DeviceInformation.SectorSize *
489 DeviceInformation.SectorCount.QuadPart;
490 }
491
492 /* QueryDeviceInformation returns more accurate volume length and works with
493 * unformatted volumes, however it will NOT return volume length on XP/2003.
494 * Fallback to GetFreeDiskSpaceExW if we did not get any volume length. */
495 if (totalNumberOfBytes.QuadPart == 0 &&
496 !GetDiskFreeSpaceExW(RootDirectory,
497 NULL,
498 &totalNumberOfBytes,
499 NULL))
500 {
501 dwError = GetLastError();
502 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
503 PrintWin32Error(szMsg, dwError);
504 return -1;
505 }
506
507 //
508 // Make sure they want to do this
509 //
510 if (driveType == DRIVE_FIXED)
511 {
512 if (volumeName[0])
513 {
514 while (TRUE)
515 {
516 ConResPrintf(StdOut, STRING_LABEL_NAME_EDIT, RootDirectory[0]);
517 fgetws(input, ARRAYSIZE(input), stdin);
518 input[wcslen(input) - 1] = 0;
519
520 if (!wcsicmp(input, volumeName))
521 break;
522
523 ConResPuts(StdOut, STRING_ERROR_LABEL);
524 }
525 }
526
527 ConResPrintf(StdOut, STRING_YN_FORMAT, RootDirectory[0]);
528
529 K32LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg));
530 while (TRUE)
531 {
532 fgetws(input, ARRAYSIZE(input), stdin);
533 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break;
534 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0)
535 {
536 ConPuts(StdOut, L"\n");
537 return 0;
538 }
539 }
540 }
541
542 //
543 // Tell the user we're doing a long format if appropriate
544 //
545 if (!QuickFormat)
546 {
547 K32LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg));
548 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
549 {
550 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
551 }
552 else
553 {
554 ConPrintf(StdOut, L"%s %.1fM\n", szMsg,
555 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
556 }
557 }
558 else
559 {
560 K32LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg));
561 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
562 {
563 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
564 }
565 else
566 {
567 ConPrintf(StdOut, L"%s %.2fM\n", szMsg,
568 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
569 }
570 ConResPuts(StdOut, STRING_CREATE_FSYS);
571 }
572
573 //
574 // Format away!
575 //
576 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat,
577 ClusterSize, FormatExCallback);
578 if (Error) return -1;
579 ConPuts(StdOut, L"\n");
580 ConResPuts(StdOut, STRING_FMT_COMPLETE);
581
582 //
583 // Enable compression if desired
584 //
585 if (CompressDrive)
586 {
587 if (!EnableVolumeCompression(RootDirectory, TRUE))
588 ConResPuts(StdOut, STRING_VOL_COMPRESS);
589 }
590
591 //
592 // Get the label if we don't have it
593 //
594 if (!GotALabel)
595 {
596 ConResPuts(StdOut, STRING_ENTER_LABEL);
597 fgetws(input, ARRAYSIZE(LabelString), stdin);
598
599 input[wcslen(input) - 1] = 0;
600 if (!SetVolumeLabelW(RootDirectory, input))
601 {
602 dwError = GetLastError();
603 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg));
604 PrintWin32Error(szMsg, dwError);
605 return -1;
606 }
607 }
608
609 //
610 // Get and print out some stuff including the formatted size
611 //
612 if (!GetDiskFreeSpaceExW(RootDirectory,
613 NULL,
614 &totalNumberOfBytes,
615 &totalNumberOfFreeBytes))
616 {
617 dwError = GetLastError();
618 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
619 PrintWin32Error(szMsg, dwError);
620 return -1;
621 }
622
623 ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart,
624 totalNumberOfFreeBytes.QuadPart);
625
626 //
627 // Get and print out the new serial number
628 //
629 if (!GetVolumeInformationW(RootDirectory,
630 NULL, 0,
631 &serialNumber, NULL, NULL,
632 NULL, 0))
633 {
634 dwError = GetLastError();
635 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
636 PrintWin32Error(szMsg, dwError);
637 return -1;
638 }
639
640 ConResPrintf(StdOut, STRING_SERIAL_NUMBER,
641 (unsigned int)(serialNumber >> 16),
642 (unsigned int)(serialNumber & 0xFFFF));
643
644 return 0;
645}
646
647/* EOF */