Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/filesystem.h @ 69:33d812a61356
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 17:55:14 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
67:0e9998148a16 | 69:33d812a61356 |
---|---|
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 #pragma once | |
23 | |
24 #include "memory.h" | |
25 #include "io.h" | |
26 #include <inttypes.h> | |
27 #include "time.h" | |
28 #include "function.h" | |
29 #include "hash.h" | |
30 | |
31 KJ_BEGIN_HEADER | |
32 | |
33 namespace kj { | |
34 | |
35 template <typename T> | |
36 class Vector; | |
37 | |
38 class PathPtr; | |
39 | |
40 class Path { | |
41 // A Path identifies a file in a directory tree. | |
42 // | |
43 // In KJ, we avoid representing paths as plain strings because this can lead to path injection | |
44 // bugs as well as numerous kinds of bugs relating to path parsing edge cases. The Path class's | |
45 // interface is designed to "make it hard to screw up". | |
46 // | |
47 // A "Path" is in fact a list of strings, each string being one component of the path (as would | |
48 // normally be separated by '/'s). Path components are not allowed to contain '/' nor '\0', nor | |
49 // are they allowed to be the special names "", ".", nor "..". | |
50 // | |
51 // If you explicitly want to parse a path that contains '/'s, ".", and "..", you must use | |
52 // parse() and/or eval(). However, users of this interface are encouraged to avoid parsing | |
53 // paths at all, and instead express paths as string arrays. | |
54 // | |
55 // Note that when using the Path class, ".." is always canonicalized in path space without | |
56 // consulting the actual filesystem. This means that "foo/some-symlink/../bar" is exactly | |
57 // equivalent to "foo/bar". This differs from the kernel's behavior when resolving paths passed | |
58 // to system calls: the kernel would have resolved "some-symlink" to its target physical path, | |
59 // and then would have interpreted ".." relative to that. In practice, the kernel's behavior is | |
60 // rarely what the user or programmer intended, hence canonicalizing in path space produces a | |
61 // better result. | |
62 // | |
63 // Path objects are "immutable": functions that "modify" the path return a new path. However, | |
64 // if the path being operated on is an rvalue, copying can be avoided. Hence it makes sense to | |
65 // write code like: | |
66 // | |
67 // Path p = ...; | |
68 // p = kj::mv(p).append("bar"); // in-place update, avoids string copying | |
69 | |
70 public: | |
71 Path(decltype(nullptr)); // empty path | |
72 | |
73 explicit Path(StringPtr name); | |
74 explicit Path(String&& name); | |
75 // Create a Path containing only one component. `name` is a single filename; it cannot contain | |
76 // '/' nor '\0' nor can it be exactly "" nor "." nor "..". | |
77 // | |
78 // If you want to allow '/'s and such, you must call Path::parse(). We force you to do this to | |
79 // prevent path injection bugs where you didn't consider what would happen if the path contained | |
80 // a '/'. | |
81 | |
82 explicit Path(std::initializer_list<StringPtr> parts); | |
83 explicit Path(ArrayPtr<const StringPtr> parts); | |
84 explicit Path(Array<String> parts); | |
85 // Construct a path from an array. Note that this means you can do: | |
86 // | |
87 // Path{"foo", "bar", "baz"} // equivalent to Path::parse("foo/bar/baz") | |
88 | |
89 KJ_DISALLOW_COPY(Path); | |
90 Path(Path&&) = default; | |
91 Path& operator=(Path&&) = default; | |
92 | |
93 Path clone() const; | |
94 | |
95 static Path parse(StringPtr path); | |
96 // Parses a path in traditional format. Components are separated by '/'. Any use of "." or | |
97 // ".." will be canonicalized (if they can't be canonicalized, e.g. because the path starts with | |
98 // "..", an exception is thrown). Multiple consecutive '/'s will be collapsed. A leading '/' | |
99 // is NOT accepted -- if that is a problem, you probably want `eval()`. Trailing '/'s are | |
100 // ignored. | |
101 | |
102 Path append(Path&& suffix) const&; | |
103 Path append(Path&& suffix) &&; | |
104 Path append(PathPtr suffix) const&; | |
105 Path append(PathPtr suffix) &&; | |
106 Path append(StringPtr suffix) const&; | |
107 Path append(StringPtr suffix) &&; | |
108 Path append(String&& suffix) const&; | |
109 Path append(String&& suffix) &&; | |
110 // Create a new path by appending the given path to this path. | |
111 // | |
112 // `suffix` cannot contain '/' characters. Instead, you can append an array: | |
113 // | |
114 // path.append({"foo", "bar"}) | |
115 // | |
116 // Or, use Path::parse(): | |
117 // | |
118 // path.append(Path::parse("foo//baz/../bar")) | |
119 | |
120 Path eval(StringPtr pathText) const&; | |
121 Path eval(StringPtr pathText) &&; | |
122 // Evaluates a traditional path relative to this one. `pathText` is parsed like `parse()` would, | |
123 // except that: | |
124 // - It can contain leading ".." components that traverse up the tree. | |
125 // - It can have a leading '/' which completely replaces the current path. | |
126 // | |
127 // THE NAME OF THIS METHOD WAS CHOSEN TO INSPIRE FEAR. | |
128 // | |
129 // Instead of using `path.eval(str)`, always consider whether you really want | |
130 // `path.append(Path::parse(str))`. The former is much riskier than the latter in terms of path | |
131 // injection vulnerabilities. | |
132 | |
133 PathPtr basename() const&; | |
134 Path basename() &&; | |
135 // Get the last component of the path. (Use `basename()[0]` to get just the string.) | |
136 | |
137 PathPtr parent() const&; | |
138 Path parent() &&; | |
139 // Get the parent path. | |
140 | |
141 String toString(bool absolute = false) const; | |
142 // Converts the path to a traditional path string, appropriate to pass to a unix system call. | |
143 // Never throws. | |
144 | |
145 const String& operator[](size_t i) const&; | |
146 String operator[](size_t i) &&; | |
147 size_t size() const; | |
148 const String* begin() const; | |
149 const String* end() const; | |
150 PathPtr slice(size_t start, size_t end) const&; | |
151 Path slice(size_t start, size_t end) &&; | |
152 // A Path can be accessed as an array of strings. | |
153 | |
154 bool operator==(PathPtr other) const; | |
155 bool operator!=(PathPtr other) const; | |
156 bool operator< (PathPtr other) const; | |
157 bool operator> (PathPtr other) const; | |
158 bool operator<=(PathPtr other) const; | |
159 bool operator>=(PathPtr other) const; | |
160 // Compare path components lexically. | |
161 | |
162 bool operator==(const Path& other) const; | |
163 bool operator!=(const Path& other) const; | |
164 bool operator< (const Path& other) const; | |
165 bool operator> (const Path& other) const; | |
166 bool operator<=(const Path& other) const; | |
167 bool operator>=(const Path& other) const; | |
168 | |
169 uint hashCode() const; | |
170 // Can use in HashMap. | |
171 | |
172 bool startsWith(PathPtr prefix) const; | |
173 bool endsWith(PathPtr suffix) const; | |
174 // Compare prefix / suffix. | |
175 | |
176 Path evalWin32(StringPtr pathText) const&; | |
177 Path evalWin32(StringPtr pathText) &&; | |
178 // Evaluates a Win32-style path, as might be written by a user. Differences from `eval()` | |
179 // include: | |
180 // | |
181 // - Backslashes can be used as path separators. | |
182 // - Absolute paths begin with a drive letter followed by a colon. The drive letter, including | |
183 // the colon, will become the first component of the path, e.g. "c:\foo" becomes {"c:", "foo"}. | |
184 // - A network path like "\\host\share\path" is parsed as {"host", "share", "path"}. | |
185 | |
186 Path evalNative(StringPtr pathText) const&; | |
187 Path evalNative(StringPtr pathText) &&; | |
188 // Alias for either eval() or evalWin32() depending on the target platform. Use this when you are | |
189 // parsing a path provided by a user and you want the user to be able to use the "natural" format | |
190 // for their platform. | |
191 | |
192 String toWin32String(bool absolute = false) const; | |
193 // Converts the path to a Win32 path string, as you might display to a user. | |
194 // | |
195 // This is meant for display. For making Win32 system calls, consider `toWin32Api()` instead. | |
196 // | |
197 // If `absolute` is true, the path is expected to be an absolute path, meaning the first | |
198 // component is a drive letter, namespace, or network host name. These are converted to their | |
199 // regular Win32 format -- i.e. this method does the reverse of `evalWin32()`. | |
200 // | |
201 // This throws if the path would have unexpected special meaning or is otherwise invalid on | |
202 // Windows, such as if it contains backslashes (within a path component), colons, or special | |
203 // names like "con". | |
204 | |
205 String toNativeString(bool absolute = false) const; | |
206 // Alias for either toString() or toWin32String() depending on the target platform. Use this when | |
207 // you are formatting a path to display to a user and you want to present it in the "natural" | |
208 // format for the user's platform. | |
209 | |
210 Array<wchar_t> forWin32Api(bool absolute) const; | |
211 // Like toWin32String, but additionally: | |
212 // - Converts the path to UTF-16, with a NUL terminator included. | |
213 // - For absolute paths, adds the "\\?\" prefix which opts into permitting paths longer than | |
214 // MAX_PATH, and turns off relative path processing (which KJ paths already handle in userspace | |
215 // anyway). | |
216 // | |
217 // This method is good to use when making a Win32 API call, e.g.: | |
218 // | |
219 // DeleteFileW(path.forWin32Api(true).begin()); | |
220 | |
221 static Path parseWin32Api(ArrayPtr<const wchar_t> text); | |
222 // Parses an absolute path as returned by a Win32 API call like GetFinalPathNameByHandle() or | |
223 // GetCurrentDirectory(). A "\\?\" prefix is optional but understood if present. | |
224 // | |
225 // Since such Win32 API calls generally return a length, this function inputs an array slice. | |
226 // The slice should not include any NUL terminator. | |
227 | |
228 private: | |
229 Array<String> parts; | |
230 | |
231 // TODO(perf): Consider unrolling one element from `parts`, so that a one-element path doesn't | |
232 // require allocation of an array. | |
233 | |
234 enum { ALREADY_CHECKED }; | |
235 Path(Array<String> parts, decltype(ALREADY_CHECKED)); | |
236 | |
237 friend class PathPtr; | |
238 | |
239 static String stripNul(String input); | |
240 static void validatePart(StringPtr part); | |
241 static void evalPart(Vector<String>& parts, ArrayPtr<const char> part); | |
242 static Path evalImpl(Vector<String>&& parts, StringPtr path); | |
243 static Path evalWin32Impl(Vector<String>&& parts, StringPtr path, bool fromApi = false); | |
244 static size_t countParts(StringPtr path); | |
245 static size_t countPartsWin32(StringPtr path); | |
246 static bool isWin32Drive(ArrayPtr<const char> part); | |
247 static bool isNetbiosName(ArrayPtr<const char> part); | |
248 static bool isWin32Special(StringPtr part); | |
249 }; | |
250 | |
251 class PathPtr { | |
252 // Points to a Path or a slice of a Path, but doesn't own it. | |
253 // | |
254 // PathPtr is to Path as ArrayPtr is to Array and StringPtr is to String. | |
255 | |
256 public: | |
257 PathPtr(decltype(nullptr)); | |
258 PathPtr(const Path& path); | |
259 | |
260 Path clone(); | |
261 Path append(Path&& suffix) const; | |
262 Path append(PathPtr suffix) const; | |
263 Path append(StringPtr suffix) const; | |
264 Path append(String&& suffix) const; | |
265 Path eval(StringPtr pathText) const; | |
266 PathPtr basename() const; | |
267 PathPtr parent() const; | |
268 String toString(bool absolute = false) const; | |
269 const String& operator[](size_t i) const; | |
270 size_t size() const; | |
271 const String* begin() const; | |
272 const String* end() const; | |
273 PathPtr slice(size_t start, size_t end) const; | |
274 bool operator==(PathPtr other) const; | |
275 bool operator!=(PathPtr other) const; | |
276 bool operator< (PathPtr other) const; | |
277 bool operator> (PathPtr other) const; | |
278 bool operator<=(PathPtr other) const; | |
279 bool operator>=(PathPtr other) const; | |
280 uint hashCode() const; | |
281 bool startsWith(PathPtr prefix) const; | |
282 bool endsWith(PathPtr suffix) const; | |
283 Path evalWin32(StringPtr pathText) const; | |
284 Path evalNative(StringPtr pathText) const; | |
285 String toWin32String(bool absolute = false) const; | |
286 String toNativeString(bool absolute = false) const; | |
287 Array<wchar_t> forWin32Api(bool absolute) const; | |
288 // Equivalent to the corresponding methods of `Path`. | |
289 | |
290 private: | |
291 ArrayPtr<const String> parts; | |
292 | |
293 explicit PathPtr(ArrayPtr<const String> parts); | |
294 | |
295 String toWin32StringImpl(bool absolute, bool forApi) const; | |
296 | |
297 friend class Path; | |
298 }; | |
299 | |
300 // ======================================================================================= | |
301 // The filesystem API | |
302 // | |
303 // This API is strictly synchronous because, unfortunately, there's no such thing as asynchronous | |
304 // filesystem access in practice. The filesystem drivers on Linux are written to assume they can | |
305 // block. The AIO API is only actually asynchronous for reading/writing the raw file blocks, but if | |
306 // the filesystem needs to be involved (to allocate blocks, update metadata, etc.) that will block. | |
307 // It's best to imagine that the filesystem is just another tier of memory that happens to be | |
308 // slower than RAM (which is slower than L3 cache, which is slower than L2, which is slower than | |
309 // L1). You can't do asynchronous RAM access so why asynchronous filesystem? The only way to | |
310 // parallelize these is using threads. | |
311 // | |
312 // All KJ filesystem objects are thread-safe, and so all methods are marked "const" (even write | |
313 // methods). Of course, if you concurrently write the same bytes of a file from multiple threads, | |
314 // it's unspecified which write will "win". | |
315 | |
316 class FsNode { | |
317 // Base class for filesystem node types. | |
318 | |
319 public: | |
320 Own<const FsNode> clone() const; | |
321 // Creates a new object of exactly the same type as this one, pointing at exactly the same | |
322 // external object. | |
323 // | |
324 // Under the hood, this will call dup(), so the FD number will not be the same. | |
325 | |
326 virtual Maybe<int> getFd() const { return nullptr; } | |
327 // Get the underlying Unix file descriptor, if any. Returns nullptr if this object actually isn't | |
328 // wrapping a file descriptor. | |
329 | |
330 virtual Maybe<void*> getWin32Handle() const { return nullptr; } | |
331 // Get the underlying Win32 HANDLE, if any. Returns nullptr if this object actually isn't | |
332 // wrapping a handle. | |
333 | |
334 enum class Type { | |
335 FILE, | |
336 DIRECTORY, | |
337 SYMLINK, | |
338 BLOCK_DEVICE, | |
339 CHARACTER_DEVICE, | |
340 NAMED_PIPE, | |
341 SOCKET, | |
342 OTHER, | |
343 }; | |
344 | |
345 struct Metadata { | |
346 Type type = Type::FILE; | |
347 | |
348 uint64_t size = 0; | |
349 // Logical size of the file. | |
350 | |
351 uint64_t spaceUsed = 0; | |
352 // Physical size of the file on disk. May be smaller for sparse files, or larger for | |
353 // pre-allocated files. | |
354 | |
355 Date lastModified = UNIX_EPOCH; | |
356 // Last modification time of the file. | |
357 | |
358 uint linkCount = 1; | |
359 // Number of hard links pointing to this node. | |
360 | |
361 uint64_t hashCode = 0; | |
362 // Hint which can be used to determine if two FsNode instances point to the same underlying | |
363 // file object. If two FsNodes report different hashCodes, then they are not the same object. | |
364 // If they report the same hashCode, then they may or may not be the same object. | |
365 // | |
366 // The Unix filesystem implementation builds the hashCode based on st_dev and st_ino of | |
367 // `struct stat`. However, note that some filesystems -- especially FUSE-based -- may not fill | |
368 // in st_ino. | |
369 // | |
370 // The Windows filesystem implementation builds the hashCode based on dwVolumeSerialNumber and | |
371 // dwFileIndex{Low,High} of the BY_HANDLE_FILE_INFORMATION structure. However, these are again | |
372 // not guaranteed to be unique on all filesystems. In particular the documentation says that | |
373 // ReFS uses 128-bit identifiers which can't be represented here, and again virtual filesystems | |
374 // may often not report real identifiers. | |
375 // | |
376 // Of course, the process of hashing values into a single hash code can also cause collisions | |
377 // even if the filesystem reports reliable information. | |
378 // | |
379 // Additionally note that this value is not reliable when returned by `lstat()`. You should | |
380 // actually open the object, then call `stat()` on the opened object. | |
381 | |
382 // Not currently included: | |
383 // - Access control info: Differs wildly across platforms, and KJ prefers capabilities anyway. | |
384 // - Other timestamps: Differs across platforms. | |
385 // - Device number: If you care, you're probably doing platform-specific stuff anyway. | |
386 | |
387 Metadata() = default; | |
388 Metadata(Type type, uint64_t size, uint64_t spaceUsed, Date lastModified, uint linkCount, | |
389 uint64_t hashCode) | |
390 : type(type), size(size), spaceUsed(spaceUsed), lastModified(lastModified), | |
391 linkCount(linkCount), hashCode(hashCode) {} | |
392 // TODO(cleanup): This constructor is redundant in C++14, but needed in C++11. | |
393 }; | |
394 | |
395 virtual Metadata stat() const = 0; | |
396 | |
397 virtual void sync() const = 0; | |
398 virtual void datasync() const = 0; | |
399 // Maps to fsync() and fdatasync() system calls. | |
400 // | |
401 // Also, when creating or overwriting a file, the first call to sync() atomically links the file | |
402 // into the filesystem (*after* syncing the data), so than incomplete data is never visible to | |
403 // other processes. (In practice this works by writing into a temporary file and then rename()ing | |
404 // it.) | |
405 | |
406 protected: | |
407 virtual Own<const FsNode> cloneFsNode() const = 0; | |
408 // Implements clone(). Required to return an object with exactly the same type as this one. | |
409 // Hence, every subclass must implement this. | |
410 }; | |
411 | |
412 class ReadableFile: public FsNode { | |
413 public: | |
414 Own<const ReadableFile> clone() const; | |
415 | |
416 String readAllText() const; | |
417 // Read all text in the file and return as a big string. | |
418 | |
419 Array<byte> readAllBytes() const; | |
420 // Read all bytes in the file and return as a big byte array. | |
421 // | |
422 // This differs from mmap() in that the read is performed all at once. Future changes to the file | |
423 // do not affect the returned copy. Consider using mmap() instead, particularly for large files. | |
424 | |
425 virtual size_t read(uint64_t offset, ArrayPtr<byte> buffer) const = 0; | |
426 // Fills `buffer` with data starting at `offset`. Returns the number of bytes actually read -- | |
427 // the only time this is less than `buffer.size()` is when EOF occurs mid-buffer. | |
428 | |
429 virtual Array<const byte> mmap(uint64_t offset, uint64_t size) const = 0; | |
430 // Maps the file to memory read-only. The returned array always has exactly the requested size. | |
431 // Depending on the capabilities of the OS and filesystem, the mapping may or may not reflect | |
432 // changes that happen to the file after mmap() returns. | |
433 // | |
434 // Multiple calls to mmap() on the same file may or may not return the same mapping (it is | |
435 // immutable, so there's no possibility of interference). | |
436 // | |
437 // If the file cannot be mmap()ed, an implementation may choose to allocate a buffer on the heap, | |
438 // read into it, and return that. This should only happen if a real mmap() is impossible. | |
439 // | |
440 // The returned array is always exactly the size requested. However, accessing bytes beyond the | |
441 // current end of the file may raise SIGBUS, or may simply return zero. | |
442 | |
443 virtual Array<byte> mmapPrivate(uint64_t offset, uint64_t size) const = 0; | |
444 // Like mmap() but returns a view that the caller can modify. Modifications will not be written | |
445 // to the underlying file. Every call to this method returns a unique mapping. Changes made to | |
446 // the underlying file by other clients may or may not be reflected in the mapping -- in fact, | |
447 // some changes may be reflected while others aren't, even within the same mapping. | |
448 // | |
449 // In practice this is often implemented using copy-on-write pages. When you first write to a | |
450 // page, a copy is made. Hence, changes to the underlying file within that page stop being | |
451 // reflected in the mapping. | |
452 }; | |
453 | |
454 class AppendableFile: public FsNode, public OutputStream { | |
455 public: | |
456 Own<const AppendableFile> clone() const; | |
457 | |
458 // All methods are inherited. | |
459 }; | |
460 | |
461 class WritableFileMapping { | |
462 public: | |
463 virtual ArrayPtr<byte> get() const = 0; | |
464 // Gets the mapped bytes. The returned array can be modified, and those changes may be written to | |
465 // the underlying file, but there is no guarantee that they are written unless you subsequently | |
466 // call changed(). | |
467 | |
468 virtual void changed(ArrayPtr<byte> slice) const = 0; | |
469 // Notifies the implementation that the given bytes have changed. For some implementations this | |
470 // may be a no-op while for others it may be necessary in order for the changes to be written | |
471 // back at all. | |
472 // | |
473 // `slice` must be a slice of `bytes()`. | |
474 | |
475 virtual void sync(ArrayPtr<byte> slice) const = 0; | |
476 // Implies `changed()`, and then waits until the range has actually been written to disk before | |
477 // returning. | |
478 // | |
479 // `slice` must be a slice of `bytes()`. | |
480 // | |
481 // On Windows, this calls FlushViewOfFile(). The documentation for this function implies that in | |
482 // some circumstances, to fully sync to physical disk, you may need to call FlushFileBuffers() on | |
483 // the file HANDLE as well. The documentation is not very clear on when and why this is needed. | |
484 // If you believe your program needs this, you can accomplish it by calling `.sync()` on the File | |
485 // object after calling `.sync()` on the WritableFileMapping. | |
486 }; | |
487 | |
488 class File: public ReadableFile { | |
489 public: | |
490 Own<const File> clone() const; | |
491 | |
492 void writeAll(ArrayPtr<const byte> bytes) const; | |
493 void writeAll(StringPtr text) const; | |
494 // Completely replace the file with the given bytes or text. | |
495 | |
496 virtual void write(uint64_t offset, ArrayPtr<const byte> data) const = 0; | |
497 // Write the given data starting at the given offset in the file. | |
498 | |
499 virtual void zero(uint64_t offset, uint64_t size) const = 0; | |
500 // Write zeros to the file, starting at `offset` and continuing for `size` bytes. If the platform | |
501 // supports it, this will "punch a hole" in the file, such that blocks that are entirely zeros | |
502 // do not take space on disk. | |
503 | |
504 virtual void truncate(uint64_t size) const = 0; | |
505 // Set the file end pointer to `size`. If `size` is less than the current size, data past the end | |
506 // is truncated. If `size` is larger than the current size, zeros are added to the end of the | |
507 // file. If the platform supports it, blocks containing all-zeros will not be stored to disk. | |
508 | |
509 virtual Own<const WritableFileMapping> mmapWritable(uint64_t offset, uint64_t size) const = 0; | |
510 // Like ReadableFile::mmap() but returns a mapping for which any changes will be immediately | |
511 // visible in other mappings of the file on the same system and will eventually be written back | |
512 // to the file. | |
513 | |
514 virtual size_t copy(uint64_t offset, const ReadableFile& from, uint64_t fromOffset, | |
515 uint64_t size) const; | |
516 // Copies bytes from one file to another. | |
517 // | |
518 // Copies `size` bytes or to EOF, whichever comes first. Returns the number of bytes actually | |
519 // copied. Hint: Pass kj::maxValue for `size` to always copy to EOF. | |
520 // | |
521 // The copy is not atomic. Concurrent writes may lead to garbage results. | |
522 // | |
523 // The default implementation performs a series of reads and writes. Subclasses can often provide | |
524 // superior implementations that offload the work to the OS or even implement copy-on-write. | |
525 }; | |
526 | |
527 class ReadableDirectory: public FsNode { | |
528 // Read-only subset of `Directory`. | |
529 | |
530 public: | |
531 Own<const ReadableDirectory> clone() const; | |
532 | |
533 virtual Array<String> listNames() const = 0; | |
534 // List the contents of this directory. Does NOT include "." nor "..". | |
535 | |
536 struct Entry { | |
537 FsNode::Type type; | |
538 String name; | |
539 | |
540 inline bool operator< (const Entry& other) const { return name < other.name; } | |
541 inline bool operator> (const Entry& other) const { return name > other.name; } | |
542 inline bool operator<=(const Entry& other) const { return name <= other.name; } | |
543 inline bool operator>=(const Entry& other) const { return name >= other.name; } | |
544 // Convenience comparison operators to sort entries by name. | |
545 }; | |
546 | |
547 virtual Array<Entry> listEntries() const = 0; | |
548 // List the contents of the directory including the type of each file. On some platforms and | |
549 // filesystems, this is just as fast as listNames(), but on others it may require stat()ing each | |
550 // file. | |
551 | |
552 virtual bool exists(PathPtr path) const = 0; | |
553 // Does the specified path exist? | |
554 // | |
555 // If the path is a symlink, the symlink is followed and the return value indicates if the target | |
556 // exists. If you want to know if the symlink exists, use lstat(). (This implies that listNames() | |
557 // may return names for which exists() reports false.) | |
558 | |
559 FsNode::Metadata lstat(PathPtr path) const; | |
560 virtual Maybe<FsNode::Metadata> tryLstat(PathPtr path) const = 0; | |
561 // Gets metadata about the path. If the path is a symlink, it is not followed -- the metadata | |
562 // describes the symlink itself. `tryLstat()` returns null if the path doesn't exist. | |
563 | |
564 Own<const ReadableFile> openFile(PathPtr path) const; | |
565 virtual Maybe<Own<const ReadableFile>> tryOpenFile(PathPtr path) const = 0; | |
566 // Open a file for reading. | |
567 // | |
568 // `tryOpenFile()` returns null if the path doesn't exist. Other errors still throw exceptions. | |
569 | |
570 Own<const ReadableDirectory> openSubdir(PathPtr path) const; | |
571 virtual Maybe<Own<const ReadableDirectory>> tryOpenSubdir(PathPtr path) const = 0; | |
572 // Opens a subdirectory. | |
573 // | |
574 // `tryOpenSubdir()` returns null if the path doesn't exist. Other errors still throw exceptions. | |
575 | |
576 String readlink(PathPtr path) const; | |
577 virtual Maybe<String> tryReadlink(PathPtr path) const = 0; | |
578 // If `path` is a symlink, reads and returns the link contents. | |
579 // | |
580 // Note that tryReadlink() differs subtly from tryOpen*(). For example, tryOpenFile() throws if | |
581 // the path is not a file (e.g. if it's a directory); it only returns null if the path doesn't | |
582 // exist at all. tryReadlink() returns null if either the path doesn't exist, or if it does exist | |
583 // but isn't a symlink. This is because if it were to throw instead, then almost every real-world | |
584 // use case of tryReadlink() would be forced to perform an lstat() first for the sole purpose of | |
585 // checking if it is a link, wasting a syscall and a path traversal. | |
586 // | |
587 // See Directory::symlink() for warnings about symlinks. | |
588 }; | |
589 | |
590 enum class WriteMode { | |
591 // Mode for opening a file (or directory) for write. | |
592 // | |
593 // (To open a file or directory read-only, do not specify a mode.) | |
594 // | |
595 // WriteMode is a bitfield. Hence, it overloads the bitwise logic operators. To check if a | |
596 // particular bit is set in a bitfield, use kj::has(), like: | |
597 // | |
598 // if (kj::has(mode, WriteMode::MUST_EXIST)) { | |
599 // requireExists(path); | |
600 // } | |
601 // | |
602 // (`if (mode & WriteMode::MUST_EXIST)` doesn't work because WriteMode is an enum class, which | |
603 // cannot be converted to bool. Alas, C++ does not allow you to define a conversion operator | |
604 // on an enum type, so we can't define a conversion to bool.) | |
605 | |
606 // ----------------------------------------- | |
607 // Core flags | |
608 // | |
609 // At least one of CREATE or MODIFY must be specified. Optionally, the two flags can be combined | |
610 // with a bitwise-OR. | |
611 | |
612 CREATE = 1, | |
613 // Create a new empty file. | |
614 // | |
615 // When not combined with MODIFY, if the file already exists (including as a broken symlink), | |
616 // tryOpenFile() returns null (and openFile() throws). | |
617 // | |
618 // When combined with MODIFY, if the path already exists, it will be opened as if CREATE hadn't | |
619 // been specified at all. If the path refers to a broken symlink, the file at the target of the | |
620 // link will be created (if its parent directory exists). | |
621 | |
622 MODIFY = 2, | |
623 // Modify an existing file. | |
624 // | |
625 // When not combined with CREATE, if the file doesn't exist (including if it is a broken symlink), | |
626 // tryOpenFile() returns null (and openFile() throws). | |
627 // | |
628 // When combined with CREATE, if the path doesn't exist, it will be created as if MODIFY hadn't | |
629 // been specified at all. If the path refers to a broken symlink, the file at the target of the | |
630 // link will be created (if its parent directory exists). | |
631 | |
632 // ----------------------------------------- | |
633 // Additional flags | |
634 // | |
635 // Any number of these may be OR'd with the core flags. | |
636 | |
637 CREATE_PARENT = 4, | |
638 // Indicates that if the target node's parent directory doesn't exist, it should be created | |
639 // automatically, along with its parent, and so on. This creation is NOT atomic. | |
640 // | |
641 // This bit only makes sense with CREATE or REPLACE. | |
642 | |
643 EXECUTABLE = 8, | |
644 // Mark this file executable, if this is a meaningful designation on the host platform. | |
645 | |
646 PRIVATE = 16, | |
647 // Indicates that this file is sensitive and should have permissions masked so that it is only | |
648 // accessible by the current user. | |
649 // | |
650 // When this is not used, the platform's default access control settings are used. On Unix, | |
651 // that usually means the umask is applied. On Windows, it means permissions are inherited from | |
652 // the parent. | |
653 }; | |
654 | |
655 inline constexpr WriteMode operator|(WriteMode a, WriteMode b) { | |
656 return static_cast<WriteMode>(static_cast<uint>(a) | static_cast<uint>(b)); | |
657 } | |
658 inline constexpr WriteMode operator&(WriteMode a, WriteMode b) { | |
659 return static_cast<WriteMode>(static_cast<uint>(a) & static_cast<uint>(b)); | |
660 } | |
661 inline constexpr WriteMode operator+(WriteMode a, WriteMode b) { | |
662 return static_cast<WriteMode>(static_cast<uint>(a) | static_cast<uint>(b)); | |
663 } | |
664 inline constexpr WriteMode operator-(WriteMode a, WriteMode b) { | |
665 return static_cast<WriteMode>(static_cast<uint>(a) & ~static_cast<uint>(b)); | |
666 } | |
667 template <typename T, typename = EnableIf<__is_enum(T)>> | |
668 bool has(T haystack, T needle) { | |
669 return (static_cast<__underlying_type(T)>(haystack) & | |
670 static_cast<__underlying_type(T)>(needle)) == | |
671 static_cast<__underlying_type(T)>(needle); | |
672 } | |
673 | |
674 enum class TransferMode { | |
675 // Specifies desired behavior for Directory::transfer(). | |
676 | |
677 MOVE, | |
678 // The node is moved to the new location, i.e. the old location is deleted. If possible, this | |
679 // move is performed without copying, otherwise it is performed as a copy followed by a delete. | |
680 | |
681 LINK, | |
682 // The new location becomes a synonym for the old location (a "hard link"). Filesystems have | |
683 // varying support for this -- typically, it is not supported on directories. | |
684 | |
685 COPY | |
686 // The new location becomes a copy of the old. | |
687 // | |
688 // Some filesystems may implement this in terms of copy-on-write. | |
689 // | |
690 // If the filesystem supports sparse files, COPY takes sparseness into account -- it will punch | |
691 // holes in the target file where holes exist in the source file. | |
692 }; | |
693 | |
694 class Directory: public ReadableDirectory { | |
695 // Refers to a specific directory on disk. | |
696 // | |
697 // A `Directory` object *only* provides access to children of the directory, not parents. That | |
698 // is, you cannot open the file "..", nor jump to the root directory with "/". | |
699 // | |
700 // On OSs that support it, a `Directory` is backed by an open handle to the directory node. This | |
701 // means: | |
702 // - If the directory is renamed on-disk, the `Directory` object still points at it. | |
703 // - Opening files in the directory only requires the OS to traverse the path from the directory | |
704 // to the file; it doesn't have to re-traverse all the way from the filesystem root. | |
705 // | |
706 // On Windows, a `Directory` object holds a lock on the underlying directory such that it cannot | |
707 // be renamed nor deleted while the object exists. This is necessary because Windows does not | |
708 // fully support traversing paths relative to file handles (it does for some operations but not | |
709 // all), so the KJ filesystem implementation is forced to remember the full path and needs to | |
710 // ensure that the path is not invalidated. If, in the future, Windows fully supports | |
711 // handle-relative paths, KJ may stop locking directories in this way, so do not rely on this | |
712 // behavior. | |
713 | |
714 public: | |
715 Own<const Directory> clone() const; | |
716 | |
717 template <typename T> | |
718 class Replacer { | |
719 // Implements an atomic replacement of a file or directory, allowing changes to be made to | |
720 // storage in a way that avoids losing data in a power outage and prevents other processes | |
721 // from observing content in an inconsistent state. | |
722 // | |
723 // `T` may be `File` or `Directory`. For readability, the text below describes replacing a | |
724 // file, but the logic is the same for directories. | |
725 // | |
726 // When you call `Directory::replaceFile()`, a temporary file is created, but the specified | |
727 // path is not yet touched. You may call `get()` to obtain the temporary file object, through | |
728 // which you may initialize its content, knowing that no other process can see it yet. The file | |
729 // is atomically moved to its final path when you call `commit()`. If you destroy the Replacer | |
730 // without calling commit(), the temporary file is deleted. | |
731 // | |
732 // Note that most operating systems sadly do not support creating a truly unnamed temporary file | |
733 // and then linking it in later. Moreover, the file cannot necessarily be created in the system | |
734 // temporary directory because it might not be on the same filesystem as the target. Therefore, | |
735 // the replacement file may initially be created in the same directory as its eventual target. | |
736 // The implementation of Directory will choose a name that is unique and "hidden" according to | |
737 // the conventions of the filesystem. Additionally, the implementation of Directory will avoid | |
738 // returning these temporary files from its list*() methods, in order to avoid observable | |
739 // inconsistencies across platforms. | |
740 public: | |
741 explicit Replacer(WriteMode mode); | |
742 | |
743 virtual const T& get() = 0; | |
744 // Gets the File or Directory representing the replacement data. Fill in this object before | |
745 // calling commit(). | |
746 | |
747 void commit(); | |
748 virtual bool tryCommit() = 0; | |
749 // Commit the replacement. | |
750 // | |
751 // `tryCommit()` may return false based on the CREATE/MODIFY bits passed as the WriteMode when | |
752 // the replacement was initiated. (If CREATE but not MODIFY was used, tryCommit() returns | |
753 // false to indicate that the target file already existed. If MODIFY but not CREATE was used, | |
754 // tryCommit() returns false to indicate that the file didn't exist.) | |
755 // | |
756 // `commit()` is atomic, meaning that there is no point in time at which other processes | |
757 // observing the file will see it in an intermediate state -- they will either see the old | |
758 // content or the complete new content. This includes in the case of a power outage or machine | |
759 // failure: on recovery, the file will either be in the old state or the new state, but not in | |
760 // some intermediate state. | |
761 // | |
762 // It's important to note that a power failure *after commit() returns* can still revert the | |
763 // file to its previous state. That is, `commit()` does NOT guarantee that, upon return, the | |
764 // new content is durable. In order to guarantee this, you must call `sync()` on the immediate | |
765 // parent directory of the replaced file. | |
766 // | |
767 // Note that, sadly, not all filesystems / platforms are capable of supporting all of the | |
768 // guarantees documented above. In such cases, commit() will make a best-effort attempt to do | |
769 // what it claims. Some examples of possible problems include: | |
770 // - Any guarantees about durability through a power outage probably require a journaling | |
771 // filesystem. | |
772 // - Many platforms do not support atomically replacing a non-empty directory. Linux does as | |
773 // of kernel 3.15 (via the renameat2() syscall using RENAME_EXCHANGE). Where not supported, | |
774 // the old directory will be moved away just before the replacement is moved into place. | |
775 // - Many platforms do not support atomically requiring the existence or non-existence of a | |
776 // file before replacing it. In these cases, commit() may have to perform the check as a | |
777 // separate step, with a small window for a race condition. | |
778 // - Many platforms do not support "unlinking" a non-empty directory, meaning that a replaced | |
779 // directory will need to be deconstructed by deleting all contents. If another process has | |
780 // the directory open when it is replaced, that process will observe the contents | |
781 // disappearing after the replacement (actually, a swap) has taken place. This differs from | |
782 // files, where a process that has opened a file before it is replaced will continue see the | |
783 // file's old content unchanged after the replacement. | |
784 // - On Windows, there are multiple ways to replace one file with another in a single system | |
785 // call, but none are documented as being atomic. KJ always uses `MoveFileEx()` with | |
786 // MOVEFILE_REPLACE_EXISTING. While the alternative `ReplaceFile()` is attractive for many | |
787 // reasons, it has the critical problem that it cannot be used when the source file has open | |
788 // file handles, which is generally the case when using Replacer. | |
789 | |
790 protected: | |
791 const WriteMode mode; | |
792 }; | |
793 | |
794 using ReadableDirectory::openFile; | |
795 using ReadableDirectory::openSubdir; | |
796 using ReadableDirectory::tryOpenFile; | |
797 using ReadableDirectory::tryOpenSubdir; | |
798 | |
799 Own<const File> openFile(PathPtr path, WriteMode mode) const; | |
800 virtual Maybe<Own<const File>> tryOpenFile(PathPtr path, WriteMode mode) const = 0; | |
801 // Open a file for writing. | |
802 // | |
803 // `tryOpenFile()` returns null if the path is required to exist but doesn't (MODIFY or REPLACE) | |
804 // or if the path is required not to exist but does (CREATE or RACE). These are the only cases | |
805 // where it returns null -- all other types of errors (like "access denied") throw exceptions. | |
806 | |
807 virtual Own<Replacer<File>> replaceFile(PathPtr path, WriteMode mode) const = 0; | |
808 // Construct a file which, when ready, will be atomically moved to `path`, replacing whatever | |
809 // is there already. See `Replacer<T>` for detalis. | |
810 // | |
811 // The `CREATE` and `MODIFY` bits of `mode` are not enforced until commit time, hence | |
812 // `replaceFile()` has no "try" variant. | |
813 | |
814 virtual Own<const File> createTemporary() const = 0; | |
815 // Create a temporary file backed by this directory's filesystem, but which isn't linked into | |
816 // the directory tree. The file is deleted from disk when all references to it have been dropped. | |
817 | |
818 Own<AppendableFile> appendFile(PathPtr path, WriteMode mode) const; | |
819 virtual Maybe<Own<AppendableFile>> tryAppendFile(PathPtr path, WriteMode mode) const = 0; | |
820 // Opens the file for appending only. Useful for log files. | |
821 // | |
822 // If the underlying filesystem supports it, writes to the file will always be appended even if | |
823 // other writers are writing to the same file at the same time -- however, some implementations | |
824 // may instead assume that no other process is changing the file size between writes. | |
825 | |
826 Own<const Directory> openSubdir(PathPtr path, WriteMode mode) const; | |
827 virtual Maybe<Own<const Directory>> tryOpenSubdir(PathPtr path, WriteMode mode) const = 0; | |
828 // Opens a subdirectory for writing. | |
829 | |
830 virtual Own<Replacer<Directory>> replaceSubdir(PathPtr path, WriteMode mode) const = 0; | |
831 // Construct a directory which, when ready, will be atomically moved to `path`, replacing | |
832 // whatever is there already. See `Replacer<T>` for detalis. | |
833 // | |
834 // The `CREATE` and `MODIFY` bits of `mode` are not enforced until commit time, hence | |
835 // `replaceSubdir()` has no "try" variant. | |
836 | |
837 void symlink(PathPtr linkpath, StringPtr content, WriteMode mode) const; | |
838 virtual bool trySymlink(PathPtr linkpath, StringPtr content, WriteMode mode) const = 0; | |
839 // Create a symlink. `content` is the raw text which will be written into the symlink node. | |
840 // How this text is interpreted is entirely dependent on the filesystem. Note in particular that: | |
841 // - Windows will require a path that uses backslashes as the separator. | |
842 // - InMemoryDirectory does not support symlinks containing "..". | |
843 // | |
844 // Unfortunately under many implementations symlink() can be used to break out of the directory | |
845 // by writing an absolute path or utilizing "..". Do not call this method with a value for | |
846 // `target` that you don't trust. | |
847 // | |
848 // `mode` must be CREATE or REPLACE, not MODIFY. CREATE_PARENT is honored but EXECUTABLE and | |
849 // PRIVATE have no effect. `trySymlink()` returns false in CREATE mode when the target already | |
850 // exists. | |
851 | |
852 void transfer(PathPtr toPath, WriteMode toMode, | |
853 PathPtr fromPath, TransferMode mode) const; | |
854 void transfer(PathPtr toPath, WriteMode toMode, | |
855 const Directory& fromDirectory, PathPtr fromPath, | |
856 TransferMode mode) const; | |
857 virtual bool tryTransfer(PathPtr toPath, WriteMode toMode, | |
858 const Directory& fromDirectory, PathPtr fromPath, | |
859 TransferMode mode) const; | |
860 virtual Maybe<bool> tryTransferTo(const Directory& toDirectory, PathPtr toPath, WriteMode toMode, | |
861 PathPtr fromPath, TransferMode mode) const; | |
862 // Move, link, or copy a file/directory tree from one location to another. | |
863 // | |
864 // Filesystems vary in what kinds of transfers are allowed, especially for TransferMode::LINK, | |
865 // and whether TransferMode::MOVE is implemented as an actual move vs. copy+delete. | |
866 // | |
867 // tryTransfer() returns false if the source location didn't exist, or when `toMode` is CREATE | |
868 // and the target already exists. The default implementation implements only TransferMode::COPY. | |
869 // | |
870 // tryTransferTo() exists to implement double-dispatch. It should be called as a fallback by | |
871 // implementations of tryTransfer() in cases where the target directory would otherwise fail or | |
872 // perform a pessimal transfer. The default implementation returns nullptr, which the caller | |
873 // should interpret as: "I don't have any special optimizations; do the obvious thing." | |
874 // | |
875 // `toMode` controls how the target path is created. CREATE_PARENT is honored but EXECUTABLE and | |
876 // PRIVATE have no effect. | |
877 | |
878 void remove(PathPtr path) const; | |
879 virtual bool tryRemove(PathPtr path) const = 0; | |
880 // Deletes/unlinks the given path. If the path names a directory, it is recursively deleted. | |
881 // | |
882 // tryRemove() returns false in the specific case that the path doesn't exist. remove() would | |
883 // throw in this case. In all other error cases (like "access denied"), tryRemove() still throws; | |
884 // it is only "does not exist" that produces a false return. | |
885 // | |
886 // WARNING: The Windows implementation of recursive deletion is currently not safe to call from a | |
887 // privileged process to delete directories writable by unprivileged users, due to a race | |
888 // condition in which the user could trick the algorithm into following a symlink and deleting | |
889 // everything at the destination. This race condition is not present in the Unix | |
890 // implementation. Fixing it for Windows would require rewriting a lot of code to use different | |
891 // APIs. If you're interested, see the TODO(security) in filesystem-disk-win32.c++. | |
892 | |
893 // TODO(someday): | |
894 // - Support sockets? There's no openat()-like interface for sockets, so it's hard to support | |
895 // them currently. Also you'd probably want to use them with the async library. | |
896 // - Support named pipes? Unclear if there's a use case that isn't better-served by sockets. | |
897 // Then again, they can be openat()ed. | |
898 // - Support watching for changes (inotify). Probably also requires the async library. Also | |
899 // lacks openat()-like semantics. | |
900 // - xattrs -- linux-specific | |
901 // - chown/chmod/etc. -- unix-specific, ACLs, eww | |
902 // - set timestamps -- only needed by archiving programs/ | |
903 // - advisory locks | |
904 // - sendfile? | |
905 // - fadvise and such | |
906 | |
907 private: | |
908 static void commitFailed(WriteMode mode); | |
909 }; | |
910 | |
911 class Filesystem { | |
912 public: | |
913 virtual const Directory& getRoot() const = 0; | |
914 // Get the filesystem's root directory, as of the time the Filesystem object was created. | |
915 | |
916 virtual const Directory& getCurrent() const = 0; | |
917 // Get the filesystem's current directory, as of the time the Filesystem object was created. | |
918 | |
919 virtual PathPtr getCurrentPath() const = 0; | |
920 // Get the path from the root to the current directory, as of the time the Filesystem object was | |
921 // created. Note that because a `Directory` does not provide access to its parent, if you want to | |
922 // follow `..` from the current directory, you must use `getCurrentPath().eval("..")` or | |
923 // `getCurrentPath().parent()`. | |
924 // | |
925 // This function attempts to determine the path as it appeared in the user's shell before this | |
926 // program was started. That means, if the user had `cd`ed into a symlink, the path through that | |
927 // symlink is returned, *not* the canonical path. | |
928 // | |
929 // Because of this, there is an important difference between how the operating system interprets | |
930 // "../foo" and what you get when you write `getCurrentPath().eval("../foo")`: The former | |
931 // will interpret ".." relative to the directory's canonical path, whereas the latter will | |
932 // interpret it relative to the path shown in the user's shell. In practice, the latter is | |
933 // almost always what the user wants! But the former behavior is what almost all commands do | |
934 // in practice, and it leads to confusion. KJ commands should implement the behavior the user | |
935 // expects. | |
936 }; | |
937 | |
938 // ======================================================================================= | |
939 | |
940 Own<File> newInMemoryFile(const Clock& clock); | |
941 Own<Directory> newInMemoryDirectory(const Clock& clock); | |
942 // Construct file and directory objects which reside in-memory. | |
943 // | |
944 // InMemoryFile has the following special properties: | |
945 // - The backing store is not sparse and never gets smaller even if you truncate the file. | |
946 // - While a non-private memory mapping exists, the backing store cannot get larger. Any operation | |
947 // which would expand it will throw. | |
948 // | |
949 // InMemoryDirectory has the following special properties: | |
950 // - Symlinks are processed using Path::parse(). This implies that a symlink cannot point to a | |
951 // parent directory -- InMemoryDirectory does not know its parent. | |
952 // - link() can link directory nodes in addition to files. | |
953 // - link() and rename() accept any kind of Directory as `fromDirectory` -- it doesn't need to be | |
954 // another InMemoryDirectory. However, for rename(), the from path must be a directory. | |
955 | |
956 Own<AppendableFile> newFileAppender(Own<const File> inner); | |
957 // Creates an AppendableFile by wrapping a File. Note that this implementation assumes it is the | |
958 // only writer. A correct implementation should always append to the file even if other writes | |
959 // are happening simultaneously, as is achieved with the O_APPEND flag to open(2), but that | |
960 // behavior is not possible to emulate on top of `File`. | |
961 | |
962 #if _WIN32 | |
963 typedef AutoCloseHandle OsFileHandle; | |
964 #else | |
965 typedef AutoCloseFd OsFileHandle; | |
966 #endif | |
967 | |
968 Own<ReadableFile> newDiskReadableFile(OsFileHandle fd); | |
969 Own<AppendableFile> newDiskAppendableFile(OsFileHandle fd); | |
970 Own<File> newDiskFile(OsFileHandle fd); | |
971 Own<ReadableDirectory> newDiskReadableDirectory(OsFileHandle fd); | |
972 Own<Directory> newDiskDirectory(OsFileHandle fd); | |
973 // Wrap a file descriptor (or Windows HANDLE) as various filesystem types. | |
974 | |
975 Own<Filesystem> newDiskFilesystem(); | |
976 // Get at implementation of `Filesystem` representing the real filesystem. | |
977 // | |
978 // DO NOT CALL THIS except at the top level of your program, e.g. in main(). Anywhere else, you | |
979 // should instead have your caller pass in a Filesystem object, or a specific Directory object, | |
980 // or whatever it is that your code needs. This ensures that your code supports dependency | |
981 // injection, which makes it more reusable and testable. | |
982 // | |
983 // newDiskFilesystem() reads the current working directory at the time it is called. The returned | |
984 // object is not affected by subsequent calls to chdir(). | |
985 | |
986 // ======================================================================================= | |
987 // inline implementation details | |
988 | |
989 inline Path::Path(decltype(nullptr)): parts(nullptr) {} | |
990 inline Path::Path(std::initializer_list<StringPtr> parts) | |
991 : Path(arrayPtr(parts.begin(), parts.end())) {} | |
992 inline Path::Path(Array<String> parts, decltype(ALREADY_CHECKED)) | |
993 : parts(kj::mv(parts)) {} | |
994 inline Path Path::clone() const { return PathPtr(*this).clone(); } | |
995 inline Path Path::append(Path&& suffix) const& { return PathPtr(*this).append(kj::mv(suffix)); } | |
996 inline Path Path::append(PathPtr suffix) const& { return PathPtr(*this).append(suffix); } | |
997 inline Path Path::append(StringPtr suffix) const& { return append(Path(suffix)); } | |
998 inline Path Path::append(StringPtr suffix) && { return kj::mv(*this).append(Path(suffix)); } | |
999 inline Path Path::append(String&& suffix) const& { return append(Path(kj::mv(suffix))); } | |
1000 inline Path Path::append(String&& suffix) && { return kj::mv(*this).append(Path(kj::mv(suffix))); } | |
1001 inline Path Path::eval(StringPtr pathText) const& { return PathPtr(*this).eval(pathText); } | |
1002 inline PathPtr Path::basename() const& { return PathPtr(*this).basename(); } | |
1003 inline PathPtr Path::parent() const& { return PathPtr(*this).parent(); } | |
1004 inline const String& Path::operator[](size_t i) const& { return parts[i]; } | |
1005 inline String Path::operator[](size_t i) && { return kj::mv(parts[i]); } | |
1006 inline size_t Path::size() const { return parts.size(); } | |
1007 inline const String* Path::begin() const { return parts.begin(); } | |
1008 inline const String* Path::end() const { return parts.end(); } | |
1009 inline PathPtr Path::slice(size_t start, size_t end) const& { | |
1010 return PathPtr(*this).slice(start, end); | |
1011 } | |
1012 inline bool Path::operator==(PathPtr other) const { return PathPtr(*this) == other; } | |
1013 inline bool Path::operator!=(PathPtr other) const { return PathPtr(*this) != other; } | |
1014 inline bool Path::operator< (PathPtr other) const { return PathPtr(*this) < other; } | |
1015 inline bool Path::operator> (PathPtr other) const { return PathPtr(*this) > other; } | |
1016 inline bool Path::operator<=(PathPtr other) const { return PathPtr(*this) <= other; } | |
1017 inline bool Path::operator>=(PathPtr other) const { return PathPtr(*this) >= other; } | |
1018 inline bool Path::operator==(const Path& other) const { return PathPtr(*this) == PathPtr(other); } | |
1019 inline bool Path::operator!=(const Path& other) const { return PathPtr(*this) != PathPtr(other); } | |
1020 inline bool Path::operator< (const Path& other) const { return PathPtr(*this) < PathPtr(other); } | |
1021 inline bool Path::operator> (const Path& other) const { return PathPtr(*this) > PathPtr(other); } | |
1022 inline bool Path::operator<=(const Path& other) const { return PathPtr(*this) <= PathPtr(other); } | |
1023 inline bool Path::operator>=(const Path& other) const { return PathPtr(*this) >= PathPtr(other); } | |
1024 inline uint Path::hashCode() const { return kj::hashCode(parts); } | |
1025 inline bool Path::startsWith(PathPtr prefix) const { return PathPtr(*this).startsWith(prefix); } | |
1026 inline bool Path::endsWith (PathPtr suffix) const { return PathPtr(*this).endsWith (suffix); } | |
1027 inline String Path::toString(bool absolute) const { return PathPtr(*this).toString(absolute); } | |
1028 inline Path Path::evalWin32(StringPtr pathText) const& { | |
1029 return PathPtr(*this).evalWin32(pathText); | |
1030 } | |
1031 inline String Path::toWin32String(bool absolute) const { | |
1032 return PathPtr(*this).toWin32String(absolute); | |
1033 } | |
1034 inline Array<wchar_t> Path::forWin32Api(bool absolute) const { | |
1035 return PathPtr(*this).forWin32Api(absolute); | |
1036 } | |
1037 | |
1038 inline PathPtr::PathPtr(decltype(nullptr)): parts(nullptr) {} | |
1039 inline PathPtr::PathPtr(const Path& path): parts(path.parts) {} | |
1040 inline PathPtr::PathPtr(ArrayPtr<const String> parts): parts(parts) {} | |
1041 inline Path PathPtr::append(StringPtr suffix) const { return append(Path(suffix)); } | |
1042 inline Path PathPtr::append(String&& suffix) const { return append(Path(kj::mv(suffix))); } | |
1043 inline const String& PathPtr::operator[](size_t i) const { return parts[i]; } | |
1044 inline size_t PathPtr::size() const { return parts.size(); } | |
1045 inline const String* PathPtr::begin() const { return parts.begin(); } | |
1046 inline const String* PathPtr::end() const { return parts.end(); } | |
1047 inline PathPtr PathPtr::slice(size_t start, size_t end) const { | |
1048 return PathPtr(parts.slice(start, end)); | |
1049 } | |
1050 inline bool PathPtr::operator!=(PathPtr other) const { return !(*this == other); } | |
1051 inline bool PathPtr::operator> (PathPtr other) const { return other < *this; } | |
1052 inline bool PathPtr::operator<=(PathPtr other) const { return !(other < *this); } | |
1053 inline bool PathPtr::operator>=(PathPtr other) const { return !(*this < other); } | |
1054 inline uint PathPtr::hashCode() const { return kj::hashCode(parts); } | |
1055 inline String PathPtr::toWin32String(bool absolute) const { | |
1056 return toWin32StringImpl(absolute, false); | |
1057 } | |
1058 | |
1059 #if _WIN32 | |
1060 inline Path Path::evalNative(StringPtr pathText) const& { | |
1061 return evalWin32(pathText); | |
1062 } | |
1063 inline Path Path::evalNative(StringPtr pathText) && { | |
1064 return kj::mv(*this).evalWin32(pathText); | |
1065 } | |
1066 inline String Path::toNativeString(bool absolute) const { | |
1067 return toWin32String(absolute); | |
1068 } | |
1069 inline Path PathPtr::evalNative(StringPtr pathText) const { | |
1070 return evalWin32(pathText); | |
1071 } | |
1072 inline String PathPtr::toNativeString(bool absolute) const { | |
1073 return toWin32String(absolute); | |
1074 } | |
1075 #else | |
1076 inline Path Path::evalNative(StringPtr pathText) const& { | |
1077 return eval(pathText); | |
1078 } | |
1079 inline Path Path::evalNative(StringPtr pathText) && { | |
1080 return kj::mv(*this).eval(pathText); | |
1081 } | |
1082 inline String Path::toNativeString(bool absolute) const { | |
1083 return toString(absolute); | |
1084 } | |
1085 inline Path PathPtr::evalNative(StringPtr pathText) const { | |
1086 return eval(pathText); | |
1087 } | |
1088 inline String PathPtr::toNativeString(bool absolute) const { | |
1089 return toString(absolute); | |
1090 } | |
1091 #endif // _WIN32, else | |
1092 | |
1093 inline Own<const FsNode> FsNode::clone() const { return cloneFsNode(); } | |
1094 inline Own<const ReadableFile> ReadableFile::clone() const { | |
1095 return cloneFsNode().downcast<const ReadableFile>(); | |
1096 } | |
1097 inline Own<const AppendableFile> AppendableFile::clone() const { | |
1098 return cloneFsNode().downcast<const AppendableFile>(); | |
1099 } | |
1100 inline Own<const File> File::clone() const { return cloneFsNode().downcast<const File>(); } | |
1101 inline Own<const ReadableDirectory> ReadableDirectory::clone() const { | |
1102 return cloneFsNode().downcast<const ReadableDirectory>(); | |
1103 } | |
1104 inline Own<const Directory> Directory::clone() const { | |
1105 return cloneFsNode().downcast<const Directory>(); | |
1106 } | |
1107 | |
1108 inline void Directory::transfer( | |
1109 PathPtr toPath, WriteMode toMode, PathPtr fromPath, TransferMode mode) const { | |
1110 return transfer(toPath, toMode, *this, fromPath, mode); | |
1111 } | |
1112 | |
1113 template <typename T> | |
1114 inline Directory::Replacer<T>::Replacer(WriteMode mode): mode(mode) {} | |
1115 | |
1116 template <typename T> | |
1117 void Directory::Replacer<T>::commit() { | |
1118 if (!tryCommit()) commitFailed(mode); | |
1119 } | |
1120 | |
1121 } // namespace kj | |
1122 | |
1123 KJ_END_HEADER |