Sync with trunk r47129
[reactos.git] / base / system / format / format.c
1 // Copyright (c) 1998 Mark Russinovich
2 // Systems Internals
3 // http://www.sysinternals.com
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <windows.h>
8 #include <winternl.h>
9 #include <fmifs/fmifs.h>
10 #include <tchar.h>
11 #include "resource.h"
12
13 // Globals
14 BOOL Error = FALSE;
15
16 // switches
17 BOOL QuickFormat = FALSE;
18 DWORD ClusterSize = 0;
19 BOOL CompressDrive = FALSE;
20 BOOL GotALabel = FALSE;
21 LPTSTR Label = _T("");
22 LPTSTR Drive = NULL;
23 LPTSTR Format = _T("FAT");
24
25 TCHAR RootDirectory[MAX_PATH];
26 TCHAR LabelString[12];
27
28 //
29 // Size array
30 //
31 typedef struct {
32 TCHAR SizeString[16];
33 DWORD ClusterSize;
34 } SIZEDEFINITION, *PSIZEDEFINITION;
35
36 SIZEDEFINITION LegalSizes[] = {
37 { _T("512"), 512 },
38 { _T("1024"), 1024 },
39 { _T("2048"), 2048 },
40 { _T("4096"), 4096 },
41 { _T("8192"), 8192 },
42 { _T("16K"), 16384 },
43 { _T("32K"), 32768 },
44 { _T("64K"), 65536 },
45 { _T("128K"), 65536 * 2 },
46 { _T("256K"), 65536 * 4 },
47 { _T(""), 0 },
48 };
49
50
51 //----------------------------------------------------------------------
52 //
53 // PrintWin32Error
54 //
55 // Takes the win32 error code and prints the text version.
56 //
57 //----------------------------------------------------------------------
58 static VOID PrintWin32Error( LPTSTR Message, DWORD ErrorCode )
59 {
60 LPTSTR lpMsgBuf;
61
62 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
63 NULL, ErrorCode,
64 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
65 (LPTSTR)&lpMsgBuf, 0, NULL );
66
67 _tprintf(_T("%s: %s\n"), Message, lpMsgBuf );
68 LocalFree( lpMsgBuf );
69 }
70
71
72 //----------------------------------------------------------------------
73 //
74 // ParseCommandLine
75 //
76 // Get the switches.
77 //
78 //----------------------------------------------------------------------
79 static int ParseCommandLine( int argc, TCHAR *argv[] )
80 {
81 int i, j;
82 BOOLEAN gotFormat = FALSE;
83 BOOLEAN gotQuick = FALSE;
84 BOOLEAN gotSize = FALSE;
85 BOOLEAN gotLabel = FALSE;
86 BOOLEAN gotCompressed = FALSE;
87
88
89 for( i = 1; i < argc; i++ ) {
90
91 switch( argv[i][0] ) {
92
93 case '-':
94 case '/':
95
96 if( !_tcsnicmp( &argv[i][1], _T("FS:"), 3 )) {
97
98 if( gotFormat) return -1;
99 Format = &argv[i][4];
100 gotFormat = TRUE;
101
102
103 } else if( !_tcsnicmp( &argv[i][1], _T("A:"), 2 )) {
104
105 if( gotSize ) return -1;
106 j = 0;
107 while( LegalSizes[j].ClusterSize &&
108 _tcsicmp( LegalSizes[j].SizeString, &argv[i][3] )) j++;
109
110 if( !LegalSizes[j].ClusterSize ) return i;
111 ClusterSize = LegalSizes[j].ClusterSize;
112 gotSize = TRUE;
113
114 } else if( ! _tcsnicmp( &argv[i][1], _T("V:"), 2 )) {
115
116 if( gotLabel ) return -1;
117 Label = &argv[i][3];
118 gotLabel = TRUE;
119 GotALabel = TRUE;
120
121 } else if( !_tcsicmp( &argv[i][1], _T("Q") )) {
122
123 if( gotQuick ) return -1;
124 QuickFormat = TRUE;
125 gotQuick = TRUE;
126
127 } else if( !_tcsicmp( &argv[i][1], _T("C") )) {
128
129 if( gotCompressed ) return -1;
130 CompressDrive = TRUE;
131 gotCompressed = TRUE;
132
133 } else return i;
134 break;
135
136 default:
137
138 if( Drive ) return i;
139 if( argv[i][1] != _T(':') ) return i;
140
141 Drive = argv[i];
142 break;
143 }
144 }
145 return 0;
146 }
147
148 //----------------------------------------------------------------------
149 //
150 // FormatExCallback
151 //
152 // The file system library will call us back with commands that we
153 // can interpret. If we wanted to halt the chkdsk we could return FALSE.
154 //
155 //----------------------------------------------------------------------
156 BOOLEAN WINAPI
157 FormatExCallback (
158 CALLBACKCOMMAND Command,
159 ULONG Modifier,
160 PVOID Argument)
161 {
162 PDWORD percent;
163 PTEXTOUTPUT output;
164 PBOOLEAN status;
165 TCHAR szMsg[RC_STRING_MAX_SIZE];
166
167 //
168 // We get other types of commands, but we don't have to pay attention to them
169 //
170 switch( Command ) {
171
172 case PROGRESS:
173 percent = (PDWORD) Argument;
174 LoadString( GetModuleHandle(NULL), STRING_COMPLETE, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
175 _tprintf(szMsg, *percent);
176 break;
177
178 case OUTPUT:
179 output = (PTEXTOUTPUT) Argument;
180 fprintf(stdout, "%s", output->Output);
181 break;
182
183 case DONE:
184 status = (PBOOLEAN) Argument;
185 if( *status == FALSE ) {
186
187 LoadString( GetModuleHandle(NULL), STRING_FORMAT_FAIL, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
188 _tprintf(szMsg);
189 Error = TRUE;
190 }
191 break;
192 case DONEWITHSTRUCTURE:
193 case UNKNOWN2:
194 case UNKNOWN3:
195 case UNKNOWN4:
196 case UNKNOWN5:
197 case INSUFFICIENTRIGHTS:
198 case FSNOTSUPPORTED:
199 case VOLUMEINUSE:
200 case UNKNOWN9:
201 case UNKNOWNA:
202 case UNKNOWNC:
203 case UNKNOWND:
204 case STRUCTUREPROGRESS:
205 case CLUSTERSIZETOOSMALL:
206 LoadString( GetModuleHandle(NULL), STRING_NO_SUPPORT, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
207 _tprintf(szMsg);
208 return FALSE;
209 }
210 return TRUE;
211 }
212
213
214 //----------------------------------------------------------------------
215 //
216 // LoadFMIFSEntryPoints
217 //
218 // Loads FMIFS.DLL and locates the entry point(s) we are going to use
219 //
220 //----------------------------------------------------------------------
221 BOOLEAN LoadFMIFSEntryPoints()
222 {
223 HMODULE hFmifs = LoadLibrary( _T("fmifs.dll") );
224 if( !(void*) GetProcAddress( hFmifs, "FormatEx" ) ) {
225
226 return FALSE;
227 }
228
229 if( !((void *) GetProcAddress( hFmifs,
230 "EnableVolumeCompression" )) ) {
231
232 return FALSE;
233 }
234
235 if( !((void *) GetProcAddress( hFmifs,
236 "QueryAvailableFileSystemFormat" )) ) {
237
238 return FALSE;
239 }
240
241 return TRUE;
242 }
243
244
245 //----------------------------------------------------------------------
246 //
247 // Usage
248 //
249 // Tell the user how to use the program
250 //
251 //----------------------------------------------------------------------
252 static VOID Usage( LPTSTR ProgramName )
253 {
254 TCHAR szMsg[RC_STRING_MAX_SIZE];
255 TCHAR szFormats[MAX_PATH];
256 #ifndef UNICODE
257 TCHAR szFormatA[MAX_PATH];
258 #endif
259 WCHAR szFormatW[MAX_PATH];
260 DWORD Index = 0;
261 BYTE dummy;
262 BOOLEAN lastestVersion;
263
264 LoadString( GetModuleHandle(NULL), STRING_HELP, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
265 if (!LoadFMIFSEntryPoints())
266 {
267 _tprintf(szMsg, ProgramName, _T(""));
268 return;
269 }
270
271 szFormats[0] = 0;
272 while (QueryAvailableFileSystemFormat(Index++, szFormatW, &dummy, &dummy, &lastestVersion))
273 {
274 if (!lastestVersion)
275 continue;
276 if (szFormats[0])
277 _tcscat(szFormats, _T(", "));
278 #ifdef UNICODE
279 _tcscat(szFormats, szFormatW);
280 #else
281 if (0 != WideCharToMultiByte(CP_ACP, 0, szFormatW, -1, szFormatA, sizeof(szFormatA), NULL, NULL))
282 _tcscat(szFormats, szFormatA);
283 #endif
284 }
285 _tprintf(szMsg, ProgramName, szFormats);
286 }
287
288
289 //----------------------------------------------------------------------
290 //
291 // WMain
292 //
293 // Engine. Just get command line switches and fire off a format. This
294 // could also be done in a GUI like Explorer does when you select a
295 // drive and run a check on it.
296 //
297 // We do this in UNICODE because the chkdsk command expects PWCHAR
298 // arguments.
299 //
300 //----------------------------------------------------------------------
301 int
302 _tmain(int argc, TCHAR *argv[])
303 {
304 int badArg;
305 DWORD media = FMIFS_HARDDISK;
306 DWORD driveType;
307 TCHAR fileSystem[1024];
308 TCHAR volumeName[1024];
309 TCHAR input[1024];
310 DWORD serialNumber;
311 DWORD flags, maxComponent;
312 ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes;
313 #ifndef UNICODE
314 WCHAR RootDirectoryW[MAX_PATH], FormatW[MAX_PATH], LabelW[MAX_PATH];
315 #endif
316 TCHAR szMsg[RC_STRING_MAX_SIZE];
317
318 //
319 // Get function pointers
320 //
321 if( !LoadFMIFSEntryPoints()) {
322 LoadString( GetModuleHandle(NULL), STRING_FMIFS_FAIL, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
323 _tprintf(szMsg);
324 return -1;
325 }
326
327 //
328 // Parse command line
329 //
330 if( (badArg = ParseCommandLine( argc, argv ))) {
331
332 LoadString( GetModuleHandle(NULL), STRING_UNKNOW_ARG, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
333 _tprintf(szMsg, argv[badArg] );
334
335 Usage(argv[0]);
336 return -1;
337 }
338
339 //
340 // Get the drive's format
341 //
342 if( !Drive ) {
343
344 LoadString( GetModuleHandle(NULL), STRING_DRIVE_PARM, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
345 _tprintf(szMsg);
346 Usage( argv[0] );
347 return -1;
348
349 } else {
350
351 _tcscpy( RootDirectory, Drive );
352 }
353 RootDirectory[2] = _T('\\');
354 RootDirectory[3] = _T('\0');
355
356 //
357 // See if the drive is removable or not
358 //
359 driveType = GetDriveType( RootDirectory );
360
361 if( driveType == 0 ) {
362 LoadString( GetModuleHandle(NULL), STRING_ERROR_DRIVE_TYPE, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
363 PrintWin32Error( szMsg, GetLastError());
364 return -1;
365 }
366 else if ( driveType == 1 )
367 {
368 LoadString( GetModuleHandle(NULL), STRING_NO_VOLUME, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
369 PrintWin32Error( szMsg, GetLastError());
370 return -1;
371 }
372
373 if( driveType != DRIVE_FIXED ) {
374 LoadString( GetModuleHandle(NULL), STRING_INSERT_DISK, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
375 _tprintf(szMsg, RootDirectory[0] );
376 _fgetts( input, sizeof(input)/2, stdin );
377
378 media = FMIFS_FLOPPY;
379 }
380
381 //
382 // Determine the drive's file system format
383 //
384 if( !GetVolumeInformation( RootDirectory,
385 volumeName, sizeof(volumeName)/2,
386 &serialNumber, &maxComponent, &flags,
387 fileSystem, sizeof(fileSystem)/2)) {
388
389 LoadString( GetModuleHandle(NULL), STRING_NO_VOLUME, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
390 PrintWin32Error( szMsg, GetLastError());
391 return -1;
392 }
393
394 if( !GetDiskFreeSpaceEx( RootDirectory,
395 &freeBytesAvailableToCaller,
396 &totalNumberOfBytes,
397 &totalNumberOfFreeBytes )) {
398
399 LoadString( GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
400 PrintWin32Error( szMsg, GetLastError());
401 return -1;
402 }
403 LoadString( GetModuleHandle(NULL), STRING_FILESYSTEM, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
404 _tprintf(szMsg, fileSystem );
405
406 //
407 // Make sure they want to do this
408 //
409 if( driveType == DRIVE_FIXED ) {
410
411 if( volumeName[0] ) {
412
413 while(1 ) {
414
415 LoadString( GetModuleHandle(NULL), STRING_LABEL_NAME_EDIT, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
416 _tprintf(szMsg, RootDirectory[0] );
417 _fgetts( input, sizeof(input)/2, stdin );
418 input[ _tcslen( input ) - 1] = 0;
419
420 if( !_tcsicmp( input, volumeName )) {
421
422 break;
423 }
424 LoadString( GetModuleHandle(NULL), STRING_ERROR_LABEL, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
425 _tprintf(szMsg);
426 }
427 }
428
429 LoadString( GetModuleHandle(NULL), STRING_YN_FORMAT, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
430 _tprintf(szMsg, RootDirectory[0] );
431
432 LoadString( GetModuleHandle(NULL), STRING_YES_NO_FAQ, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
433
434 while( 1 ) {
435 _fgetts( input, sizeof(input)/2, stdin );
436 if(_strnicmp(&input[0],&szMsg[0],1) == 0) break;
437 if(_strnicmp(&input[0],&szMsg[1],1) == 0) {
438 _tprintf(_T("\n"));
439 return 0;
440 }
441 }
442 media = FMIFS_HARDDISK;
443 }
444
445 //
446 // Tell the user we're doing a long format if appropriate
447 //
448 if( !QuickFormat ) {
449
450 LoadString( GetModuleHandle(NULL), STRING_VERIFYING, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
451
452 if( totalNumberOfBytes.QuadPart > 1024*1024*10 ) {
453
454 _tprintf(_T("%s %luM\n"),szMsg, (DWORD) (totalNumberOfBytes.QuadPart/(1024*1024)));
455
456 } else {
457
458 _tprintf(_T("%s %.1fM\n"),szMsg,
459 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
460 }
461 } else {
462
463 LoadString( GetModuleHandle(NULL), STRING_FAST_FMT, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
464 if( totalNumberOfBytes.QuadPart > 1024*1024*10 ) {
465
466 _tprintf(_T("%s %luM\n"),szMsg, (DWORD) (totalNumberOfBytes.QuadPart/(1024*1024)));
467
468 } else {
469
470 _tprintf(_T("%s %.2fM\n"),szMsg,
471 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
472 }
473 LoadString( GetModuleHandle(NULL), STRING_CREATE_FSYS, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
474 _tprintf(szMsg);
475 }
476
477 //
478 // Format away!
479 //
480 #ifndef UNICODE
481 MultiByteToWideChar(CP_ACP, 0, RootDirectory, -1, RootDirectoryW, MAX_PATH);
482 MultiByteToWideChar(CP_ACP, 0, Format, -1, FormatW, MAX_PATH);
483 MultiByteToWideChar(CP_ACP, 0, Label, -1, LabelW, MAX_PATH);
484 FormatEx( RootDirectoryW, media, FormatW, LabelW, QuickFormat,
485 ClusterSize, FormatExCallback );
486 #else
487 FormatEx( RootDirectory, media, Format, Label, QuickFormat,
488 ClusterSize, FormatExCallback );
489 #endif
490 if( Error ) return -1;
491 LoadString( GetModuleHandle(NULL), STRING_FMT_COMPLETE, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
492 _tprintf(szMsg);
493
494 //
495 // Enable compression if desired
496 //
497 if( CompressDrive ) {
498
499 #ifndef UNICODE
500 MultiByteToWideChar(CP_ACP, 0, RootDirectory, -1, RootDirectoryW, MAX_PATH);
501 if( !EnableVolumeCompression( RootDirectoryW, TRUE )) {
502 #else
503 if( !EnableVolumeCompression( RootDirectory, TRUE )) {
504 #endif
505
506 LoadString( GetModuleHandle(NULL), STRING_VOL_COMPRESS, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
507 _tprintf(szMsg);
508 }
509 }
510
511 //
512 // Get the label if we don't have it
513 //
514 if( !GotALabel ) {
515
516 LoadString( GetModuleHandle(NULL), STRING_ENTER_LABEL, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
517 _tprintf(szMsg);
518 _fgetts( input, sizeof(LabelString)/2, stdin );
519
520 input[ _tcslen(input)-1] = 0;
521 if( !SetVolumeLabel( RootDirectory, input )) {
522
523 LoadString( GetModuleHandle(NULL), STRING_NO_LABEL, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
524 PrintWin32Error(szMsg, GetLastError());
525 return -1;
526 }
527 }
528
529 if( !GetVolumeInformation( RootDirectory,
530 volumeName, sizeof(volumeName)/2,
531 &serialNumber, &maxComponent, &flags,
532 fileSystem, sizeof(fileSystem)/2)) {
533
534 LoadString( GetModuleHandle(NULL), STRING_NO_VOLUME, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
535 PrintWin32Error( szMsg, GetLastError());
536 return -1;
537 }
538
539 //
540 // Print out some stuff including the formatted size
541 //
542 if( !GetDiskFreeSpaceEx( RootDirectory,
543 &freeBytesAvailableToCaller,
544 &totalNumberOfBytes,
545 &totalNumberOfFreeBytes )) {
546
547 LoadString( GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
548 PrintWin32Error(szMsg, GetLastError());
549 return -1;
550 }
551
552 LoadString( GetModuleHandle(NULL), STRING_FREE_SPACE, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
553 _tprintf(szMsg, totalNumberOfBytes.QuadPart, totalNumberOfFreeBytes.QuadPart );
554
555 //
556 // Get the drive's serial number
557 //
558 if( !GetVolumeInformation( RootDirectory,
559 volumeName, sizeof(volumeName)/2,
560 &serialNumber, &maxComponent, &flags,
561 fileSystem, sizeof(fileSystem)/2)) {
562
563 LoadString( GetModuleHandle(NULL), STRING_NO_VOLUME, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
564 PrintWin32Error( szMsg, GetLastError());
565 return -1;
566 }
567 LoadString( GetModuleHandle(NULL), STRING_SERIAL_NUMBER, (LPTSTR) szMsg,RC_STRING_MAX_SIZE);
568 _tprintf(szMsg, (unsigned int)(serialNumber >> 16),
569 (unsigned int)(serialNumber & 0xFFFF) );
570
571 return 0;
572 }
573