Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Expand Down Expand Up @@ -436,6 +436,13 @@ Arguments for managing Windows Subsystem for Linux:
--from-file &lt;Path&gt;
Install a distribution from a local file.

--fs-type &lt;FsType&gt;
Specify the filesystem type to use for the distribution root.
Defaults to ext4.

--fs-mount-options &lt;Options&gt;
Specify additional mount options for the filesystem.

--legacy
Use the legacy distribution manifest.

Expand Down Expand Up @@ -476,6 +483,9 @@ Arguments for managing Windows Subsystem for Linux:
--resize &lt;MemoryString&gt;
Resize the disk of the distribution to the specified size.

--set-fs-mount-options &lt;Options&gt;
Set the filesystem mount options for the distribution root.

--mount &lt;Disk&gt;
Attaches and mounts a physical or virtual disk in all WSL 2 distributions.

Expand Down Expand Up @@ -551,6 +561,13 @@ Arguments for managing distributions in Windows Subsystem for Linux:
Specifies that the provided file is a .vhd or .vhdx file, not a tar file.
This operation makes a copy of the VHD file at the specified install location.

--fs-type &lt;FsType&gt;
Specify the filesystem type to use for the distribution root.
Defaults to ext4.

--fs-mount-options &lt;Options&gt;
Specify additional mount options for the filesystem.

--import-in-place &lt;Distro&gt; &lt;FileName&gt;
Imports the specified VHD file as a new distribution.
This virtual hard disk must be formatted with the ext4 filesystem type.
Expand Down Expand Up @@ -591,10 +608,10 @@ Arguments for managing distributions in Windows Subsystem for Linux:
"}{Locked="--debug-shell
"}{Locked="--install "}{Locked="--list "}{Locked="--online'"}{Locked="--enable-wsl1
"}{Locked="--fixed-vhd
"}{Locked="--from-file "}{Locked="--legacy
"}{Locked="--from-file "}{Locked="--fs-type "}{Locked="--fs-mount-options "}{Locked="--legacy
"}{Locked="--location "}{Locked="--name "}{Locked="--no-distribution
"}{Locked="--no-launch,"}{Locked="--version "}{Locked="--vhd-size "}{Locked="--web-download
"}{Locked="--manage "}{Locked="--move "}{Locked="--set-sparse,"}{Locked="--set-default-user "}{Locked="--resize "}{Locked="--mount "}{Locked="--vhd
"}{Locked="--manage "}{Locked="--move "}{Locked="--set-sparse,"}{Locked="--set-default-user "}{Locked="--resize "}{Locked="--set-fs-mount-options "}{Locked="--mount "}{Locked="--vhd
"}{Locked="--bare
"}{Locked="--name "}{Locked="--type "}{Locked="--options "}{Locked="--partition "}{Locked="--set-default-version "}{Locked="--shutdown
"}{Locked="--force
Expand Down Expand Up @@ -1039,8 +1056,8 @@ Falling back to NAT networking.</value>
<value>The distribution failed to start. Error code: {}, failure step: {}</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageDiskCorrupted" xml:space="preserve">
<value>The distribution failed to start because its virtual disk is corrupted.</value>
<data name="MessageMountFailed" xml:space="preserve">
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are auto generated, right?

<value>The distribution failed to start because its virtual disk is corrupted, has an unexpected filesystem type, or has invalid mount options.</value>
</data>
<data name="MessageConfigKeyDuplicated" xml:space="preserve">
<value>Duplicated config key '{}' in {}:{} (Conflicting key: '{}' in {}:{})</value>
Expand Down
132 changes: 124 additions & 8 deletions src/linux/init/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ Module Name:
#include "SocketChannel.h"

