Skip to content
This repository has been archived by the owner on Jul 5, 2023. It is now read-only.

Commit

Permalink
add mkfs command
Browse files Browse the repository at this point in the history
  • Loading branch information
a1ive committed Aug 24, 2022
1 parent 9f6ff51 commit daa649e
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 0 deletions.
1 change: 1 addition & 0 deletions command.c
Expand Up @@ -63,6 +63,7 @@ grub_command_init(void)
grub_command_register(&grub_cmd_echo);
grub_command_register(&grub_cmd_loopback);
grub_command_register(&grub_cmd_fslabel);
grub_command_register(&grub_cmd_mkfs);

grub_command_register(&grub_cmd_help);
}
176 changes: 176 additions & 0 deletions commands/mkfs.c
@@ -0,0 +1,176 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "compat.h"
#include "disk.h"
#include "command.h"
#include "misc.h"

#include <stdio.h>

static ULONG
get_cluster_size(const char* str)
{
const char* p = NULL;
ULONG sz = 0;
ULONG unit = 1;
sz = grub_strtoul(str, &p, 10);
if (*p == 'K' || *p == 'k')
unit = 1024;
else if (*p == 'M' || *p == 'm')
unit = 1024 * 1024;
return sz * unit;
}

static wchar_t*
get_volume_label(const wchar_t* volume)
{
size_t len = wcslen(volume) + 2;
wchar_t* path = calloc(len, sizeof(wchar_t));
wchar_t label[MAX_PATH + 1] = L"\0";
if (!path)
return NULL;
swprintf(path, len, L"%s\\", volume);
GetVolumeInformationW(path, label, MAX_PATH + 1, NULL, NULL, NULL, NULL, 0);
free(path);
return _wcsdup(label);
}

/*
* https://github.com/pbatard/rufus/blob/master/src/format.c
* FormatEx callback. Return FALSE to halt operations
*/
static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
{
(void)Action;
switch (Command)
{
case FCC_PROGRESS:
break;
case FCC_STRUCTURE_PROGRESS: // No progress on quick format
break;
case FCC_DONE:
if (*(BOOLEAN*)pData == FALSE)
grub_error(GRUB_ERR_IO, "error while formatting");
break;
case FCC_DONE_WITH_STRUCTURE:
break;
case FCC_INCOMPATIBLE_FILE_SYSTEM:
grub_error(GRUB_ERR_BAD_FS, "incompatible fs");
break;
case FCC_ACCESS_DENIED:
grub_error(GRUB_ERR_ACCESS_DENIED, "access denied");
break;
case FCC_MEDIA_WRITE_PROTECTED:
grub_error(GRUB_ERR_WRITE_ERROR, "write protected media");
break;
case FCC_VOLUME_IN_USE:
grub_error(GRUB_ERR_WRITE_ERROR, "volume in use");
break;
case FCC_DEVICE_NOT_READY:
grub_error(GRUB_ERR_BAD_DEVICE, "device not ready");
break;
case FCC_CANT_QUICK_FORMAT:
grub_error(GRUB_ERR_IO, "cannot quick format");
break;
case FCC_BAD_LABEL:
grub_error(GRUB_ERR_BAD_ARGUMENT, "bad label");
break;
case FCC_OUTPUT:
//OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
break;
case FCC_CLUSTER_SIZE_TOO_BIG:
case FCC_CLUSTER_SIZE_TOO_SMALL:
grub_error(GRUB_ERR_BAD_ARGUMENT, "unsupported cluster size");
break;
case FCC_VOLUME_TOO_BIG:
case FCC_VOLUME_TOO_SMALL:
grub_error(GRUB_ERR_BAD_ARGUMENT, "volume too %s", (Command == FCC_VOLUME_TOO_BIG) ? "big" : "small");
break;
case FCC_NO_MEDIA_IN_DRIVE:
grub_error(GRUB_ERR_BAD_DEVICE, "no media in drive");
break;
case FCC_ALIGNMENT_VIOLATION:
grub_error(GRUB_ERR_BAD_DEVICE, "partition start offset is not aligned to the cluster size");
break;
default:
grub_error(GRUB_ERR_BAD_ARGUMENT, "FormatExCallback: Received unhandled command 0x%02X - aborting", Command);
break;
}
return (grub_errno ? FALSE : TRUE);
}

