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 
getHandlePointerHack(File & file)44 static AutoCloseHandle* getHandlePointerHack(File& file) { return nullptr; }
45 static AutoCloseHandle* getHandlePointerHack(Directory& dir);
getPathPointerHack(File & file)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 
toKjDate(FILETIME t)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 
modeToType(DWORD attrs,DWORD reparseTag)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 
statToMetadata(const BY_HANDLE_FILE_INFORMATION & stats)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 
statToMetadata(const WIN32_FIND_DATAW & stats)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 
join16(ArrayPtr<const wchar_t> path,const wchar_t * file)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 
dbgStr(ArrayPtr<const wchar_t> wstr)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 
rmrfChildren(ArrayPtr<const wchar_t> path)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 
rmrf(ArrayPtr<const wchar_t> path)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 
getPathFromHandle(HANDLE handle)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 
getAllocationGranularity()265 static size_t getAllocationGranularity() {
266   SYSTEM_INFO info;
267   GetSystemInfo(&info);
268   return info.dwAllocationGranularity;
269 };
270 
getMmapRange(uint64_t offset,uint64_t size)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:
disposeImpl(void * firstElement,size_t elementSize,size_t elementCount,size_t capacity,void (* destroyElement)(void *)) const287   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 
win32Mmap(HANDLE handle,MmapRange range,DWORD pageProtect,DWORD access)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:
DiskHandle(AutoCloseHandle && handle,Maybe<Path> dirPath)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 
nativePath(PathPtr path) const340   Array<wchar_t> nativePath(PathPtr path) const {
341     return KJ_ASSERT_NONNULL(dirPath).append(path).forWin32Api(true);
342   }
343 
344   // OsHandle ------------------------------------------------------------------
345 
clone() const346   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 
getWin32Handle() const353   HANDLE getWin32Handle() const {
354     return handle.get();
355   }
356 
357   // FsNode --------------------------------------------------------------------
358 
stat() const359   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 
sync() const382   void sync() const { KJ_WIN32(FlushFileBuffers(handle)); }
datasync() const383   void datasync() const { KJ_WIN32(FlushFileBuffers(handle)); }
384 
385   // ReadableFile --------------------------------------------------------------
386 
read(uint64_t offset,ArrayPtr<byte> buffer) const387   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 
mmap(uint64_t offset,uint64_t size) const418   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 
mmapPrivate(uint64_t offset,uint64_t size) const426   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 
write(uint64_t offset,ArrayPtr<const byte> data) const436   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 
zero(uint64_t offset,uint64_t size) const456   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 
truncate(uint64_t size) const484   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:
WritableFileMappingImpl(Array<byte> bytes)526     WritableFileMappingImpl(Array<byte> bytes): bytes(kj::mv(bytes)) {}
527 
get() const528     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 
changed(ArrayPtr<byte> slice) const534     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 
sync(ArrayPtr<byte> slice) const541     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 
mmapWritable(uint64_t offset,uint64_t size) const556   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>
list(bool needTypes,Func && func) const573   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 
listNames() const610   Array<String> listNames() const {
611     return list(false, [](StringPtr name, FsNode::Type type) { return heapString(name); });
612   }
613 
listEntries() const614   Array<ReadableDirectory::Entry> listEntries() const {
615     return list(true, [](StringPtr name, FsNode::Type type) {
616       return ReadableDirectory::Entry { type, heapString(name), };
617     });
618   }
619 
exists(PathPtr path) const620   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 
tryLstat(PathPtr path) const636   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 
tryOpenFile(PathPtr path) const651   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 
tryOpenSubdirInternal(PathPtr path) const671   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 
tryOpenSubdir(PathPtr path) const702   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 
tryReadlink(PathPtr path) const708   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 
makeSecAttr(WriteMode mode)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 
tryMkdir(PathPtr path,WriteMode mode,bool noThrow) const727   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 
createNamedTemporary(PathPtr finalName,WriteMode mode,Path & kjTempPath,Function<BOOL (const wchar_t *)> tryCreate) const775   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 
createNamedTemporary(PathPtr finalName,WriteMode mode,Function<BOOL (const wchar_t *)> tryCreate) const825   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 
tryReplaceNode(PathPtr path,WriteMode mode,Function<BOOL (const wchar_t *)> tryCreate) const831   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 
tryOpenFileInternal(PathPtr path,WriteMode mode,bool append) const898   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 
tryCommitReplacement(PathPtr toPath,ArrayPtr<const wchar_t> fromPath,WriteMode mode,kj::Maybe<kj::PathPtr> pathForCreatingParents=nullptr) const969   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:
ReplacerImpl(Own<T> && object,const DiskHandle & parentDirectory,Array<wchar_t> && tempPath,Path && path,WriteMode mode)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 
~ReplacerImpl()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 
get()1076     const T& get() override {
1077       return *object;
1078     }
1079 
tryCommit()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:
BrokenReplacer(Own<const T> inner)1124     BrokenReplacer(Own<const T> inner)
1125         : Directory::Replacer<T>(WriteMode::CREATE | WriteMode::MODIFY),
1126           inner(kj::mv(inner)) {}
1127 
get()1128     const T& get() override { return *inner; }
tryCommit()1129     bool tryCommit() override { return false; }
1130 
1131   private:
1132     Own<const T> inner;
1133   };
1134 
tryOpenFile(PathPtr path,WriteMode mode) const1135   Maybe<Own<const File>> tryOpenFile(PathPtr path, WriteMode mode) const {
1136     return tryOpenFileInternal(path, mode, false).map(newDiskFile);
1137   }
1138 
replaceFile(PathPtr path,WriteMode mode) const1139   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 
createTemporary() const1162   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 
tryAppendFile(PathPtr path,WriteMode mode) const1184   Maybe<Own<AppendableFile>> tryAppendFile(PathPtr path, WriteMode mode) const {
1185     return tryOpenFileInternal(path, mode, true).map(newDiskAppendableFile);
1186   }
1187 
tryOpenSubdir(PathPtr path,WriteMode mode) const1188   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 
replaceSubdir(PathPtr path,WriteMode mode) const1199   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 
trySymlink(PathPtr linkpath,StringPtr content,WriteMode mode) const1232   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 
tryTransfer(PathPtr toPath,WriteMode toMode,const Directory & fromDirectory,PathPtr fromPath,TransferMode mode,const Directory & self) const1243   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 
tryRemove(PathPtr path) const1304   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:
DiskReadableFile(AutoCloseHandle && handle)1318   DiskReadableFile(AutoCloseHandle&& handle): DiskHandle(kj::mv(handle), nullptr) {}
1319 
cloneFsNode() const1320   Own<const FsNode> cloneFsNode() const override {
1321     return heap<DiskReadableFile>(DiskHandle::clone());
1322   }
1323 
1324   FSNODE_METHODS
1325 
read(uint64_t offset,ArrayPtr<byte> buffer) const1326   size_t read(uint64_t offset, ArrayPtr<byte> buffer) const override {
1327     return DiskHandle::read(offset, buffer);
1328   }
mmap(uint64_t offset,uint64_t size) const1329   Array<const byte> mmap(uint64_t offset, uint64_t size) const override {
1330     return DiskHandle::mmap(offset, size);
1331   }
mmapPrivate(uint64_t offset,uint64_t size) const1332   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:
DiskAppendableFile(AutoCloseHandle && handle)1339   DiskAppendableFile(AutoCloseHandle&& handle)
1340       : DiskHandle(kj::mv(handle), nullptr),
1341         stream(DiskHandle::handle.get()) {}
1342 
cloneFsNode() const1343   Own<const FsNode> cloneFsNode() const override {
1344     return heap<DiskAppendableFile>(DiskHandle::clone());
1345   }
1346 
1347   FSNODE_METHODS
1348 
write(const void * buffer,size_t size)1349   void write(const void* buffer, size_t size) override { stream.write(buffer, size); }
write(ArrayPtr<const ArrayPtr<const byte>> pieces)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:
DiskFile(AutoCloseHandle && handle)1360   DiskFile(AutoCloseHandle&& handle): DiskHandle(kj::mv(handle), nullptr) {}
1361 
cloneFsNode() const1362   Own<const FsNode> cloneFsNode() const override {
1363     return heap<DiskFile>(DiskHandle::clone());
1364   }
1365 
1366   FSNODE_METHODS
1367 
read(uint64_t offset,ArrayPtr<byte> buffer) const1368   size_t read(uint64_t offset, ArrayPtr<byte> buffer) const override {
1369     return DiskHandle::read(offset, buffer);
1370   }
mmap(uint64_t offset,uint64_t size) const1371   Array<const byte> mmap(uint64_t offset, uint64_t size) const override {
1372     return DiskHandle::mmap(offset, size);
1373   }
mmapPrivate(uint64_t offset,uint64_t size) const1374   Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const override {
1375     return DiskHandle::mmapPrivate(offset, size);
1376   }
1377 
write(uint64_t offset,ArrayPtr<const byte> data) const1378   void write(uint64_t offset, ArrayPtr<const byte> data) const override {
1379     DiskHandle::write(offset, data);
1380   }
zero(uint64_t offset,uint64_t size) const1381   void zero(uint64_t offset, uint64_t size) const override {
1382     DiskHandle::zero(offset, size);
1383   }
truncate(uint64_t size) const1384   void truncate(uint64_t size) const override {
1385     DiskHandle::truncate(size);
1386   }
mmapWritable(uint64_t offset,uint64_t size) const1387   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:
DiskReadableDirectory(AutoCloseHandle && handle,Path && path)1395   DiskReadableDirectory(AutoCloseHandle&& handle, Path&& path)
1396       : DiskHandle(kj::mv(handle), kj::mv(path)) {}
1397 
cloneFsNode() const1398   Own<const FsNode> cloneFsNode() const override {
1399     return heap<DiskReadableDirectory>(DiskHandle::clone(), KJ_ASSERT_NONNULL(dirPath).clone());
1400   }
1401 
1402   FSNODE_METHODS
1403 
listNames() const1404   Array<String> listNames() const override { return DiskHandle::listNames(); }
listEntries() const1405   Array<Entry> listEntries() const override { return DiskHandle::listEntries(); }
exists(PathPtr path) const1406   bool exists(PathPtr path) const override { return DiskHandle::exists(path); }
tryLstat(PathPtr path) const1407   Maybe<FsNode::Metadata> tryLstat(PathPtr path) const override {
1408     return DiskHandle::tryLstat(path);
1409   }
tryOpenFile(PathPtr path) const1410   Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const override {
1411     return DiskHandle::tryOpenFile(path);
1412   }
tryOpenSubdir(PathPtr path) const1413   Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const override {
1414     return DiskHandle::tryOpenSubdir(path);
1415   }
tryReadlink(PathPtr path) const1416   Maybe<String> tryReadlink(PathPtr path) const override { return DiskHandle::tryReadlink(path); }
1417 };
1418 
1419 class DiskDirectoryBase: public Directory, public DiskHandle {
1420 public:
DiskDirectoryBase(AutoCloseHandle && handle,Path && path)1421   DiskDirectoryBase(AutoCloseHandle&& handle, Path&& path)
1422       : DiskHandle(kj::mv(handle), kj::mv(path)) {}
1423 
exists(PathPtr path) const1424   bool exists(PathPtr path) const override { return DiskHandle::exists(path); }
tryLstat(PathPtr path) const1425   Maybe<FsNode::Metadata> tryLstat(PathPtr path) const override { return DiskHandle::tryLstat(path); }
tryOpenFile(PathPtr path) const1426   Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const override {
1427     return DiskHandle::tryOpenFile(path);
1428   }
tryOpenSubdir(PathPtr path) const1429   Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const override {
1430     return DiskHandle::tryOpenSubdir(path);
1431   }
tryReadlink(PathPtr path) const1432   Maybe<String> tryReadlink(PathPtr path) const override { return DiskHandle::tryReadlink(path); }
1433 
tryOpenFile(PathPtr path,WriteMode mode) const1434   Maybe<Own<const File>> tryOpenFile(PathPtr path, WriteMode mode) const override {
1435     return DiskHandle::tryOpenFile(path, mode);
1436   }
replaceFile(PathPtr path,WriteMode mode) const1437   Own<Replacer<File>> replaceFile(PathPtr path, WriteMode mode) const override {
1438     return DiskHandle::replaceFile(path, mode);
1439   }
tryAppendFile(PathPtr path,WriteMode mode) const1440   Maybe<Own<AppendableFile>> tryAppendFile(PathPtr path, WriteMode mode) const override {
1441     return DiskHandle::tryAppendFile(path, mode);
1442   }
tryOpenSubdir(PathPtr path,WriteMode mode) const1443   Maybe<Own<const Directory>> tryOpenSubdir(PathPtr path, WriteMode mode) const override {
1444     return DiskHandle::tryOpenSubdir(path, mode);
1445   }
replaceSubdir(PathPtr path,WriteMode mode) const1446   Own<Replacer<Directory>> replaceSubdir(PathPtr path, WriteMode mode) const override {
1447     return DiskHandle::replaceSubdir(path, mode);
1448   }
trySymlink(PathPtr linkpath,StringPtr content,WriteMode mode) const1449   bool trySymlink(PathPtr linkpath, StringPtr content, WriteMode mode) const override {
1450     return DiskHandle::trySymlink(linkpath, content, mode);
1451   }
tryTransfer(PathPtr toPath,WriteMode toMode,const Directory & fromDirectory,PathPtr fromPath,TransferMode mode) const1452   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.
tryRemove(PathPtr path) const1458   bool tryRemove(PathPtr path) const override {
1459     return DiskHandle::tryRemove(path);
1460   }
1461 };
1462 
1463 class DiskDirectory final: public DiskDirectoryBase {
1464 public:
DiskDirectory(AutoCloseHandle && handle,Path && path)1465   DiskDirectory(AutoCloseHandle&& handle, Path&& path)
1466       : DiskDirectoryBase(kj::mv(handle), kj::mv(path)) {}
1467 
cloneFsNode() const1468   Own<const FsNode> cloneFsNode() const override {
1469     return heap<DiskDirectory>(DiskHandle::clone(), KJ_ASSERT_NONNULL(dirPath).clone());
1470   }
1471 
1472   FSNODE_METHODS
1473 
listNames() const1474   Array<String> listNames() const override { return DiskHandle::listNames(); }
listEntries() const1475   Array<Entry> listEntries() const override { return DiskHandle::listEntries(); }
createTemporary() const1476   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:
RootDiskDirectory()1489   RootDiskDirectory(): DiskDirectoryBase(nullptr, Path(nullptr)) {}
1490 
cloneFsNode() const1491   Own<const FsNode> cloneFsNode() const override {
1492     return heap<RootDiskDirectory>();
1493   }
1494 
stat() const1495   Metadata stat() const override {
1496     return { Type::DIRECTORY, 0, 0, UNIX_EPOCH, 1, 0 };
1497   }
sync() const1498   void sync() const override {}
datasync() const1499   void datasync() const override {}
1500 
listNames() const1501   Array<String> listNames() const override {
1502     return KJ_MAP(e, listEntries()) { return kj::mv(e.name); };
1503   }
listEntries() const1504   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 
createTemporary() const1521   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:
DiskFilesystem()1528   DiskFilesystem()
1529       : DiskFilesystem(computeCurrentPath()) {}
DiskFilesystem(Path currentPath)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 
getRoot() const1535   const Directory& getRoot() const override {
1536     return root;
1537   }
1538 
getCurrent() const1539   const Directory& getCurrent() const override {
1540     return current;
1541   }
1542 
getCurrentPath() const1543   PathPtr getCurrentPath() const override {
1544     return KJ_ASSERT_NONNULL(current.dirPath);
1545   }
1546 
1547 private:
1548   RootDiskDirectory root;
1549   DiskDirectory current;
1550 
computeCurrentPath()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 
newDiskReadableFile(AutoCloseHandle fd)1571 Own<ReadableFile> newDiskReadableFile(AutoCloseHandle fd) {
1572   return heap<DiskReadableFile>(kj::mv(fd));
1573 }
newDiskAppendableFile(AutoCloseHandle fd)1574 Own<AppendableFile> newDiskAppendableFile(AutoCloseHandle fd) {
1575   return heap<DiskAppendableFile>(kj::mv(fd));
1576 }
newDiskFile(AutoCloseHandle fd)1577 Own<File> newDiskFile(AutoCloseHandle fd) {
1578   return heap<DiskFile>(kj::mv(fd));
1579 }
newDiskReadableDirectory(AutoCloseHandle fd)1580 Own<ReadableDirectory> newDiskReadableDirectory(AutoCloseHandle fd) {
1581   return heap<DiskReadableDirectory>(kj::mv(fd), getPathFromHandle(fd));
1582 }
newDiskReadableDirectory(AutoCloseHandle fd,Path && path)1583 static Own<ReadableDirectory> newDiskReadableDirectory(AutoCloseHandle fd, Path&& path) {
1584   return heap<DiskReadableDirectory>(kj::mv(fd), kj::mv(path));
1585 }
newDiskDirectory(AutoCloseHandle fd)1586 Own<Directory> newDiskDirectory(AutoCloseHandle fd) {
1587   return heap<DiskDirectory>(kj::mv(fd), getPathFromHandle(fd));
1588 }
newDiskDirectory(AutoCloseHandle fd,Path && path)1589 static Own<Directory> newDiskDirectory(AutoCloseHandle fd, Path&& path) {
1590   return heap<DiskDirectory>(kj::mv(fd), kj::mv(path));
1591 }
1592 
newDiskFilesystem()1593 Own<Filesystem> newDiskFilesystem() {
1594   return heap<DiskFilesystem>();
1595 }
1596 
getHandlePointerHack(Directory & dir)1597 static AutoCloseHandle* getHandlePointerHack(Directory& dir) {
1598   return &static_cast<DiskDirectoryBase&>(dir).handle;
1599 }
getPathPointerHack(Directory & dir)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
1607