xref: /reactos/base/system/format/format.c (revision 682f85ad)
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     FMIFS_MEDIA_FLAG 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     ConPuts(StdOut, L"\n");
566     ConResPuts(StdOut, STRING_FMT_COMPLETE);
567 
568     //
569     // Enable compression if desired
570     //
571     if (CompressDrive)
572     {
573         if (!EnableVolumeCompression(RootDirectory, TRUE))
574             ConResPuts(StdOut, STRING_VOL_COMPRESS);
575     }
576 
577     //
578     // Get the label if we don't have it
579     //
580     if (!GotALabel)
581     {
582         ConResPuts(StdOut, STRING_ENTER_LABEL);
583         fgetws(input, ARRAYSIZE(LabelString), stdin);
584 
585         input[wcslen(input) - 1] = 0;
586         if (!SetVolumeLabelW(RootDirectory, input))
587         {
588             K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg));
589             PrintWin32Error(szMsg, GetLastError());
590             return -1;
591         }
592     }
593 
594     if (!GetVolumeInformationW(RootDirectory,
595                                volumeName, ARRAYSIZE(volumeName),
596                                &serialNumber, &maxComponent, &flags,
597                                fileSystem, ARRAYSIZE(fileSystem)))
598     {
599         K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
600         PrintWin32Error(szMsg, GetLastError());
601         return -1;
602     }
603 
604     //
605     // Print out some stuff including the formatted size
606     //
607     if (!GetDiskFreeSpaceExW(RootDirectory,
608                              &freeBytesAvailableToCaller,
609                              &totalNumberOfBytes,
610                              &totalNumberOfFreeBytes))
611     {
612         K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
613         PrintWin32Error(szMsg, GetLastError());
614         return -1;
615     }
616 
617     ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart,
618                                             totalNumberOfFreeBytes.QuadPart);
619 
620     //
621     // Get the drive's serial number
622     //
623     if (!GetVolumeInformationW(RootDirectory,
624                                volumeName, ARRAYSIZE(volumeName),
625                                &serialNumber, &maxComponent, &flags,
626                                fileSystem, ARRAYSIZE(fileSystem)))
627     {
628         K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
629         PrintWin32Error(szMsg, GetLastError());
630         return -1;
631     }
632     ConResPrintf(StdOut, STRING_SERIAL_NUMBER,
633                          (unsigned int)(serialNumber >> 16),
634                          (unsigned int)(serialNumber & 0xFFFF));
635 
636     return 0;
637 }
638 
639 /* EOF */
640