static grub_err_t
cmd_mkfs(struct grub_command* cmd, int argc, char* argv[])
{
(void)cmd;
int i;
BOOL ntfs_compression = FALSE;
BOOL quick_format = FALSE;
BOOL keep_label = FALSE;
WCHAR* drive = NULL;
WCHAR* fs = NULL;
WCHAR* label = NULL;
ULONG sz = 0;
for (i = 0; i < argc; i++)
{
if (grub_strncmp(argv[i], "-f=", 3) == 0)
fs = grub_get_utf16(&argv[i][3]);
else if (grub_strncmp(argv[i], "-v=", 3) == 0)
label = grub_get_utf16(&argv[i][3]);
else if (grub_strcmp(argv[i], "-v") == 0)
keep_label = TRUE;
else if (grub_strcmp(argv[i], "-c") == 0)
ntfs_compression = TRUE;
else if (grub_strcmp(argv[i], "-q") == 0)
quick_format = TRUE;
else if (grub_strncmp(argv[i], "-a=", 3) == 0)
sz = get_cluster_size(&argv[i][3]);
else
drive = grub_get_utf16(argv[i]);
}
if (!drive)
{
grub_error(GRUB_ERR_BAD_ARGUMENT, "missing volume path");
goto fail;
}
if (!fs)
{
grub_error(GRUB_ERR_BAD_ARGUMENT, "missing fs type");
goto fail;
}
if (keep_label && !label)
label = get_volume_label(drive);
FmifsFormatEx(drive, fs, label, quick_format, ntfs_compression, sz, FormatExCallback);
fail:
if (drive)
free(drive);
if (fs)
free(fs);
if (label)
free(label);
return grub_errno;
}

static void
help_mkfs(struct grub_command* cmd)
{
grub_printf("%s [OPTIONS] DRIVE_LETTER|MOUNT_POINT\n", cmd->name);
grub_printf("Format a disk.\nOPTIONS:\n");
grub_printf(" -f=TYPE Specify filesystem type (FAT|FAT32|exFAT|NTFS).\n");
grub_printf(" -v[=TEXT] Specify filesystem label.\n");
grub_printf(" -c Enable NTFS compression.\n");
grub_printf(" -q Perform quick format.\n");
grub_printf(" -a=SIZE Override the default allocation unit size.\n");
grub_printf(" NTFS supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K, 128K, 256K, 512K, 1M, 2M.\n");
grub_printf(" FAT supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K.\n");
grub_printf(" FAT32 supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K.\n");
grub_printf(" exFAT supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K, 128K, 256K, 512K, 1M, 2M, 4M, 8M, 16M, 32M.\n");
grub_printf(" NTFS compression is not supported for allocation unit sizes above 4096.\n");
}

struct grub_command grub_cmd_mkfs =
{
.name = "mkfs",
.func = cmd_mkfs,
.help = help_mkfs,
.next = 0,
};
1 change: 1 addition & 0 deletions diskfire.vcxproj
Expand Up @@ -159,6 +159,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)partmap</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)partmap</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="commands\mkfs.c" />
<ClCompile Include="commands\mount.c" />
<ClCompile Include="commands\pbr.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)fs</AdditionalIncludeDirectories>
Expand Down
3 changes: 3 additions & 0 deletions diskfire.vcxproj.filters
Expand Up @@ -430,6 +430,9 @@
<ClCompile Include="commands\fslabel.c">
<Filter>源文件\命令</Filter>
</ClCompile>
<ClCompile Include="commands\mkfs.c">
<Filter>源文件\命令</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
1 change: 1 addition & 0 deletions include/command.h
Expand Up @@ -61,6 +61,7 @@ extern struct grub_command grub_cmd_uuid;
extern struct grub_command grub_cmd_echo;
extern struct grub_command grub_cmd_loopback;
extern struct grub_command grub_cmd_fslabel;
extern struct grub_command grub_cmd_mkfs;

