| 1 | /*++ |
| 2 | |
| 3 | Copyright (c) Microsoft. All rights reserved. |
| 4 | |
| 5 | Module Name: |
| 6 | |
| 7 | filesystem.hpp |
| 8 | |
| 9 | Abstract: |
| 10 | |
| 11 | This file contains file system function declarations. |
| 12 | |
| 13 | --*/ |
| 14 | |
| 15 | #pragma once |
| 16 | |
| 17 | #include "wslservice.h" |
| 18 | |
| 19 | #define LXSS_FS_TYPE_DRVFS "drvfs" |
| 20 | #define LXSS_FS_TYPE_LXFS "lxfs" |
| 21 | #define LXSS_FS_TYPE_SHAREFS "sharefs" |
| 22 | #define LXSS_FS_TYPE_TMPFS "tmpfs" |
| 23 | #define LXSS_FS_TYPE_WSLFS "wslfs" |
| 24 | |
| 25 | namespace wsl::windows::common::filesystem { |
| 26 | |
| 27 | enum class TempFileFlags |
| 28 | { |
| 29 | None = 0x0, |
| 30 | DeleteOnClose = 0x1, |
| 31 | InheritHandle = 0x2 |
| 32 | }; |
| 33 | |
| 34 | DEFINE_ENUM_FLAG_OPERATORS(TempFileFlags); |
| 35 | |
| 36 | // Used only in unit tests. |
| 37 | constexpr ULONG c_case_sensitive_folders_only = 0x100; |
| 38 | |
| 39 | // Make sure that the above flag doesn't conflict with create instance flags |
| 40 | static_assert((LXSS_CREATE_INSTANCE_FLAGS_ALL & c_case_sensitive_folders_only) == 0); |
| 41 | |
| 42 | struct TempFile |
| 43 | { |
| 44 | std::filesystem::path Path; |
| 45 | wil::unique_hfile Handle; |
| 46 | TempFileFlags Flags = TempFileFlags::None; |
| 47 | |
| 48 | TempFile( |
| 49 | _In_ DWORD DesiredAccess, |
| 50 | _In_ DWORD ShareMode, |
| 51 | _In_ DWORD CreationDisposition, |
| 52 | _In_ TempFileFlags Flags = TempFileFlags::None, |
| 53 | _In_opt_ std::wstring_view Extension = {}); |
| 54 | |
| 55 | ~TempFile(); |
| 56 | |
| 57 | TempFile(const TempFile&) = delete; |
| 58 | TempFile& operator=(const TempFile&) = delete; |
| 59 | |
| 60 | TempFile(TempFile&& other) noexcept |
| 61 | { |
| 62 | *this = std::move(other); |
| 63 | } |
| 64 | |
| 65 | TempFile& operator=(TempFile&& other) noexcept |
| 66 | { |
| 67 | std::swap(Path, other.Path); |
| 68 | std::swap(Handle, other.Handle); |
| 69 | std::swap(Flags, other.Flags); |
| 70 | return *this; |
| 71 | } |
| 72 | }; |
| 73 | |
| 74 | inline void FreeLXSS_ADDMOUNT(_Inout_opt_ PLX_KMAPPATHS_ADDMOUNT pMount) |
| 75 | { |
| 76 | if (pMount) |
| 77 | { |
| 78 | if (pMount->Source) |
| 79 | { |
| 80 | CoTaskMemFree((LPVOID)pMount->Source); |
| 81 | } |
| 82 | |
| 83 | if (pMount->Target) |
| 84 | { |
| 85 | CoTaskMemFree((LPVOID)pMount->Target); |
| 86 | } |
| 87 | |
| 88 | if (pMount->FsType) |
| 89 | { |
| 90 | CoTaskMemFree((LPVOID)pMount->FsType); |
| 91 | } |
| 92 | |
| 93 | if (pMount->WindowsDataRoot && (pMount->WindowsDataRoot != INVALID_HANDLE_VALUE)) |
| 94 | { |
| 95 | CloseHandle(pMount->WindowsDataRoot); |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | using unique_lxss_addmount = wil::unique_struct<LX_KMAPPATHS_ADDMOUNT, decltype(&FreeLXSS_ADDMOUNT), FreeLXSS_ADDMOUNT>; |
| 101 | |
| 102 | /// <summary> |
| 103 | /// Creates a mount for instance creation. |
| 104 | /// </summary> |
| 105 | unique_lxss_addmount CreateMount( |
| 106 | _In_ PCWSTR NtPath, _In_ PCWSTR Source, _In_opt_ LPCSTR Target, _In_ LPCSTR FsType, _In_ ULONG Mode, _In_ bool forWrite = true); |
| 107 | |
| 108 | /// <summary> |
| 109 | /// Creates a directory for the root file system. |
| 110 | /// </summary> |
| 111 | void CreateRootFs(_In_ PCWSTR Path, _In_ ULONG Version); |
| 112 | |
| 113 | void DeviceIoControl(_In_ HANDLE handle, _In_ ULONG code, _In_ gsl::span<const gsl::byte> input = {}); |
| 114 | |
| 115 | NTSTATUS |
| 116 | DeviceIoControlNoThrow(_In_ HANDLE handle, _In_ ULONG code, _In_ gsl::span<const gsl::byte> input = {}); |
| 117 | |
| 118 | std::pair<ULONG, ULONG> EnumerateFixedDrives(HANDLE Token = nullptr); |
| 119 | |
| 120 | /// <summary> |
| 121 | /// Creates a directory with the given path if it does not exist. Throws if creating the directory |
| 122 | /// failed. |
| 123 | /// </summary> |
| 124 | bool EnsureDirectory(_In_ LPCWSTR pPath); |
| 125 | |
| 126 | /// <summary> |
| 127 | /// Marks every directory in a tree case-sensitive. |
| 128 | /// </summary> |
| 129 | void EnsureCaseSensitiveDirectory(_In_ PCWSTR Path, _In_ ULONG Flags); |
| 130 | |
| 131 | /// <summary> |
| 132 | /// Creates a directory with the given path if it does not exist, and applies |
| 133 | /// the specified attributes if the directory doesn't have any. Throws if |
| 134 | /// creating the directory or applying the attributes failed. |
| 135 | /// </summary> |
| 136 | void EnsureDirectoryWithAttributes(_In_ PCWSTR Path, _In_ ULONG Mode, _In_ ULONG Uid, _In_ ULONG Gid, _In_ ULONG Flags, _In_ ULONG DistroVersion); |
| 137 | |
| 138 | bool FileExists(_In_ LPCWSTR Path); |
| 139 | |
| 140 | /// <summary> |
| 141 | /// Resolves Path to an absolute, canonical form. The path is made absolute against the current |
| 142 | /// directory first because std::filesystem::weakly_canonical does not reliably resolve a relative |
| 143 | /// path on its own. '..' components are collapsed and symlinks are resolved for the portion of the |
| 144 | /// path that exists, so a path naming a file that does not exist yet still succeeds. |
| 145 | /// Throws on failure. |
| 146 | /// </summary> |
| 147 | std::filesystem::path GetCanonicalPath(const std::filesystem::path& Path); |
| 148 | |
| 149 | /// <summary> |
| 150 | /// Non-throwing overload of GetCanonicalPath. On failure Error is set and an empty path is |
| 151 | /// returned; on success Error is cleared. |
| 152 | /// </summary> |
| 153 | std::filesystem::path GetCanonicalPath(const std::filesystem::path& Path, std::error_code& Error); |
| 154 | |
| 155 | std::filesystem::path GetFullPath(_In_ LPCWSTR Path); |
| 156 | |
| 157 | std::pair<std::string, std::string> GetHostAndDomainNames(); |
| 158 | |
| 159 | std::string GetLinuxHostName(); |
| 160 | |
| 161 | /// <summary> |
| 162 | /// Gets the base path for legacy installs. |
| 163 | /// </summary> |
| 164 | std::filesystem::path GetLegacyBasePath(_In_ HANDLE UserToken); |
| 165 | |
| 166 | std::filesystem::path GetLocalAppDataPath(_In_ HANDLE userToken); |
| 167 | |
| 168 | std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id, DWORD flags, HANDLE token = nullptr); |
| 169 | |
| 170 | std::filesystem::path GetTempFilename(); |
| 171 | |
| 172 | std::filesystem::path GetTempFolderPath(_In_ HANDLE userToken); |
| 173 | |
| 174 | std::string GetWindowsHosts(const std::filesystem::path& Path); |
| 175 | |
| 176 | /// <summary> |
| 177 | /// Opens a directory handle with read/execute, optionally also write, & full sharing. The path |
| 178 | /// must exist and be a directory. Throws if the directory cannot be opened. |
| 179 | /// </summary> |
| 180 | wil::unique_hfile OpenDirectoryHandle(_In_ LPCWSTR pPath, _In_ bool forWrite); |
| 181 | |
| 182 | /// <summary> |
| 183 | /// Opens a directory handle with read/execute, optionally also write, & full sharing. The path |
| 184 | /// must exist and be a directory. |
| 185 | /// </summary> |
| 186 | wil::unique_hfile OpenDirectoryHandleNoThrow(_In_ LPCWSTR pPath, _In_ bool forWrite); |
| 187 | |
| 188 | /// <summary> |
| 189 | /// Opens the null device. |
| 190 | /// </summary> |
| 191 | wil::unique_hfile OpenNulDevice(_In_ DWORD DesiredAccess); |
| 192 | |
| 193 | wil::unique_hfile OpenRelativeFile( |
| 194 | _In_opt_ HANDLE Parent, |
| 195 | _In_ PUNICODE_STRING RelativePath, |
| 196 | _In_ ACCESS_MASK DesiredAccess, |
| 197 | _In_ ULONG Disposition, |
| 198 | _In_ ULONG CreateOptions, |
| 199 | _In_opt_ PVOID EaBuffer = nullptr, |
| 200 | _In_ ULONG EaSize = 0); |
| 201 | |
| 202 | std::pair<NTSTATUS, wil::unique_hfile> OpenRelativeFileNoThrow( |
| 203 | _In_opt_ HANDLE Parent, |
| 204 | _In_ PUNICODE_STRING RelativePath, |
| 205 | _In_ ACCESS_MASK DesiredAccess, |
| 206 | _In_ ULONG Disposition, |
| 207 | _In_ ULONG CreateOptions, |
| 208 | _In_opt_ PVOID EaBuffer = nullptr, |
| 209 | _In_ ULONG EaSize = 0); |
| 210 | |
| 211 | wil::unique_hfile ReopenFile(_In_ HANDLE Handle, _In_ ACCESS_MASK DesiredAccess, _In_ ULONG CreateOptions); |
| 212 | |
| 213 | void QueryInformationFile(_In_ HANDLE Handle, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass); |
| 214 | |
| 215 | template <typename T> |
| 216 | void QueryInformationFile(_In_ HANDLE Handle, _Out_ T& Buffer, _In_ FILE_INFORMATION_CLASS FileInformationClass) |
| 217 | { |
| 218 | QueryInformationFile(Handle, &Buffer, sizeof(Buffer), FileInformationClass); |
| 219 | } |
| 220 | |
| 221 | VOID QuerySingleEaFile(_In_ HANDLE Handle, _Out_ PIO_STATUS_BLOCK IoStatus, _In_ std::string_view EaName, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length); |
| 222 | |
| 223 | std::vector<CHAR> QuerySingleEaFile(_In_ HANDLE Handle, _In_ std::string_view EaName); |
| 224 | |
| 225 | NTSTATUS |
| 226 | QuerySingleEaFileNoThrow( |
| 227 | _In_ HANDLE Handle, _Out_ PIO_STATUS_BLOCK IoStatus, _In_ std::string_view EaName, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length); |
| 228 | |
| 229 | void SetInformationFile(_In_ HANDLE Handle, _In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass); |
| 230 | |
| 231 | template <typename T> |
| 232 | void SetInformationFile(_In_ HANDLE Handle, _In_ T& Buffer, _In_ FILE_INFORMATION_CLASS FileInformationClass) |
| 233 | { |
| 234 | SetInformationFile(Handle, &Buffer, sizeof(Buffer), FileInformationClass); |
| 235 | } |
| 236 | |
| 237 | std::optional<std::filesystem::path> TryGetPathFromFileUrl(const std::wstring& Url); |
| 238 | |
| 239 | std::wstring UnquotePath(_In_ LPCWSTR Path); |
| 240 | |
| 241 | /// <summary> |
| 242 | /// Updates the init binary. |
| 243 | /// </summary> |
| 244 | void UpdateInit(_In_ PCWSTR BasePath, _In_ ULONG DistroVersion); |
| 245 | |
| 246 | /// <summary> |
| 247 | /// Wipes out the directory with the given path if it exists, then creates it again and returns |
| 248 | /// an open directory handle onto it. |
| 249 | /// </summary> |
| 250 | wil::unique_hfile WipeAndOpenDirectory(_In_ LPCWSTR pPath); |
| 251 | |
| 252 | } // namespace wsl::windows::common::filesystem |