#define BSDTAR_PATH "/usr/bin/bsdtar"
#define BTRFS_CREATE_ARG "create"
#define BTRFS_PATH "/usr/sbin/btrfs"
#define BTRFS_SUBVOLUME_ARG "subvolume"
#define BINFMT_REGISTER_STRING ":" LX_INIT_BINFMT_NAME ":M::MZ::" LX_INIT_PATH ":FP\n"
#define BINFMT_PATH PROCFS_PATH "/sys/fs/binfmt_misc"
#define CHRONY_CONF_PATH ETC_PATH "/chrony.conf"
Expand Down Expand Up @@ -136,7 +139,8 @@ int EnableInterface(int Socket, const char* Name);

int ExportToSocket(const char* Source, int Socket, int ErrorSocket, unsigned int flags);

int FormatDevice(unsigned int Lun);
int FormatDevice(unsigned int Lun, const char* FsType);
int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions);

std::string GetLunDeviceName(unsigned int Lun);

Expand Down Expand Up @@ -901,19 +905,23 @@ Return Value:
return Result;
}

int FormatDevice(unsigned int Lun)
int FormatDevice(unsigned int Lun, const char* FsType)

/*++

Routine Description:

This routine formats the specified SCSI device with the ext4 file system.
N.B. The group size was chosen based on the best practices for Linux VHDs:
This routine formats the specified SCSI device with the <FsType> file system.

N.B. The ext4 group size was chosen based on the best practices for Linux VHDs:
https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v

N.B. The xfs data section options (-d) are also determined based on the VHD sector size of 1MB, as suggested in the link above.

Arguments:

Lun - Supplies the LUN number of the SCSI device.
FsType - The filesystem type to format the device with.

Return Value:

Expand All @@ -927,7 +935,30 @@ try

WaitForBlockDevice(DevicePath.c_str());

std::string CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath);
if (FsType == nullptr)
{
FsType = "ext4";
}

std::string CommandLine;
if (strcmp(FsType, "ext4") == 0)
{
CommandLine = std::format("/usr/sbin/mkfs.ext4 -G 4096 '{}'", DevicePath);
}
else if (strcmp(FsType, "btrfs") == 0)
{
CommandLine = std::format("/usr/sbin/mkfs.btrfs '{}'", DevicePath);
}
else if (strcmp(FsType, "xfs") == 0)
{
CommandLine = std::format("/usr/sbin/mkfs.xfs -s size=4096 -d su=1m,sw=1 '{}'", DevicePath);
}
else
{
LOG_ERROR("Unsupported filesystem type: {}", FsType);
errno = ENOSYS;
return -1;
}
if (UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0)
{
return -1;
Expand All @@ -937,6 +968,84 @@ try
}
CATCH_RETURN_ERRNO()

int CreateBtrfsSubvolumeOnDevice(unsigned int Lun, const char* MountOptions)
/*++
This routine creates a btrfs subvolume on the specified SCSI device.
It is a no-op if there is no subvolume name in the mount options or
the subvolume already exists.

Arguments:

Lun - Supplies the LUN number of the SCSI device.
MountOptions - The mount options to use when mounting the device.
The subvolume name is extracted from these options.

Return Value:

0 on success, < 0 on failure.
--*/
try
{
if (MountOptions == nullptr)
{
return 0;
}

std::string_view options(MountOptions);
std::string_view subvolName;

size_t pos = 0;
while (pos < options.size())
{
size_t end = options.find(',', pos);
if (end == std::string_view::npos)
{
end = options.size();
}

std::string_view option = options.substr(pos, end - pos);
if (option.starts_with("subvol="))
{
subvolName = option.substr(7); // Skip "subvol="
break;
}

pos = end + 1;
}

if (subvolName.empty())
{
return 0;
}

std::string DevicePath = GetLunDevicePath(Lun);
std::string TempMountPath;
THROW_LAST_ERROR_IF(CreateTempDirectory("/tmp", TempMountPath) < 0);

auto unmountOnExit = wil::scope_exit([&TempMountPath]() {
umount(TempMountPath.c_str());
rmdir(TempMountPath.c_str());
});

THROW_LAST_ERROR_IF(UtilMount(DevicePath.c_str(), TempMountPath.c_str(), "btrfs", 0, nullptr, c_defaultRetryTimeout) < 0);

std::string SubvolPath = std::format("{}/{}", TempMountPath, subvolName);

struct stat st;
if (stat(SubvolPath.c_str(), &st) == 0)
{
return 0;
}

THROW_LAST_ERROR_IF(errno != ENOENT);

const char* Argv[] = {BTRFS_PATH, BTRFS_SUBVOLUME_ARG, BTRFS_CREATE_ARG, SubvolPath.c_str(), nullptr};
THROW_LAST_ERROR_IF(UtilCreateProcessAndWait(Argv[0], Argv) < 0);

return 0;
}
CATCH_RETURN_ERRNO()

