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.c++ (58211B)


      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 #include "filesystem.h"
     23 #include "vector.h"
     24 #include "debug.h"
     25 #include "one-of.h"
     26 #include "encoding.h"
     27 #include "refcount.h"
     28 #include "mutex.h"
     29 #include <map>
     30 
     31 namespace kj {
     32 
     33 Path::Path(StringPtr name): Path(heapString(name)) {}
     34 Path::Path(String&& name): parts(heapArray<String>(1)) {
     35   parts[0] = kj::mv(name);
     36   validatePart(parts[0]);
     37 }
     38 
     39 Path::Path(ArrayPtr<const StringPtr> parts)
     40     : Path(KJ_MAP(p, parts) { return heapString(p); }) {}
     41 Path::Path(Array<String> partsParam)
     42     : Path(kj::mv(partsParam), ALREADY_CHECKED) {
     43   for (auto& p: parts) {
     44     validatePart(p);
     45   }
     46 }
     47 
     48 Path PathPtr::clone() {
     49   return Path(KJ_MAP(p, parts) { return heapString(p); }, Path::ALREADY_CHECKED);
     50 }
     51 
     52 Path Path::parse(StringPtr path) {
     53   KJ_REQUIRE(!path.startsWith("/"), "expected a relative path, got absolute", path) {
     54     // When exceptions are disabled, go on -- the leading '/' will end up ignored.
     55     break;
     56   }
     57   return evalImpl(Vector<String>(countParts(path)), path);
     58 }
     59 
     60 Path Path::parseWin32Api(ArrayPtr<const wchar_t> text) {
     61   auto utf8 = decodeWideString(text);
     62   return evalWin32Impl(Vector<String>(countPartsWin32(utf8)), utf8, true);
     63 }
     64 
     65 Path PathPtr::append(Path&& suffix) const {
     66   auto newParts = kj::heapArrayBuilder<String>(parts.size() + suffix.parts.size());
     67   for (auto& p: parts) newParts.add(heapString(p));
     68   for (auto& p: suffix.parts) newParts.add(kj::mv(p));
     69   return Path(newParts.finish(), Path::ALREADY_CHECKED);
     70 }
     71 Path Path::append(Path&& suffix) && {
     72   auto newParts = kj::heapArrayBuilder<String>(parts.size() + suffix.parts.size());
     73   for (auto& p: parts) newParts.add(kj::mv(p));
     74   for (auto& p: suffix.parts) newParts.add(kj::mv(p));
     75   return Path(newParts.finish(), ALREADY_CHECKED);
     76 }
     77 Path PathPtr::append(PathPtr suffix) const {
     78   auto newParts = kj::heapArrayBuilder<String>(parts.size() + suffix.parts.size());
     79   for (auto& p: parts) newParts.add(heapString(p));
     80   for (auto& p: suffix.parts) newParts.add(heapString(p));
     81   return Path(newParts.finish(), Path::ALREADY_CHECKED);
     82 }
     83 Path Path::append(PathPtr suffix) && {
     84   auto newParts = kj::heapArrayBuilder<String>(parts.size() + suffix.parts.size());
     85   for (auto& p: parts) newParts.add(kj::mv(p));
     86   for (auto& p: suffix.parts) newParts.add(heapString(p));
     87   return Path(newParts.finish(), ALREADY_CHECKED);
     88 }
     89 
     90 Path PathPtr::eval(StringPtr pathText) const {
     91   if (pathText.startsWith("/")) {
     92     // Optimization: avoid copying parts that will just be dropped.
     93     return Path::evalImpl(Vector<String>(Path::countParts(pathText)), pathText);
     94   } else {
     95     Vector<String> newParts(parts.size() + Path::countParts(pathText));
     96     for (auto& p: parts) newParts.add(heapString(p));
     97     return Path::evalImpl(kj::mv(newParts), pathText);
     98   }
     99 }
    100 Path Path::eval(StringPtr pathText) && {
    101   if (pathText.startsWith("/")) {
    102     // Optimization: avoid copying parts that will just be dropped.
    103     return evalImpl(Vector<String>(countParts(pathText)), pathText);
    104   } else {
    105     Vector<String> newParts(parts.size() + countParts(pathText));
    106     for (auto& p: parts) newParts.add(kj::mv(p));
    107     return evalImpl(kj::mv(newParts), pathText);
    108   }
    109 }
    110 
    111 PathPtr PathPtr::basename() const {
    112   KJ_REQUIRE(parts.size() > 0, "root path has no basename");
    113   return PathPtr(parts.slice(parts.size() - 1, parts.size()));
    114 }
    115 Path Path::basename() && {
    116   KJ_REQUIRE(parts.size() > 0, "root path has no basename");
    117   auto newParts = kj::heapArrayBuilder<String>(1);
    118   newParts.add(kj::mv(parts[parts.size() - 1]));
    119   return Path(newParts.finish(), ALREADY_CHECKED);
    120 }
    121 
    122 PathPtr PathPtr::parent() const {
    123   KJ_REQUIRE(parts.size() > 0, "root path has no parent");
    124   return PathPtr(parts.slice(0, parts.size() - 1));
    125 }
    126 Path Path::parent() && {
    127   KJ_REQUIRE(parts.size() > 0, "root path has no parent");
    128   return Path(KJ_MAP(p, parts.slice(0, parts.size() - 1)) { return kj::mv(p); }, ALREADY_CHECKED);
    129 }
    130 
    131 String PathPtr::toString(bool absolute) const {
    132   if (parts.size() == 0) {
    133     // Special-case empty path.
    134     return absolute ? kj::str("/") : kj::str(".");
    135   }
    136 
    137   size_t size = absolute + (parts.size() - 1);
    138   for (auto& p: parts) size += p.size();
    139 
    140   String result = kj::heapString(size);
    141 
    142   char* ptr = result.begin();
    143   bool leadingSlash = absolute;
    144   for (auto& p: parts) {
    145     if (leadingSlash) *ptr++ = '/';
    146     leadingSlash = true;
    147     memcpy(ptr, p.begin(), p.size());
    148     ptr += p.size();
    149   }
    150   KJ_ASSERT(ptr == result.end());
    151 
    152   return result;
    153 }
    154 
    155 Path Path::slice(size_t start, size_t end) && {
    156   return Path(KJ_MAP(p, parts.slice(start, end)) { return kj::mv(p); });
    157 }
    158 
    159 bool PathPtr::operator==(PathPtr other) const {
    160   return parts == other.parts;
    161 }
    162 bool PathPtr::operator< (PathPtr other) const {
    163   for (size_t i = 0; i < kj::min(parts.size(), other.parts.size()); i++) {
    164     int comp = strcmp(parts[i].cStr(), other.parts[i].cStr());
    165     if (comp < 0) return true;
    166     if (comp > 0) return false;
    167   }
    168 
    169   return parts.size() < other.parts.size();
    170 }
    171 
    172 bool PathPtr::startsWith(PathPtr prefix) const {
    173   return parts.size() >= prefix.parts.size() &&
    174          parts.slice(0, prefix.parts.size()) == prefix.parts;
    175 }
    176 
    177 bool PathPtr::endsWith(PathPtr suffix) const {
    178   return parts.size() >= suffix.parts.size() &&
    179          parts.slice(parts.size() - suffix.parts.size(), parts.size()) == suffix.parts;
    180 }
    181 
    182 Path PathPtr::evalWin32(StringPtr pathText) const {
    183   Vector<String> newParts(parts.size() + Path::countPartsWin32(pathText));
    184   for (auto& p: parts) newParts.add(heapString(p));
    185   return Path::evalWin32Impl(kj::mv(newParts), pathText);
    186 }
    187 Path Path::evalWin32(StringPtr pathText) && {
    188   Vector<String> newParts(parts.size() + countPartsWin32(pathText));
    189   for (auto& p: parts) newParts.add(kj::mv(p));
    190   return evalWin32Impl(kj::mv(newParts), pathText);
    191 }
    192 
    193 String PathPtr::toWin32StringImpl(bool absolute, bool forApi) const {
    194   if (parts.size() == 0) {
    195     // Special-case empty path.
    196     KJ_REQUIRE(!absolute, "absolute path is missing disk designator") {
    197       break;
    198     }
    199     return absolute ? kj::str("\\\\") : kj::str(".");
    200   }
    201 
    202   bool isUncPath = false;
    203   if (absolute) {
    204     if (Path::isWin32Drive(parts[0])) {
    205       // It's a win32 drive
    206     } else if (Path::isNetbiosName(parts[0])) {
    207       isUncPath = true;
    208     } else {
    209       KJ_FAIL_REQUIRE("absolute win32 path must start with drive letter or netbios host name",
    210                       parts[0]);
    211     }
    212   } else {
    213     // Currently we do nothing differently in the forApi case for relative paths.
    214     forApi = false;
    215   }
    216 
    217   size_t size = forApi
    218       ? (isUncPath ? 8 : 4) + (parts.size() - 1)
    219       : (isUncPath ? 2 : 0) + (parts.size() - 1);
    220   for (auto& p: parts) size += p.size();
    221 
    222   String result = heapString(size);
    223 
    224   char* ptr = result.begin();
    225 
    226   if (forApi) {
    227     *ptr++ = '\\';
    228     *ptr++ = '\\';
    229     *ptr++ = '?';
    230     *ptr++ = '\\';
    231     if (isUncPath) {
    232       *ptr++ = 'U';
    233       *ptr++ = 'N';
    234       *ptr++ = 'C';
    235       *ptr++ = '\\';
    236     }
    237   } else {
    238     if (isUncPath) {
    239       *ptr++ = '\\';
    240       *ptr++ = '\\';
    241     }
    242   }
    243 
    244   bool leadingSlash = false;
    245   for (auto& p: parts) {
    246     if (leadingSlash) *ptr++ = '\\';
    247     leadingSlash = true;
    248 
    249     KJ_REQUIRE(!Path::isWin32Special(p), "path cannot contain DOS reserved name", p) {
    250       // Recover by blotting out the name with invalid characters which Win32 syscalls will reject.
    251       for (size_t i = 0; i < p.size(); i++) {
    252         *ptr++ = '|';
    253       }
    254       goto skip;
    255     }
    256 
    257     memcpy(ptr, p.begin(), p.size());
    258     ptr += p.size();
    259   skip:;
    260   }
    261 
    262   KJ_ASSERT(ptr == result.end());
    263 
    264   // Check for colons (other than in drive letter), which on NTFS would be interpreted as an
    265   // "alternate data stream", which can lead to surprising results. If we want to support ADS, we
    266   // should do so using an explicit API. Note that this check also prevents a relative path from
    267   // appearing to start with a drive letter.
    268   for (size_t i: kj::indices(result)) {
    269     if (result[i] == ':') {
    270       if (absolute && i == (forApi ? 5 : 1)) {
    271         // False alarm: this is the drive letter.
    272       } else {
    273         KJ_FAIL_REQUIRE(
    274             "colons are prohibited in win32 paths to avoid triggering alterante data streams",
    275             result) {
    276           // Recover by using a different character which we know Win32 syscalls will reject.
    277           result[i] = '|';
    278           break;
    279         }
    280       }
    281     }
    282   }
    283 
    284   return result;
    285 }
    286 
    287 Array<wchar_t> PathPtr::forWin32Api(bool absolute) const {
    288   return encodeWideString(toWin32StringImpl(absolute, true), true);
    289 }
    290 
    291 // -----------------------------------------------------------------------------
    292 
    293 String Path::stripNul(String input) {
    294   kj::Vector<char> output(input.size());
    295   for (char c: input) {
    296     if (c != '\0') output.add(c);
    297   }
    298   output.add('\0');
    299   return String(output.releaseAsArray());
    300 }
    301 
    302 void Path::validatePart(StringPtr part) {
    303   KJ_REQUIRE(part != "" && part != "." && part != "..", "invalid path component", part);
    304   KJ_REQUIRE(strlen(part.begin()) == part.size(), "NUL character in path component", part);
    305   KJ_REQUIRE(part.findFirst('/') == nullptr,
    306       "'/' character in path component; did you mean to use Path::parse()?", part);
    307 }
    308 
    309 void Path::evalPart(Vector<String>& parts, ArrayPtr<const char> part) {
    310   if (part.size() == 0) {
    311     // Ignore consecutive or trailing '/'s.
    312   } else if (part.size() == 1 && part[0] == '.') {
    313     // Refers to current directory; ignore.
    314   } else if (part.size() == 2 && part[0] == '.' && part [1] == '.') {
    315     KJ_REQUIRE(parts.size() > 0, "can't use \"..\" to break out of starting directory") {
    316       // When exceptions are disabled, ignore.
    317       return;
    318     }
    319     parts.removeLast();
    320   } else {
    321     auto str = heapString(part);
    322     KJ_REQUIRE(strlen(str.begin()) == str.size(), "NUL character in path component", str) {
    323       // When exceptions are disabled, strip out '\0' chars.
    324       str = stripNul(kj::mv(str));
    325       break;
    326     }
    327     parts.add(kj::mv(str));
    328   }
    329 }
    330 
    331 Path Path::evalImpl(Vector<String>&& parts, StringPtr path) {
    332   if (path.startsWith("/")) {
    333     parts.clear();
    334   }
    335 
    336   size_t partStart = 0;
    337   for (auto i: kj::indices(path)) {
    338     if (path[i] == '/') {
    339       evalPart(parts, path.slice(partStart, i));
    340       partStart = i + 1;
    341     }
    342   }
    343   evalPart(parts, path.slice(partStart));
    344 
    345   return Path(parts.releaseAsArray(), Path::ALREADY_CHECKED);
    346 }
    347 
    348 Path Path::evalWin32Impl(Vector<String>&& parts, StringPtr path, bool fromApi) {
    349   // Convert all forward slashes to backslashes.
    350   String ownPath;
    351   if (!fromApi && path.findFirst('/') != nullptr) {
    352     ownPath = heapString(path);
    353     for (char& c: ownPath) {
    354       if (c == '/') c = '\\';
    355     }
    356     path = ownPath;
    357   }
    358 
    359   // Interpret various forms of absolute paths.
    360   if (fromApi && path.startsWith("\\\\?\\")) {
    361     path = path.slice(4);
    362     if (path.startsWith("UNC\\")) {
    363       path = path.slice(4);
    364     }
    365 
    366     // The path is absolute.
    367     parts.clear();
    368   } else if (path.startsWith("\\\\")) {
    369     // UNC path.
    370     path = path.slice(2);
    371 
    372     // This path is absolute. The first component is a server name.
    373     parts.clear();
    374   } else if (path.startsWith("\\")) {
    375     KJ_REQUIRE(!fromApi, "parseWin32Api() requires absolute path");
    376 
    377     // Path is relative to the current drive / network share.
    378     if (parts.size() >= 1 && isWin32Drive(parts[0])) {
    379       // Leading \ interpreted as root of current drive.
    380       parts.truncate(1);
    381     } else if (parts.size() >= 2) {
    382       // Leading \ interpreted as root of current network share (which is indicated by the first
    383       // *two* components of the path).
    384       parts.truncate(2);
    385     } else {
    386       KJ_FAIL_REQUIRE("must specify drive letter", path) {
    387         // Recover by assuming C drive.
    388         parts.clear();
    389         parts.add(kj::str("c:"));
    390         break;
    391       }
    392     }
    393   } else if ((path.size() == 2 || (path.size() > 2 && path[2] == '\\')) &&
    394              isWin32Drive(path.slice(0, 2))) {
    395     // Starts with a drive letter.
    396     parts.clear();
    397   } else {
    398     KJ_REQUIRE(!fromApi, "parseWin32Api() requires absolute path");
    399   }
    400 
    401   size_t partStart = 0;
    402   for (auto i: kj::indices(path)) {
    403     if (path[i] == '\\') {
    404       evalPart(parts, path.slice(partStart, i));
    405       partStart = i + 1;
    406     }
    407   }
    408   evalPart(parts, path.slice(partStart));
    409 
    410   return Path(parts.releaseAsArray(), Path::ALREADY_CHECKED);
    411 }
    412 
    413 size_t Path::countParts(StringPtr path) {
    414   size_t result = 1;
    415   for (char c: path) {
    416     result += (c == '/');
    417   }
    418   return result;
    419 }
    420 
    421 size_t Path::countPartsWin32(StringPtr path) {
    422   size_t result = 1;
    423   for (char c: path) {
    424     result += (c == '/' || c == '\\');
    425   }
    426   return result;
    427 }
    428 
    429 bool Path::isWin32Drive(ArrayPtr<const char> part) {
    430   return part.size() == 2 && part[1] == ':' &&
    431          (('a' <= part[0] && part[0] <= 'z') || ('A' <= part[0] && part[0] <= 'Z'));
    432 }
    433 
    434 bool Path::isNetbiosName(ArrayPtr<const char> part) {
    435   // Characters must be alphanumeric or '.' or '-'.
    436   for (char c: part) {
    437     if (c != '.' && c != '-' &&
    438         (c < 'a' || 'z' < c) &&
    439         (c < 'A' || 'Z' < c) &&
    440         (c < '0' || '9' < c)) {
    441       return false;
    442     }
    443   }
    444 
    445   // Can't be empty nor start or end with a '.' or a '-'.
    446   return part.size() > 0 &&
    447       part[0] != '.' && part[0] != '-' &&
    448       part[part.size() - 1] != '.' && part[part.size() - 1] != '-';
    449 }
    450 
    451 bool Path::isWin32Special(StringPtr part) {
    452   bool isNumbered;
    453   if (part.size() == 3 || (part.size() > 3 && part[3] == '.')) {
    454     // Filename is three characters or three characters followed by an extension.
    455     isNumbered = false;
    456   } else if ((part.size() == 4 || (part.size() > 4 && part[4] == '.')) &&
    457              '1' <= part[3] && part[3] <= '9') {
    458     // Filename is four characters or four characters followed by an extension, and the fourth
    459     // character is a nonzero digit.
    460     isNumbered = true;
    461   } else {
    462     return false;
    463   }
    464 
    465   // OK, this could be a Win32 special filename. We need to match the first three letters against
    466   // the list of specials, case-insensitively.
    467   char tmp[4];
    468   memcpy(tmp, part.begin(), 3);
    469   tmp[3] = '\0';
    470   for (char& c: tmp) {
    471     if ('A' <= c && c <= 'Z') {
    472       c += 'a' - 'A';
    473     }
    474   }
    475 
    476   StringPtr str(tmp, 3);
    477   if (isNumbered) {
    478     // Specials that are followed by a digit.
    479     return str == "com" || str == "lpt";
    480   } else {
    481     // Specials that are not followed by a digit.
    482     return str == "con" || str == "prn" || str == "aux" || str == "nul";
    483   }
    484 }
    485 
    486 // =======================================================================================
    487 
    488 String ReadableFile::readAllText() const {
    489   String result = heapString(stat().size);
    490   size_t n = read(0, result.asBytes());
    491   if (n < result.size()) {
    492     // Apparently file was truncated concurrently. Reduce to new size to match.
    493     result = heapString(result.slice(0, n));
    494   }
    495   return result;
    496 }
    497 
    498 Array<byte> ReadableFile::readAllBytes() const {
    499   Array<byte> result = heapArray<byte>(stat().size);
    500   size_t n = read(0, result.asBytes());
    501   if (n < result.size()) {
    502     // Apparently file was truncated concurrently. Reduce to new size to match.
    503     result = heapArray(result.slice(0, n));
    504   }
    505   return result;
    506 }
    507 
    508 void File::writeAll(ArrayPtr<const byte> bytes) const {
    509   truncate(0);
    510   write(0, bytes);
    511 }
    512 
    513 void File::writeAll(StringPtr text) const {
    514   writeAll(text.asBytes());
    515 }
    516 
    517 size_t File::copy(uint64_t offset, const ReadableFile& from,
    518                   uint64_t fromOffset, uint64_t size) const {
    519   byte buffer[8192];
    520 
    521   size_t result = 0;
    522   while (size > 0) {
    523     size_t n = from.read(fromOffset, kj::arrayPtr(buffer, kj::min(sizeof(buffer), size)));
    524     write(offset, arrayPtr(buffer, n));
    525     result += n;
    526     if (n < sizeof(buffer)) {
    527       // Either we copied the amount requested or we hit EOF.
    528       break;
    529     }
    530     fromOffset += n;
    531     offset += n;
    532     size -= n;
    533   }
    534 
    535   return result;
    536 }
    537 
    538 FsNode::Metadata ReadableDirectory::lstat(PathPtr path) const {
    539   KJ_IF_MAYBE(meta, tryLstat(path)) {
    540     return *meta;
    541   } else {
    542     KJ_FAIL_REQUIRE("no such file", path) { break; }
    543     return FsNode::Metadata();
    544   }
    545 }
    546 
    547 Own<const ReadableFile> ReadableDirectory::openFile(PathPtr path) const {
    548   KJ_IF_MAYBE(file, tryOpenFile(path)) {
    549     return kj::mv(*file);
    550   } else {
    551     KJ_FAIL_REQUIRE("no such directory", path) { break; }
    552     return newInMemoryFile(nullClock());
    553   }
    554 }
    555 
    556 Own<const ReadableDirectory> ReadableDirectory::openSubdir(PathPtr path) const {
    557   KJ_IF_MAYBE(dir, tryOpenSubdir(path)) {
    558     return kj::mv(*dir);
    559   } else {
    560     KJ_FAIL_REQUIRE("no such file or directory", path) { break; }
    561     return newInMemoryDirectory(nullClock());
    562   }
    563 }
    564 
    565 String ReadableDirectory::readlink(PathPtr path) const {
    566   KJ_IF_MAYBE(p, tryReadlink(path)) {
    567     return kj::mv(*p);
    568   } else {
    569     KJ_FAIL_REQUIRE("not a symlink", path) { break; }
    570     return kj::str(".");
    571   }
    572 }
    573 
    574 Own<const File> Directory::openFile(PathPtr path, WriteMode mode) const {
    575   KJ_IF_MAYBE(f, tryOpenFile(path, mode)) {
    576     return kj::mv(*f);
    577   } else if (has(mode, WriteMode::CREATE) && !has(mode, WriteMode::MODIFY)) {
    578     KJ_FAIL_REQUIRE("file already exists", path) { break; }
    579   } else if (has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    580     KJ_FAIL_REQUIRE("file does not exist", path) { break; }
    581   } else if (!has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    582     KJ_FAIL_ASSERT("neither WriteMode::CREATE nor WriteMode::MODIFY was given", path) { break; }
    583   } else {
    584     // Shouldn't happen.
    585     KJ_FAIL_ASSERT("tryOpenFile() returned null despite no preconditions", path) { break; }
    586   }
    587   return newInMemoryFile(nullClock());
    588 }
    589 
    590 Own<AppendableFile> Directory::appendFile(PathPtr path, WriteMode mode) const {
    591   KJ_IF_MAYBE(f, tryAppendFile(path, mode)) {
    592     return kj::mv(*f);
    593   } else if (has(mode, WriteMode::CREATE) && !has(mode, WriteMode::MODIFY)) {
    594     KJ_FAIL_REQUIRE("file already exists", path) { break; }
    595   } else if (has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    596     KJ_FAIL_REQUIRE("file does not exist", path) { break; }
    597   } else if (!has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    598     KJ_FAIL_ASSERT("neither WriteMode::CREATE nor WriteMode::MODIFY was given", path) { break; }
    599   } else {
    600     // Shouldn't happen.
    601     KJ_FAIL_ASSERT("tryAppendFile() returned null despite no preconditions", path) { break; }
    602   }
    603   return newFileAppender(newInMemoryFile(nullClock()));
    604 }
    605 
    606 Own<const Directory> Directory::openSubdir(PathPtr path, WriteMode mode) const {
    607   KJ_IF_MAYBE(f, tryOpenSubdir(path, mode)) {
    608     return kj::mv(*f);
    609   } else if (has(mode, WriteMode::CREATE) && !has(mode, WriteMode::MODIFY)) {
    610     KJ_FAIL_REQUIRE("directory already exists", path) { break; }
    611   } else if (has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    612     KJ_FAIL_REQUIRE("directory does not exist", path) { break; }
    613   } else if (!has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    614     KJ_FAIL_ASSERT("neither WriteMode::CREATE nor WriteMode::MODIFY was given", path) { break; }
    615   } else {
    616     // Shouldn't happen.
    617     KJ_FAIL_ASSERT("tryOpenSubdir() returned null despite no preconditions", path) { break; }
    618   }
    619   return newInMemoryDirectory(nullClock());
    620 }
    621 
    622 void Directory::symlink(PathPtr linkpath, StringPtr content, WriteMode mode) const {
    623   if (!trySymlink(linkpath, content, mode)) {
    624     if (has(mode, WriteMode::CREATE)) {
    625       KJ_FAIL_REQUIRE("path already exists", linkpath) { break; }
    626     } else {
    627       // Shouldn't happen.
    628       KJ_FAIL_ASSERT("symlink() returned null despite no preconditions", linkpath) { break; }
    629     }
    630   }
    631 }
    632 
    633 void Directory::transfer(PathPtr toPath, WriteMode toMode,
    634                          const Directory& fromDirectory, PathPtr fromPath,
    635                          TransferMode mode) const {
    636   if (!tryTransfer(toPath, toMode, fromDirectory, fromPath, mode)) {
    637     if (has(toMode, WriteMode::CREATE)) {
    638       KJ_FAIL_REQUIRE("toPath already exists or fromPath doesn't exist", toPath, fromPath) {
    639         break;
    640       }
    641     } else {
    642       KJ_FAIL_ASSERT("fromPath doesn't exist", fromPath) { break; }
    643     }
    644   }
    645 }
    646 
    647 static void copyContents(const Directory& to, const ReadableDirectory& from);
    648 
    649 static bool tryCopyDirectoryEntry(const Directory& to, PathPtr toPath, WriteMode toMode,
    650                                   const ReadableDirectory& from, PathPtr fromPath,
    651                                   FsNode::Type type, bool atomic) {
    652   // TODO(cleanup): Make this reusable?
    653 
    654   switch (type) {
    655     case FsNode::Type::FILE: {
    656       KJ_IF_MAYBE(fromFile, from.tryOpenFile(fromPath)) {
    657         if (atomic) {
    658           auto replacer = to.replaceFile(toPath, toMode);
    659           replacer->get().copy(0, **fromFile, 0, kj::maxValue);
    660           return replacer->tryCommit();
    661         } else KJ_IF_MAYBE(toFile, to.tryOpenFile(toPath, toMode)) {
    662           toFile->get()->copy(0, **fromFile, 0, kj::maxValue);
    663           return true;
    664         } else {
    665           return false;
    666         }
    667       } else {
    668         // Apparently disappeared. Treat as source-doesn't-exist.
    669         return false;
    670       }
    671     }
    672     case FsNode::Type::DIRECTORY:
    673       KJ_IF_MAYBE(fromSubdir, from.tryOpenSubdir(fromPath)) {
    674         if (atomic) {
    675           auto replacer = to.replaceSubdir(toPath, toMode);
    676           copyContents(replacer->get(), **fromSubdir);
    677           return replacer->tryCommit();
    678         } else KJ_IF_MAYBE(toSubdir, to.tryOpenSubdir(toPath, toMode)) {
    679           copyContents(**toSubdir, **fromSubdir);
    680           return true;
    681         } else {
    682           return false;
    683         }
    684       } else {
    685         // Apparently disappeared. Treat as source-doesn't-exist.
    686         return false;
    687       }
    688     case FsNode::Type::SYMLINK:
    689       KJ_IF_MAYBE(content, from.tryReadlink(fromPath)) {
    690         return to.trySymlink(toPath, *content, toMode);
    691       } else {
    692         // Apparently disappeared. Treat as source-doesn't-exist.
    693         return false;
    694       }
    695       break;
    696 
    697     default:
    698       // Note: Unclear whether it's better to throw an error here or just ignore it / log a
    699       //   warning. Can reconsider when we see an actual use case.
    700       KJ_FAIL_REQUIRE("can only copy files, directories, and symlinks", fromPath) {
    701         return false;
    702       }
    703   }
    704 }
    705 
    706 static void copyContents(const Directory& to, const ReadableDirectory& from) {
    707   for (auto& entry: from.listEntries()) {
    708     Path subPath(kj::mv(entry.name));
    709     tryCopyDirectoryEntry(to, subPath, WriteMode::CREATE, from, subPath, entry.type, false);
    710   }
    711 }
    712 
    713 bool Directory::tryTransfer(PathPtr toPath, WriteMode toMode,
    714                             const Directory& fromDirectory, PathPtr fromPath,
    715                             TransferMode mode) const {
    716   KJ_REQUIRE(toPath.size() > 0, "can't replace self") { return false; }
    717 
    718   // First try reversing.
    719   KJ_IF_MAYBE(result, fromDirectory.tryTransferTo(*this, toPath, toMode, fromPath, mode)) {
    720     return *result;
    721   }
    722 
    723   switch (mode) {
    724     case TransferMode::COPY:
    725       KJ_IF_MAYBE(meta, fromDirectory.tryLstat(fromPath)) {
    726         return tryCopyDirectoryEntry(*this, toPath, toMode, fromDirectory,
    727                                      fromPath, meta->type, true);
    728       } else {
    729         // Source doesn't exist.
    730         return false;
    731       }
    732     case TransferMode::MOVE:
    733       // Implement move as copy-then-delete.
    734       if (!tryTransfer(toPath, toMode, fromDirectory, fromPath, TransferMode::COPY)) {
    735         return false;
    736       }
    737       fromDirectory.remove(fromPath);
    738       return true;
    739     case TransferMode::LINK:
    740       KJ_FAIL_REQUIRE("can't link across different Directory implementations") { return false; }
    741   }
    742 
    743   KJ_UNREACHABLE;
    744 }
    745 
    746 Maybe<bool> Directory::tryTransferTo(const Directory& toDirectory, PathPtr toPath, WriteMode toMode,
    747                                      PathPtr fromPath, TransferMode mode) const {
    748   return nullptr;
    749 }
    750 
    751 void Directory::remove(PathPtr path) const {
    752   if (!tryRemove(path)) {
    753     KJ_FAIL_REQUIRE("path to remove doesn't exist", path) { break; }
    754   }
    755 }
    756 
    757 void Directory::commitFailed(WriteMode mode) {
    758   if (has(mode, WriteMode::CREATE) && !has(mode, WriteMode::MODIFY)) {
    759     KJ_FAIL_REQUIRE("replace target already exists") { break; }
    760   } else if (has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    761     KJ_FAIL_REQUIRE("replace target does not exist") { break; }
    762   } else if (!has(mode, WriteMode::MODIFY) && !has(mode, WriteMode::CREATE)) {
    763     KJ_FAIL_ASSERT("neither WriteMode::CREATE nor WriteMode::MODIFY was given") { break; }
    764   } else {
    765     KJ_FAIL_ASSERT("tryCommit() returned null despite no preconditions") { break; }
    766   }
    767 }
    768 
    769 // =======================================================================================
    770 
    771 namespace {
    772 
    773 class InMemoryFile final: public File, public AtomicRefcounted {
    774 public:
    775   InMemoryFile(const Clock& clock): impl(clock) {}
    776 
    777   Own<const FsNode> cloneFsNode() const override {
    778     return atomicAddRef(*this);
    779   }
    780 
    781   Maybe<int> getFd() const override {
    782     return nullptr;
    783   }
    784 
    785   Metadata stat() const override {
    786     auto lock = impl.lockShared();
    787     uint64_t hash = reinterpret_cast<uintptr_t>(this);
    788     return Metadata { Type::FILE, lock->size, lock->size, lock->lastModified, 1, hash };
    789   }
    790 
    791   void sync() const override {}
    792   void datasync() const override {}
    793   // no-ops
    794 
    795   size_t read(uint64_t offset, ArrayPtr<byte> buffer) const override {
    796     auto lock = impl.lockShared();
    797     if (offset >= lock->size) {
    798       // Entirely out-of-range.
    799       return 0;
    800     }
    801 
    802     size_t readSize = kj::min(buffer.size(), lock->size - offset);
    803     memcpy(buffer.begin(), lock->bytes.begin() + offset, readSize);
    804     return readSize;
    805   }
    806 
    807   Array<const byte> mmap(uint64_t offset, uint64_t size) const override {
    808     KJ_REQUIRE(offset + size >= offset, "mmap() request overflows uint64");
    809     auto lock = impl.lockExclusive();
    810     lock->ensureCapacity(offset + size);
    811 
    812     ArrayDisposer* disposer = new MmapDisposer(atomicAddRef(*this));
    813     return Array<const byte>(lock->bytes.begin() + offset, size, *disposer);
    814   }
    815 
    816   Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const override {
    817     // Return a copy.
    818 
    819     // Allocate exactly the size requested.
    820     auto result = heapArray<byte>(size);
    821 
    822     // Use read() to fill it.
    823     size_t actual = read(offset, result);
    824 
    825     // Ignore the rest.
    826     if (actual < size) {
    827       memset(result.begin() + actual, 0, size - actual);
    828     }
    829 
    830     return result;
    831   }
    832 
    833   void write(uint64_t offset, ArrayPtr<const byte> data) const override {
    834     if (data.size() == 0) return;
    835     auto lock = impl.lockExclusive();
    836     lock->modified();
    837     uint64_t end = offset + data.size();
    838     KJ_REQUIRE(end >= offset, "write() request overflows uint64");
    839     lock->ensureCapacity(end);
    840     lock->size = kj::max(lock->size, end);
    841     memcpy(lock->bytes.begin() + offset, data.begin(), data.size());
    842   }
    843 
    844   void zero(uint64_t offset, uint64_t zeroSize) const override {
    845     if (zeroSize == 0) return;
    846     auto lock = impl.lockExclusive();
    847     lock->modified();
    848     uint64_t end = offset + zeroSize;
    849     KJ_REQUIRE(end >= offset, "zero() request overflows uint64");
    850     lock->ensureCapacity(end);
    851     lock->size = kj::max(lock->size, end);
    852     memset(lock->bytes.begin() + offset, 0, zeroSize);
    853   }
    854 
    855   void truncate(uint64_t newSize) const override {
    856     auto lock = impl.lockExclusive();
    857     if (newSize < lock->size) {
    858       lock->modified();
    859       memset(lock->bytes.begin() + newSize, 0, lock->size - newSize);
    860       lock->size = newSize;
    861     } else if (newSize > lock->size) {
    862       lock->modified();
    863       lock->ensureCapacity(newSize);
    864       lock->size = newSize;
    865     }
    866   }
    867 
    868   Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const override {
    869     uint64_t end = offset + size;
    870     KJ_REQUIRE(end >= offset, "mmapWritable() request overflows uint64");
    871     auto lock = impl.lockExclusive();
    872     lock->ensureCapacity(end);
    873     return heap<WritableFileMappingImpl>(atomicAddRef(*this), lock->bytes.slice(offset, end));
    874   }
    875 
    876   size_t copy(uint64_t offset, const ReadableFile& from,
    877               uint64_t fromOffset, uint64_t copySize) const override {
    878     size_t fromFileSize = from.stat().size;
    879     if (fromFileSize <= fromOffset) return 0;
    880 
    881     // Clamp size to EOF.
    882     copySize = kj::min(copySize, fromFileSize - fromOffset);
    883     if (copySize == 0) return 0;
    884 
    885     auto lock = impl.lockExclusive();
    886 
    887     // Allocate space for the copy.
    888     uint64_t end = offset + copySize;
    889     lock->ensureCapacity(end);
    890 
    891     // Read directly into our backing store.
    892     size_t n = from.read(fromOffset, lock->bytes.slice(offset, end));
    893     lock->size = kj::max(lock->size, offset + n);
    894 
    895     lock->modified();
    896     return n;
    897   }
    898 
    899 private:
    900   struct Impl {
    901     const Clock& clock;
    902     Array<byte> bytes;
    903     size_t size = 0;     // bytes may be larger than this to accommodate mmaps
    904     Date lastModified;
    905     uint mmapCount = 0;  // number of mappings outstanding
    906 
    907     Impl(const Clock& clock): clock(clock), lastModified(clock.now()) {}
    908 
    909     void ensureCapacity(size_t capacity) {
    910       if (bytes.size() < capacity) {
    911         KJ_ASSERT(mmapCount == 0,
    912             "InMemoryFile cannot resize the file backing store while memory mappings exist.");
    913 
    914         auto newBytes = heapArray<byte>(kj::max(capacity, bytes.size() * 2));
    915         if (size > 0) {  // placate ubsan; bytes.begin() might be null
    916           memcpy(newBytes.begin(), bytes.begin(), size);
    917         }
    918         memset(newBytes.begin() + size, 0, newBytes.size() - size);
    919         bytes = kj::mv(newBytes);
    920       }
    921     }
    922 
    923     void modified() {
    924       lastModified = clock.now();
    925     }
    926   };
    927   kj::MutexGuarded<Impl> impl;
    928 
    929   class MmapDisposer final: public ArrayDisposer {
    930   public:
    931     MmapDisposer(Own<const InMemoryFile>&& refParam): ref(kj::mv(refParam)) {
    932       ++ref->impl.getAlreadyLockedExclusive().mmapCount;
    933     }
    934     ~MmapDisposer() noexcept(false) {
    935       --ref->impl.lockExclusive()->mmapCount;
    936     }
    937 
    938     void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
    939                      size_t capacity, void (*destroyElement)(void*)) const override {
    940       delete this;
    941     }
    942 
    943   private:
    944     Own<const InMemoryFile> ref;
    945   };
    946 
    947   class WritableFileMappingImpl final: public WritableFileMapping {
    948   public:
    949     WritableFileMappingImpl(Own<const InMemoryFile>&& refParam, ArrayPtr<byte> range)
    950         : ref(kj::mv(refParam)), range(range) {
    951       ++ref->impl.getAlreadyLockedExclusive().mmapCount;
    952     }
    953     ~WritableFileMappingImpl() noexcept(false) {
    954       --ref->impl.lockExclusive()->mmapCount;
    955     }
    956 
    957     ArrayPtr<byte> get() const override {
    958       // const_cast OK because WritableFileMapping does indeed provide a writable view despite
    959       // being const itself.
    960       return arrayPtr(const_cast<byte*>(range.begin()), range.size());
    961     }
    962 
    963     void changed(ArrayPtr<byte> slice) const override {
    964       ref->impl.lockExclusive()->modified();
    965     }
    966 
    967     void sync(ArrayPtr<byte> slice) const override {
    968       ref->impl.lockExclusive()->modified();
    969     }
    970 
    971   private:
    972     Own<const InMemoryFile> ref;
    973     ArrayPtr<byte> range;
    974   };
    975 };
    976 
    977 // -----------------------------------------------------------------------------
    978 
    979 class InMemoryDirectory final: public Directory, public AtomicRefcounted {
    980 public:
    981   InMemoryDirectory(const Clock& clock): impl(clock) {}
    982 
    983   Own<const FsNode> cloneFsNode() const override {
    984     return atomicAddRef(*this);
    985   }
    986 
    987   Maybe<int> getFd() const override {
    988     return nullptr;
    989   }
    990 
    991   Metadata stat() const override {
    992     auto lock = impl.lockShared();
    993     uint64_t hash = reinterpret_cast<uintptr_t>(this);
    994     return Metadata { Type::DIRECTORY, 0, 0, lock->lastModified, 1, hash };
    995   }
    996 
    997   void sync() const override {}
    998   void datasync() const override {}
    999   // no-ops
   1000 
   1001   Array<String> listNames() const override {
   1002     auto lock = impl.lockShared();
   1003     return KJ_MAP(e, lock->entries) { return heapString(e.first); };
   1004   }
   1005 
   1006   Array<Entry> listEntries() const override {
   1007     auto lock = impl.lockShared();
   1008     return KJ_MAP(e, lock->entries) {
   1009       FsNode::Type type;
   1010       if (e.second.node.is<SymlinkNode>()) {
   1011         type = FsNode::Type::SYMLINK;
   1012       } else if (e.second.node.is<FileNode>()) {
   1013         type = FsNode::Type::FILE;
   1014       } else {
   1015         KJ_ASSERT(e.second.node.is<DirectoryNode>());
   1016         type = FsNode::Type::DIRECTORY;
   1017       }
   1018 
   1019       return Entry { type, heapString(e.first) };
   1020     };
   1021   }
   1022 
   1023   bool exists(PathPtr path) const override {
   1024     if (path.size() == 0) {
   1025       return true;
   1026     } else if (path.size() == 1) {
   1027       auto lock = impl.lockShared();
   1028       KJ_IF_MAYBE(entry, lock->tryGetEntry(path[0])) {
   1029         return exists(lock, *entry);
   1030       } else {
   1031         return false;
   1032       }
   1033     } else {
   1034       KJ_IF_MAYBE(subdir, tryGetParent(path[0])) {
   1035         return subdir->get()->exists(path.slice(1, path.size()));
   1036       } else {
   1037         return false;
   1038       }
   1039     }
   1040   }
   1041 
   1042   Maybe<FsNode::Metadata> tryLstat(PathPtr path) const override {
   1043     if (path.size() == 0) {
   1044       return stat();
   1045     } else if (path.size() == 1) {
   1046       auto lock = impl.lockShared();
   1047       KJ_IF_MAYBE(entry, lock->tryGetEntry(path[0])) {
   1048         if (entry->node.is<FileNode>()) {
   1049           return entry->node.get<FileNode>().file->stat();
   1050         } else if (entry->node.is<DirectoryNode>()) {
   1051           return entry->node.get<DirectoryNode>().directory->stat();
   1052         } else if (entry->node.is<SymlinkNode>()) {
   1053           auto& link = entry->node.get<SymlinkNode>();
   1054           uint64_t hash = reinterpret_cast<uintptr_t>(link.content.begin());
   1055           return FsNode::Metadata { FsNode::Type::SYMLINK, 0, 0, link.lastModified, 1, hash };
   1056         } else {
   1057           KJ_FAIL_ASSERT("unknown node type") { return nullptr; }
   1058         }
   1059       } else {
   1060         return nullptr;
   1061       }
   1062     } else {
   1063       KJ_IF_MAYBE(subdir, tryGetParent(path[0])) {
   1064         return subdir->get()->tryLstat(path.slice(1, path.size()));
   1065       } else {
   1066         return nullptr;
   1067       }
   1068     }
   1069   }
   1070 
   1071   Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const override {
   1072     if (path.size() == 0) {
   1073       KJ_FAIL_REQUIRE("not a file") { return nullptr; }
   1074     } else if (path.size() == 1) {
   1075       auto lock = impl.lockShared();
   1076       KJ_IF_MAYBE(entry, lock->tryGetEntry(path[0])) {
   1077         return asFile(lock, *entry);
   1078       } else {
   1079         return nullptr;
   1080       }
   1081     } else {
   1082       KJ_IF_MAYBE(subdir, tryGetParent(path[0])) {
   1083         return subdir->get()->tryOpenFile(path.slice(1, path.size()));
   1084       } else {
   1085         return nullptr;
   1086       }
   1087     }
   1088   }
   1089 
   1090   Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const override {
   1091     if (path.size() == 0) {
   1092       return clone();
   1093     } else if (path.size() == 1) {
   1094       auto lock = impl.lockShared();
   1095       KJ_IF_MAYBE(entry, lock->tryGetEntry(path[0])) {
   1096         return asDirectory(lock, *entry);
   1097       } else {
   1098         return nullptr;
   1099       }
   1100     } else {
   1101       KJ_IF_MAYBE(subdir, tryGetParent(path[0])) {
   1102         return subdir->get()->tryOpenSubdir(path.slice(1, path.size()));
   1103       } else {
   1104         return nullptr;
   1105       }
   1106     }
   1107   }
   1108 
   1109   Maybe<String> tryReadlink(PathPtr path) const override {
   1110     if (path.size() == 0) {
   1111       KJ_FAIL_REQUIRE("not a symlink") { return nullptr; }
   1112     } else if (path.size() == 1) {
   1113       auto lock = impl.lockShared();
   1114       KJ_IF_MAYBE(entry, lock->tryGetEntry(path[0])) {
   1115         return asSymlink(lock, *entry);
   1116       } else {
   1117         return nullptr;
   1118       }
   1119     } else {
   1120       KJ_IF_MAYBE(subdir, tryGetParent(path[0])) {
   1121         return subdir->get()->tryReadlink(path.slice(1, path.size()));
   1122       } else {
   1123         return nullptr;
   1124       }
   1125     }
   1126   }
   1127 
   1128   Maybe<Own<const File>> tryOpenFile(PathPtr path, WriteMode mode) const override {
   1129     if (path.size() == 0) {
   1130       if (has(mode, WriteMode::MODIFY)) {
   1131         KJ_FAIL_REQUIRE("not a file") { return nullptr; }
   1132       } else if (has(mode, WriteMode::CREATE)) {
   1133         return nullptr;  // already exists (as a directory)
   1134       } else {
   1135         KJ_FAIL_REQUIRE("can't replace self") { return nullptr; }
   1136       }
   1137     } else if (path.size() == 1) {
   1138       auto lock = impl.lockExclusive();
   1139       KJ_IF_MAYBE(entry, lock->openEntry(path[0], mode)) {
   1140         return asFile(lock, *entry, mode);
   1141       } else {
   1142         return nullptr;
   1143       }
   1144     } else {
   1145       KJ_IF_MAYBE(child, tryGetParent(path[0], mode)) {
   1146         return child->get()->tryOpenFile(path.slice(1, path.size()), mode);
   1147       } else {
   1148         return nullptr;
   1149       }
   1150     }
   1151   }
   1152 
   1153   Own<Replacer<File>> replaceFile(PathPtr path, WriteMode mode) const override {
   1154     if (path.size() == 0) {
   1155       KJ_FAIL_REQUIRE("can't replace self") { break; }
   1156     } else if (path.size() == 1) {
   1157       // don't need lock just to read the clock ref
   1158       return heap<ReplacerImpl<File>>(*this, path[0],
   1159           newInMemoryFile(impl.getWithoutLock().clock), mode);
   1160     } else {
   1161       KJ_IF_MAYBE(child, tryGetParent(path[0], mode)) {
   1162         return child->get()->replaceFile(path.slice(1, path.size()), mode);
   1163       }
   1164     }
   1165     return heap<BrokenReplacer<File>>(newInMemoryFile(impl.getWithoutLock().clock));
   1166   }
   1167 
   1168   Maybe<Own<const Directory>> tryOpenSubdir(PathPtr path, WriteMode mode) const override {
   1169     if (path.size() == 0) {
   1170       if (has(mode, WriteMode::MODIFY)) {
   1171         return atomicAddRef(*this);
   1172       } else if (has(mode, WriteMode::CREATE)) {
   1173         return nullptr;  // already exists
   1174       } else {
   1175         KJ_FAIL_REQUIRE("can't replace self") { return nullptr; }
   1176       }
   1177     } else if (path.size() == 1) {
   1178       auto lock = impl.lockExclusive();
   1179       KJ_IF_MAYBE(entry, lock->openEntry(path[0], mode)) {
   1180         return asDirectory(lock, *entry, mode);
   1181       } else {
   1182         return nullptr;
   1183       }
   1184     } else {
   1185       KJ_IF_MAYBE(child, tryGetParent(path[0], mode)) {
   1186         return child->get()->tryOpenSubdir(path.slice(1, path.size()), mode);
   1187       } else {
   1188         return nullptr;
   1189       }
   1190     }
   1191   }
   1192 
   1193   Own<Replacer<Directory>> replaceSubdir(PathPtr path, WriteMode mode) const override {
   1194     if (path.size() == 0) {
   1195       KJ_FAIL_REQUIRE("can't replace self") { break; }
   1196     } else if (path.size() == 1) {
   1197       // don't need lock just to read the clock ref
   1198       return heap<ReplacerImpl<Directory>>(*this, path[0],
   1199           newInMemoryDirectory(impl.getWithoutLock().clock), mode);
   1200     } else {
   1201       KJ_IF_MAYBE(child, tryGetParent(path[0], mode)) {
   1202         return child->get()->replaceSubdir(path.slice(1, path.size()), mode);
   1203       }
   1204     }
   1205     return heap<BrokenReplacer<Directory>>(newInMemoryDirectory(impl.getWithoutLock().clock));
   1206   }
   1207 
   1208   Maybe<Own<AppendableFile>> tryAppendFile(PathPtr path, WriteMode mode) const override {
   1209     if (path.size() == 0) {
   1210       if (has(mode, WriteMode::MODIFY)) {
   1211         KJ_FAIL_REQUIRE("not a file") { return nullptr; }
   1212       } else if (has(mode, WriteMode::CREATE)) {
   1213         return nullptr;  // already exists (as a directory)
   1214       } else {
   1215         KJ_FAIL_REQUIRE("can't replace self") { return nullptr; }
   1216       }
   1217     } else if (path.size() == 1) {
   1218       auto lock = impl.lockExclusive();
   1219       KJ_IF_MAYBE(entry, lock->openEntry(path[0], mode)) {
   1220         return asFile(lock, *entry, mode).map(newFileAppender);
   1221       } else {
   1222         return nullptr;
   1223       }
   1224     } else {
   1225       KJ_IF_MAYBE(child, tryGetParent(path[0], mode)) {
   1226         return child->get()->tryAppendFile(path.slice(1, path.size()), mode);
   1227       } else {
   1228         return nullptr;
   1229       }
   1230     }
   1231   }
   1232 
   1233   bool trySymlink(PathPtr path, StringPtr content, WriteMode mode) const override {
   1234     if (path.size() == 0) {
   1235       if (has(mode, WriteMode::CREATE)) {
   1236         return false;
   1237       } else {
   1238         KJ_FAIL_REQUIRE("can't replace self") { return false; }
   1239       }
   1240     } else if (path.size() == 1) {
   1241       auto lock = impl.lockExclusive();
   1242       KJ_IF_MAYBE(entry, lock->openEntry(path[0], mode)) {
   1243         entry->init(SymlinkNode { lock->clock.now(), heapString(content) });
   1244         lock->modified();
   1245         return true;
   1246       } else {
   1247         return false;
   1248       }
   1249     } else {
   1250       KJ_IF_MAYBE(child, tryGetParent(path[0], mode)) {
   1251         return child->get()->trySymlink(path.slice(1, path.size()), content, mode);
   1252       } else {
   1253         KJ_FAIL_REQUIRE("couldn't create parent directory") { return false; }
   1254       }
   1255     }
   1256   }
   1257 
   1258   Own<const File> createTemporary() const override {
   1259     // Don't need lock just to read the clock ref.
   1260     return newInMemoryFile(impl.getWithoutLock().clock);
   1261   }
   1262 
   1263   bool tryTransfer(PathPtr toPath, WriteMode toMode,
   1264                    const Directory& fromDirectory, PathPtr fromPath,
   1265                    TransferMode mode) const override {
   1266     if (toPath.size() == 0) {
   1267       if (has(toMode, WriteMode::CREATE)) {
   1268         return false;
   1269       } else {
   1270         KJ_FAIL_REQUIRE("can't replace self") { return false; }
   1271       }
   1272     } else if (toPath.size() == 1) {
   1273       // tryTransferChild() needs to at least know the node type, so do an lstat.
   1274       KJ_IF_MAYBE(meta, fromDirectory.tryLstat(fromPath)) {
   1275         auto lock = impl.lockExclusive();
   1276         KJ_IF_MAYBE(entry, lock->openEntry(toPath[0], toMode)) {
   1277           // Make sure if we just cerated a new entry, and we don't successfully transfer to it, we
   1278           // remove the entry before returning.
   1279           bool needRollback = entry->node == nullptr;
   1280           KJ_DEFER(if (needRollback) { lock->entries.erase(toPath[0]); });
   1281 
   1282           if (lock->tryTransferChild(*entry, meta->type, meta->lastModified, meta->size,
   1283                                      fromDirectory, fromPath, mode)) {
   1284             lock->modified();
   1285             needRollback = false;
   1286             return true;
   1287           } else {
   1288             KJ_FAIL_REQUIRE("InMemoryDirectory can't link an inode of this type", fromPath) {
   1289               return false;
   1290             }
   1291           }
   1292         } else {
   1293           return false;
   1294         }
   1295       } else {
   1296         return false;
   1297       }
   1298     } else {
   1299       // TODO(someday): Ideally we wouldn't create parent directories if fromPath doesn't exist.
   1300       //   This requires a different approach to the code here, though.
   1301       KJ_IF_MAYBE(child, tryGetParent(toPath[0], toMode)) {
   1302         return child->get()->tryTransfer(
   1303             toPath.slice(1, toPath.size()), toMode, fromDirectory, fromPath, mode);
   1304       } else {
   1305         return false;
   1306       }
   1307     }
   1308   }
   1309 
   1310   Maybe<bool> tryTransferTo(const Directory& toDirectory, PathPtr toPath, WriteMode toMode,
   1311                             PathPtr fromPath, TransferMode mode) const override {
   1312     if (fromPath.size() <= 1) {
   1313       // If `fromPath` is in this directory (or *is* this directory) then we don't have any
   1314       // optimizations.
   1315       return nullptr;
   1316     }
   1317 
   1318     // `fromPath` is in a subdirectory. It could turn out that that subdirectory is not an
   1319     // InMemoryDirectory and is instead something `toDirectory` is friendly with. So let's follow
   1320     // the path.
   1321 
   1322     KJ_IF_MAYBE(child, tryGetParent(fromPath[0], WriteMode::MODIFY)) {
   1323       // OK, switch back to tryTransfer() but use the subdirectory.
   1324       return toDirectory.tryTransfer(toPath, toMode,
   1325           **child, fromPath.slice(1, fromPath.size()), mode);
   1326     } else {
   1327       // Hmm, doesn't exist. Fall back to standard path.
   1328       return nullptr;
   1329     }
   1330   }
   1331 
   1332   bool tryRemove(PathPtr path) const override {
   1333     if (path.size() == 0) {
   1334       KJ_FAIL_REQUIRE("can't remove self from self") { return false; }
   1335     } else if (path.size() == 1) {
   1336       auto lock = impl.lockExclusive();
   1337       auto iter = lock->entries.find(path[0]);
   1338       if (iter == lock->entries.end()) {
   1339         return false;
   1340       } else {
   1341         lock->entries.erase(iter);
   1342         lock->modified();
   1343         return true;
   1344       }
   1345     } else {
   1346       KJ_IF_MAYBE(child, tryGetParent(path[0], WriteMode::MODIFY)) {
   1347         return child->get()->tryRemove(path.slice(1, path.size()));
   1348       } else {
   1349         return false;
   1350       }
   1351     }
   1352   }
   1353 
   1354 private:
   1355   struct FileNode {
   1356     Own<const File> file;
   1357   };
   1358   struct DirectoryNode {
   1359     Own<const Directory> directory;
   1360   };
   1361   struct SymlinkNode {
   1362     Date lastModified;
   1363     String content;
   1364 
   1365     Path parse() const {
   1366       KJ_CONTEXT("parsing symlink", content);
   1367       return Path::parse(content);
   1368     }
   1369   };
   1370 
   1371   struct EntryImpl {
   1372     String name;
   1373     OneOf<FileNode, DirectoryNode, SymlinkNode> node;
   1374 
   1375     EntryImpl(String&& name): name(kj::mv(name)) {}
   1376 
   1377     Own<const File> init(FileNode&& value) {
   1378       return node.init<FileNode>(kj::mv(value)).file->clone();
   1379     }
   1380     Own<const Directory> init(DirectoryNode&& value) {
   1381       return node.init<DirectoryNode>(kj::mv(value)).directory->clone();
   1382     }
   1383     void init(SymlinkNode&& value) {
   1384       node.init<SymlinkNode>(kj::mv(value));
   1385     }
   1386     bool init(OneOf<FileNode, DirectoryNode, SymlinkNode>&& value) {
   1387       node = kj::mv(value);
   1388       return node != nullptr;
   1389     }
   1390 
   1391     void set(Own<const File>&& value) {
   1392       node.init<FileNode>(FileNode { kj::mv(value) });
   1393     }
   1394     void set(Own<const Directory>&& value) {
   1395       node.init<DirectoryNode>(DirectoryNode { kj::mv(value) });
   1396     }
   1397   };
   1398 
   1399   template <typename T>
   1400   class ReplacerImpl final: public Replacer<T> {
   1401   public:
   1402     ReplacerImpl(const InMemoryDirectory& directory, kj::StringPtr name,
   1403                  Own<const T> inner, WriteMode mode)
   1404         : Replacer<T>(mode), directory(atomicAddRef(directory)), name(heapString(name)),
   1405           inner(kj::mv(inner)) {}
   1406 
   1407     const T& get() override { return *inner; }
   1408 
   1409     bool tryCommit() override {
   1410       KJ_REQUIRE(!committed, "commit() already called") { return true; }
   1411 
   1412       auto lock = directory->impl.lockExclusive();
   1413       KJ_IF_MAYBE(entry, lock->openEntry(name, Replacer<T>::mode)) {
   1414         entry->set(inner->clone());
   1415         lock->modified();
   1416         return true;
   1417       } else {
   1418         return false;
   1419       }
   1420     }
   1421 
   1422   private:
   1423     bool committed = false;
   1424     Own<const InMemoryDirectory> directory;
   1425     kj::String name;
   1426     Own<const T> inner;
   1427   };
   1428 
   1429   template <typename T>
   1430   class BrokenReplacer final: public Replacer<T> {
   1431     // For recovery path when exceptions are disabled.
   1432 
   1433   public:
   1434     BrokenReplacer(Own<const T> inner)
   1435         : Replacer<T>(WriteMode::CREATE | WriteMode::MODIFY),
   1436           inner(kj::mv(inner)) {}
   1437 
   1438     const T& get() override { return *inner; }
   1439     bool tryCommit() override { return false; }
   1440 
   1441   private:
   1442     Own<const T> inner;
   1443   };
   1444 
   1445   struct Impl {
   1446     const Clock& clock;
   1447 
   1448     std::map<StringPtr, EntryImpl> entries;
   1449     // Note: If this changes to a non-sorted map, listNames() and listEntries() must be updated to
   1450     //   sort their results.
   1451 
   1452     Date lastModified;
   1453 
   1454     Impl(const Clock& clock): clock(clock), lastModified(clock.now()) {}
   1455 
   1456     Maybe<EntryImpl&> openEntry(kj::StringPtr name, WriteMode mode) {
   1457       // TODO(perf): We could avoid a copy if the entry exists, at the expense of a double-lookup
   1458       //   if it doesn't. Maybe a better map implementation will solve everything?
   1459       return openEntry(heapString(name), mode);
   1460     }
   1461 
   1462     Maybe<EntryImpl&> openEntry(String&& name, WriteMode mode) {
   1463       if (has(mode, WriteMode::CREATE)) {
   1464         EntryImpl entry(kj::mv(name));
   1465         StringPtr nameRef = entry.name;
   1466         auto insertResult = entries.insert(std::make_pair(nameRef, kj::mv(entry)));
   1467 
   1468         if (!insertResult.second && !has(mode, WriteMode::MODIFY)) {
   1469           // Entry already existed and MODIFY not specified.
   1470           return nullptr;
   1471         }
   1472 
   1473         return insertResult.first->second;
   1474       } else if (has(mode, WriteMode::MODIFY)) {
   1475         return tryGetEntry(name);
   1476       } else {
   1477         // Neither CREATE nor MODIFY specified: precondition always fails.
   1478         return nullptr;
   1479       }
   1480     }
   1481 
   1482     kj::Maybe<const EntryImpl&> tryGetEntry(kj::StringPtr name) const {
   1483       auto iter = entries.find(name);
   1484       if (iter == entries.end()) {
   1485         return nullptr;
   1486       } else {
   1487         return iter->second;
   1488       }
   1489     }
   1490 
   1491     kj::Maybe<EntryImpl&> tryGetEntry(kj::StringPtr name) {
   1492       auto iter = entries.find(name);
   1493       if (iter == entries.end()) {
   1494         return nullptr;
   1495       } else {
   1496         return iter->second;
   1497       }
   1498     }
   1499 
   1500     void modified() {
   1501       lastModified = clock.now();
   1502     }
   1503 
   1504     bool tryTransferChild(EntryImpl& entry, const FsNode::Type type, kj::Maybe<Date> lastModified,
   1505                           kj::Maybe<uint64_t> size, const Directory& fromDirectory,
   1506                           PathPtr fromPath, TransferMode mode) {
   1507       switch (type) {
   1508         case FsNode::Type::FILE:
   1509           KJ_IF_MAYBE(file, fromDirectory.tryOpenFile(fromPath, WriteMode::MODIFY)) {
   1510             if (mode == TransferMode::COPY) {
   1511               auto copy = newInMemoryFile(clock);
   1512               copy->copy(0, **file, 0, size.orDefault(kj::maxValue));
   1513               entry.set(kj::mv(copy));
   1514             } else {
   1515               if (mode == TransferMode::MOVE) {
   1516                 KJ_ASSERT(fromDirectory.tryRemove(fromPath), "couldn't move node", fromPath) {
   1517                   return false;
   1518                 }
   1519               }
   1520               entry.set(kj::mv(*file));
   1521             }
   1522             return true;
   1523           } else {
   1524             KJ_FAIL_ASSERT("source node deleted concurrently during transfer", fromPath) {
   1525               return false;
   1526             }
   1527           }
   1528         case FsNode::Type::DIRECTORY:
   1529           KJ_IF_MAYBE(subdir, fromDirectory.tryOpenSubdir(fromPath, WriteMode::MODIFY)) {
   1530             if (mode == TransferMode::COPY) {
   1531               auto copy = atomicRefcounted<InMemoryDirectory>(clock);
   1532               auto& cpim = copy->impl.getWithoutLock();  // safe because just-created
   1533               for (auto& subEntry: subdir->get()->listEntries()) {
   1534                 EntryImpl newEntry(kj::mv(subEntry.name));
   1535                 Path filename(newEntry.name);
   1536                 if (!cpim.tryTransferChild(newEntry, subEntry.type, nullptr, nullptr, **subdir,
   1537                                            filename, TransferMode::COPY)) {
   1538                   KJ_LOG(ERROR, "couldn't copy node of type not supported by InMemoryDirectory",
   1539                          filename);
   1540                 } else {
   1541                   StringPtr nameRef = newEntry.name;
   1542                   cpim.entries.insert(std::make_pair(nameRef, kj::mv(newEntry)));
   1543                 }
   1544               }
   1545               entry.set(kj::mv(copy));
   1546             } else {
   1547               if (mode == TransferMode::MOVE) {
   1548                 KJ_ASSERT(fromDirectory.tryRemove(fromPath), "couldn't move node", fromPath) {
   1549                   return false;
   1550                 }
   1551               }
   1552               entry.set(kj::mv(*subdir));
   1553             }
   1554             return true;
   1555           } else {
   1556             KJ_FAIL_ASSERT("source node deleted concurrently during transfer", fromPath) {
   1557               return false;
   1558             }
   1559           }
   1560         case FsNode::Type::SYMLINK:
   1561           KJ_IF_MAYBE(content, fromDirectory.tryReadlink(fromPath)) {
   1562             // Since symlinks are immutable, we can implement LINK the same as COPY.
   1563             entry.init(SymlinkNode { lastModified.orDefault(clock.now()), kj::mv(*content) });
   1564             if (mode == TransferMode::MOVE) {
   1565               KJ_ASSERT(fromDirectory.tryRemove(fromPath), "couldn't move node", fromPath) {
   1566                 return false;
   1567               }
   1568             }
   1569             return true;
   1570           } else {
   1571             KJ_FAIL_ASSERT("source node deleted concurrently during transfer", fromPath) {
   1572               return false;
   1573             }
   1574           }
   1575         default:
   1576           return false;
   1577       }
   1578     }
   1579   };
   1580 
   1581   kj::MutexGuarded<Impl> impl;
   1582 
   1583   bool exists(kj::Locked<const Impl>& lock, const EntryImpl& entry) const {
   1584     if (entry.node.is<SymlinkNode>()) {
   1585       auto newPath = entry.node.get<SymlinkNode>().parse();
   1586       lock.release();
   1587       return exists(newPath);
   1588     } else {
   1589       return true;
   1590     }
   1591   }
   1592   Maybe<Own<const ReadableFile>> asFile(
   1593       kj::Locked<const Impl>& lock, const EntryImpl& entry) const {
   1594     if (entry.node.is<FileNode>()) {
   1595       return entry.node.get<FileNode>().file->clone();
   1596     } else if (entry.node.is<SymlinkNode>()) {
   1597       auto newPath = entry.node.get<SymlinkNode>().parse();
   1598       lock.release();
   1599       return tryOpenFile(newPath);
   1600     } else {
   1601       KJ_FAIL_REQUIRE("not a file") { return nullptr; }
   1602     }
   1603   }
   1604   Maybe<Own<const ReadableDirectory>> asDirectory(
   1605       kj::Locked<const Impl>& lock, const EntryImpl& entry) const {
   1606     if (entry.node.is<DirectoryNode>()) {
   1607       return entry.node.get<DirectoryNode>().directory->clone();
   1608     } else if (entry.node.is<SymlinkNode>()) {
   1609       auto newPath = entry.node.get<SymlinkNode>().parse();
   1610       lock.release();
   1611       return tryOpenSubdir(newPath);
   1612     } else {
   1613       KJ_FAIL_REQUIRE("not a directory") { return nullptr; }
   1614     }
   1615   }
   1616   Maybe<String> asSymlink(kj::Locked<const Impl>& lock, const EntryImpl& entry) const {
   1617     if (entry.node.is<SymlinkNode>()) {
   1618       return heapString(entry.node.get<SymlinkNode>().content);
   1619     } else {
   1620       KJ_FAIL_REQUIRE("not a symlink") { return nullptr; }
   1621     }
   1622   }
   1623 
   1624   Maybe<Own<const File>> asFile(kj::Locked<Impl>& lock, EntryImpl& entry, WriteMode mode) const {
   1625     if (entry.node.is<FileNode>()) {
   1626       return entry.node.get<FileNode>().file->clone();
   1627     } else if (entry.node.is<SymlinkNode>()) {
   1628       // CREATE_PARENT doesn't apply to creating the parents of a symlink target. However, the
   1629       // target itself can still be created.
   1630       auto newPath = entry.node.get<SymlinkNode>().parse();
   1631       lock.release();
   1632       return tryOpenFile(newPath, mode - WriteMode::CREATE_PARENT);
   1633     } else if (entry.node == nullptr) {
   1634       KJ_ASSERT(has(mode, WriteMode::CREATE));
   1635       lock->modified();
   1636       return entry.init(FileNode { newInMemoryFile(lock->clock) });
   1637     } else {
   1638       KJ_FAIL_REQUIRE("not a file") { return nullptr; }
   1639     }
   1640   }
   1641   Maybe<Own<const Directory>> asDirectory(
   1642       kj::Locked<Impl>& lock, EntryImpl& entry, WriteMode mode) const {
   1643     if (entry.node.is<DirectoryNode>()) {
   1644       return entry.node.get<DirectoryNode>().directory->clone();
   1645     } else if (entry.node.is<SymlinkNode>()) {
   1646       // CREATE_PARENT doesn't apply to creating the parents of a symlink target. However, the
   1647       // target itself can still be created.
   1648       auto newPath = entry.node.get<SymlinkNode>().parse();
   1649       lock.release();
   1650       return tryOpenSubdir(newPath, mode - WriteMode::CREATE_PARENT);
   1651     } else if (entry.node == nullptr) {
   1652       KJ_ASSERT(has(mode, WriteMode::CREATE));
   1653       lock->modified();
   1654       return entry.init(DirectoryNode { newInMemoryDirectory(lock->clock) });
   1655     } else {
   1656       KJ_FAIL_REQUIRE("not a directory") { return nullptr; }
   1657     }
   1658   }
   1659 
   1660   kj::Maybe<Own<const ReadableDirectory>> tryGetParent(kj::StringPtr name) const {
   1661     auto lock = impl.lockShared();
   1662     KJ_IF_MAYBE(entry, impl.lockShared()->tryGetEntry(name)) {
   1663       return asDirectory(lock, *entry);
   1664     } else {
   1665       return nullptr;
   1666     }
   1667   }
   1668 
   1669   kj::Maybe<Own<const Directory>> tryGetParent(kj::StringPtr name, WriteMode mode) const {
   1670     // Get a directory which is a parent of the eventual target. If `mode` includes
   1671     // WriteMode::CREATE_PARENTS, possibly create the parent directory.
   1672 
   1673     auto lock = impl.lockExclusive();
   1674 
   1675     WriteMode parentMode = has(mode, WriteMode::CREATE) && has(mode, WriteMode::CREATE_PARENT)
   1676         ? WriteMode::CREATE | WriteMode::MODIFY   // create parent
   1677         : WriteMode::MODIFY;                      // don't create parent
   1678 
   1679     // Possibly create parent.
   1680     KJ_IF_MAYBE(entry, lock->openEntry(name, parentMode)) {
   1681       if (entry->node.is<DirectoryNode>()) {
   1682         return entry->node.get<DirectoryNode>().directory->clone();
   1683       } else if (entry->node == nullptr) {
   1684         lock->modified();
   1685         return entry->init(DirectoryNode { newInMemoryDirectory(lock->clock) });
   1686       }
   1687       // Continue on.
   1688     }
   1689 
   1690     if (has(mode, WriteMode::CREATE)) {
   1691       // CREATE is documented as returning null when the file already exists. In this case, the
   1692       // file does NOT exist because the parent directory does not exist or is not a directory.
   1693       KJ_FAIL_REQUIRE("parent is not a directory") { return nullptr; }
   1694     } else {
   1695       return nullptr;
   1696     }
   1697   }
   1698 };
   1699 
   1700 // -----------------------------------------------------------------------------
   1701 
   1702 class AppendableFileImpl final: public AppendableFile {
   1703 public:
   1704   AppendableFileImpl(Own<const File>&& fileParam): file(kj::mv(fileParam)) {}
   1705 
   1706   Own<const FsNode> cloneFsNode() const override {
   1707     return heap<AppendableFileImpl>(file->clone());
   1708   }
   1709 
   1710   Maybe<int> getFd() const override {
   1711     return nullptr;
   1712   }
   1713 
   1714   Metadata stat() const override {
   1715     return file->stat();
   1716   }
   1717 
   1718   void sync() const override { file->sync(); }
   1719   void datasync() const override { file->datasync(); }
   1720 
   1721   void write(const void* buffer, size_t size) override {
   1722     file->write(file->stat().size, arrayPtr(reinterpret_cast<const byte*>(buffer), size));
   1723   }
   1724 
   1725 private:
   1726   Own<const File> file;
   1727 };
   1728 
   1729 }  // namespace
   1730 
   1731 // -----------------------------------------------------------------------------
   1732 
   1733 Own<File> newInMemoryFile(const Clock& clock) {
   1734   return atomicRefcounted<InMemoryFile>(clock);
   1735 }
   1736 Own<Directory> newInMemoryDirectory(const Clock& clock) {
   1737   return atomicRefcounted<InMemoryDirectory>(clock);
   1738 }
   1739 Own<AppendableFile> newFileAppender(Own<const File> inner) {
   1740   return heap<AppendableFileImpl>(kj::mv(inner));
   1741 }
   1742 
   1743 } // namespace kj