| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_BIN_FILE_H_ |
| #define RUNTIME_BIN_FILE_H_ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include "bin/builtin.h" |
| #include "bin/dartutils.h" |
| #include "bin/namespace.h" |
| #include "bin/reference_counting.h" |
| #include "platform/syslog.h" |
| #include "platform/utils.h" |
| |
| namespace dart { |
| namespace bin { |
| |
| // Forward declaration. |
| class FileHandle; |
| |
| class MappedMemory { |
| public: |
| MappedMemory(void* address, intptr_t size, bool should_unmap = true) |
| : should_unmap_(should_unmap), address_(address), size_(size) {} |
| ~MappedMemory() { |
| if (should_unmap_) Unmap(); |
| } |
| |
| void* address() const { return address_; } |
| intptr_t size() const { return size_; } |
| uword start() const { return reinterpret_cast<uword>(address()); } |
| |
| void Leak() { should_unmap_ = false; } |
| |
| private: |
| void Unmap(); |
| |
| // False for mappings which reside inside another, and will be removed when |
| // the outer mapping is removed. |
| bool should_unmap_; |
| |
| void* address_; |
| intptr_t size_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MappedMemory); |
| }; |
| |
| class File : public ReferenceCounted<File> { |
| public: |
| enum FileOpenMode { |
| kRead = 0, |
| kWrite = 1, |
| kTruncate = 1 << 2, |
| kWriteOnly = 1 << 3, |
| kWriteTruncate = kWrite | kTruncate, |
| kWriteOnlyTruncate = kWriteOnly | kTruncate |
| }; |
| |
| // These values have to be kept in sync with the mode values of |
| // FileMode.READ, FileMode.WRITE, FileMode.APPEND, |
| // FileMode.WRITE_ONLY and FileMode.WRITE_ONLY_APPEND in file.dart. |
| enum DartFileOpenMode { |
| kDartRead = 0, |
| kDartWrite = 1, |
| kDartAppend = 2, |
| kDartWriteOnly = 3, |
| kDartWriteOnlyAppend = 4 |
| }; |
| |
| enum Type { |
| kIsFile = 0, |
| kIsDirectory = 1, |
| kIsLink = 2, |
| kIsSock = 3, // Unix Domain Socket. |
| kIsPipe = 4, // FIFO/Pipe. |
| kDoesNotExist = 5 |
| }; |
| |
| enum Identical { kIdentical = 0, kDifferent = 1, kError = 2 }; |
| |
| enum StdioHandleType { |
| // These match the constants in stdio.dart. |
| kTerminal = 0, |
| kPipe = 1, |
| kFile = 2, |
| kSocket = 3, |
| kOther = 4, |
| kTypeError = 5 |
| }; |
| |
| enum FileStat { |
| // These match the constants in FileStat in file_system_entity.dart. |
| kType = 0, |
| kCreatedTime = 1, |
| kModifiedTime = 2, |
| kAccessedTime = 3, |
| kMode = 4, |
| kSize = 5, |
| kStatSize = 6 |
| }; |
| |
| enum LockType { |
| // These match the constants in FileStat in file_impl.dart. |
| kLockMin = 0, |
| kLockUnlock = 0, |
| kLockShared = 1, |
| kLockExclusive = 2, |
| kLockBlockingShared = 3, |
| kLockBlockingExclusive = 4, |
| kLockMax = 4 |
| }; |
| |
| intptr_t GetFD(); |
| |
| enum MapType { |
| kReadOnly = 0, |
| kReadExecute = 1, |
| kReadWrite = 2, |
| }; |
| |
| /// Maps or copies the file into memory. |
| /// |
| /// 'position' and 'length' should be page-aligned. |
| /// |
| /// If 'start' is zero, allocates virtual memory for the mapping. When the |
| /// returned 'MappedMemory' is destroyed, the mapping is removed. |
| /// |
| /// If 'start' is non-zero, it must point within a suitably sized existing |
| /// mapping. The returned 'MappedMemory' will not remove the mapping when it |
| /// is destroyed; rather, the mapping will be removed when the enclosing |
| /// mapping is removed. This mode is not supported on Fuchsia. |
| /// |
| /// If 'type' is 'kReadWrite', writes to the mapping are *not* copied back to |
| /// the file. |
| /// |
| /// 'position' + 'length' may be larger than the file size. In this case, the |
| /// extra memory is zero-filled. |
| MappedMemory* Map(MapType type, |
| int64_t position, |
| int64_t length, |
| void* start = nullptr); |
| |
| // Read at most 'num_bytes' from the file. It may read less than 'num_bytes' |
| // even when EOF is not encountered. If no data is available then `Read` |
| // will block waiting for input (e.g. if the file represents a pipe that |
| // contains no unread data). A positive return value indicates the number of |
| // bytes read. Zero indicates an attempt to read past the end-of-file. -1 |
| // indicates an error. |
| int64_t Read(void* buffer, int64_t num_bytes); |
| // Attempt to write 'num_bytes' bytes from 'buffer'. It returns the number |
| // of bytes written. |
| int64_t Write(const void* buffer, int64_t num_bytes); |
| |
| // ReadFully and WriteFully do attempt to transfer num_bytes to/from |
| // the buffer. In the event of short accesses they will loop internally until |
| // the whole buffer has been transferred or an error occurs. If an error |
| // occurred the result will be set to false. |
| bool ReadFully(void* buffer, int64_t num_bytes); |
| bool WriteFully(const void* buffer, int64_t num_bytes); |
| bool WriteByte(uint8_t byte) { return WriteFully(&byte, 1); } |
| |
| bool Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3) { |
| va_list args; |
| va_start(args, format); |
| bool result = VPrint(format, args); |
| va_end(args); |
| return result; |
| } |
| bool VPrint(const char* format, va_list args); |
| |
| // Get the length of the file. Returns a negative value if the length cannot |
| // be determined (e.g. not seekable device). |
| int64_t Length(); |
| |
| // Get the current position in the file. |
| // Returns a negative value if position cannot be determined. |
| int64_t Position(); |
| |
| // Set the byte position in the file. |
| bool SetPosition(int64_t position); |
| |
| // Truncate (or extend) the file to the given length in bytes. |
| bool Truncate(int64_t length); |
| |
| // Flush contents of file. |
| bool Flush(); |
| |
| // Lock range of a file. |
| bool Lock(LockType lock, int64_t start, int64_t end); |
| |
| // Returns whether the file has been closed. |
| bool IsClosed(); |
| |
| // Calls the platform-specific functions to close the file. |
| void Close(); |
| |
| // Returns the finalizable handle for the File's Dart wrapper. |
| Dart_FinalizableHandle FinalizableHandle() const { |
| return finalizable_handle_; |
| } |
| |
| // Set the finalizable handle for the File's Dart wrapper. |
| void SetFinalizableHandle(Dart_FinalizableHandle handle) { |
| ASSERT(finalizable_handle_ == nullptr); |
| finalizable_handle_ = handle; |
| } |
| |
| // Deletes the finalizable handle for the File's Dart wrapper. Call |
| // when the file is explicitly closed and the finalizer is no longer |
| // needed. |
| void DeleteFinalizableHandle(Dart_Isolate isolate, Dart_Handle strong_ref) { |
| Dart_DeleteFinalizableHandle(finalizable_handle_, strong_ref); |
| finalizable_handle_ = nullptr; |
| } |
| |
| // Open the file with the given path. The file is always opened for |
| // reading. If mode contains kWrite the file is opened for both |
| // reading and writing. If mode contains kWrite and the file does |
| // not exist the file is created. The file is truncated to length 0 if |
| // mode contains kTruncate. |
| static File* Open(Namespace* namespc, const char* path, FileOpenMode mode); |
| |
| // Same as [File::Open], but attempts to convert uri to path before opening |
| // the file. If conversion fails, uri is treated as a path. |
| static File* OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode); |
| |
| // Attempts to convert the given [uri] to a file path. |
| static CStringUniquePtr UriToPath(const char* uri); |
| |
| // Create a file object for the specified stdio file descriptor |
| // (stdin, stout or stderr). |
| static File* OpenStdio(int fd); |
| |
| static File* OpenFD(int fd); |
| |
| static bool Exists(Namespace* namespc, const char* path); |
| static bool ExistsUri(Namespace* namespc, const char* uri); |
| static bool Create(Namespace* namespc, const char* path, bool exclusive); |
| static bool CreateLink(Namespace* namespc, |
| const char* path, |
| const char* target); |
| static bool CreatePipe(Namespace* namespc, File** readPipe, File** writePipe); |
| static bool Delete(Namespace* namespc, const char* path); |
| static bool DeleteLink(Namespace* namespc, const char* path); |
| static bool Rename(Namespace* namespc, |
| const char* old_path, |
| const char* new_path); |
| static bool RenameLink(Namespace* namespc, |
| const char* old_path, |
| const char* new_path); |
| static bool Copy(Namespace* namespc, |
| const char* old_path, |
| const char* new_path); |
| static int64_t LengthFromPath(Namespace* namespc, const char* path); |
| static void Stat(Namespace* namespc, const char* path, int64_t* data); |
| static time_t LastModified(Namespace* namespc, const char* path); |
| static bool SetLastModified(Namespace* namespc, |
| const char* path, |
| int64_t millis); |
| static time_t LastAccessed(Namespace* namespc, const char* path); |
| static bool SetLastAccessed(Namespace* namespc, |
| const char* path, |
| int64_t millis); |
| static bool IsAbsolutePath(const char* path); |
| static const char* PathSeparator(); |
| static const char* StringEscapedPathSeparator(); |
| #if defined(DART_HOST_OS_WINDOWS) |
| static Type GetType(const wchar_t* path, bool follow_links); |
| #endif |
| static Type GetType(Namespace* namespc, const char* path, bool follow_links); |
| static Identical AreIdentical(Namespace* namespc_1, |
| const char* file_1, |
| Namespace* namespc_2, |
| const char* file_2); |
| static StdioHandleType GetStdioHandleType(int fd); |
| |
| // LinkTarget, GetCanonicalPath, and ReadLink may call Dart_ScopeAllocate. |
| // If dest and its size are provided, Dart String will not be created. |
| // The result will be populated into dest. |
| static const char* LinkTarget(Namespace* namespc, |
| const char* pathname, |
| char* dest = nullptr, |
| int dest_size = 0); |
| static const char* GetCanonicalPath(Namespace* namespc, |
| const char* path, |
| char* dest = nullptr, |
| int dest_size = 0); |
| // Link LinkTarget, but pathname must be absolute. |
| static const char* ReadLink(const char* pathname); |
| static intptr_t ReadLinkInto(const char* pathname, |
| char* result, |
| size_t result_size); |
| |
| // Cleans an input path, transforming it to out, according to the rules |
| // defined by "Lexical File Names in Plan 9 or Getting Dot-Dot Right", |
| // accessible at: https://9p.io/sys/doc/lexnames.html. |
| // Returns -1 if out isn't big enough, and the length of out otherwise. |
| static intptr_t CleanUnixPath(const char* in, char* out, intptr_t outlen); |
| |
| static FileOpenMode DartModeToFileMode(DartFileOpenMode mode); |
| |
| static CObject* ExistsRequest(const CObjectArray& request); |
| static CObject* CreateRequest(const CObjectArray& request); |
| static CObject* CreatePipeRequest(const CObjectArray& request); |
| static CObject* DeleteRequest(const CObjectArray& request); |
| static CObject* RenameRequest(const CObjectArray& request); |
| static CObject* CopyRequest(const CObjectArray& request); |
| static CObject* OpenRequest(const CObjectArray& request); |
| static CObject* ResolveSymbolicLinksRequest(const CObjectArray& request); |
| static CObject* CloseRequest(const CObjectArray& request); |
| static CObject* PositionRequest(const CObjectArray& request); |
| static CObject* SetPositionRequest(const CObjectArray& request); |
| static CObject* TruncateRequest(const CObjectArray& request); |
| static CObject* LengthRequest(const CObjectArray& request); |
| static CObject* LengthFromPathRequest(const CObjectArray& request); |
| static CObject* LastModifiedRequest(const CObjectArray& request); |
| static CObject* SetLastModifiedRequest(const CObjectArray& request); |
| static CObject* LastAccessedRequest(const CObjectArray& request); |
| static CObject* SetLastAccessedRequest(const CObjectArray& request); |
| static CObject* FlushRequest(const CObjectArray& request); |
| static CObject* ReadByteRequest(const CObjectArray& request); |
| static CObject* WriteByteRequest(const CObjectArray& request); |
| static CObject* ReadRequest(const CObjectArray& request); |
| static CObject* ReadIntoRequest(const CObjectArray& request); |
| static CObject* WriteFromRequest(const CObjectArray& request); |
| static CObject* CreateLinkRequest(const CObjectArray& request); |
| static CObject* DeleteLinkRequest(const CObjectArray& request); |
| static CObject* RenameLinkRequest(const CObjectArray& request); |
| static CObject* LinkTargetRequest(const CObjectArray& request); |
| static CObject* TypeRequest(const CObjectArray& request); |
| static CObject* IdenticalRequest(const CObjectArray& request); |
| static CObject* StatRequest(const CObjectArray& request); |
| static CObject* LockRequest(const CObjectArray& request); |
| |
| private: |
| explicit File(FileHandle* handle) |
| : ReferenceCounted(), handle_(handle), finalizable_handle_(nullptr) {} |
| |
| ~File(); |
| |
| static File* FileOpenW(const wchar_t* system_name, FileOpenMode mode); |
| |
| static constexpr int kClosedFd = -1; |
| |
| // FileHandle is an OS specific class which stores data about the file. |
| FileHandle* handle_; // OS specific handle for the file. |
| |
| // We retain the finalizable handle because we can do cleanup eagerly when |
| // Dart code calls closeSync(). In that case, we delete the finalizable |
| // handle so that the finalizer doesn't run. |
| Dart_FinalizableHandle finalizable_handle_; |
| |
| friend class ReferenceCounted<File>; |
| DISALLOW_COPY_AND_ASSIGN(File); |
| }; |
| |
| class UriDecoder { |
| public: |
| explicit UriDecoder(const char* uri); |
| ~UriDecoder(); |
| |
| const char* decoded() const { return decoded_; } |
| |
| private: |
| bool HexCharPairToByte(const char* pch, char* dest); |
| |
| char* decoded_; |
| const char* uri_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UriDecoder); |
| }; |
| |
| } // namespace bin |
| } // namespace dart |
| |
| #endif // RUNTIME_BIN_FILE_H_ |