#include "node_dir.h"#include "node_external_reference.h"#include "node_file-inl.h"#include "node_process-inl.h"#include "memory_tracker-inl.h"#include "util.h"#include "tracing/trace_event.h"#include "string_bytes.h"#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <cstring>#include <cerrno>#include <climits>#include <memory>namespace node {namespace fs_dir {using fs::FSReqAfterScope;using fs::FSReqBase;using fs::FSReqWrapSync;using fs::GetReqWrap;using v8::Array;using v8::Context;using v8::FunctionCallbackInfo;using v8::FunctionTemplate;using v8::HandleScope;using v8::Integer;using v8::Isolate;using v8::Local;using v8::MaybeLocal;using v8::Null;using v8::Number;using v8::Object;using v8::ObjectTemplate;using v8::Value;#define TRACE_NAME(name) "fs_dir.sync." #name#define GET_TRACE_ENABLED \(*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \(TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...) \if (GET_TRACE_ENABLED) \TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \##__VA_ARGS__);#define FS_DIR_SYNC_TRACE_END(syscall, ...) \if (GET_TRACE_ENABLED) \TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \##__VA_ARGS__);DirHandle::DirHandle(Environment* env, Local<Object> obj, uv_dir_t* dir): AsyncWrap(env, obj, AsyncWrap::PROVIDER_DIRHANDLE),dir_(dir) {MakeWeak();dir_->nentries = 0;dir_->dirents = nullptr;}DirHandle* DirHandle::New(Environment* env, uv_dir_t* dir) {Local<Object> obj;if (!env->dir_instance_template()->NewInstance(env->context()).ToLocal(&obj)) {return nullptr;}return new DirHandle(env, obj, dir);}void DirHandle::New(const FunctionCallbackInfo<Value>& args) {CHECK(args.IsConstructCall());}DirHandle::~DirHandle() {CHECK(!closing_); // We should not be deleting while explicitly closing!GCClose(); // Close synchronously and emit warningCHECK(closed_); // We have to be closed at the point}void DirHandle::MemoryInfo(MemoryTracker* tracker) const {tracker->TrackFieldWithSize("dir", sizeof(*dir_));}// Close the directory handle if it hasn't already been closed. A process// warning will be emitted using a SetImmediate to avoid calling back to// JS during GC. If closing the fd fails at this point, a fatal exception// will crash the process immediately.inline void DirHandle::GCClose() {if (closed_) return;uv_fs_t req;int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr);uv_fs_req_cleanup(&req);closing_ = false;closed_ = true;struct err_detail { int ret; };err_detail detail { ret };if (ret < 0) {// Do not unref thisenv()->SetImmediate([detail](Environment* env) {const char* msg = "Closing directory handle on garbage collection failed";// This exception will end up being fatal for the process because// it is being thrown from within the SetImmediate handler and// there is no JS stack to bubble it to. In other words, tearing// down the process is the only reasonable thing we can do here.HandleScope handle_scope(env->isolate());env->ThrowUVException(detail.ret, "close", msg);});return;}// If the close was successful, we still want to emit a process warning// to notify that the file descriptor was gc'd. We want to be noisy about// this because not explicitly closing the DirHandle is a bug.env()->SetImmediate([](Environment* env) {ProcessEmitWarning(env,"Closing directory handle on garbage collection");}, CallbackFlags::kUnrefed);}void AfterClose(uv_fs_t* req) {FSReqBase* req_wrap = FSReqBase::from_req(req);FSReqAfterScope after(req_wrap, req);if (after.Proceed())req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));}void DirHandle::Close(const FunctionCallbackInfo<Value>& args) {Environment* env = Environment::GetCurrent(args);const int argc = args.Length();CHECK_GE(argc, 1);DirHandle* dir;ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());dir->closing_ = false;dir->closed_ = true;FSReqBase* req_wrap_async = GetReqWrap(args, 0);if (req_wrap_async != nullptr) { // close(req)AsyncCall(env, req_wrap_async, args, "closedir", UTF8, AfterClose,uv_fs_closedir, dir->dir());} else { // close(undefined, ctx)CHECK_EQ(argc, 2);FSReqWrapSync req_wrap_sync;FS_DIR_SYNC_TRACE_BEGIN(closedir);SyncCall(env, args[1], &req_wrap_sync, "closedir", uv_fs_closedir,dir->dir());FS_DIR_SYNC_TRACE_END(closedir);}}static MaybeLocal<Array> DirentListToArray(Environment* env,uv_dirent_t* ents,int num,enum encoding encoding,Local<Value>* err_out) {MaybeStackBuffer<Local<Value>, 64> entries(num * 2);// Return an array of all read filenames.int j = 0;for (int i = 0; i < num; i++) {Local<Value> filename;Local<Value> error;const size_t namelen = strlen(ents[i].name);if (!StringBytes::Encode(env->isolate(),ents[i].name,namelen,encoding,&error).ToLocal(&filename)) {*err_out = error;return MaybeLocal<Array>();}entries[j++] = filename;entries[j++] = Integer::New(env->isolate(), ents[i].type);}return Array::New(env->isolate(), entries.out(), j);}static void AfterDirRead(uv_fs_t* req) {BaseObjectPtr<FSReqBase> req_wrap { FSReqBase::from_req(req) };FSReqAfterScope after(req_wrap.get(), req);if (!after.Proceed()) {return;}Environment* env = req_wrap->env();Isolate* isolate = env->isolate();if (req->result == 0) {// DoneLocal<Value> done = Null(isolate);after.Clear();req_wrap->Resolve(done);return;}uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);Local<Value> error;Local<Array> js_array;if (!DirentListToArray(env,dir->dirents,static_cast<int>(req->result),req_wrap->encoding(),&error).ToLocal(&js_array)) {// Clear libuv resources *before* delivering results to JS land because// that can schedule another operation on the same uv_dir_t. Ditto below.after.Clear();return req_wrap->Reject(error);}after.Clear();req_wrap->Resolve(js_array);}void DirHandle::Read(const FunctionCallbackInfo<Value>& args) {Environment* env = Environment::GetCurrent(args);Isolate* isolate = env->isolate();const int argc = args.Length();CHECK_GE(argc, 3);const enum encoding encoding = ParseEncoding(isolate, args[0], UTF8);DirHandle* dir;ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());CHECK(args[1]->IsNumber());uint64_t buffer_size = static_cast<uint64_t>(args[1].As<Number>()->Value());if (buffer_size != dir->dirents_.size()) {dir->dirents_.resize(buffer_size);dir->dir_->nentries = buffer_size;dir->dir_->dirents = dir->dirents_.data();}FSReqBase* req_wrap_async = GetReqWrap(args, 2);if (req_wrap_async != nullptr) { // dir.read(encoding, bufferSize, req)AsyncCall(env, req_wrap_async, args, "readdir", encoding,AfterDirRead, uv_fs_readdir, dir->dir());} else { // dir.read(encoding, bufferSize, undefined, ctx)CHECK_EQ(argc, 4);FSReqWrapSync req_wrap_sync;FS_DIR_SYNC_TRACE_BEGIN(readdir);int err = SyncCall(env, args[3], &req_wrap_sync, "readdir", uv_fs_readdir,dir->dir());FS_DIR_SYNC_TRACE_END(readdir);if (err < 0) {return; // syscall failed, no need to continue, error info is in ctx}if (req_wrap_sync.req.result == 0) {// DoneLocal<Value> done = Null(isolate);args.GetReturnValue().Set(done);return;}CHECK_GE(req_wrap_sync.req.result, 0);Local<Value> error;Local<Array> js_array;if (!DirentListToArray(env,dir->dir()->dirents,static_cast<int>(req_wrap_sync.req.result),encoding,&error).ToLocal(&js_array)) {Local<Object> ctx = args[2].As<Object>();USE(ctx->Set(env->context(), env->error_string(), error));return;}args.GetReturnValue().Set(js_array);}}void AfterOpenDir(uv_fs_t* req) {FSReqBase* req_wrap = FSReqBase::from_req(req);FSReqAfterScope after(req_wrap, req);if (!after.Proceed()) {return;}Environment* env = req_wrap->env();uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);DirHandle* handle = DirHandle::New(env, dir);req_wrap->Resolve(handle->object().As<Value>());}static void OpenDir(const FunctionCallbackInfo<Value>& args) {Environment* env = Environment::GetCurrent(args);Isolate* isolate = env->isolate();const int argc = args.Length();CHECK_GE(argc, 3);BufferValue path(isolate, args[0]);CHECK_NOT_NULL(*path);const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);FSReqBase* req_wrap_async = GetReqWrap(args, 2);if (req_wrap_async != nullptr) { // openDir(path, encoding, req)AsyncCall(env, req_wrap_async, args, "opendir", encoding, AfterOpenDir,uv_fs_opendir, *path);} else { // openDir(path, encoding, undefined, ctx)CHECK_EQ(argc, 4);FSReqWrapSync req_wrap_sync;FS_DIR_SYNC_TRACE_BEGIN(opendir);int result = SyncCall(env, args[3], &req_wrap_sync, "opendir",uv_fs_opendir, *path);FS_DIR_SYNC_TRACE_END(opendir);if (result < 0) {return; // syscall failed, no need to continue, error info is in ctx}uv_fs_t* req = &req_wrap_sync.req;uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);DirHandle* handle = DirHandle::New(env, dir);args.GetReturnValue().Set(handle->object().As<Value>());}}void Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void* priv) {Environment* env = Environment::GetCurrent(context);env->SetMethod(target, "opendir", OpenDir);// Create FunctionTemplate for DirHandleLocal<FunctionTemplate> dir = env->NewFunctionTemplate(DirHandle::New);dir->Inherit(AsyncWrap::GetConstructorTemplate(env));env->SetProtoMethod(dir, "read", DirHandle::Read);env->SetProtoMethod(dir, "close", DirHandle::Close);Local<ObjectTemplate> dirt = dir->InstanceTemplate();dirt->SetInternalFieldCount(DirHandle::kInternalFieldCount);env->SetConstructorFunction(target, "DirHandle", dir);env->set_dir_instance_template(dirt);}void RegisterExternalReferences(ExternalReferenceRegistry* registry) {registry->Register(OpenDir);registry->Register(DirHandle::New);registry->Register(DirHandle::Read);registry->Register(DirHandle::Close);}} // namespace fs_dir} // end namespace nodeNODE_MODULE_CONTEXT_AWARE_INTERNAL(fs_dir, node::fs_dir::Initialize)NODE_MODULE_EXTERNAL_REFERENCE(fs_dir, node::fs_dir::RegisterExternalReferences)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。