extern struct grub_command grub_cmd_help;

Expand Down
58 changes: 58 additions & 0 deletions include/misc.h
Expand Up @@ -52,4 +52,62 @@ void SetDebug(const char* cond);
extern PHY_DRIVE_INFO* gDriveList;
extern DWORD gDriveCount;

/* Callback command types (some errorcode were filled from HPUSBFW V2.2.3 and their
designation from docs.microsoft.com/windows/win32/api/vds/nf-vds-ivdsvolumemf2-formatex */
typedef enum
{
FCC_PROGRESS,
FCC_DONE_WITH_STRUCTURE,
FCC_UNKNOWN2,
FCC_INCOMPATIBLE_FILE_SYSTEM,
FCC_UNKNOWN4,
FCC_UNKNOWN5,
FCC_ACCESS_DENIED,
FCC_MEDIA_WRITE_PROTECTED,
FCC_VOLUME_IN_USE,
FCC_CANT_QUICK_FORMAT,
FCC_UNKNOWNA,
FCC_DONE,
FCC_BAD_LABEL,
FCC_UNKNOWND,
FCC_OUTPUT,
FCC_STRUCTURE_PROGRESS,
FCC_CLUSTER_SIZE_TOO_SMALL,
FCC_CLUSTER_SIZE_TOO_BIG,
FCC_VOLUME_TOO_SMALL,
FCC_VOLUME_TOO_BIG,
FCC_NO_MEDIA_IN_DRIVE,
FCC_UNKNOWN15,
FCC_UNKNOWN16,
FCC_UNKNOWN17,
FCC_DEVICE_NOT_READY,
FCC_CHECKDISK_PROGRESS,
FCC_UNKNOWN1A,
FCC_UNKNOWN1B,
FCC_UNKNOWN1C,
FCC_UNKNOWN1D,
FCC_UNKNOWN1E,
FCC_UNKNOWN1F,
FCC_READ_ONLY_MODE,
FCC_UNKNOWN21,
FCC_UNKNOWN22,
FCC_UNKNOWN23,
FCC_UNKNOWN24,
FCC_ALIGNMENT_VIOLATION,
} FILE_SYSTEM_CALLBACK_COMMAND;

typedef struct
{
DWORD Lines;
CHAR* Output;
} TEXTOUTPUT, * PTEXTOUTPUT;

typedef BOOLEAN(__stdcall* FILE_SYSTEM_CALLBACK)(FILE_SYSTEM_CALLBACK_COMMAND Command, ULONG Action, PVOID pData);

BOOL
FmifsFormatEx(CONST WCHAR* DriveRoot, WCHAR* FileSystemTypeName,
WCHAR* Label, BOOL QuickFormat, BOOL EnableCompression,
ULONG DesiredUnitAllocationSize,
FILE_SYSTEM_CALLBACK Callback);

#endif
104 changes: 104 additions & 0 deletions misc.c
Expand Up @@ -4,6 +4,7 @@
#include <windows.h>
#include <process.h>
#include <tlhelp32.h>
#include <locale.h>
#include "misc.h"
#include "compat.h"

Expand Down Expand Up @@ -417,3 +418,106 @@ KillProcessById(DWORD dwProcessId, UINT uExitCode)
TerminateProcess(hProcess, uExitCode);
CloseHandle(hProcess);
}

/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383357.aspx */
typedef enum
{
FPF_COMPRESSED = 0x01
} FILE_SYSTEM_PROP_FLAG;

