mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
This reverts commit 4e58cde589.
PR-URL: https://github.com/nodejs/node/pull/53563
Refs: https://github.com/nodejs/node/pull/50983
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Daniel Lemire <daniel@lemire.me>
Reviewed-By: James M Snell <jasnell@gmail.com>
538 lines
17 KiB
C++
538 lines
17 KiB
C++
#ifndef SRC_NODE_FILE_H_
|
|
#define SRC_NODE_FILE_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include <optional>
|
|
#include "aliased_buffer.h"
|
|
#include "node_messaging.h"
|
|
#include "node_snapshotable.h"
|
|
#include "stream_base.h"
|
|
|
|
namespace node {
|
|
namespace fs {
|
|
|
|
class FileHandleReadWrap;
|
|
|
|
enum class FsStatsOffset {
|
|
kDev = 0,
|
|
kMode,
|
|
kNlink,
|
|
kUid,
|
|
kGid,
|
|
kRdev,
|
|
kBlkSize,
|
|
kIno,
|
|
kSize,
|
|
kBlocks,
|
|
kATimeSec,
|
|
kATimeNsec,
|
|
kMTimeSec,
|
|
kMTimeNsec,
|
|
kCTimeSec,
|
|
kCTimeNsec,
|
|
kBirthTimeSec,
|
|
kBirthTimeNsec,
|
|
kFsStatsFieldsNumber
|
|
};
|
|
|
|
// Stat fields buffers contain twice the number of entries in an uv_stat_t
|
|
// because `fs.StatWatcher` needs room to store 2 `fs.Stats` instances.
|
|
constexpr size_t kFsStatsBufferLength =
|
|
static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber) * 2;
|
|
|
|
enum class FsStatFsOffset {
|
|
kType = 0,
|
|
kBSize,
|
|
kBlocks,
|
|
kBFree,
|
|
kBAvail,
|
|
kFiles,
|
|
kFFree,
|
|
kFsStatFsFieldsNumber
|
|
};
|
|
|
|
constexpr size_t kFsStatFsBufferLength =
|
|
static_cast<size_t>(FsStatFsOffset::kFsStatFsFieldsNumber);
|
|
|
|
class BindingData : public SnapshotableObject {
|
|
public:
|
|
struct InternalFieldInfo : public node::InternalFieldInfoBase {
|
|
AliasedBufferIndex stats_field_array;
|
|
AliasedBufferIndex stats_field_bigint_array;
|
|
AliasedBufferIndex statfs_field_array;
|
|
AliasedBufferIndex statfs_field_bigint_array;
|
|
};
|
|
|
|
enum class FilePathIsFileReturnType {
|
|
kIsFile = 0,
|
|
kIsNotFile,
|
|
kThrowInsufficientPermissions
|
|
};
|
|
|
|
explicit BindingData(Realm* realm,
|
|
v8::Local<v8::Object> wrap,
|
|
InternalFieldInfo* info = nullptr);
|
|
|
|
AliasedFloat64Array stats_field_array;
|
|
AliasedBigInt64Array stats_field_bigint_array;
|
|
|
|
AliasedFloat64Array statfs_field_array;
|
|
AliasedBigInt64Array statfs_field_bigint_array;
|
|
|
|
std::vector<BaseObjectPtr<FileHandleReadWrap>>
|
|
file_handle_read_wrap_freelist;
|
|
|
|
SERIALIZABLE_OBJECT_METHODS()
|
|
SET_BINDING_ID(fs_binding_data)
|
|
|
|
static void LegacyMainResolve(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|
v8::Local<v8::ObjectTemplate> ctor);
|
|
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_SELF_SIZE(BindingData)
|
|
SET_MEMORY_INFO_NAME(BindingData)
|
|
|
|
private:
|
|
InternalFieldInfo* internal_field_info_ = nullptr;
|
|
|
|
static FilePathIsFileReturnType FilePathIsFile(Environment* env,
|
|
const std::string& file_path);
|
|
};
|
|
|
|
// structure used to store state during a complex operation, e.g., mkdirp.
|
|
class FSContinuationData : public MemoryRetainer {
|
|
public:
|
|
inline FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb);
|
|
|
|
inline void PushPath(std::string&& path);
|
|
inline void PushPath(const std::string& path);
|
|
inline std::string PopPath();
|
|
// Used by mkdirp to track the first path created:
|
|
inline void MaybeSetFirstPath(const std::string& path);
|
|
inline void Done(int result);
|
|
|
|
int mode() const { return mode_; }
|
|
const std::vector<std::string>& paths() const { return paths_; }
|
|
const std::string& first_path() const { return first_path_; }
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(FSContinuationData)
|
|
SET_SELF_SIZE(FSContinuationData)
|
|
|
|
private:
|
|
uv_fs_cb done_cb_;
|
|
uv_fs_t* req_;
|
|
int mode_;
|
|
std::vector<std::string> paths_;
|
|
std::string first_path_;
|
|
};
|
|
|
|
class FSReqBase : public ReqWrap<uv_fs_t> {
|
|
public:
|
|
typedef MaybeStackBuffer<char, 64> FSReqBuffer;
|
|
|
|
inline FSReqBase(BindingData* binding_data,
|
|
v8::Local<v8::Object> req,
|
|
AsyncWrap::ProviderType type,
|
|
bool use_bigint);
|
|
~FSReqBase() override;
|
|
|
|
inline void Init(const char* syscall,
|
|
const char* data,
|
|
size_t len,
|
|
enum encoding encoding);
|
|
inline FSReqBuffer& Init(const char* syscall, size_t len,
|
|
enum encoding encoding);
|
|
|
|
virtual void Reject(v8::Local<v8::Value> reject) = 0;
|
|
virtual void Resolve(v8::Local<v8::Value> value) = 0;
|
|
virtual void ResolveStat(const uv_stat_t* stat) = 0;
|
|
virtual void ResolveStatFs(const uv_statfs_t* stat) = 0;
|
|
virtual void SetReturnValue(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) = 0;
|
|
|
|
const char* syscall() const { return syscall_; }
|
|
const char* data() const { return has_data_ ? *buffer_ : nullptr; }
|
|
enum encoding encoding() const { return encoding_; }
|
|
bool use_bigint() const { return use_bigint_; }
|
|
bool is_plain_open() const { return is_plain_open_; }
|
|
bool with_file_types() const { return with_file_types_; }
|
|
|
|
void set_is_plain_open(bool value) { is_plain_open_ = value; }
|
|
void set_with_file_types(bool value) { with_file_types_ = value; }
|
|
|
|
FSContinuationData* continuation_data() const {
|
|
return continuation_data_.get();
|
|
}
|
|
void set_continuation_data(std::unique_ptr<FSContinuationData> data) {
|
|
continuation_data_ = std::move(data);
|
|
}
|
|
|
|
static FSReqBase* from_req(uv_fs_t* req) {
|
|
return static_cast<FSReqBase*>(ReqWrap::from_req(req));
|
|
}
|
|
|
|
FSReqBase(const FSReqBase&) = delete;
|
|
FSReqBase& operator=(const FSReqBase&) = delete;
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
|
|
BindingData* binding_data();
|
|
|
|
private:
|
|
std::unique_ptr<FSContinuationData> continuation_data_;
|
|
enum encoding encoding_ = UTF8;
|
|
bool has_data_ = false;
|
|
bool use_bigint_ = false;
|
|
bool is_plain_open_ = false;
|
|
bool with_file_types_ = false;
|
|
const char* syscall_ = nullptr;
|
|
|
|
BaseObjectPtr<BindingData> binding_data_;
|
|
|
|
// Typically, the content of buffer_ is something like a file name, so
|
|
// something around 64 bytes should be enough.
|
|
FSReqBuffer buffer_;
|
|
};
|
|
|
|
class FSReqCallback final : public FSReqBase {
|
|
public:
|
|
inline FSReqCallback(BindingData* binding_data,
|
|
v8::Local<v8::Object> req,
|
|
bool use_bigint);
|
|
|
|
void Reject(v8::Local<v8::Value> reject) override;
|
|
void Resolve(v8::Local<v8::Value> value) override;
|
|
void ResolveStat(const uv_stat_t* stat) override;
|
|
void ResolveStatFs(const uv_statfs_t* stat) override;
|
|
void SetReturnValue(const v8::FunctionCallbackInfo<v8::Value>& args) override;
|
|
|
|
SET_MEMORY_INFO_NAME(FSReqCallback)
|
|
SET_SELF_SIZE(FSReqCallback)
|
|
|
|
FSReqCallback(const FSReqCallback&) = delete;
|
|
FSReqCallback& operator=(const FSReqCallback&) = delete;
|
|
};
|
|
|
|
template <typename NativeT, typename V8T>
|
|
void FillStatsArray(AliasedBufferBase<NativeT, V8T>* fields,
|
|
const uv_stat_t* s,
|
|
const size_t offset = 0);
|
|
|
|
inline v8::Local<v8::Value> FillGlobalStatsArray(BindingData* binding_data,
|
|
const bool use_bigint,
|
|
const uv_stat_t* s,
|
|
const bool second = false);
|
|
|
|
template <typename NativeT, typename V8T>
|
|
void FillStatFsArray(AliasedBufferBase<NativeT, V8T>* fields,
|
|
const uv_statfs_t* s);
|
|
|
|
inline v8::Local<v8::Value> FillGlobalStatFsArray(BindingData* binding_data,
|
|
const bool use_bigint,
|
|
const uv_statfs_t* s);
|
|
|
|
template <typename AliasedBufferT>
|
|
class FSReqPromise final : public FSReqBase {
|
|
public:
|
|
static inline FSReqPromise* New(BindingData* binding_data,
|
|
bool use_bigint);
|
|
inline ~FSReqPromise() override;
|
|
|
|
inline void Reject(v8::Local<v8::Value> reject) override;
|
|
inline void Resolve(v8::Local<v8::Value> value) override;
|
|
inline void ResolveStat(const uv_stat_t* stat) override;
|
|
inline void ResolveStatFs(const uv_statfs_t* stat) override;
|
|
inline void SetReturnValue(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) override;
|
|
inline void MemoryInfo(MemoryTracker* tracker) const override;
|
|
|
|
SET_MEMORY_INFO_NAME(FSReqPromise)
|
|
SET_SELF_SIZE(FSReqPromise)
|
|
|
|
FSReqPromise(const FSReqPromise&) = delete;
|
|
FSReqPromise& operator=(const FSReqPromise&) = delete;
|
|
FSReqPromise(const FSReqPromise&&) = delete;
|
|
FSReqPromise& operator=(const FSReqPromise&&) = delete;
|
|
|
|
private:
|
|
inline FSReqPromise(BindingData* binding_data,
|
|
v8::Local<v8::Object> obj,
|
|
bool use_bigint);
|
|
|
|
bool finished_ = false;
|
|
AliasedBufferT stats_field_array_;
|
|
AliasedBufferT statfs_field_array_;
|
|
};
|
|
|
|
class FSReqAfterScope final {
|
|
public:
|
|
FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req);
|
|
~FSReqAfterScope();
|
|
void Clear();
|
|
|
|
bool Proceed();
|
|
|
|
void Reject(uv_fs_t* req);
|
|
|
|
FSReqAfterScope(const FSReqAfterScope&) = delete;
|
|
FSReqAfterScope& operator=(const FSReqAfterScope&) = delete;
|
|
FSReqAfterScope(const FSReqAfterScope&&) = delete;
|
|
FSReqAfterScope& operator=(const FSReqAfterScope&&) = delete;
|
|
|
|
private:
|
|
BaseObjectPtr<FSReqBase> wrap_;
|
|
uv_fs_t* req_ = nullptr;
|
|
v8::HandleScope handle_scope_;
|
|
v8::Context::Scope context_scope_;
|
|
};
|
|
|
|
class FileHandle;
|
|
|
|
// A request wrap specifically for uv_fs_read()s scheduled for reading
|
|
// from a FileHandle.
|
|
class FileHandleReadWrap final : public ReqWrap<uv_fs_t> {
|
|
public:
|
|
FileHandleReadWrap(FileHandle* handle, v8::Local<v8::Object> obj);
|
|
~FileHandleReadWrap() override;
|
|
|
|
static inline FileHandleReadWrap* from_req(uv_fs_t* req) {
|
|
return static_cast<FileHandleReadWrap*>(ReqWrap::from_req(req));
|
|
}
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(FileHandleReadWrap)
|
|
SET_SELF_SIZE(FileHandleReadWrap)
|
|
|
|
private:
|
|
FileHandle* file_handle_;
|
|
uv_buf_t buffer_;
|
|
|
|
friend class FileHandle;
|
|
};
|
|
|
|
// A wrapper for a file descriptor that will automatically close the fd when
|
|
// the object is garbage collected
|
|
class FileHandle final : public AsyncWrap, public StreamBase {
|
|
public:
|
|
enum InternalFields {
|
|
kFileHandleBaseField = StreamBase::kInternalFieldCount,
|
|
kClosingPromiseSlot,
|
|
kInternalFieldCount
|
|
};
|
|
|
|
static FileHandle* New(BindingData* binding_data,
|
|
int fd,
|
|
v8::Local<v8::Object> obj = v8::Local<v8::Object>(),
|
|
std::optional<int64_t> maybeOffset = std::nullopt,
|
|
std::optional<int64_t> maybeLength = std::nullopt);
|
|
~FileHandle() override;
|
|
|
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
int GetFD() override { return fd_; }
|
|
|
|
int Release();
|
|
|
|
// Will asynchronously close the FD and return a Promise that will
|
|
// be resolved once closing is complete.
|
|
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
// Releases ownership of the FD.
|
|
static void ReleaseFD(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
// StreamBase interface:
|
|
int ReadStart() override;
|
|
int ReadStop() override;
|
|
|
|
bool IsAlive() override { return !closed_; }
|
|
bool IsClosing() override { return closing_; }
|
|
AsyncWrap* GetAsyncWrap() override { return this; }
|
|
|
|
// In the case of file streams, shutting down corresponds to closing.
|
|
ShutdownWrap* CreateShutdownWrap(v8::Local<v8::Object> object) override;
|
|
int DoShutdown(ShutdownWrap* req_wrap) override;
|
|
|
|
int DoWrite(WriteWrap* w,
|
|
uv_buf_t* bufs,
|
|
size_t count,
|
|
uv_stream_t* send_handle) override;
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
|
|
SET_MEMORY_INFO_NAME(FileHandle)
|
|
SET_SELF_SIZE(FileHandle)
|
|
|
|
FileHandle(const FileHandle&) = delete;
|
|
FileHandle& operator=(const FileHandle&) = delete;
|
|
FileHandle(const FileHandle&&) = delete;
|
|
FileHandle& operator=(const FileHandle&&) = delete;
|
|
|
|
BaseObject::TransferMode GetTransferMode() const override;
|
|
std::unique_ptr<worker::TransferData> TransferForMessaging() override;
|
|
|
|
private:
|
|
class TransferData : public worker::TransferData {
|
|
public:
|
|
explicit TransferData(int fd);
|
|
~TransferData();
|
|
|
|
BaseObjectPtr<BaseObject> Deserialize(
|
|
Environment* env,
|
|
v8::Local<v8::Context> context,
|
|
std::unique_ptr<worker::TransferData> self) override;
|
|
|
|
SET_NO_MEMORY_INFO()
|
|
SET_MEMORY_INFO_NAME(FileHandleTransferData)
|
|
SET_SELF_SIZE(TransferData)
|
|
|
|
private:
|
|
int fd_;
|
|
};
|
|
|
|
FileHandle(BindingData* binding_data, v8::Local<v8::Object> obj, int fd);
|
|
|
|
// Synchronous close that emits a warning
|
|
void Close();
|
|
void AfterClose();
|
|
|
|
class CloseReq final : public ReqWrap<uv_fs_t> {
|
|
public:
|
|
CloseReq(Environment* env,
|
|
v8::Local<v8::Object> obj,
|
|
v8::Local<v8::Promise> promise,
|
|
v8::Local<v8::Value> ref);
|
|
~CloseReq() override;
|
|
|
|
FileHandle* file_handle();
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
|
|
SET_MEMORY_INFO_NAME(CloseReq)
|
|
SET_SELF_SIZE(CloseReq)
|
|
|
|
void Resolve();
|
|
|
|
void Reject(v8::Local<v8::Value> reason);
|
|
|
|
static CloseReq* from_req(uv_fs_t* req) {
|
|
return static_cast<CloseReq*>(ReqWrap::from_req(req));
|
|
}
|
|
|
|
CloseReq(const CloseReq&) = delete;
|
|
CloseReq& operator=(const CloseReq&) = delete;
|
|
CloseReq(const CloseReq&&) = delete;
|
|
CloseReq& operator=(const CloseReq&&) = delete;
|
|
|
|
private:
|
|
v8::Global<v8::Promise> promise_{};
|
|
v8::Global<v8::Value> ref_{};
|
|
};
|
|
|
|
// Asynchronous close
|
|
v8::MaybeLocal<v8::Promise> ClosePromise();
|
|
|
|
int fd_;
|
|
bool closing_ = false;
|
|
bool closed_ = false;
|
|
bool reading_ = false;
|
|
int64_t read_offset_ = -1;
|
|
int64_t read_length_ = -1;
|
|
|
|
BaseObjectPtr<FileHandleReadWrap> current_read_;
|
|
|
|
BaseObjectPtr<BindingData> binding_data_;
|
|
};
|
|
|
|
int MKDirpSync(uv_loop_t* loop,
|
|
uv_fs_t* req,
|
|
const std::string& path,
|
|
int mode,
|
|
uv_fs_cb cb = nullptr);
|
|
|
|
class FSReqWrapSync {
|
|
public:
|
|
FSReqWrapSync(const char* syscall = nullptr,
|
|
const char* path = nullptr,
|
|
const char* dest = nullptr)
|
|
: syscall_p(syscall), path_p(path), dest_p(dest) {}
|
|
~FSReqWrapSync() { uv_fs_req_cleanup(&req); }
|
|
|
|
uv_fs_t req;
|
|
const char* syscall_p;
|
|
const char* path_p;
|
|
const char* dest_p;
|
|
|
|
FSReqWrapSync(const FSReqWrapSync&) = delete;
|
|
FSReqWrapSync& operator=(const FSReqWrapSync&) = delete;
|
|
|
|
// TODO(joyeecheung): move these out of FSReqWrapSync and into a special
|
|
// class for mkdirp
|
|
FSContinuationData* continuation_data() const {
|
|
return continuation_data_.get();
|
|
}
|
|
void set_continuation_data(std::unique_ptr<FSContinuationData> data) {
|
|
continuation_data_ = std::move(data);
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<FSContinuationData> continuation_data_;
|
|
};
|
|
|
|
// TODO(addaleax): Currently, callers check the return value and assume
|
|
// that nullptr indicates a synchronous call, rather than a failure.
|
|
// Failure conditions should be disambiguated and handled appropriately.
|
|
inline FSReqBase* GetReqWrap(const v8::FunctionCallbackInfo<v8::Value>& args,
|
|
int index,
|
|
bool use_bigint = false);
|
|
|
|
// Returns nullptr if the operation fails from the start.
|
|
template <typename Func, typename... Args>
|
|
inline FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap,
|
|
const v8::FunctionCallbackInfo<v8::Value>& args,
|
|
const char* syscall, const char* dest,
|
|
size_t len, enum encoding enc, uv_fs_cb after,
|
|
Func fn, Args... fn_args);
|
|
|
|
// Returns nullptr if the operation fails from the start.
|
|
template <typename Func, typename... Args>
|
|
inline FSReqBase* AsyncCall(Environment* env,
|
|
FSReqBase* req_wrap,
|
|
const v8::FunctionCallbackInfo<v8::Value>& args,
|
|
const char* syscall, enum encoding enc,
|
|
uv_fs_cb after, Func fn, Args... fn_args);
|
|
|
|
// Template counterpart of SYNC_CALL, except that it only puts
|
|
// the error number and the syscall in the context instead of
|
|
// creating an error in the C++ land.
|
|
// ctx must be checked using value->IsObject() before being passed.
|
|
template <typename Func, typename... Args>
|
|
inline int SyncCall(Environment* env, v8::Local<v8::Value> ctx,
|
|
FSReqWrapSync* req_wrap, const char* syscall,
|
|
Func fn, Args... args);
|
|
|
|
// Similar to SyncCall but throws immediately if there is an error.
|
|
template <typename Predicate, typename Func, typename... Args>
|
|
int SyncCallAndThrowIf(Predicate should_throw,
|
|
Environment* env,
|
|
FSReqWrapSync* req_wrap,
|
|
Func fn,
|
|
Args... args);
|
|
template <typename Func, typename... Args>
|
|
int SyncCallAndThrowOnError(Environment* env,
|
|
FSReqWrapSync* req_wrap,
|
|
Func fn,
|
|
Args... args);
|
|
} // namespace fs
|
|
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_NODE_FILE_H_
|