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