capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

filesystem-disk-win32.c++ (59218B)


      1 // Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #if _WIN32
     23 // For Unix implementation, see filesystem-disk-unix.c++.
     24 
     25 // Request Vista-level APIs.
     26 #include "win32-api-version.h"
     27 
     28 #include "filesystem.h"
     29 #include "debug.h"
     30 #include "encoding.h"
     31 #include "vector.h"
     32 #include <algorithm>
     33 #include <wchar.h>
     34 
     35 #include <windows.h>
     36 #include <winioctl.h>
     37 #include "windows-sanity.h"
     38 
     39 namespace kj {
     40 
     41 static Own<ReadableDirectory> newDiskReadableDirectory(AutoCloseHandle fd, Path&& path);
     42 static Own<Directory> newDiskDirectory(AutoCloseHandle fd, Path&& path);
     43 
     44 static AutoCloseHandle* getHandlePointerHack(File& file) { return nullptr; }
     45 static AutoCloseHandle* getHandlePointerHack(Directory& dir);
     46 static Path* getPathPointerHack(File& file) { return nullptr; }
     47 static Path* getPathPointerHack(Directory& dir);
     48 
     49 namespace {
     50 
     51 struct REPARSE_DATA_BUFFER {
     52   // From ntifs.h, which is part of the driver development kit so not necessarily available I
     53   // guess.
     54   ULONG ReparseTag;
     55   USHORT ReparseDataLength;
     56   USHORT Reserved;
     57   union {
     58     struct {
     59       USHORT SubstituteNameOffset;
     60       USHORT SubstituteNameLength;
     61       USHORT PrintNameOffset;
     62       USHORT PrintNameLength;
     63       ULONG Flags;
     64       WCHAR PathBuffer[1];
     65     } SymbolicLinkReparseBuffer;
     66     struct {
     67       USHORT SubstituteNameOffset;
     68       USHORT SubstituteNameLength;
     69       USHORT PrintNameOffset;
     70       USHORT PrintNameLength;
     71       WCHAR PathBuffer[1];
     72     } MountPointReparseBuffer;
     73     struct {
     74       UCHAR DataBuffer[1];
     75     } GenericReparseBuffer;
     76   };
     77 };
     78 
     79 #define HIDDEN_PREFIX ".kj-tmp."
     80 // Prefix for temp files which should be hidden when listing a directory.
     81 //
     82 // If you change this, make sure to update the unit test.
     83 
     84 static constexpr int64_t WIN32_EPOCH_OFFSET = 116444736000000000ull;
     85 // Number of 100ns intervals from Jan 1, 1601 to Jan 1, 1970.
     86 
     87 static Date toKjDate(FILETIME t) {
     88   int64_t value = (static_cast<uint64_t>(t.dwHighDateTime) << 32) | t.dwLowDateTime;
     89   return (value - WIN32_EPOCH_OFFSET) * (100 * kj::NANOSECONDS) + UNIX_EPOCH;
     90 }
     91 
     92 static FsNode::Type modeToType(DWORD attrs, DWORD reparseTag) {
     93   if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) &&
     94       reparseTag == IO_REPARSE_TAG_SYMLINK) {
     95     return FsNode::Type::SYMLINK;
     96   }
     97   if (attrs & FILE_ATTRIBUTE_DIRECTORY) return FsNode::Type::DIRECTORY;
     98   return FsNode::Type::FILE;
     99 }
    100 
    101 static FsNode::Metadata statToMetadata(const BY_HANDLE_FILE_INFORMATION& stats) {
    102   uint64_t size = (implicitCast<uint64_t>(stats.nFileSizeHigh) << 32) | stats.nFileSizeLow;
    103 
    104   // Assume file index is usually a small number, i.e. nFileIndexHigh is usually 0. So we try to
    105   // put the serial number in the upper 32 bits and the index in the lower.
    106   uint64_t hash = ((uint64_t(stats.dwVolumeSerialNumber) << 32)
    107                  ^ (uint64_t(stats.nFileIndexHigh) << 32))
    108                 | (uint64_t(stats.nFileIndexLow));
    109 
    110   return FsNode::Metadata {
    111     modeToType(stats.dwFileAttributes, 0),
    112     size,
    113     // In theory, spaceUsed should be based on GetCompressedFileSize(), but requiring an extra
    114     // syscall for something rarely used would be sad.
    115     size,
    116     toKjDate(stats.ftLastWriteTime),
    117     stats.nNumberOfLinks,
    118     hash
    119   };
    120 }
    121 
    122 static FsNode::Metadata statToMetadata(const WIN32_FIND_DATAW& stats) {
    123   uint64_t size = (implicitCast<uint64_t>(stats.nFileSizeHigh) << 32) | stats.nFileSizeLow;
    124 
    125   return FsNode::Metadata {
    126     modeToType(stats.dwFileAttributes, stats.dwReserved0),
    127     size,
    128     // In theory, spaceUsed should be based on GetCompressedFileSize(), but requiring an extra
    129     // syscall for something rarely used would be sad.
    130     size,
    131     toKjDate(stats.ftLastWriteTime),
    132     // We can't get the number of links without opening the file, apparently. Meh.
    133     1,
    134     // We can't produce a reliable hashCode without opening the file.
    135     0
    136   };
    137 }
    138 
    139 static Array<wchar_t> join16(ArrayPtr<const wchar_t> path, const wchar_t* file) {
    140   // Assumes `path` ends with a NUL terminator (and `file` is of course NUL terminated as well).
    141 
    142   size_t len = wcslen(file) + 1;
    143   auto result = kj::heapArray<wchar_t>(path.size() + len);
    144   memcpy(result.begin(), path.begin(), path.asBytes().size() - sizeof(wchar_t));
    145   result[path.size() - 1] = '\\';
    146   memcpy(result.begin() + path.size(), file, len * sizeof(wchar_t));
    147   return result;
    148 }
    149 
    150 static String dbgStr(ArrayPtr<const wchar_t> wstr) {
    151   if (wstr.size() > 0 && wstr[wstr.size() - 1] == L'\0') {
    152     wstr = wstr.slice(0, wstr.size() - 1);
    153   }
    154   return decodeWideString(wstr);
    155 }
    156 
    157 static void rmrfChildren(ArrayPtr<const wchar_t> path) {
    158   auto glob = join16(path, L"*");
    159 
    160   WIN32_FIND_DATAW data;
    161   HANDLE handle = FindFirstFileW(glob.begin(), &data);
    162   if (handle == INVALID_HANDLE_VALUE) {
    163     auto error = GetLastError();
    164     if (error == ERROR_FILE_NOT_FOUND) return;
    165     KJ_FAIL_WIN32("FindFirstFile", error, dbgStr(glob)) { return; }
    166   }
    167   KJ_DEFER(KJ_WIN32(FindClose(handle)) { break; });
    168 
    169   do {
    170     // Ignore "." and "..", ugh.
    171     if (data.cFileName[0] == L'.') {
    172       if (data.cFileName[1] == L'\0' ||
    173           (data.cFileName[1] == L'.' && data.cFileName[2] == L'\0')) {
    174         continue;
    175       }
    176     }
    177 
    178     auto child = join16(path, data.cFileName);
    179     // For rmrf purposes, we assume any "reparse points" are symlink-like, even if they aren't
    180     // actually the "symbolic link" reparse type, because we don't want to recursively delete any
    181     // shared content.
    182     if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
    183         !(data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
    184       rmrfChildren(child);
    185       uint retryCount = 0;
    186     retry:
    187       KJ_WIN32_HANDLE_ERRORS(RemoveDirectoryW(child.begin())) {
    188         case ERROR_DIR_NOT_EMPTY:
    189           // On Windows, deleting a file actually only schedules it for deletion. Under heavy
    190           // load it may take a bit for the deletion to go through. Or, if another process has
    191           // the file open, it may not be deleted until that process closes it.
    192           //
    193           // We'll repeatedly retry for up to 100ms, then give up. This is awful but there's no
    194           // way to tell for sure if the system is just being slow or if someone has the file
    195           // open.
    196           if (retryCount++ < 10) {
    197             Sleep(10);
    198             goto retry;
    199           }
    200           KJ_FALLTHROUGH;
    201         default:
    202           KJ_FAIL_WIN32("RemoveDirectory", error, dbgStr(child)) { break; }
    203       }
    204     } else {
    205       KJ_WIN32(DeleteFileW(child.begin()));
    206     }
    207   } while (FindNextFileW(handle, &data));
    208 
    209   auto error = GetLastError();
    210   if (error != ERROR_NO_MORE_FILES) {
    211     KJ_FAIL_WIN32("FindNextFile", error, dbgStr(path)) { return; }
    212   }
    213 }
    214 
    215 static bool rmrf(ArrayPtr<const wchar_t> path) {
    216   // Figure out whether this is a file or a directory.
    217   //
    218   // We use FindFirstFileW() because in the case of symlinks it will return info about the
    219   // symlink rather than info about the target.
    220   WIN32_FIND_DATAW data;
    221   HANDLE handle = FindFirstFileW(path.begin(), &data);
    222   if (handle == INVALID_HANDLE_VALUE) {
    223     auto error = GetLastError();
    224     if (error == ERROR_FILE_NOT_FOUND) return false;
    225     KJ_FAIL_WIN32("FindFirstFile", error, dbgStr(path));
    226   }
    227   KJ_WIN32(FindClose(handle));
    228 
    229   // For remove purposes, we assume any "reparse points" are symlink-like, even if they aren't
    230   // actually the "symbolic link" reparse type, because we don't want to recursively delete any
    231   // shared content.
    232   if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
    233       !(data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
    234     // directory
    235     rmrfChildren(path);
    236     KJ_WIN32(RemoveDirectoryW(path.begin()), dbgStr(path));
    237   } else {
    238     KJ_WIN32(DeleteFileW(path.begin()), dbgStr(path));
    239   }
    240 
    241   return true;
    242 }
    243 
    244 static Path getPathFromHandle(HANDLE handle) {
    245   DWORD tryLen = MAX_PATH;
    246   for (;;) {
    247     auto temp = kj::heapArray<wchar_t>(tryLen + 1);
    248     DWORD len = GetFinalPathNameByHandleW(handle, temp.begin(), tryLen, 0);
    249     if (len == 0) {
    250       KJ_FAIL_WIN32("GetFinalPathNameByHandleW", GetLastError());
    251     }
    252     if (len < temp.size()) {
    253       return Path::parseWin32Api(temp.slice(0, len));
    254     }
    255     // Try again with new length.
    256     tryLen = len;
    257   }
    258 }
    259 
    260 struct MmapRange {
    261   uint64_t offset;
    262   uint64_t size;
    263 };
    264 
    265 static size_t getAllocationGranularity() {
    266   SYSTEM_INFO info;
    267   GetSystemInfo(&info);
    268   return info.dwAllocationGranularity;
    269 };
    270 
    271 static MmapRange getMmapRange(uint64_t offset, uint64_t size) {
    272   // Rounds the given offset down to the nearest page boundary, and adjusts the size up to match.
    273   // (This is somewhat different from Unix: we do NOT round the size up to an even multiple of
    274   // pages.)
    275   static const uint64_t pageSize = getAllocationGranularity();
    276   uint64_t pageMask = pageSize - 1;
    277 
    278   uint64_t realOffset = offset & ~pageMask;
    279 
    280   uint64_t end = offset + size;
    281 
    282   return { realOffset, end - realOffset };
    283 }
    284 
    285 class MmapDisposer: public ArrayDisposer {
    286 protected:
    287   void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
    288                    size_t capacity, void (*destroyElement)(void*)) const {
    289     auto range = getMmapRange(reinterpret_cast<uintptr_t>(firstElement),
    290                               elementSize * elementCount);
    291     void* mapping = reinterpret_cast<void*>(range.offset);
    292     if (mapping != nullptr) {
    293       KJ_ASSERT(UnmapViewOfFile(mapping)) { break; }
    294     }
    295   }
    296 };
    297 
    298 #if _MSC_VER && _MSC_VER < 1910 && !defined(__clang__)
    299 // TODO(msvc): MSVC 2015 can't initialize a constexpr's vtable correctly.
    300 const MmapDisposer mmapDisposer = MmapDisposer();
    301 #else
    302 constexpr MmapDisposer mmapDisposer = MmapDisposer();
    303 #endif
    304 
    305 void* win32Mmap(HANDLE handle, MmapRange range, DWORD pageProtect, DWORD access) {
    306   HANDLE mappingHandle;
    307   KJ_WIN32(mappingHandle = CreateFileMappingW(handle, NULL, pageProtect, 0, 0, NULL));
    308   KJ_DEFER(KJ_WIN32(CloseHandle(mappingHandle)) { break; });
    309 
    310   void* mapping = MapViewOfFile(mappingHandle, access,
    311       static_cast<DWORD>(range.offset >> 32), static_cast<DWORD>(range.offset), range.size);
    312   if (mapping == nullptr) {
    313     KJ_FAIL_WIN32("MapViewOfFile", GetLastError());
    314   }
    315 
    316   // It's unclear from the documentation whether mappings will always start at a multiple of the
    317   // allocation granularity, but we depend on that later, so check it...
    318   KJ_ASSERT(getMmapRange(reinterpret_cast<uintptr_t>(mapping), 0).size == 0);
    319 
    320   return mapping;
    321 }
    322 
    323 class DiskHandle {
    324   // We need to implement each of ReadableFile, AppendableFile, File, ReadableDirectory, and
    325   // Directory for disk handles. There is a lot of implementation overlap between these, especially
    326   // stat(), sync(), etc. We can't have everything inherit from a common DiskFsNode that implements
    327   // these because then we get diamond inheritance which means we need to make all our inheritance
    328   // virtual which means downcasting requires RTTI which violates our goal of supporting compiling
    329   // with no RTTI. So instead we have the DiskHandle class which implements all the methods without
    330   // inheriting anything, and then we have DiskFile, DiskDirectory, etc. hold this and delegate to
    331   // it. Ugly, but works.
    332 
    333 public:
    334   DiskHandle(AutoCloseHandle&& handle, Maybe<Path> dirPath)
    335       : handle(kj::mv(handle)), dirPath(kj::mv(dirPath)) {}
    336 
    337   AutoCloseHandle handle;
    338   kj::Maybe<Path> dirPath;  // needed for directories, empty for files
    339 
    340   Array<wchar_t> nativePath(PathPtr path) const {
    341     return KJ_ASSERT_NONNULL(dirPath).append(path).forWin32Api(true);
    342   }
    343 
    344   // OsHandle ------------------------------------------------------------------
    345 
    346   AutoCloseHandle clone() const {
    347     HANDLE newHandle;
    348     KJ_WIN32(DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &newHandle,
    349                              0, FALSE, DUPLICATE_SAME_ACCESS));
    350     return AutoCloseHandle(newHandle);
    351   }
    352 
    353   HANDLE getWin32Handle() const {
    354     return handle.get();
    355   }
    356 
    357   // FsNode --------------------------------------------------------------------
    358 
    359   FsNode::Metadata stat() const {
    360     BY_HANDLE_FILE_INFORMATION stats;
    361     KJ_WIN32(GetFileInformationByHandle(handle, &stats));
    362     auto metadata = statToMetadata(stats);
    363 
    364     // Get space usage, e.g. for sparse files. Apparently the correct way to do this is to query
    365     // "compression".
    366     FILE_COMPRESSION_INFO compInfo;
    367     KJ_WIN32_HANDLE_ERRORS(GetFileInformationByHandleEx(
    368         handle, FileCompressionInfo, &compInfo, sizeof(compInfo))) {
    369       case ERROR_CALL_NOT_IMPLEMENTED:
    370         // Probably WINE.
    371         break;
    372       default:
    373         KJ_FAIL_WIN32("GetFileInformationByHandleEx(FileCompressionInfo)", error) { break; }
    374         break;
    375     } else {
    376       metadata.spaceUsed = compInfo.CompressedFileSize.QuadPart;
    377     }
    378 
    379     return metadata;
    380   }
    381 
    382   void sync() const { KJ_WIN32(FlushFileBuffers(handle)); }
    383   void datasync() const { KJ_WIN32(FlushFileBuffers(handle)); }
    384 
    385   // ReadableFile --------------------------------------------------------------
    386 
    387   size_t read(uint64_t offset, ArrayPtr<byte> buffer) const {
    388     // ReadFile() probably never returns short reads unless it hits EOF. Unfortunately, though,
    389     // this is not documented, and it's unclear whether we can rely on it.
    390 
    391     size_t total = 0;
    392     while (buffer.size() > 0) {
    393       // Apparently, the way to fake pread() on Windows is to provide an OVERLAPPED structure even
    394       // though we're not doing overlapped I/O.
    395       OVERLAPPED overlapped;
    396       memset(&overlapped, 0, sizeof(overlapped));
    397       overlapped.Offset = static_cast<DWORD>(offset);
    398       overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
    399 
    400       DWORD n;
    401       KJ_WIN32_HANDLE_ERRORS(ReadFile(handle, buffer.begin(), buffer.size(), &n, &overlapped)) {
    402         case ERROR_HANDLE_EOF:
    403           // The documentation claims this shouldn't happen for synchronous reads, but it seems
    404           // to happen for me, at least under WINE.
    405           n = 0;
    406           break;
    407         default:
    408           KJ_FAIL_WIN32("ReadFile", offset, buffer.size()) { return total; }
    409       }
    410       if (n == 0) break;
    411       total += n;
    412       offset += n;
    413       buffer = buffer.slice(n, buffer.size());
    414     }
    415     return total;
    416   }
    417 
    418   Array<const byte> mmap(uint64_t offset, uint64_t size) const {
    419     if (size == 0) return nullptr;  // Windows won't allow zero-length mappings
    420     auto range = getMmapRange(offset, size);
    421     const void* mapping = win32Mmap(handle, range, PAGE_READONLY, FILE_MAP_READ);
    422     return Array<const byte>(reinterpret_cast<const byte*>(mapping) + (offset - range.offset),
    423                              size, mmapDisposer);
    424   }
    425 
    426   Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const {
    427     if (size == 0) return nullptr;  // Windows won't allow zero-length mappings
    428     auto range = getMmapRange(offset, size);
    429     void* mapping = win32Mmap(handle, range, PAGE_READONLY, FILE_MAP_COPY);
    430     return Array<byte>(reinterpret_cast<byte*>(mapping) + (offset - range.offset),
    431                        size, mmapDisposer);
    432   }
    433 
    434   // File ----------------------------------------------------------------------
    435 
    436   void write(uint64_t offset, ArrayPtr<const byte> data) const {
    437     // WriteFile() probably never returns short writes unless there's no space left on disk.
    438     // Unfortunately, though, this is not documented, and it's unclear whether we can rely on it.
    439 
    440     while (data.size() > 0) {
    441       // Apparently, the way to fake pwrite() on Windows is to provide an OVERLAPPED structure even
    442       // though we're not doing overlapped I/O.
    443       OVERLAPPED overlapped;
    444       memset(&overlapped, 0, sizeof(overlapped));
    445       overlapped.Offset = static_cast<DWORD>(offset);
    446       overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
    447 
    448       DWORD n;
    449       KJ_WIN32(WriteFile(handle, data.begin(), data.size(), &n, &overlapped));
    450       KJ_ASSERT(n > 0, "WriteFile() returned zero?");
    451       offset += n;
    452       data = data.slice(n, data.size());
    453     }
    454   }
    455 
    456   void zero(uint64_t offset, uint64_t size) const {
    457     FILE_ZERO_DATA_INFORMATION info;
    458     memset(&info, 0, sizeof(info));
    459     info.FileOffset.QuadPart = offset;
    460     info.BeyondFinalZero.QuadPart = offset + size;
    461 
    462     DWORD dummy;
    463     KJ_WIN32_HANDLE_ERRORS(DeviceIoControl(handle, FSCTL_SET_ZERO_DATA, &info,
    464                                            sizeof(info), NULL, 0, &dummy, NULL)) {
    465       case ERROR_NOT_SUPPORTED: {
    466         // Dang. Let's do it the hard way.
    467         static const byte ZEROS[4096] = { 0 };
    468 
    469         while (size > sizeof(ZEROS)) {
    470           write(offset, ZEROS);
    471           size -= sizeof(ZEROS);
    472           offset += sizeof(ZEROS);
    473         }
    474         write(offset, kj::arrayPtr(ZEROS, size));
    475         break;
    476       }
    477 
    478       default:
    479         KJ_FAIL_WIN32("DeviceIoControl(FSCTL_SET_ZERO_DATA)", error);
    480         break;
    481     }
    482   }
    483 
    484   void truncate(uint64_t size) const {
    485     // SetEndOfFile() would require seeking the file. It looks like SetFileInformationByHandle()
    486     // lets us avoid this!
    487     FILE_END_OF_FILE_INFO info;
    488     memset(&info, 0, sizeof(info));
    489     info.EndOfFile.QuadPart = size;
    490     KJ_WIN32_HANDLE_ERRORS(
    491         SetFileInformationByHandle(handle, FileEndOfFileInfo, &info, sizeof(info))) {
    492       case ERROR_CALL_NOT_IMPLEMENTED: {
    493         // Wine doesn't implement this. :(
    494 
    495         LONG currentHigh = 0;
    496         LONG currentLow = SetFilePointer(handle, 0, &currentHigh, FILE_CURRENT);
    497         if (currentLow == INVALID_SET_FILE_POINTER) {
    498           KJ_FAIL_WIN32("SetFilePointer", GetLastError());
    499         }
    500         uint64_t current = (uint64_t(currentHigh) << 32) | uint64_t((ULONG)currentLow);
    501 
    502         LONG endLow = size & 0x00000000ffffffffull;
    503         LONG endHigh = size >> 32;
    504         if (SetFilePointer(handle, endLow, &endHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
    505           KJ_FAIL_WIN32("SetFilePointer", GetLastError());
    506         }
    507 
    508         KJ_WIN32(SetEndOfFile(handle));
    509 
    510         if (current < size) {
    511           if (SetFilePointer(handle, currentLow, &currentHigh, FILE_BEGIN) ==
    512                   INVALID_SET_FILE_POINTER) {
    513             KJ_FAIL_WIN32("SetFilePointer", GetLastError());
    514           }
    515         }
    516 
    517         break;
    518       }
    519       default:
    520         KJ_FAIL_WIN32("SetFileInformationByHandle", error);
    521     }
    522   }
    523 
    524   class WritableFileMappingImpl final: public WritableFileMapping {
    525   public:
    526     WritableFileMappingImpl(Array<byte> bytes): bytes(kj::mv(bytes)) {}
    527 
    528     ArrayPtr<byte> get() const override {
    529       // const_cast OK because WritableFileMapping does indeed provide a writable view despite
    530       // being const itself.
    531       return arrayPtr(const_cast<byte*>(bytes.begin()), bytes.size());
    532     }
    533 
    534     void changed(ArrayPtr<byte> slice) const override {
    535       KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(),
    536                  "byte range is not part of this mapping");
    537 
    538       // Nothing needed here -- NT tracks dirty pages.
    539     }
    540 
    541     void sync(ArrayPtr<byte> slice) const override {
    542       KJ_REQUIRE(slice.begin() >= bytes.begin() && slice.end() <= bytes.end(),
    543                  "byte range is not part of this mapping");
    544 
    545       // Zero is treated specially by FlushViewOfFile(), so check for it. (This also handles the
    546       // case where `bytes` is actually empty and not a real mapping.)
    547       if (slice.size() > 0) {
    548         KJ_WIN32(FlushViewOfFile(slice.begin(), slice.size()));
    549       }
    550     }
    551 
    552   private:
    553     Array<byte> bytes;
    554   };
    555 
    556   Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const {
    557     if (size == 0) {
    558       // Windows won't allow zero-length mappings
    559       return heap<WritableFileMappingImpl>(nullptr);
    560     }
    561     auto range = getMmapRange(offset, size);
    562     void* mapping = win32Mmap(handle, range, PAGE_READWRITE, FILE_MAP_ALL_ACCESS);
    563     auto array = Array<byte>(reinterpret_cast<byte*>(mapping) + (offset - range.offset),
    564                              size, mmapDisposer);
    565     return heap<WritableFileMappingImpl>(kj::mv(array));
    566   }
    567 
    568   // copy() is not optimized on Windows.
    569 
    570   // ReadableDirectory ---------------------------------------------------------
    571 
    572   template <typename Func>
    573   auto list(bool needTypes, Func&& func) const
    574       -> Array<Decay<decltype(func(instance<StringPtr>(), instance<FsNode::Type>()))>> {
    575     PathPtr path = KJ_ASSERT_NONNULL(dirPath);
    576     auto glob = join16(path.forWin32Api(true), L"*");
    577 
    578     // TODO(perf): Use FindFileEx() with FindExInfoBasic? Not apparently supported on Vista.
    579     // TODO(someday): Use NtQueryDirectoryObject() instead? It's "internal", but so much cleaner.
    580     WIN32_FIND_DATAW data;
    581     HANDLE handle = FindFirstFileW(glob.begin(), &data);
    582     if (handle == INVALID_HANDLE_VALUE) {
    583       auto error = GetLastError();
    584       if (error == ERROR_FILE_NOT_FOUND) return nullptr;
    585       KJ_FAIL_WIN32("FindFirstFile", error, dbgStr(glob));
    586     }
    587     KJ_DEFER(KJ_WIN32(FindClose(handle)) { break; });
    588 
    589     typedef Decay<decltype(func(instance<StringPtr>(), instance<FsNode::Type>()))> Entry;
    590     kj::Vector<Entry> entries;
    591 
    592     do {
    593       auto name = decodeUtf16(
    594           arrayPtr(reinterpret_cast<char16_t*>(data.cFileName), wcslen(data.cFileName)));
    595       if (name != "." && name != ".." && !name.startsWith(HIDDEN_PREFIX)) {
    596         entries.add(func(name, modeToType(data.dwFileAttributes, data.dwReserved0)));
    597       }
    598     } while (FindNextFileW(handle, &data));
    599 
    600     auto error = GetLastError();
    601     if (error != ERROR_NO_MORE_FILES) {
    602       KJ_FAIL_WIN32("FindNextFile", error, path);
    603     }
    604 
    605     auto result = entries.releaseAsArray();
    606     std::sort(result.begin(), result.end());
    607     return result;
    608   }
    609 
    610   Array<String> listNames() const {
    611     return list(false, [](StringPtr name, FsNode::Type type) { return heapString(name); });
    612   }
    613 
    614   Array<ReadableDirectory::Entry> listEntries() const {
    615     return list(true, [](StringPtr name, FsNode::Type type) {
    616       return ReadableDirectory::Entry { type, heapString(name), };
    617     });
    618   }
    619 
    620   bool exists(PathPtr path) const {
    621     DWORD result = GetFileAttributesW(nativePath(path).begin());
    622     if (result == INVALID_FILE_ATTRIBUTES) {
    623       auto error = GetLastError();
    624       switch (error) {
    625         case ERROR_FILE_NOT_FOUND:
    626         case ERROR_PATH_NOT_FOUND:
    627           return false;
    628         default:
    629           KJ_FAIL_WIN32("GetFileAttributesEx(path)", error, path) { return false; }
    630       }
    631     } else {
    632       return true;
    633     }
    634   }
    635 
    636   Maybe<FsNode::Metadata> tryLstat(PathPtr path) const {
    637     // We use FindFirstFileW() because in the case of symlinks it will return info about the
    638     // symlink rather than info about the target.
    639     WIN32_FIND_DATAW data;
    640     HANDLE handle = FindFirstFileW(nativePath(path).begin(), &data);
    641     if (handle == INVALID_HANDLE_VALUE) {
    642       auto error = GetLastError();
    643       if (error == ERROR_FILE_NOT_FOUND) return nullptr;
    644       KJ_FAIL_WIN32("FindFirstFile", error, path);
    645     } else {
    646       KJ_WIN32(FindClose(handle));
    647       return statToMetadata(data);
    648     }
    649   }
    650 
    651   Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const {
    652     HANDLE newHandle;
    653     KJ_WIN32_HANDLE_ERRORS(newHandle = CreateFileW(
    654         nativePath(path).begin(),
    655         GENERIC_READ,
    656         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    657         NULL,
    658         OPEN_EXISTING,
    659         FILE_ATTRIBUTE_NORMAL,
    660         NULL)) {
    661       case ERROR_FILE_NOT_FOUND:
    662       case ERROR_PATH_NOT_FOUND:
    663         return nullptr;
    664       default:
    665         KJ_FAIL_WIN32("CreateFile(path, OPEN_EXISTING)", error, path) { return nullptr; }
    666     }
    667 
    668     return newDiskReadableFile(kj::AutoCloseHandle(newHandle));
    669   }
    670 
    671   Maybe<AutoCloseHandle> tryOpenSubdirInternal(PathPtr path) const {
    672     HANDLE newHandle;
    673     KJ_WIN32_HANDLE_ERRORS(newHandle = CreateFileW(
    674         nativePath(path).begin(),
    675         GENERIC_READ,
    676         // When opening directories, we do NOT use FILE_SHARE_DELETE, because we need the directory
    677         // path to remain vaild.
    678         //
    679         // TODO(someday): Use NtCreateFile() and related "internal" APIs that allow for
    680         //   openat()-like behavior?
    681         FILE_SHARE_READ | FILE_SHARE_WRITE,
    682         NULL,
    683         OPEN_EXISTING,
    684         FILE_FLAG_BACKUP_SEMANTICS,  // apparently, this flag is required for directories
    685         NULL)) {
    686       case ERROR_FILE_NOT_FOUND:
    687       case ERROR_PATH_NOT_FOUND:
    688         return nullptr;
    689       default:
    690         KJ_FAIL_WIN32("CreateFile(directoryPath, OPEN_EXISTING)", error, path) { return nullptr; }
    691     }
    692 
    693     kj::AutoCloseHandle ownHandle(newHandle);
    694 
    695     BY_HANDLE_FILE_INFORMATION info;
    696     KJ_WIN32(GetFileInformationByHandle(ownHandle, &info));
    697 
    698     KJ_REQUIRE(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY, "not a directory", path);
    699     return kj::mv(ownHandle);
    700   }
    701 
    702   Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const {
    703     return tryOpenSubdirInternal(path).map([&](AutoCloseHandle&& handle) {
    704       return newDiskReadableDirectory(kj::mv(handle), KJ_ASSERT_NONNULL(dirPath).append(path));
    705     });
    706   }
    707 
    708   Maybe<String> tryReadlink(PathPtr path) const {
    709     // Windows symlinks work differently from Unix. Generally they are set up by the system
    710     // administrator and apps are expected to treat them transparently. Hence, on Windows, we act
    711     // as if nothing is a symlink by always returning null here.
    712     // TODO(someday): If we want to treat Windows symlinks more like Unix ones, start by reverting
    713     //   the comment that added this comment.
    714     return nullptr;
    715   }
    716 
    717   // Directory -----------------------------------------------------------------
    718 
    719   static LPSECURITY_ATTRIBUTES makeSecAttr(WriteMode mode) {
    720     if (has(mode, WriteMode::PRIVATE)) {
    721       KJ_UNIMPLEMENTED("WriteMode::PRIVATE on Win32 is not implemented");
    722     }
    723 
    724     return nullptr;
    725   }
    726 
    727   bool tryMkdir(PathPtr path, WriteMode mode, bool noThrow) const {
    728     // Internal function to make a directory.
    729 
    730     auto filename = nativePath(path);
    731 
    732     KJ_WIN32_HANDLE_ERRORS(CreateDirectoryW(filename.begin(), makeSecAttr(mode))) {
    733       case ERROR_ALREADY_EXISTS:
    734       case ERROR_FILE_EXISTS: {
    735         // Apparently this path exists.
    736         if (!has(mode, WriteMode::MODIFY)) {
    737           // Require exclusive create.
    738           return false;
    739         }
    740 
    741         // MODIFY is allowed, so we just need to check whether the existing entry is a directory.
    742         DWORD attr = GetFileAttributesW(filename.begin());
    743         if (attr == INVALID_FILE_ATTRIBUTES) {
    744           // CreateDirectory() says it already exists but we can't get attributes. Maybe it's a
    745           // dangling link, or maybe we can't access it for some reason. Assume failure.
    746           //
    747           // TODO(someday): Maybe we should be creating the directory at the target of the
    748           //   link?
    749           goto failed;
    750         }
    751         return attr & FILE_ATTRIBUTE_DIRECTORY;
    752       }
    753       case ERROR_PATH_NOT_FOUND:
    754         if (has(mode, WriteMode::CREATE_PARENT) && path.size() > 0 &&
    755             tryMkdir(path.parent(), WriteMode::CREATE | WriteMode::MODIFY |
    756                                     WriteMode::CREATE_PARENT, true)) {
    757           // Retry, but make sure we don't try to create the parent again.
    758           return tryMkdir(path, mode - WriteMode::CREATE_PARENT, noThrow);
    759         } else {
    760           goto failed;
    761         }
    762       default:
    763       failed:
    764         if (noThrow) {
    765           // Caller requested no throwing.
    766           return false;
    767         } else {
    768           KJ_FAIL_WIN32("CreateDirectory", error, path);
    769         }
    770     }
    771 
    772     return true;
    773   }
    774 
    775   kj::Maybe<Array<wchar_t>> createNamedTemporary(
    776       PathPtr finalName, WriteMode mode, Path& kjTempPath,
    777       Function<BOOL(const wchar_t*)> tryCreate) const {
    778     // Create a temporary file which will eventually replace `finalName`.
    779     //
    780     // Calls `tryCreate` to actually create the temporary, passing in the desired path. tryCreate()
    781     // is expected to behave like a win32 call, returning a BOOL and setting `GetLastError()` on
    782     // error. tryCreate() MUST fail with ERROR_{FILE,ALREADY}_EXISTS if the path exists -- this is
    783     // not checked in advance, since it needs to be checked atomically. In the case of
    784     // ERROR_*_EXISTS, tryCreate() will be called again with a new path.
    785     //
    786     // Returns the temporary path that succeeded. Only returns nullptr if there was an exception
    787     // but we're compiled with -fno-exceptions.
    788     //
    789     // The optional parameter `kjTempPath` is filled in with the KJ Path of the temporary.
    790 
    791     if (finalName.size() == 0) {
    792       KJ_FAIL_REQUIRE("can't replace self") { break; }
    793       return nullptr;
    794     }
    795 
    796     static uint counter = 0;
    797     static const DWORD pid = GetCurrentProcessId();
    798     auto tempName = kj::str(HIDDEN_PREFIX, pid, '.', counter++, '.',
    799                             finalName.basename()[0], ".partial");
    800     kjTempPath = finalName.parent().append(tempName);
    801     auto path = nativePath(kjTempPath);
    802 
    803     KJ_WIN32_HANDLE_ERRORS(tryCreate(path.begin())) {
    804       case ERROR_ALREADY_EXISTS:
    805       case ERROR_FILE_EXISTS:
    806         // Try again with a new counter value.
    807         return createNamedTemporary(finalName, mode, kj::mv(tryCreate));
    808       case ERROR_PATH_NOT_FOUND:
    809         if (has(mode, WriteMode::CREATE_PARENT) && finalName.size() > 1 &&
    810             tryMkdir(finalName.parent(), WriteMode::CREATE | WriteMode::MODIFY |
    811                                          WriteMode::CREATE_PARENT, true)) {
    812           // Retry, but make sure we don't try to create the parent again.
    813           mode = mode - WriteMode::CREATE_PARENT;
    814           return createNamedTemporary(finalName, mode, kj::mv(tryCreate));
    815         }
    816         KJ_FALLTHROUGH;
    817       default:
    818         KJ_FAIL_WIN32("create(path)", error, path) { break; }
    819         return nullptr;
    820     }
    821 
    822     return kj::mv(path);
    823   }
    824 
    825   kj::Maybe<Array<wchar_t>> createNamedTemporary(
    826       PathPtr finalName, WriteMode mode, Function<BOOL(const wchar_t*)> tryCreate) const {
    827     Path dummy = nullptr;
    828     return createNamedTemporary(finalName, mode, dummy, kj::mv(tryCreate));
    829   }
    830 
    831   bool tryReplaceNode(PathPtr path, WriteMode mode,
    832                       Function<BOOL(const wchar_t*)> tryCreate) const {
    833     // Replaces the given path with an object created by calling tryCreate().
    834     //
    835     // tryCreate() must behave like a win32 call which creates the node at the path passed to it,
    836     // returning FALSE error. If the path passed to tryCreate already exists, it MUST fail with
    837     // ERROR_{FILE,ALREADY}_EXISTS.
    838     //
    839     // When `mode` includes MODIFY, replaceNode() reacts to ERROR_*_EXISTS by creating the
    840     // node in a temporary location and then rename()ing it into place.
    841 
    842     if (path.size() == 0) {
    843       KJ_FAIL_REQUIRE("can't replace self") { return false; }
    844     }
    845 
    846     auto filename = nativePath(path);
    847 
    848     if (has(mode, WriteMode::CREATE)) {
    849       // First try just cerating the node in-place.
    850       KJ_WIN32_HANDLE_ERRORS(tryCreate(filename.begin())) {
    851         case ERROR_ALREADY_EXISTS:
    852         case ERROR_FILE_EXISTS:
    853           // Target exists.
    854           if (has(mode, WriteMode::MODIFY)) {
    855             // Fall back to MODIFY path, below.
    856             break;
    857           } else {
    858             return false;
    859           }
    860         case ERROR_PATH_NOT_FOUND:
    861           if (has(mode, WriteMode::CREATE_PARENT) && path.size() > 0 &&
    862               tryMkdir(path.parent(), WriteMode::CREATE | WriteMode::MODIFY |
    863                                       WriteMode::CREATE_PARENT, true)) {
    864             // Retry, but make sure we don't try to create the parent again.
    865             return tryReplaceNode(path, mode - WriteMode::CREATE_PARENT, kj::mv(tryCreate));
    866           }
    867           KJ_FALLTHROUGH;
    868         default:
    869           KJ_FAIL_WIN32("create(path)", error, path) { return false; }
    870       } else {
    871         // Success.
    872         return true;
    873       }
    874     }
    875 
    876     // Either we don't have CREATE mode or the target already exists. We need to perform a
    877     // replacement instead.
    878 
    879     KJ_IF_MAYBE(tempPath, createNamedTemporary(path, mode, kj::mv(tryCreate))) {
    880       if (tryCommitReplacement(path, *tempPath, mode)) {
    881         return true;
    882       } else {
    883         KJ_WIN32_HANDLE_ERRORS(DeleteFileW(tempPath->begin())) {
    884           case ERROR_FILE_NOT_FOUND:
    885             // meh
    886             break;
    887           default:
    888             KJ_FAIL_WIN32("DeleteFile(tempPath)", error, dbgStr(*tempPath));
    889         }
    890         return false;
    891       }
    892     } else {
    893       // threw, but exceptions are disabled
    894       return false;
    895     }
    896   }
    897 
    898   Maybe<AutoCloseHandle> tryOpenFileInternal(PathPtr path, WriteMode mode, bool append) const {
    899     DWORD disposition;
    900     if (has(mode, WriteMode::MODIFY)) {
    901       if (has(mode, WriteMode::CREATE)) {
    902         disposition = OPEN_ALWAYS;
    903       } else {
    904         disposition = OPEN_EXISTING;
    905       }
    906     } else {
    907       if (has(mode, WriteMode::CREATE)) {
    908         disposition = CREATE_NEW;
    909       } else {
    910         // Neither CREATE nor MODIFY -- impossible to satisfy preconditions.
    911         return nullptr;
    912       }
    913     }
    914 
    915     DWORD access = GENERIC_READ | GENERIC_WRITE;
    916     if (append) {
    917       // FILE_GENERIC_WRITE includes both FILE_APPEND_DATA and FILE_WRITE_DATA, but we only want
    918       // the former. There are also a zillion other bits that we need, annoyingly.
    919       access = (FILE_READ_ATTRIBUTES | FILE_GENERIC_WRITE) & ~FILE_WRITE_DATA;
    920     }
    921 
    922     auto filename = path.toString();
    923 
    924     HANDLE newHandle;
    925     KJ_WIN32_HANDLE_ERRORS(newHandle = CreateFileW(
    926         nativePath(path).begin(),
    927         access,
    928         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    929         makeSecAttr(mode),
    930         disposition,
    931         FILE_ATTRIBUTE_NORMAL,
    932         NULL)) {
    933       case ERROR_PATH_NOT_FOUND:
    934         if (has(mode, WriteMode::CREATE)) {
    935           // A parent directory didn't exist. Maybe cerate it.
    936           if (has(mode, WriteMode::CREATE_PARENT) && path.size() > 0 &&
    937               tryMkdir(path.parent(), WriteMode::CREATE | WriteMode::MODIFY |
    938                                       WriteMode::CREATE_PARENT, true)) {
    939             // Retry, but make sure we don't try to create the parent again.
    940             return tryOpenFileInternal(path, mode - WriteMode::CREATE_PARENT, append);
    941           }
    942 
    943           KJ_FAIL_REQUIRE("parent is not a directory", path) { return nullptr; }
    944         } else {
    945           // MODIFY-only mode. ERROR_PATH_NOT_FOUND = parent path doesn't exist = return null.
    946           return nullptr;
    947         }
    948       case ERROR_FILE_NOT_FOUND:
    949         if (!has(mode, WriteMode::CREATE)) {
    950           // MODIFY-only mode. ERROR_FILE_NOT_FOUND = doesn't exist = return null.
    951           return nullptr;
    952         }
    953         goto failed;
    954       case ERROR_ALREADY_EXISTS:
    955       case ERROR_FILE_EXISTS:
    956         if (!has(mode, WriteMode::MODIFY)) {
    957           // CREATE-only mode. ERROR_ALREADY_EXISTS = already exists = return null.
    958           return nullptr;
    959         }
    960         goto failed;
    961       default:
    962       failed:
    963         KJ_FAIL_WIN32("CreateFile", error, path) { return nullptr; }
    964     }
    965 
    966     return kj::AutoCloseHandle(newHandle);
    967   }
    968 
    969   bool tryCommitReplacement(
    970       PathPtr toPath, ArrayPtr<const wchar_t> fromPath,
    971       WriteMode mode, kj::Maybe<kj::PathPtr> pathForCreatingParents = nullptr) const {
    972     // Try to use MoveFileEx() to replace `toPath` with `fromPath`.
    973 
    974     auto wToPath = nativePath(toPath);
    975 
    976     DWORD flags = has(mode, WriteMode::MODIFY) ? MOVEFILE_REPLACE_EXISTING : 0;
    977 
    978     if (!has(mode, WriteMode::CREATE)) {
    979       // Non-atomically verify that target exists. There's no way to make this atomic.
    980       DWORD result = GetFileAttributesW(wToPath.begin());
    981       if (result == INVALID_FILE_ATTRIBUTES) {
    982         auto error = GetLastError();
    983         switch (error) {
    984           case ERROR_FILE_NOT_FOUND:
    985           case ERROR_PATH_NOT_FOUND:
    986             return false;
    987           default:
    988             KJ_FAIL_WIN32("GetFileAttributesEx(toPath)", error, toPath) { return false; }
    989         }
    990       }
    991     }
    992 
    993     KJ_WIN32_HANDLE_ERRORS(MoveFileExW(fromPath.begin(), wToPath.begin(), flags)) {
    994       case ERROR_ALREADY_EXISTS:
    995       case ERROR_FILE_EXISTS:
    996         // We must not be in MODIFY mode.
    997         return false;
    998       case ERROR_PATH_NOT_FOUND:
    999         KJ_IF_MAYBE(p, pathForCreatingParents) {
   1000           if (has(mode, WriteMode::CREATE_PARENT) &&
   1001               p->size() > 0 && tryMkdir(p->parent(),
   1002                   WriteMode::CREATE | WriteMode::MODIFY | WriteMode::CREATE_PARENT, true)) {
   1003             // Retry, but make sure we don't try to create the parent again.
   1004             return tryCommitReplacement(toPath, fromPath, mode - WriteMode::CREATE_PARENT);
   1005           }
   1006         }
   1007         goto default_;
   1008 
   1009       case ERROR_ACCESS_DENIED: {
   1010         // This often means that the target already exists and cannot be replaced, e.g. because
   1011         // it is a directory. Move it out of the way first, then move our replacement in, then
   1012         // delete the old thing.
   1013 
   1014         if (has(mode, WriteMode::MODIFY)) {
   1015           KJ_IF_MAYBE(tempName,
   1016               createNamedTemporary(toPath, WriteMode::CREATE, [&](const wchar_t* tempName2) {
   1017             return MoveFileW(wToPath.begin(), tempName2);
   1018           })) {
   1019             KJ_WIN32_HANDLE_ERRORS(MoveFileW(fromPath.begin(), wToPath.begin())) {
   1020               default:
   1021                 // Try to move back.
   1022                 MoveFileW(tempName->begin(), wToPath.begin());
   1023                 KJ_FAIL_WIN32("MoveFile", error, dbgStr(fromPath), dbgStr(wToPath)) {
   1024                   return false;
   1025                 }
   1026             }
   1027 
   1028             // Succeeded, delete temporary.
   1029             rmrf(*tempName);
   1030             return true;
   1031           } else {
   1032             // createNamedTemporary() threw exception but exceptions are disabled.
   1033             return false;
   1034           }
   1035         } else {
   1036           // Not MODIFY, so no overwrite allowed. If the file really does exist, we need to return
   1037           // false.
   1038           if (GetFileAttributesW(wToPath.begin()) != INVALID_FILE_ATTRIBUTES) {
   1039             return false;
   1040           }
   1041         }
   1042 
   1043         goto default_;
   1044       }
   1045 
   1046       default:
   1047       default_:
   1048         KJ_FAIL_WIN32("MoveFileEx", error, dbgStr(wToPath), dbgStr(fromPath)) { return false; }
   1049     }
   1050 
   1051     return true;
   1052   }
   1053 
   1054   template <typename T>
   1055   class ReplacerImpl final: public Directory::Replacer<T> {
   1056   public:
   1057     ReplacerImpl(Own<T>&& object, const DiskHandle& parentDirectory,
   1058                  Array<wchar_t>&& tempPath, Path&& path, WriteMode mode)
   1059         : Directory::Replacer<T>(mode),
   1060           object(kj::mv(object)), parentDirectory(parentDirectory),
   1061           tempPath(kj::mv(tempPath)), path(kj::mv(path)) {}
   1062 
   1063     ~ReplacerImpl() noexcept(false) {
   1064       if (!committed) {
   1065         object = Own<T>();  // Force close of handle before trying to delete.
   1066 
   1067         if (kj::isSameType<T, File>()) {
   1068           KJ_WIN32(DeleteFileW(tempPath.begin())) { break; }
   1069         } else {
   1070           rmrfChildren(tempPath);
   1071           KJ_WIN32(RemoveDirectoryW(tempPath.begin())) { break; }
   1072         }
   1073       }
   1074     }
   1075 
   1076     const T& get() override {
   1077       return *object;
   1078     }
   1079 
   1080     bool tryCommit() override {
   1081       KJ_ASSERT(!committed, "already committed") { return false; }
   1082 
   1083       // For directories, we intentionally don't use FILE_SHARE_DELETE on our handle because if the
   1084       // directory name changes our paths would be wrong. But, this means we can't rename the
   1085       // directory here to commit it. So, we need to close the handle and then re-open it
   1086       // afterwards. Ick.
   1087       AutoCloseHandle* objectHandle = getHandlePointerHack(*object);
   1088       if (kj::isSameType<T, Directory>()) {
   1089         *objectHandle = nullptr;
   1090       }
   1091       KJ_DEFER({
   1092         if (kj::isSameType<T, Directory>()) {
   1093           HANDLE newHandle = nullptr;
   1094           KJ_WIN32(newHandle = CreateFileW(
   1095               committed ? parentDirectory.nativePath(path).begin() : tempPath.begin(),
   1096               GENERIC_READ,
   1097               FILE_SHARE_READ | FILE_SHARE_WRITE,
   1098               NULL,
   1099               OPEN_EXISTING,
   1100               FILE_FLAG_BACKUP_SEMANTICS,  // apparently, this flag is required for directories
   1101               NULL)) { return; }
   1102           *objectHandle = AutoCloseHandle(newHandle);
   1103           *getPathPointerHack(*object) = KJ_ASSERT_NONNULL(parentDirectory.dirPath).append(path);
   1104         }
   1105       });
   1106 
   1107       return committed = parentDirectory.tryCommitReplacement(
   1108           path, tempPath, Directory::Replacer<T>::mode);
   1109     }
   1110 
   1111   private:
   1112     Own<T> object;
   1113     const DiskHandle& parentDirectory;
   1114     Array<wchar_t> tempPath;
   1115     Path path;
   1116     bool committed = false;  // true if *successfully* committed (in which case tempPath is gone)
   1117   };
   1118 
   1119   template <typename T>
   1120   class BrokenReplacer final: public Directory::Replacer<T> {
   1121     // For recovery path when exceptions are disabled.
   1122 
   1123   public:
   1124     BrokenReplacer(Own<const T> inner)
   1125         : Directory::Replacer<T>(WriteMode::CREATE | WriteMode::MODIFY),
   1126           inner(kj::mv(inner)) {}
   1127 
   1128     const T& get() override { return *inner; }
   1129     bool tryCommit() override { return false; }
   1130 
   1131   private:
   1132     Own<const T> inner;
   1133   };
   1134 
   1135   Maybe<Own<const File>> tryOpenFile(PathPtr path, WriteMode mode) const {
   1136     return tryOpenFileInternal(path, mode, false).map(newDiskFile);
   1137   }
   1138 
   1139   Own<Directory::Replacer<File>> replaceFile(PathPtr path, WriteMode mode) const {
   1140     HANDLE newHandle_;
   1141     KJ_IF_MAYBE(temp, createNamedTemporary(path, mode,
   1142         [&](const wchar_t* candidatePath) {
   1143       newHandle_ = CreateFileW(
   1144           candidatePath,
   1145           GENERIC_READ | GENERIC_WRITE,
   1146           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
   1147           makeSecAttr(mode),
   1148           CREATE_NEW,
   1149           FILE_ATTRIBUTE_NORMAL,
   1150           NULL);
   1151       return newHandle_ != INVALID_HANDLE_VALUE;
   1152     })) {
   1153       AutoCloseHandle newHandle(newHandle_);
   1154       return heap<ReplacerImpl<File>>(newDiskFile(kj::mv(newHandle)), *this, kj::mv(*temp),
   1155                                       path.clone(), mode);
   1156     } else {
   1157       // threw, but exceptions are disabled
   1158       return heap<BrokenReplacer<File>>(newInMemoryFile(nullClock()));
   1159     }
   1160   }
   1161 
   1162   Own<const File> createTemporary() const {
   1163     HANDLE newHandle_;
   1164     KJ_IF_MAYBE(temp, createNamedTemporary(Path("unnamed"), WriteMode::CREATE,
   1165         [&](const wchar_t* candidatePath) {
   1166       newHandle_ = CreateFileW(
   1167           candidatePath,
   1168           GENERIC_READ | GENERIC_WRITE,
   1169           0,
   1170           NULL,   // TODO(someday): makeSecAttr(WriteMode::PRIVATE), when it's implemented
   1171           CREATE_NEW,
   1172           FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
   1173           NULL);
   1174       return newHandle_ != INVALID_HANDLE_VALUE;
   1175     })) {
   1176       AutoCloseHandle newHandle(newHandle_);
   1177       return newDiskFile(kj::mv(newHandle));
   1178     } else {
   1179       // threw, but exceptions are disabled
   1180       return newInMemoryFile(nullClock());
   1181     }
   1182   }
   1183 
   1184   Maybe<Own<AppendableFile>> tryAppendFile(PathPtr path, WriteMode mode) const {
   1185     return tryOpenFileInternal(path, mode, true).map(newDiskAppendableFile);
   1186   }
   1187 
   1188   Maybe<Own<const Directory>> tryOpenSubdir(PathPtr path, WriteMode mode) const {
   1189     // Must create before open.
   1190     if (has(mode, WriteMode::CREATE)) {
   1191       if (!tryMkdir(path, mode, false)) return nullptr;
   1192     }
   1193 
   1194     return tryOpenSubdirInternal(path).map([&](AutoCloseHandle&& handle) {
   1195       return newDiskDirectory(kj::mv(handle), KJ_ASSERT_NONNULL(dirPath).append(path));
   1196     });
   1197   }
   1198 
   1199   Own<Directory::Replacer<Directory>> replaceSubdir(PathPtr path, WriteMode mode) const {
   1200     Path kjTempPath = nullptr;
   1201     KJ_IF_MAYBE(temp, createNamedTemporary(path, mode, kjTempPath,
   1202         [&](const wchar_t* candidatePath) {
   1203       return CreateDirectoryW(candidatePath, makeSecAttr(mode));
   1204     })) {
   1205       HANDLE subdirHandle_;
   1206       KJ_WIN32_HANDLE_ERRORS(subdirHandle_ = CreateFileW(
   1207           temp->begin(),
   1208           GENERIC_READ,
   1209           FILE_SHARE_READ | FILE_SHARE_WRITE,
   1210           NULL,
   1211           OPEN_EXISTING,
   1212           FILE_FLAG_BACKUP_SEMANTICS,  // apparently, this flag is required for directories
   1213           NULL)) {
   1214         default:
   1215           KJ_FAIL_WIN32("CreateFile(just-created-temporary, OPEN_EXISTING)", error, path) {
   1216             goto fail;
   1217           }
   1218       }
   1219 
   1220       AutoCloseHandle subdirHandle(subdirHandle_);
   1221       return heap<ReplacerImpl<Directory>>(
   1222           newDiskDirectory(kj::mv(subdirHandle),
   1223               KJ_ASSERT_NONNULL(dirPath).append(kj::mv(kjTempPath))),
   1224           *this, kj::mv(*temp), path.clone(), mode);
   1225     } else {
   1226       // threw, but exceptions are disabled
   1227     fail:
   1228       return heap<BrokenReplacer<Directory>>(newInMemoryDirectory(nullClock()));
   1229     }
   1230   }
   1231 
   1232   bool trySymlink(PathPtr linkpath, StringPtr content, WriteMode mode) const {
   1233     // We can't really create symlinks on Windows. Reasons:
   1234     // - We'd need to know whether the target is a file or a directory to pass the correct flags.
   1235     //   That means we'd need to evaluate the link content and track down the target. What if the
   1236     //   taget doesn't exist? It's unclear if this is even allowed on Windows.
   1237     // - Apparently, creating symlinks is a privileged operation on Windows prior to Windows 10.
   1238     //   The flag SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is very new.
   1239     KJ_UNIMPLEMENTED(
   1240         "Creating symbolic links is not supported on Windows due to semantic differences.");
   1241   }
   1242 
   1243   bool tryTransfer(PathPtr toPath, WriteMode toMode,
   1244                    const Directory& fromDirectory, PathPtr fromPath,
   1245                    TransferMode mode, const Directory& self) const {
   1246     KJ_REQUIRE(toPath.size() > 0, "can't replace self") { return false; }
   1247 
   1248     // Try to get the "from" path.
   1249     Array<wchar_t> rawFromPath;
   1250 #if !KJ_NO_RTTI
   1251     // Oops, dynamicDowncastIfAvailable() doesn't work since this isn't a downcast, it's a
   1252     // side-cast...
   1253     if (auto dh = dynamic_cast<const DiskHandle*>(&fromDirectory)) {
   1254       rawFromPath = dh->nativePath(fromPath);
   1255     } else
   1256 #endif
   1257     KJ_IF_MAYBE(h, fromDirectory.getWin32Handle()) {
   1258       // Can't downcast to DiskHandle, but getWin32Handle() returns a handle... maybe RTTI is
   1259       // disabled? Or maybe this is some kind of wrapper?
   1260       rawFromPath = getPathFromHandle(*h).append(fromPath).forWin32Api(true);
   1261     } else {
   1262       // Not a disk directory, so fall back to default implementation.
   1263       return self.Directory::tryTransfer(toPath, toMode, fromDirectory, fromPath, mode);
   1264     }
   1265 
   1266     if (mode == TransferMode::LINK) {
   1267       return tryReplaceNode(toPath, toMode, [&](const wchar_t* candidatePath) {
   1268         return CreateHardLinkW(candidatePath, rawFromPath.begin(), NULL);
   1269       });
   1270     } else if (mode == TransferMode::MOVE) {
   1271       return tryCommitReplacement(toPath, rawFromPath, toMode, toPath);
   1272     } else if (mode == TransferMode::COPY) {
   1273       // We can accellerate copies on Windows.
   1274 
   1275       if (!has(toMode, WriteMode::CREATE)) {
   1276         // Non-atomically verify that target exists. There's no way to make this atomic.
   1277         if (!exists(toPath)) return false;
   1278       }
   1279 
   1280       bool failIfExists = !has(toMode, WriteMode::MODIFY);
   1281       KJ_WIN32_HANDLE_ERRORS(
   1282           CopyFileW(rawFromPath.begin(), nativePath(toPath).begin(), failIfExists)) {
   1283         case ERROR_ALREADY_EXISTS:
   1284         case ERROR_FILE_EXISTS:
   1285         case ERROR_FILE_NOT_FOUND:
   1286         case ERROR_PATH_NOT_FOUND:
   1287           return false;
   1288         case ERROR_ACCESS_DENIED:
   1289           // This usually means that fromPath was a directory or toPath was a direcotry. Fall back
   1290           // to default implementation.
   1291           break;
   1292         default:
   1293           KJ_FAIL_WIN32("CopyFile", error, fromPath, toPath) { return false; }
   1294       } else {
   1295         // Copy succeeded.
   1296         return true;
   1297       }
   1298     }
   1299 
   1300     // OK, we can't do anything efficient using the OS. Fall back to default implementation.
   1301     return self.Directory::tryTransfer(toPath, toMode, fromDirectory, fromPath, mode);
   1302   }
   1303 
   1304   bool tryRemove(PathPtr path) const {
   1305     return rmrf(nativePath(path));
   1306   }
   1307 };
   1308 
   1309 #define FSNODE_METHODS                                              \
   1310   Maybe<void*> getWin32Handle() const override { return DiskHandle::getWin32Handle(); } \
   1311                                                                     \
   1312   Metadata stat() const override { return DiskHandle::stat(); }     \
   1313   void sync() const override { DiskHandle::sync(); }                \
   1314   void datasync() const override { DiskHandle::datasync(); }
   1315 
   1316 class DiskReadableFile final: public ReadableFile, public DiskHandle {
   1317 public:
   1318   DiskReadableFile(AutoCloseHandle&& handle): DiskHandle(kj::mv(handle), nullptr) {}
   1319 
   1320   Own<const FsNode> cloneFsNode() const override {
   1321     return heap<DiskReadableFile>(DiskHandle::clone());
   1322   }
   1323 
   1324   FSNODE_METHODS
   1325 
   1326   size_t read(uint64_t offset, ArrayPtr<byte> buffer) const override {
   1327     return DiskHandle::read(offset, buffer);
   1328   }
   1329   Array<const byte> mmap(uint64_t offset, uint64_t size) const override {
   1330     return DiskHandle::mmap(offset, size);
   1331   }
   1332   Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const override {
   1333     return DiskHandle::mmapPrivate(offset, size);
   1334   }
   1335 };
   1336 
   1337 class DiskAppendableFile final: public AppendableFile, public DiskHandle {
   1338 public:
   1339   DiskAppendableFile(AutoCloseHandle&& handle)
   1340       : DiskHandle(kj::mv(handle), nullptr),
   1341         stream(DiskHandle::handle.get()) {}
   1342 
   1343   Own<const FsNode> cloneFsNode() const override {
   1344     return heap<DiskAppendableFile>(DiskHandle::clone());
   1345   }
   1346 
   1347   FSNODE_METHODS
   1348 
   1349   void write(const void* buffer, size_t size) override { stream.write(buffer, size); }
   1350   void write(ArrayPtr<const ArrayPtr<const byte>> pieces) override {
   1351     implicitCast<OutputStream&>(stream).write(pieces);
   1352   }
   1353 
   1354 private:
   1355   HandleOutputStream stream;
   1356 };
   1357 
   1358 class DiskFile final: public File, public DiskHandle {
   1359 public:
   1360   DiskFile(AutoCloseHandle&& handle): DiskHandle(kj::mv(handle), nullptr) {}
   1361 
   1362   Own<const FsNode> cloneFsNode() const override {
   1363     return heap<DiskFile>(DiskHandle::clone());
   1364   }
   1365 
   1366   FSNODE_METHODS
   1367 
   1368   size_t read(uint64_t offset, ArrayPtr<byte> buffer) const override {
   1369     return DiskHandle::read(offset, buffer);
   1370   }
   1371   Array<const byte> mmap(uint64_t offset, uint64_t size) const override {
   1372     return DiskHandle::mmap(offset, size);
   1373   }
   1374   Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const override {
   1375     return DiskHandle::mmapPrivate(offset, size);
   1376   }
   1377 
   1378   void write(uint64_t offset, ArrayPtr<const byte> data) const override {
   1379     DiskHandle::write(offset, data);
   1380   }
   1381   void zero(uint64_t offset, uint64_t size) const override {
   1382     DiskHandle::zero(offset, size);
   1383   }
   1384   void truncate(uint64_t size) const override {
   1385     DiskHandle::truncate(size);
   1386   }
   1387   Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const override {
   1388     return DiskHandle::mmapWritable(offset, size);
   1389   }
   1390   // copy() is not optimized on Windows.
   1391 };
   1392 
   1393 class DiskReadableDirectory final: public ReadableDirectory, public DiskHandle {
   1394 public:
   1395   DiskReadableDirectory(AutoCloseHandle&& handle, Path&& path)
   1396       : DiskHandle(kj::mv(handle), kj::mv(path)) {}
   1397 
   1398   Own<const FsNode> cloneFsNode() const override {
   1399     return heap<DiskReadableDirectory>(DiskHandle::clone(), KJ_ASSERT_NONNULL(dirPath).clone());
   1400   }
   1401 
   1402   FSNODE_METHODS
   1403 
   1404   Array<String> listNames() const override { return DiskHandle::listNames(); }
   1405   Array<Entry> listEntries() const override { return DiskHandle::listEntries(); }
   1406   bool exists(PathPtr path) const override { return DiskHandle::exists(path); }
   1407   Maybe<FsNode::Metadata> tryLstat(PathPtr path) const override {
   1408     return DiskHandle::tryLstat(path);
   1409   }
   1410   Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const override {
   1411     return DiskHandle::tryOpenFile(path);
   1412   }
   1413   Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const override {
   1414     return DiskHandle::tryOpenSubdir(path);
   1415   }
   1416   Maybe<String> tryReadlink(PathPtr path) const override { return DiskHandle::tryReadlink(path); }
   1417 };
   1418 
   1419 class DiskDirectoryBase: public Directory, public DiskHandle {
   1420 public:
   1421   DiskDirectoryBase(AutoCloseHandle&& handle, Path&& path)
   1422       : DiskHandle(kj::mv(handle), kj::mv(path)) {}
   1423 
   1424   bool exists(PathPtr path) const override { return DiskHandle::exists(path); }
   1425   Maybe<FsNode::Metadata> tryLstat(PathPtr path) const override { return DiskHandle::tryLstat(path); }
   1426   Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const override {
   1427     return DiskHandle::tryOpenFile(path);
   1428   }
   1429   Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const override {
   1430     return DiskHandle::tryOpenSubdir(path);
   1431   }
   1432   Maybe<String> tryReadlink(PathPtr path) const override { return DiskHandle::tryReadlink(path); }
   1433 
   1434   Maybe<Own<const File>> tryOpenFile(PathPtr path, WriteMode mode) const override {
   1435     return DiskHandle::tryOpenFile(path, mode);
   1436   }
   1437   Own<Replacer<File>> replaceFile(PathPtr path, WriteMode mode) const override {
   1438     return DiskHandle::replaceFile(path, mode);
   1439   }
   1440   Maybe<Own<AppendableFile>> tryAppendFile(PathPtr path, WriteMode mode) const override {
   1441     return DiskHandle::tryAppendFile(path, mode);
   1442   }
   1443   Maybe<Own<const Directory>> tryOpenSubdir(PathPtr path, WriteMode mode) const override {
   1444     return DiskHandle::tryOpenSubdir(path, mode);
   1445   }
   1446   Own<Replacer<Directory>> replaceSubdir(PathPtr path, WriteMode mode) const override {
   1447     return DiskHandle::replaceSubdir(path, mode);
   1448   }
   1449   bool trySymlink(PathPtr linkpath, StringPtr content, WriteMode mode) const override {
   1450     return DiskHandle::trySymlink(linkpath, content, mode);
   1451   }
   1452   bool tryTransfer(PathPtr toPath, WriteMode toMode,
   1453                    const Directory& fromDirectory, PathPtr fromPath,
   1454                    TransferMode mode) const override {
   1455     return DiskHandle::tryTransfer(toPath, toMode, fromDirectory, fromPath, mode, *this);
   1456   }
   1457   // tryTransferTo() not implemented because we have nothing special we can do.
   1458   bool tryRemove(PathPtr path) const override {
   1459     return DiskHandle::tryRemove(path);
   1460   }
   1461 };
   1462 
   1463 class DiskDirectory final: public DiskDirectoryBase {
   1464 public:
   1465   DiskDirectory(AutoCloseHandle&& handle, Path&& path)
   1466       : DiskDirectoryBase(kj::mv(handle), kj::mv(path)) {}
   1467 
   1468   Own<const FsNode> cloneFsNode() const override {
   1469     return heap<DiskDirectory>(DiskHandle::clone(), KJ_ASSERT_NONNULL(dirPath).clone());
   1470   }
   1471 
   1472   FSNODE_METHODS
   1473 
   1474   Array<String> listNames() const override { return DiskHandle::listNames(); }
   1475   Array<Entry> listEntries() const override { return DiskHandle::listEntries(); }
   1476   Own<const File> createTemporary() const override {
   1477     return DiskHandle::createTemporary();
   1478   }
   1479 };
   1480 
   1481 class RootDiskDirectory final: public DiskDirectoryBase {
   1482   // On Windows, the root directory is special.
   1483   //
   1484   // HACK: We only override a few functions of DiskDirectory, and we rely on the fact that
   1485   //   Path::forWin32Api(true) throws an exception complaining about missing drive letter if the
   1486   //   path is totally empty.
   1487 
   1488 public:
   1489   RootDiskDirectory(): DiskDirectoryBase(nullptr, Path(nullptr)) {}
   1490 
   1491   Own<const FsNode> cloneFsNode() const override {
   1492     return heap<RootDiskDirectory>();
   1493   }
   1494 
   1495   Metadata stat() const override {
   1496     return { Type::DIRECTORY, 0, 0, UNIX_EPOCH, 1, 0 };
   1497   }
   1498   void sync() const override {}
   1499   void datasync() const override {}
   1500 
   1501   Array<String> listNames() const override {
   1502     return KJ_MAP(e, listEntries()) { return kj::mv(e.name); };
   1503   }
   1504   Array<Entry> listEntries() const override {
   1505     DWORD drives = GetLogicalDrives();
   1506     if (drives == 0) {
   1507       KJ_FAIL_WIN32("GetLogicalDrives()", GetLastError()) { return nullptr; }
   1508     }
   1509 
   1510     Vector<Entry> results;
   1511     for (uint i = 0; i < 26; i++) {
   1512       if (drives & (1 << i)) {
   1513         char name[2] = { static_cast<char>('A' + i), ':' };
   1514         results.add(Entry { FsNode::Type::DIRECTORY, kj::heapString(name, 2) });
   1515       }
   1516     }
   1517 
   1518     return results.releaseAsArray();
   1519   }
   1520 
   1521   Own<const File> createTemporary() const override {
   1522     KJ_FAIL_REQUIRE("can't create temporaries in Windows pseudo-root directory (the drive list)");
   1523   }
   1524 };
   1525 
   1526 class DiskFilesystem final: public Filesystem {
   1527 public:
   1528   DiskFilesystem()
   1529       : DiskFilesystem(computeCurrentPath()) {}
   1530   DiskFilesystem(Path currentPath)
   1531       : current(KJ_ASSERT_NONNULL(root.tryOpenSubdirInternal(currentPath),
   1532                       "path returned by GetCurrentDirectory() doesn't exist?"),
   1533                 kj::mv(currentPath)) {}
   1534 
   1535   const Directory& getRoot() const override {
   1536     return root;
   1537   }
   1538 
   1539   const Directory& getCurrent() const override {
   1540     return current;
   1541   }
   1542 
   1543   PathPtr getCurrentPath() const override {
   1544     return KJ_ASSERT_NONNULL(current.dirPath);
   1545   }
   1546 
   1547 private:
   1548   RootDiskDirectory root;
   1549   DiskDirectory current;
   1550 
   1551   static Path computeCurrentPath() {
   1552     DWORD tryLen = MAX_PATH;
   1553     for (;;) {
   1554       auto temp = kj::heapArray<wchar_t>(tryLen + 1);
   1555       DWORD len = GetCurrentDirectoryW(temp.size(), temp.begin());
   1556       if (len == 0) {
   1557         KJ_FAIL_WIN32("GetCurrentDirectory", GetLastError()) { break; }
   1558         return Path(".");
   1559       }
   1560       if (len < temp.size()) {
   1561         return Path::parseWin32Api(temp.slice(0, len));
   1562       }
   1563       // Try again with new length.
   1564       tryLen = len;
   1565     }
   1566   }
   1567 };
   1568 
   1569 } // namespace
   1570 
   1571 Own<ReadableFile> newDiskReadableFile(AutoCloseHandle fd) {
   1572   return heap<DiskReadableFile>(kj::mv(fd));
   1573 }
   1574 Own<AppendableFile> newDiskAppendableFile(AutoCloseHandle fd) {
   1575   return heap<DiskAppendableFile>(kj::mv(fd));
   1576 }
   1577 Own<File> newDiskFile(AutoCloseHandle fd) {
   1578   return heap<DiskFile>(kj::mv(fd));
   1579 }
   1580 Own<ReadableDirectory> newDiskReadableDirectory(AutoCloseHandle fd) {
   1581   return heap<DiskReadableDirectory>(kj::mv(fd), getPathFromHandle(fd));
   1582 }
   1583 static Own<ReadableDirectory> newDiskReadableDirectory(AutoCloseHandle fd, Path&& path) {
   1584   return heap<DiskReadableDirectory>(kj::mv(fd), kj::mv(path));
   1585 }
   1586 Own<Directory> newDiskDirectory(AutoCloseHandle fd) {
   1587   return heap<DiskDirectory>(kj::mv(fd), getPathFromHandle(fd));
   1588 }
   1589 static Own<Directory> newDiskDirectory(AutoCloseHandle fd, Path&& path) {
   1590   return heap<DiskDirectory>(kj::mv(fd), kj::mv(path));
   1591 }
   1592 
   1593 Own<Filesystem> newDiskFilesystem() {
   1594   return heap<DiskFilesystem>();
   1595 }
   1596 
   1597 static AutoCloseHandle* getHandlePointerHack(Directory& dir) {
   1598   return &static_cast<DiskDirectoryBase&>(dir).handle;
   1599 }
   1600 static Path* getPathPointerHack(Directory& dir) {
   1601   return &KJ_ASSERT_NONNULL(static_cast<DiskDirectoryBase&>(dir).dirPath);
   1602 }
   1603 
   1604 } // namespace kj
   1605 
   1606 #endif  // _WIN32