This commit was manufactured by cvs2svn to create branch 'captive'.
[reactos.git] / subsys / system / format / format.c
1 // Copyright (c) 1998 Mark Russinovich
2 // Systems Internals
3 // http://www.sysinternals.com
4 #include <stdio.h>
5 #define NTOS_MODE_USER
6 #include <ntos.h>
7 #include <fmifs.h>
8
9 // Globals
10 BOOL    Error = FALSE;
11
12 // switches
13 BOOL    QuickFormat = FALSE;
14 DWORD   ClusterSize = 0;
15 BOOL    CompressDrive = FALSE;
16 BOOL    GotALabel = FALSE;
17 PWCHAR  Label = L"";
18 PWCHAR  Drive = NULL;
19 PWCHAR  Format = L"FAT";
20
21 WCHAR  RootDirectory[MAX_PATH];
22 WCHAR  LabelString[12];
23
24 //
25 // Size array
26 //
27 typedef struct {
28         WCHAR  SizeString[16];
29         DWORD  ClusterSize;
30 } SIZEDEFINITION, *PSIZEDEFINITION;
31
32 SIZEDEFINITION LegalSizes[] = {
33         { L"512", 512 },
34         { L"1024", 1024 },
35         { L"2048", 2048 },
36         { L"4096", 4096 },
37         { L"8192", 8192 },
38         { L"16K", 16384 },
39         { L"32K", 32768 },
40         { L"64K", 65536 },
41         { L"128K", 65536 * 2 },
42         { L"256K", 65536 * 4 },
43         { L"", 0 },
44 };
45
46
47 //----------------------------------------------------------------------
48 //
49 // PrintWin32Error
50 //
51 // Takes the win32 error code and prints the text version.
52 //
53 //----------------------------------------------------------------------
54 void PrintWin32Error( PWCHAR Message, DWORD ErrorCode )
55 {
56         LPVOID lpMsgBuf;
57  
58         FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
59                                         NULL, ErrorCode, 
60                                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
61                                         (PWCHAR) &lpMsgBuf, 0, NULL );
62         printf("%S: %S\n", Message, lpMsgBuf );
63         LocalFree( lpMsgBuf );
64 }
65
66
67 //----------------------------------------------------------------------
68 // 
69 // Usage
70 //
71 // Tell the user how to use the program
72 //
73 //----------------------------------------------------------------------
74 VOID Usage( PWCHAR ProgramName )
75 {
76         printf("Usage: %S drive: [-FS:file-system] [-V:label] [-Q] [-A:size] [-C]\n\n", ProgramName);
77         printf("  [drive:]         Specifies the drive to format.\n");
78         printf("  -FS:file-system  Specifies the type of file system (e.g. FAT).\n");
79         printf("  -V:label         Specifies volume label.\n");
80         printf("  -Q               Performs a quick format.\n");
81         printf("  -A:size          Overrides the default allocation unit size. Default settings\n");
82         printf("                   are strongly recommended for general use\n"); 
83         printf("                   NTFS supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K.\n");
84         printf("                   FAT supports 8192, 16K, 32K, 64K, 128K, 256K.\n");
85         printf("                   NTFS compression is not supported for allocation unit sizes\n");
86         printf("                   above 4096.\n");
87         printf("  -C               Files created on the new volume will be compressed by\n");
88         printf("                   default.\n");
89         printf("\n");
90 }
91
92
93 //----------------------------------------------------------------------
94 //
95 // ParseCommandLine
96 //
97 // Get the switches.
98 //
99 //----------------------------------------------------------------------
100 int ParseCommandLine( int argc, WCHAR *argv[] )
101 {
102         int i, j;
103         BOOLEAN gotFormat = FALSE;
104         BOOLEAN gotQuick = FALSE;
105         BOOLEAN gotSize = FALSE;
106         BOOLEAN gotLabel = FALSE;
107         BOOLEAN gotCompressed = FALSE;
108
109
110         for( i = 1; i < argc; i++ ) {
111
112                 switch( argv[i][0] ) {
113
114                 case '-':
115                 case '/':
116
117                         if( !wcsnicmp( &argv[i][1], L"FS:", 3 )) {
118
119                                 if( gotFormat) return -1;
120                                 Format = &argv[i][4];
121                                 gotFormat = TRUE;
122
123
124                         } else if( !wcsnicmp( &argv[i][1], L"A:", 2 )) {
125
126                                 if( gotSize ) return -1;
127                                 j = 0; 
128                                 while( LegalSizes[j].ClusterSize &&
129                                          wcsicmp( LegalSizes[j].SizeString, &argv[i][3] )) j++;
130
131                                 if( !LegalSizes[j].ClusterSize ) return i;
132                                 ClusterSize = LegalSizes[j].ClusterSize;
133                                 gotSize = TRUE;
134
135                         } else if( !wcsnicmp( &argv[i][1], L"V:", 2 )) {
136
137                                 if( gotLabel ) return -1;
138                                 Label = &argv[i][3];
139                                 gotLabel = TRUE;
140                                 GotALabel = TRUE;
141
142                         } else if( !wcsicmp( &argv[i][1], L"Q" )) {
143
144                                 if( gotQuick ) return -1;
145                                 QuickFormat = TRUE;
146                                 gotQuick = TRUE;
147
148                         } else if( !wcsicmp( &argv[i][1], L"C" )) {
149
150                                 if( gotCompressed ) return -1;
151                                 CompressDrive = TRUE;
152                                 gotCompressed = TRUE;
153
154                         } else return i;
155                         break;
156
157                 default:
158
159                         if( Drive ) return i;
160                         if( argv[i][1] != L':' ) return i;
161
162                         Drive = argv[i];
163                         break;
164                 }
165         }
166         return 0;
167 }
168
169 //----------------------------------------------------------------------
170 //
171 // FormatExCallback
172 //
173 // The file system library will call us back with commands that we
174 // can interpret. If we wanted to halt the chkdsk we could return FALSE.
175 //
176 //----------------------------------------------------------------------
177 BOOL __stdcall FormatExCallback( CALLBACKCOMMAND Command, DWORD Modifier, PVOID Argument )
178 {
179         PDWORD percent;
180         PTEXTOUTPUT output;
181         PBOOLEAN status;
182         static createStructures = FALSE;
183
184         // 
185         // We get other types of commands, but we don't have to pay attention to them
186         //
187         switch( Command ) {
188
189         case PROGRESS:
190                 percent = (PDWORD) Argument;
191                 printf("%d percent completed.\r", *percent);
192                 break;
193
194         case OUTPUT:
195                 output = (PTEXTOUTPUT) Argument;
196                 fprintf(stdout, "%s", output->Output);
197                 break;
198
199         case DONE:
200                 status = (PBOOLEAN) Argument;
201                 if( *status == FALSE ) {
202
203                         printf("FormatEx was unable to complete successfully.\n\n");
204                         Error = TRUE;
205                 }
206                 break;
207         }
208         return TRUE;
209 }
210
211
212 //----------------------------------------------------------------------
213 //
214 // LoadFMIFSEntryPoints
215 //
216 // Loads FMIFS.DLL and locates the entry point(s) we are going to use
217 //
218 //----------------------------------------------------------------------
219 BOOLEAN LoadFMIFSEntryPoints()
220 {
221         LoadLibrary( "fmifs.dll" );
222         if( !(void*) GetProcAddress( GetModuleHandle( "fmifs.dll"), "FormatEx" ) ) {
223
224                 return FALSE;
225         }
226
227         if( !((void *) GetProcAddress( GetModuleHandle( "fmifs.dll"),
228                         "EnableVolumeCompression" )) ) {
229
230                 return FALSE;
231         }
232         return TRUE;
233 }
234
235 //----------------------------------------------------------------------
236 // 
237 // WMain
238 //
239 // Engine. Just get command line switches and fire off a format. This 
240 // could also be done in a GUI like Explorer does when you select a 
241 // drive and run a check on it.
242 //
243 // We do this in UNICODE because the chkdsk command expects PWCHAR
244 // arguments.
245 //
246 //----------------------------------------------------------------------
247 int wmain( int argc, WCHAR *argv[] )
248 {
249         int badArg;
250         DWORD media;
251         DWORD driveType;
252         WCHAR fileSystem[1024];
253         WCHAR volumeName[1024];
254         WCHAR input[1024];
255         DWORD serialNumber;
256         DWORD flags, maxComponent;
257         ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes;
258
259         //
260         // Get function pointers
261         //
262         if( !LoadFMIFSEntryPoints()) {
263
264                 printf("Could not located FMIFS entry points.\n\n");
265                 return -1;
266         }
267
268         //
269         // Parse command line
270         //
271         if( (badArg = ParseCommandLine( argc, argv ))) {
272
273                 printf("Unknown argument: %S\n", argv[badArg] );
274
275                 Usage(argv[0]);
276                 return -1;
277         }
278
279         // 
280         // Get the drive's format
281         //
282         if( !Drive ) {
283
284                 printf("Required drive parameter is missing.\n\n");
285                 Usage( argv[0] );
286                 return -1;
287
288         } else {
289
290                 wcscpy( RootDirectory, Drive );
291         }
292         RootDirectory[2] = L'\\';
293         RootDirectory[3] = (WCHAR) 0;
294
295         //
296         // See if the drive is removable or not
297         //
298         driveType = GetDriveTypeW( RootDirectory );
299
300         if( driveType == 0 ) {
301                 PrintWin32Error( L"Could not get drive type", GetLastError());
302                 return -1;
303         }
304
305         if( driveType != DRIVE_FIXED ) {
306
307                 printf("Insert a new floppy in drive %C:\nand press Enter when ready...",
308                         RootDirectory[0] );
309                 fgetws( input, sizeof(input)/2, stdin );
310
311                 media = FMIFS_FLOPPY;
312
313
314 driveType = DRIVE_FIXED;
315 media = FMIFS_HARDDISK;
316
317
318         }
319
320         //
321         // Determine the drive's file system format
322         //
323         if( !GetVolumeInformationW( RootDirectory, 
324                                                 volumeName, sizeof(volumeName)/2, 
325                                                 &serialNumber, &maxComponent, &flags, 
326                                                 fileSystem, sizeof(fileSystem)/2)) {
327
328                 PrintWin32Error( L"Could not query volume", GetLastError());
329                 return -1;
330         }
331
332         if( !GetDiskFreeSpaceExW( RootDirectory, 
333                         &freeBytesAvailableToCaller,
334                         &totalNumberOfBytes,
335                         &totalNumberOfFreeBytes )) {
336
337                 PrintWin32Error( L"Could not query volume size", GetLastError());
338                 return -1;
339         }
340         printf("The type of the file system is %S.\n", fileSystem );
341
342         //
343         // Make sure they want to do this
344         //
345         if( driveType == DRIVE_FIXED ) {
346
347                 if( volumeName[0] ) {
348
349                         while(1 ) {
350
351                                 printf("Enter current volume label for drive %C: ", RootDirectory[0] );
352                                 fgetws( input, sizeof(input)/2, stdin );
353                                 input[ wcslen( input ) - 1] = 0;
354                                 
355                                 if( !wcsicmp( input, volumeName )) {
356
357                                         break;
358                                 }
359                                 printf("An incorrect volume label was entered for this drive.\n");
360                         }
361                 }
362
363                 while( 1 ) {
364
365                         printf("\nWARNING, ALL DATA ON NON_REMOVABLE DISK\n");
366                         printf("DRIVE %C: WILL BE LOST!\n", RootDirectory[0] );
367                         printf("Proceed with Format (Y/N)? " );
368                         fgetws( input, sizeof(input)/2, stdin );
369                 
370                         if( input[0] == L'Y' || input[0] == L'y' ) break;
371
372                         if(     input[0] == L'N' || input[0] == L'n' ) {
373                                 
374                                 printf("\n");
375                                 return 0;
376                         }
377                 }
378                 media = FMIFS_HARDDISK;
379         } 
380
381         //
382         // Tell the user we're doing a long format if appropriate
383         //
384         if( !QuickFormat ) {
385                 
386                 if( totalNumberOfBytes.QuadPart > 1024*1024*10 ) {
387                         
388                         printf("Verifying %dM\n", (DWORD) (totalNumberOfBytes.QuadPart/(1024*1024)));
389                         
390                 } else {
391
392                         printf("Verifying %.1fM\n", 
393                                 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
394                 }
395         } else  {
396
397                 if( totalNumberOfBytes.QuadPart > 1024*1024*10 ) {
398                         
399                         printf("QuickFormatting %dM\n", (DWORD) (totalNumberOfBytes.QuadPart/(1024*1024)));
400                         
401                 } else {
402
403                         printf("QuickFormatting %.2fM\n", 
404                                 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
405                 }
406                 printf("Creating file system structures.\n");
407         }
408
409         //
410         // Format away!
411         //                      
412         FormatEx( RootDirectory, media, Format, Label, QuickFormat,
413                         ClusterSize, FormatExCallback );
414         if( Error ) return -1;
415         printf("Format complete.\n");
416
417         //
418         // Enable compression if desired
419         //
420         if( CompressDrive ) {
421
422                 if( !EnableVolumeCompression( RootDirectory, TRUE )) {
423
424                         printf("Volume does not support compression.\n");
425                 }
426         }
427
428         //
429         // Get the label if we don't have it
430         //
431         if( !GotALabel ) {
432
433                 printf("Volume Label (11 characters, Enter for none)? " );
434                 fgetws( input, sizeof(LabelString)/2, stdin );
435
436                 input[ wcslen(input)-1] = 0;
437                 if( !SetVolumeLabelW( RootDirectory, input )) {
438
439                         PrintWin32Error(L"Could not label volume", GetLastError());
440                         return -1;
441                 }       
442         }
443
444         if( !GetVolumeInformationW( RootDirectory, 
445                                                 volumeName, sizeof(volumeName)/2, 
446                                                 &serialNumber, &maxComponent, &flags, 
447                                                 fileSystem, sizeof(fileSystem)/2)) {
448
449                 PrintWin32Error( L"Could not query volume", GetLastError());
450                 return -1;
451         }
452
453         // 
454         // Print out some stuff including the formatted size
455         //
456         if( !GetDiskFreeSpaceExW( RootDirectory, 
457                         &freeBytesAvailableToCaller,
458                         &totalNumberOfBytes,
459                         &totalNumberOfFreeBytes )) {
460
461                 PrintWin32Error( L"Could not query volume size", GetLastError());
462                 return -1;
463         }
464
465         printf("\n%I64d bytes total disk space.\n", totalNumberOfBytes.QuadPart );
466         printf("%I64d bytes available on disk.\n", totalNumberOfFreeBytes.QuadPart );
467
468         //
469         // Get the drive's serial number
470         //
471         if( !GetVolumeInformationW( RootDirectory, 
472                                                 volumeName, sizeof(volumeName)/2, 
473                                                 &serialNumber, &maxComponent, &flags, 
474                                                 fileSystem, sizeof(fileSystem)/2)) {
475
476                 PrintWin32Error( L"Could not query volume", GetLastError());
477                 return -1;
478         }
479         printf("\nVolume Serial Number is %04X-%04X\n", serialNumber >> 16,
480                                         serialNumber & 0xFFFF );
481                         
482         return 0;
483 }
484
485 int main(int argc, char* argv[])
486 {
487         UNICODE_STRING warg;
488         ANSI_STRING arg;
489     NTSTATUS status;
490         PWCHAR *wargv;
491         int i;
492
493         wargv = (PWCHAR *) RtlAllocateHeap(RtlGetProcessHeap(), 0, argc * sizeof(PWCHAR));
494
495         for (i = 0; i < argc; i++)
496         {
497                 RtlInitAnsiString(&arg, argv[i]);
498                 status = RtlAnsiStringToUnicodeString(&warg, &arg, TRUE);
499                 if (!NT_SUCCESS (status))
500                 {
501                         printf("Not enough free memory.\n");
502                         return 1;
503                 }
504                 wargv[i] = (PWCHAR) warg.Buffer;
505         }
506
507         wmain(argc, wargv);
508
509         for (i = 0; i < argc; i++)
510         {
511                 RtlFreeHeap(RtlGetProcessHeap(), 0, wargv[i]);
512         }
513         RtlFreeHeap(RtlGetProcessHeap(), 0, wargv);
514
515         return 0;
516 }