std::string GetLunDeviceName(unsigned int Lun)

/*++
Expand Down Expand Up @@ -2738,13 +2847,20 @@ void ProcessImportExportMessage(gsl::span<gsl::byte> Buffer, wsl::shared::Socket
ListenSocket = UtilListenVsockAnyPort(&ListenAddress, 2, true);
THROW_LAST_ERROR_IF(!ListenSocket);

auto* FsType = wsl::shared::string::FromSpan(Buffer, Message->FsTypeOffset);
auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->MountOptionsOffset);

if (Message->Header.MessageType == LxMiniInitMessageImport)
{
THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId) < 0);
THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId, FsType) < 0);

if (FsType != nullptr && strcmp(FsType, "btrfs") == 0)
{
// create the subvolume if specified in mount options
THROW_LAST_ERROR_IF(CreateBtrfsSubvolumeOnDevice(Message->DeviceId, MountOptions) < 0);
}
}

auto* FsType = wsl::shared::string::FromSpan(Buffer, Message->FsTypeOffset);
auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->MountOptionsOffset);
THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0);

Result = 0;
Expand Down
32 changes: 28 additions & 4 deletions src/windows/common/WslClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,16 @@ int ImportDistribution(_In_ std::wstring_view commandLine)
std::filesystem::path filePath;
ULONG flags = LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE;
DWORD version = LXSS_WSL_VERSION_DEFAULT;
std::optional<std::wstring> fsType;
std::optional<std::wstring> fsMountOptions;

parser.AddPositionalArgument(name, 0);
parser.AddPositionalArgument(AbsolutePath(installPath), 1);
parser.AddPositionalArgument(filePath, 2);
parser.AddArgument(WslVersion(version), WSL_IMPORT_ARG_VERSION);
parser.AddArgument(SetFlag<ULONG, LXSS_IMPORT_DISTRO_FLAGS_VHD>{flags}, WSL_IMPORT_ARG_VHD);
parser.AddArgument(fsType, WSL_IMPORT_ARG_FS_TYPE);
parser.AddArgument(fsMountOptions, WSL_IMPORT_ARG_FS_MOUNT_OPTIONS);

parser.Parse();

Expand Down Expand Up @@ -385,7 +389,15 @@ int ImportDistribution(_In_ std::wstring_view commandLine)
{
wsl::windows::common::HandleConsoleProgressBar progressBar(fileHandle, Localization::MessageImportProgress());
wsl::windows::common::SvcComm service;
service.RegisterDistribution(name, version, fileHandle, installPath->c_str(), flags);
service.RegisterDistribution(
name,
version,
fileHandle,
installPath->c_str(),
flags,
std::nullopt,
fsType.has_value() ? fsType->c_str() : nullptr,
fsMountOptions.has_value() ? fsMountOptions->c_str() : nullptr);
}

directory_cleanup.release();
Expand Down Expand Up @@ -458,6 +470,8 @@ int Install(_In_ std::wstring_view commandLine)
bool noDistribution = false;
bool legacy = false;
bool webDownload = IsWindowsServer();
std::optional<std::wstring> fsType;
std::optional<std::wstring> fsMountOptions;

ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);
parser.AddPositionalArgument(distroArgument, 0);
Expand All @@ -475,6 +489,8 @@ int Install(_In_ std::wstring_view commandLine)
parser.AddArgument(g_promptBeforeExit, WSL_INSTALL_ARG_PROMPT_BEFORE_EXIT_OPTION);
parser.AddArgument(SizeString(vhdSize), WSL_INSTALL_ARG_VHD_SIZE);
parser.AddArgument(fixedVhd, WSL_INSTALL_ARG_FIXED_VHD);
parser.AddArgument(fsType, WSL_INSTALL_ARG_FS_TYPE);
parser.AddArgument(fsMountOptions, WSL_INSTALL_ARG_FS_MOUNT_OPTIONS);

parser.Parse();

Expand Down Expand Up @@ -531,7 +547,9 @@ int Install(_In_ std::wstring_view commandLine)
file,
location.has_value() ? location->c_str() : nullptr,
fixedVhd ? LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD : 0,
vhdSize);
vhdSize,
fsType.has_value() ? fsType->c_str() : nullptr,
fsMountOptions.has_value() ? fsMountOptions->c_str() : nullptr);

wsl::windows::common::wslutil::PrintMessage(Localization::MessageDistributionInstalled(installedName.get()), stdout);

Expand All @@ -557,7 +575,7 @@ int Install(_In_ std::wstring_view commandLine)
if (!noDistribution && (legacy || !rebootRequired))
{
auto result = WslInstall::InstallDistribution(
installResult, distroArgument, version, !noLaunchAfterInstall, webDownload, legacy, fixedVhd, name, location, vhdSize);
installResult, distroArgument, version, !noLaunchAfterInstall, webDownload, legacy, fixedVhd, name, location, vhdSize, fsType, fsMountOptions);

std::optional<std::wstring> flavor;
if (installResult.Distribution.has_value())
Expand Down Expand Up @@ -891,6 +909,7 @@ int Manage(_In_ std::wstring_view commandLine)
std::optional<std::wstring> move;
std::optional<std::wstring> defaultUser;
std::optional<uint64_t> resize;
std::optional<std::wstring> fsMountOptions;
bool allowUnsafe = false;

ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME, 0);
Expand All @@ -899,6 +918,7 @@ int Manage(_In_ std::wstring_view commandLine)
parser.AddArgument(AbsolutePath(move), WSL_MANAGE_ARG_MOVE_OPTION_LONG, WSL_MANAGE_ARG_MOVE_OPTION);
parser.AddArgument(defaultUser, WSL_MANAGE_ARG_SET_DEFAULT_USER_OPTION_LONG);
parser.AddArgument(SizeString(resize), WSL_MANAGE_ARG_RESIZE_OPTION_LONG, WSL_MANAGE_ARG_RESIZE_OPTION);
parser.AddArgument(fsMountOptions, WSL_MANAGE_ARG_SET_FS_MOUNT_OPTIONS_LONG);
parser.AddArgument(allowUnsafe, WSL_MANAGE_ARG_ALLOW_UNSAFE);
parser.Parse();

Expand All @@ -907,7 +927,7 @@ int Manage(_In_ std::wstring_view commandLine)
wsl::windows::common::SvcComm service;
auto distroGuid = service.GetDistributionId(distribution);

if (sparse.has_value() + move.has_value() + defaultUser.has_value() + resize.has_value() != 1)
if (sparse.has_value() + move.has_value() + defaultUser.has_value() + resize.has_value() + fsMountOptions.has_value() != 1)
{
THROW_HR(WSL_E_INVALID_USAGE);
}
Expand Down Expand Up @@ -954,6 +974,10 @@ int Manage(_In_ std::wstring_view commandLine)
{
THROW_IF_FAILED(service.ResizeDistribution(&distroGuid, resize.value()));
}
else if (fsMountOptions)
{
service.SetFsMountOptions(&distroGuid, fsMountOptions->c_str());
}

wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);
return 0;
Expand Down
Loading
Loading