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