xref: /reactos/base/system/format/format.c (revision 358fecdc)
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