[NtUser]
[reactos.git] / reactos / base / system / format / format.c
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 #include <winnls.h>
50 #include <winuser.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 VOID PrintStringV(LPWSTR szStr, va_list args)
111 {
112 WCHAR bufFormatted[RC_STRING_MAX_SIZE];
113 CHAR bufFormattedOem[RC_STRING_MAX_SIZE];
114
115 _vsnwprintf(bufFormatted, ARRAYSIZE(bufFormatted), szStr, args);
116
117 CharToOemW(bufFormatted, bufFormattedOem);
118 puts(bufFormattedOem);
119 }
120
121 VOID PrintString(LPWSTR szStr, ...)
122 {
123 va_list args;
124
125 va_start(args, szStr);
126 PrintStringV(szStr, args);
127 va_end(args);
128 }
129
130 VOID PrintResourceString(UINT uID, ...)
131 {
132 WCHAR bufSrc[RC_STRING_MAX_SIZE];
133 va_list args;
134
135 LoadStringW(GetModuleHandleW(NULL), uID, bufSrc, ARRAYSIZE(bufSrc));
136
137 va_start(args, uID);
138 PrintStringV(bufSrc, args);
139 va_end(args);
140 }
141
142
143 //----------------------------------------------------------------------
144 //
145 // PrintWin32Error
146 //
147 // Takes the win32 error code and prints the text version.
148 //
149 //----------------------------------------------------------------------
150 static VOID PrintWin32Error(LPWSTR Message, DWORD ErrorCode)
151 {
152 LPWSTR lpMsgBuf;
153
154 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
155 NULL, ErrorCode,
156 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
157 (LPWSTR)&lpMsgBuf, 0, NULL);
158
159 PrintString(L"%s: %s\n", Message, lpMsgBuf);
160 LocalFree(lpMsgBuf);
161 }
162
163
164 //----------------------------------------------------------------------
165 //
166 // ParseCommandLine
167 //
168 // Get the switches.
169 //
170 //----------------------------------------------------------------------
171 static int ParseCommandLine(int argc, WCHAR *argv[])
172 {
173 int i, j;
174 BOOLEAN gotFormat = FALSE;
175 BOOLEAN gotQuick = FALSE;
176 BOOLEAN gotSize = FALSE;
177 BOOLEAN gotLabel = FALSE;
178 BOOLEAN gotCompressed = FALSE;
179
180 for (i = 1; i < argc; i++)
181 {
182 switch (argv[i][0])
183 {
184 case L'-': case L'/':
185
186 if (!_wcsnicmp(&argv[i][1], L"FS:", 3))
187 {
188 if (gotFormat) return -1;
189 FileSystem = &argv[i][4];
190 gotFormat = TRUE;
191 }
192 else if (!_wcsnicmp(&argv[i][1], L"A:", 2))
193 {
194 if (gotSize) return -1;
195 j = 0;
196 while (LegalSizes[j].ClusterSize &&
197 wcsicmp(LegalSizes[j].SizeString, &argv[i][3]))
198 {
199 j++;
200 }
201
202 if (!LegalSizes[j].ClusterSize) return i;
203 ClusterSize = LegalSizes[j].ClusterSize;
204 gotSize = TRUE;
205 }
206 else if (!_wcsnicmp(&argv[i][1], L"V:", 2))
207 {
208 if (gotLabel) return -1;
209 Label = &argv[i][3];
210 gotLabel = TRUE;
211 GotALabel = TRUE;
212 }
213 else if (!wcsicmp(&argv[i][1], L"Q"))
214 {
215 if (gotQuick) return -1;
216 QuickFormat = TRUE;
217 gotQuick = TRUE;
218 }
219 else if (!wcsicmp(&argv[i][1], L"C"))
220 {
221 if (gotCompressed) return -1;
222 CompressDrive = TRUE;
223 gotCompressed = TRUE;
224 }
225 else
226 {
227 return i;
228 }
229 break;
230
231 default:
232 {
233 if (Drive) return i;
234 if (argv[i][1] != L':') return i;
235
236 Drive = argv[i];
237 break;
238 }
239 }
240 }
241 return 0;
242 }
243
244 //----------------------------------------------------------------------
245 //
246 // FormatExCallback
247 //
248 // The file system library will call us back with commands that we
249 // can interpret. If we wanted to halt the chkdsk we could return FALSE.
250 //
251 //----------------------------------------------------------------------
252 BOOLEAN WINAPI
253 FormatExCallback(
254 CALLBACKCOMMAND Command,
255 ULONG Modifier,
256 PVOID Argument)
257 {
258 PDWORD percent;
259 PTEXTOUTPUT output;
260 PBOOLEAN status;
261
262 //
263 // We get other types of commands, but we don't have to pay attention to them
264 //
265 switch (Command)
266 {
267 case PROGRESS:
268 percent = (PDWORD)Argument;
269 PrintResourceString(STRING_COMPLETE, *percent);
270 break;
271
272 case OUTPUT:
273 output = (PTEXTOUTPUT)Argument;
274 wprintf(L"%S", output->Output);
275 break;
276
277 case DONE:
278 status = (PBOOLEAN)Argument;
279 if (*status == FALSE)
280 {
281 PrintResourceString(STRING_FORMAT_FAIL);
282 Error = TRUE;
283 }
284 break;
285
286 case DONEWITHSTRUCTURE:
287 case UNKNOWN2:
288 case UNKNOWN3:
289 case UNKNOWN4:
290 case UNKNOWN5:
291 case INSUFFICIENTRIGHTS:
292 case FSNOTSUPPORTED:
293 case VOLUMEINUSE:
294 case UNKNOWN9:
295 case UNKNOWNA:
296 case UNKNOWNC:
297 case UNKNOWND:
298 case STRUCTUREPROGRESS:
299 case CLUSTERSIZETOOSMALL:
300 PrintResourceString(STRING_NO_SUPPORT);
301 return FALSE;
302 }
303 return TRUE;
304 }
305
306 #ifndef FMIFS_IMPORT_DLL
307 //----------------------------------------------------------------------
308 //
309 // LoadFMIFSEntryPoints
310 //
311 // Loads FMIFS.DLL and locates the entry point(s) we are going to use
312 //
313 //----------------------------------------------------------------------
314 static BOOLEAN LoadFMIFSEntryPoints(VOID)
315 {
316 HMODULE hFmifs = LoadLibraryW( L"fmifs.dll");
317 if (hFmifs == NULL)
318 return FALSE;
319
320 FormatEx = (PFORMATEX)GetProcAddress(hFmifs, "FormatEx");
321 if (!FormatEx)
322 {
323 FreeLibrary(hFmifs);
324 return FALSE;
325 }
326
327 EnableVolumeCompression = (PENABLEVOLUMECOMPRESSION)GetProcAddress(hFmifs, "EnableVolumeCompression");
328 if (!EnableVolumeCompression)
329 {
330 FreeLibrary(hFmifs);
331 return FALSE;
332 }
333
334 QueryAvailableFileSystemFormat = (PQUERYAVAILABLEFILESYSTEMFORMAT)GetProcAddress(hFmifs, "QueryAvailableFileSystemFormat");
335 if (!QueryAvailableFileSystemFormat)
336 {
337 FreeLibrary(hFmifs);
338 return FALSE;
339 }
340
341 return TRUE;
342 }
343 #endif
344
345
346 //----------------------------------------------------------------------
347 //
348 // Usage
349 //
350 // Tell the user how to use the program
351 //
352 //----------------------------------------------------------------------
353 static VOID Usage(LPWSTR ProgramName)
354 {
355 WCHAR szMsg[RC_STRING_MAX_SIZE];
356 WCHAR szFormats[MAX_PATH];
357 WCHAR szFormatW[MAX_PATH];
358 DWORD Index = 0;
359 BYTE dummy;
360 BOOLEAN latestVersion;
361
362 LoadStringW(GetModuleHandle(NULL), STRING_HELP, szMsg, ARRAYSIZE(szMsg));
363
364 #ifndef FMIFS_IMPORT_DLL
365 if (!LoadFMIFSEntryPoints())
366 {
367 PrintString(szMsg, ProgramName, L"");
368 return;
369 }
370 #endif
371
372 szFormats[0] = 0;
373 while (QueryAvailableFileSystemFormat(Index++, szFormatW, &dummy, &dummy, &latestVersion))
374 {
375 if (!latestVersion)
376 continue;
377 if (szFormats[0])
378 wcscat(szFormats, L", ");
379
380 wcscat(szFormats, szFormatW);
381 }
382 PrintString(szMsg, ProgramName, szFormats);
383 }
384
385
386 //----------------------------------------------------------------------
387 //
388 // WMain
389 //
390 // Engine. Just get command line switches and fire off a format. This
391 // could also be done in a GUI like Explorer does when you select a
392 // drive and run a check on it.
393 //
394 // We do this in UNICODE because the chkdsk command expects PWCHAR
395 // arguments.
396 //
397 //----------------------------------------------------------------------
398 int wmain(int argc, WCHAR *argv[])
399 {
400 int badArg;
401 DWORD media = FMIFS_HARDDISK;
402 DWORD driveType;
403 WCHAR fileSystem[1024];
404 WCHAR volumeName[1024];
405 WCHAR input[1024];
406 DWORD serialNumber;
407 DWORD flags, maxComponent;
408 ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes;
409 WCHAR szMsg[RC_STRING_MAX_SIZE];
410
411 wprintf(L"\n"
412 L"Formatx v1.0 by Mark Russinovich\n"
413 L"Systems Internals - http://www.sysinternals.com\n"
414 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n");
415
416 #ifndef FMIFS_IMPORT_DLL
417 //
418 // Get function pointers
419 //
420 if (!LoadFMIFSEntryPoints())
421 {
422 PrintResourceString(STRING_FMIFS_FAIL);
423 return -1;
424 }
425 #endif
426
427 //
428 // Parse command line
429 //
430 badArg = ParseCommandLine(argc, argv);
431 if (badArg)
432 {
433 PrintResourceString(STRING_UNKNOW_ARG, argv[badArg]);
434 Usage(argv[0]);
435 return -1;
436 }
437
438 //
439 // Get the drive's format
440 //
441 if (!Drive)
442 {
443 PrintResourceString(STRING_DRIVE_PARM);
444 Usage(argv[0]);
445 return -1;
446 }
447 else
448 {
449 wcscpy(RootDirectory, Drive);
450 }
451 RootDirectory[2] = L'\\';
452 RootDirectory[3] = L'\0';
453
454 //
455 // See if the drive is removable or not
456 //
457 driveType = GetDriveTypeW(RootDirectory);
458 switch (driveType)
459 {
460 case DRIVE_UNKNOWN :
461 LoadStringW(GetModuleHandle(NULL), STRING_ERROR_DRIVE_TYPE, szMsg, ARRAYSIZE(szMsg));
462 PrintWin32Error(szMsg, GetLastError());
463 return -1;
464
465 case DRIVE_REMOTE:
466 case DRIVE_CDROM:
467 PrintResourceString(STRING_NO_SUPPORT);
468 return -1;
469
470 case DRIVE_NO_ROOT_DIR:
471 LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
472 PrintWin32Error(szMsg, GetLastError());
473 return -1;
474
475 case DRIVE_REMOVABLE:
476 PrintResourceString(STRING_INSERT_DISK, RootDirectory[0]);
477 fgetws(input, ARRAYSIZE(input), stdin);
478 media = FMIFS_FLOPPY;
479 break;
480
481 case DRIVE_FIXED:
482 case DRIVE_RAMDISK:
483 media = FMIFS_HARDDISK;
484 break;
485 }
486
487 // Reject attempts to format the system drive
488 {
489 WCHAR path[MAX_PATH + 1];
490 UINT rc;
491 rc = GetWindowsDirectoryW(path, MAX_PATH);
492 if (rc == 0 || rc > MAX_PATH)
493 // todo: Report "Unable to query system directory"
494 return -1;
495 if (towlower(path[0]) == towlower(Drive[0]))
496 {
497 // todo: report "Cannot format system drive"
498 PrintResourceString(STRING_NO_SUPPORT);
499 return -1;
500 }
501 }
502
503 //
504 // Determine the drive's file system format
505 //
506 if (!GetVolumeInformationW(RootDirectory,
507 volumeName, ARRAYSIZE(volumeName),
508 &serialNumber, &maxComponent, &flags,
509 fileSystem, ARRAYSIZE(fileSystem)))
510 {
511 LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
512 PrintWin32Error(szMsg, GetLastError());
513 return -1;
514 }
515
516 if (!GetDiskFreeSpaceExW(RootDirectory,
517 &freeBytesAvailableToCaller,
518 &totalNumberOfBytes,
519 &totalNumberOfFreeBytes))
520 {
521 LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
522 PrintWin32Error(szMsg, GetLastError());
523 return -1;
524 }
525 PrintResourceString(STRING_FILESYSTEM, fileSystem);
526
527 //
528 // Make sure they want to do this
529 //
530 if (driveType == DRIVE_FIXED)
531 {
532 if (volumeName[0])
533 {
534 while (TRUE)
535 {
536 PrintResourceString(STRING_LABEL_NAME_EDIT, RootDirectory[0]);
537 fgetws(input, ARRAYSIZE(input), stdin);
538 input[wcslen(input) - 1] = 0;
539
540 if (!wcsicmp(input, volumeName))
541 break;
542
543 PrintResourceString(STRING_ERROR_LABEL);
544 }
545 }
546
547 PrintResourceString(STRING_YN_FORMAT, RootDirectory[0]);
548
549 LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg));
550 while (TRUE)
551 {
552 fgetws(input, ARRAYSIZE(input), stdin);
553 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break;
554 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0)
555 {
556 wprintf(L"\n");
557 return 0;
558 }
559 }
560 }
561
562 //
563 // Tell the user we're doing a long format if appropriate
564 //
565 if (!QuickFormat)
566 {
567 LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg));
568 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
569 {
570 PrintString(L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
571 }
572 else
573 {
574 PrintString(L"%s %.1fM\n", szMsg,
575 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
576 }
577 }
578 else
579 {
580 LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg));
581 if (totalNumberOfBytes.QuadPart > 1024*1024*10)
582 {
583 PrintString(L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024)));
584 }
585 else
586 {
587 PrintString(L"%s %.2fM\n", szMsg,
588 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0));
589 }
590 PrintResourceString(STRING_CREATE_FSYS);
591 }
592
593 //
594 // Format away!
595 //
596 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat,
597 ClusterSize, FormatExCallback);
598 if (Error) return -1;
599 PrintResourceString(STRING_FMT_COMPLETE);
600
601 //
602 // Enable compression if desired
603 //
604 if (CompressDrive)
605 {
606 if (!EnableVolumeCompression(RootDirectory, TRUE))
607 PrintResourceString(STRING_VOL_COMPRESS);
608 }
609
610 //
611 // Get the label if we don't have it
612 //
613 if (!GotALabel)
614 {
615 PrintResourceString(STRING_ENTER_LABEL);
616 fgetws(input, ARRAYSIZE(LabelString), stdin);
617
618 input[wcslen(input) - 1] = 0;
619 if (!SetVolumeLabelW(RootDirectory, input))
620 {
621 LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg));
622 PrintWin32Error(szMsg, GetLastError());
623 return -1;
624 }
625 }
626
627 if (!GetVolumeInformationW(RootDirectory,
628 volumeName, ARRAYSIZE(volumeName),
629 &serialNumber, &maxComponent, &flags,
630 fileSystem, ARRAYSIZE(fileSystem)))
631 {
632 LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
633 PrintWin32Error(szMsg, GetLastError());
634 return -1;
635 }
636
637 //
638 // Print out some stuff including the formatted size
639 //
640 if (!GetDiskFreeSpaceExW(RootDirectory,
641 &freeBytesAvailableToCaller,
642 &totalNumberOfBytes,
643 &totalNumberOfFreeBytes))
644 {
645 LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg));
646 PrintWin32Error(szMsg, GetLastError());
647 return -1;
648 }
649
650 PrintResourceString(STRING_FREE_SPACE, totalNumberOfBytes.QuadPart,
651 totalNumberOfFreeBytes.QuadPart);
652
653 //
654 // Get the drive's serial number
655 //
656 if (!GetVolumeInformationW(RootDirectory,
657 volumeName, ARRAYSIZE(volumeName),
658 &serialNumber, &maxComponent, &flags,
659 fileSystem, ARRAYSIZE(fileSystem)))
660 {
661 LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg));
662 PrintWin32Error(szMsg, GetLastError());
663 return -1;
664 }
665 PrintResourceString(STRING_SERIAL_NUMBER,
666 (unsigned int)(serialNumber >> 16),
667 (unsigned int)(serialNumber & 0xFFFF));
668
669 return 0;
670 }
671
672 /* EOF */