static MEDIA_TYPE
GetMediaType(CONST WCHAR* DriveRoot)
{
HANDLE hDrive = INVALID_HANDLE_VALUE;
DWORD dwReturn = sizeof(DISK_GEOMETRY);
DISK_GEOMETRY diskGeometry = { 0 };
WCHAR* wDrive = NULL;
size_t wDriveLen = wcslen(DriveRoot) + 5;
wDrive = calloc(wDriveLen, sizeof(WCHAR));
if (!wDrive)
return Unknown;
swprintf(wDrive, wDriveLen, L"\\\\.\\%s", DriveRoot);
hDrive = CreateFileW(wDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (hDrive == INVALID_HANDLE_VALUE)
goto fail;
DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &diskGeometry, dwReturn, &dwReturn, NULL);
fail:
if (hDrive != INVALID_HANDLE_VALUE)
CloseHandle(hDrive);
free(wDrive);
return diskGeometry.MediaType;
}

BOOL
FmifsFormatEx(CONST WCHAR* DriveRoot, WCHAR* FileSystemTypeName,
WCHAR* Label, BOOL QuickFormat, BOOL EnableCompression,
ULONG DesiredUnitAllocationSize,
FILE_SYSTEM_CALLBACK Callback)
{
BOOL bRet = FALSE;
WCHAR* wDrive = NULL;
size_t szDriveLen = wcslen(DriveRoot);
VOID(WINAPI * NtFormatEx)(WCHAR * DriveRoot,
MEDIA_TYPE MediaType, WCHAR * FileSystemTypeName,
WCHAR * Label, BOOL QuickFormat, ULONG DesiredUnitAllocationSize,
FILE_SYSTEM_CALLBACK Callback) = NULL;
BOOLEAN(WINAPI * NtEnableVolumeCompression)(WCHAR * DriveRoot, ULONG CompressionFlags) = NULL;
// LoadLibrary("fmifs.dll") appears to changes the locale, which can lead to
// problems with tolower(). Make sure we restore the locale. For more details,
// see https://sourceforge.net/p/mingw/mailman/message/29269040/
char* locale = setlocale(LC_ALL, NULL);
HMODULE hL = LoadLibraryW(L"fmifs.dll");
setlocale(LC_ALL, locale);
if (hL)
{
*(FARPROC*)&NtFormatEx = GetProcAddress(hL, "FormatEx");
*(FARPROC*)&NtEnableVolumeCompression = GetProcAddress(hL, "EnableVolumeCompression");
}
if (!NtFormatEx || !NtEnableVolumeCompression)
{
grub_error(GRUB_ERR_ACCESS_DENIED, "fmifs.dll load error");
return FALSE;
}
wDrive = calloc(szDriveLen + 3, sizeof(WCHAR));
if (!wDrive)
{
grub_error(GRUB_ERR_OUT_OF_MEMORY, "out of memory");
return FALSE;
}
if (szDriveLen < 2)
{
/* 'X' -> 'X:' */
swprintf(wDrive, szDriveLen + 3, L"%c:", DriveRoot[0]);
szDriveLen += 1;
}
else if (DriveRoot[szDriveLen - 1] == L'\\')
{
/* 'X:\' -> 'X:' */
wcsncpy_s(wDrive, szDriveLen + 3, DriveRoot, szDriveLen);
wDrive[szDriveLen - 1] = L'\0';
szDriveLen -= 1;
}
else
{
wcsncpy_s(wDrive, szDriveLen + 3, DriveRoot, szDriveLen + 1);
}

if (DesiredUnitAllocationSize < 0x200)
{
// 0 is FormatEx's value for default, which we need to use for UDF
DesiredUnitAllocationSize = 0;
}

NtFormatEx(wDrive, GetMediaType(wDrive), FileSystemTypeName, Label, QuickFormat, DesiredUnitAllocationSize, Callback);
if (EnableCompression)
{
wDrive[szDriveLen] = L'\\';
wDrive[szDriveLen + 1] = L'\0';
if (NtEnableVolumeCompression(wDrive, FPF_COMPRESSED))
bRet = TRUE;
else
grub_error(GRUB_ERR_BAD_DEVICE, "cannot enable NTFS compression");
}
free(wDrive);
return bRet;
}

0 comments on commit daa649e

Please sign in to comment.