1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 #include "node_file.h" // NOLINT(build/include_inline)
22 #include "node_file-inl.h"
23 #include "aliased_buffer.h"
24 #include "memory_tracker-inl.h"
25 #include "node_buffer.h"
26 #include "node_process-inl.h"
27 #include "node_stat_watcher.h"
28 #include "util-inl.h"
29
30 #include "tracing/trace_event.h"
31
32 #include "req_wrap-inl.h"
33 #include "stream_base-inl.h"
34 #include "string_bytes.h"
35
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <cstring>
40 #include <cerrno>
41 #include <climits>
42
43 #if defined(__MINGW32__) || defined(_MSC_VER)
44 # include <io.h>
45 #endif
46
47 #include <memory>
48
49 namespace node {
50
51 namespace fs {
52
53 using v8::Array;
54 using v8::BigInt;
55 using v8::Boolean;
56 using v8::Context;
57 using v8::EscapableHandleScope;
58 using v8::Function;
59 using v8::FunctionCallbackInfo;
60 using v8::FunctionTemplate;
61 using v8::HandleScope;
62 using v8::Int32;
63 using v8::Integer;
64 using v8::Isolate;
65 using v8::Local;
66 using v8::MaybeLocal;
67 using v8::Number;
68 using v8::Object;
69 using v8::ObjectTemplate;
70 using v8::Promise;
71 using v8::String;
72 using v8::Symbol;
73 using v8::Undefined;
74 using v8::Value;
75
76 #ifndef S_ISDIR
77 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
78 #endif
79
80 #ifdef __POSIX__
81 constexpr char kPathSeparator = '/';
82 #else
83 const char* const kPathSeparator = "\\/";
84 #endif
85
Basename(const std::string & str,const std::string & extension)86 std::string Basename(const std::string& str, const std::string& extension) {
87 // Remove everything leading up to and including the final path separator.
88 std::string::size_type pos = str.find_last_of(kPathSeparator);
89
90 // Starting index for the resulting string
91 std::size_t start_pos = 0;
92 // String size to return
93 std::size_t str_size = str.size();
94 if (pos != std::string::npos) {
95 start_pos = pos + 1;
96 str_size -= start_pos;
97 }
98
99 // Strip away the extension, if any.
100 if (str_size >= extension.size() &&
101 str.compare(str.size() - extension.size(),
102 extension.size(), extension) == 0) {
103 str_size -= extension.size();
104 }
105
106 return str.substr(start_pos, str_size);
107 }
108
GetOffset(Local<Value> value)109 inline int64_t GetOffset(Local<Value> value) {
110 return IsSafeJsInt(value) ? value.As<Integer>()->Value() : -1;
111 }
112
113 #define TRACE_NAME(name) "fs.sync." #name
114 #define GET_TRACE_ENABLED \
115 (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \
116 (TRACING_CATEGORY_NODE2(fs, sync)) != 0)
117 #define FS_SYNC_TRACE_BEGIN(syscall, ...) \
118 if (GET_TRACE_ENABLED) \
119 TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), \
120 ##__VA_ARGS__);
121 #define FS_SYNC_TRACE_END(syscall, ...) \
122 if (GET_TRACE_ENABLED) \
123 TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), \
124 ##__VA_ARGS__);
125
126 // We sometimes need to convert a C++ lambda function to a raw C-style function.
127 // This is helpful, because ReqWrap::Dispatch() does not recognize lambda
128 // functions, and thus does not wrap them properly.
129 typedef void(*uv_fs_callback_t)(uv_fs_t*);
130
131
MemoryInfo(MemoryTracker * tracker) const132 void FSContinuationData::MemoryInfo(MemoryTracker* tracker) const {
133 tracker->TrackField("paths", paths_);
134 }
135
136 FileHandleReadWrap::~FileHandleReadWrap() = default;
137
138 FSReqBase::~FSReqBase() = default;
139
MemoryInfo(MemoryTracker * tracker) const140 void FSReqBase::MemoryInfo(MemoryTracker* tracker) const {
141 tracker->TrackField("continuation_data", continuation_data_);
142 }
143
144 // The FileHandle object wraps a file descriptor and will close it on garbage
145 // collection if necessary. If that happens, a process warning will be
146 // emitted (or a fatal exception will occur if the fd cannot be closed.)
FileHandle(BindingData * binding_data,Local<Object> obj,int fd)147 FileHandle::FileHandle(BindingData* binding_data,
148 Local<Object> obj, int fd)
149 : AsyncWrap(binding_data->env(), obj, AsyncWrap::PROVIDER_FILEHANDLE),
150 StreamBase(env()),
151 fd_(fd),
152 binding_data_(binding_data) {
153 MakeWeak();
154 StreamBase::AttachToObject(GetObject());
155 }
156
New(BindingData * binding_data,int fd,Local<Object> obj)157 FileHandle* FileHandle::New(BindingData* binding_data,
158 int fd, Local<Object> obj) {
159 Environment* env = binding_data->env();
160 if (obj.IsEmpty() && !env->fd_constructor_template()
161 ->NewInstance(env->context())
162 .ToLocal(&obj)) {
163 return nullptr;
164 }
165 return new FileHandle(binding_data, obj, fd);
166 }
167
New(const FunctionCallbackInfo<Value> & args)168 void FileHandle::New(const FunctionCallbackInfo<Value>& args) {
169 BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
170 Environment* env = binding_data->env();
171 CHECK(args.IsConstructCall());
172 CHECK(args[0]->IsInt32());
173
174 FileHandle* handle =
175 FileHandle::New(binding_data, args[0].As<Int32>()->Value(), args.This());
176 if (handle == nullptr) return;
177 if (args[1]->IsNumber())
178 handle->read_offset_ = args[1]->IntegerValue(env->context()).FromJust();
179 if (args[2]->IsNumber())
180 handle->read_length_ = args[2]->IntegerValue(env->context()).FromJust();
181 }
182
~FileHandle()183 FileHandle::~FileHandle() {
184 CHECK(!closing_); // We should not be deleting while explicitly closing!
185 Close(); // Close synchronously and emit warning
186 CHECK(closed_); // We have to be closed at the point
187 }
188
DoWrite(WriteWrap * w,uv_buf_t * bufs,size_t count,uv_stream_t * send_handle)189 int FileHandle::DoWrite(WriteWrap* w,
190 uv_buf_t* bufs,
191 size_t count,
192 uv_stream_t* send_handle) {
193 return UV_ENOSYS; // Not implemented (yet).
194 }
195
MemoryInfo(MemoryTracker * tracker) const196 void FileHandle::MemoryInfo(MemoryTracker* tracker) const {
197 tracker->TrackField("current_read", current_read_);
198 }
199
GetTransferMode() const200 FileHandle::TransferMode FileHandle::GetTransferMode() const {
201 return reading_ || closing_ || closed_ ?
202 TransferMode::kUntransferable : TransferMode::kTransferable;
203 }
204
TransferForMessaging()205 std::unique_ptr<worker::TransferData> FileHandle::TransferForMessaging() {
206 CHECK_NE(GetTransferMode(), TransferMode::kUntransferable);
207 auto ret = std::make_unique<TransferData>(fd_);
208 closed_ = true;
209 return ret;
210 }
211
TransferData(int fd)212 FileHandle::TransferData::TransferData(int fd) : fd_(fd) {}
213
~TransferData()214 FileHandle::TransferData::~TransferData() {
215 if (fd_ > 0) {
216 uv_fs_t close_req;
217 CHECK_EQ(0, uv_fs_close(nullptr, &close_req, fd_, nullptr));
218 uv_fs_req_cleanup(&close_req);
219 }
220 }
221
Deserialize(Environment * env,v8::Local<v8::Context> context,std::unique_ptr<worker::TransferData> self)222 BaseObjectPtr<BaseObject> FileHandle::TransferData::Deserialize(
223 Environment* env,
224 v8::Local<v8::Context> context,
225 std::unique_ptr<worker::TransferData> self) {
226 BindingData* bd = Environment::GetBindingData<BindingData>(context);
227 if (bd == nullptr) return {};
228
229 int fd = fd_;
230 fd_ = -1;
231 return BaseObjectPtr<BaseObject> { FileHandle::New(bd, fd) };
232 }
233
234 // Close the file descriptor if it hasn't already been closed. A process
235 // warning will be emitted using a SetImmediate to avoid calling back to
236 // JS during GC. If closing the fd fails at this point, a fatal exception
237 // will crash the process immediately.
Close()238 inline void FileHandle::Close() {
239 if (closed_) return;
240 uv_fs_t req;
241 int ret = uv_fs_close(env()->event_loop(), &req, fd_, nullptr);
242 uv_fs_req_cleanup(&req);
243
244 struct err_detail { int ret; int fd; };
245
246 err_detail detail { ret, fd_ };
247
248 AfterClose();
249
250 if (ret < 0) {
251 // Do not unref this
252 env()->SetImmediate([detail](Environment* env) {
253 char msg[70];
254 snprintf(msg, arraysize(msg),
255 "Closing file descriptor %d on garbage collection failed",
256 detail.fd);
257 // This exception will end up being fatal for the process because
258 // it is being thrown from within the SetImmediate handler and
259 // there is no JS stack to bubble it to. In other words, tearing
260 // down the process is the only reasonable thing we can do here.
261 HandleScope handle_scope(env->isolate());
262 env->ThrowUVException(detail.ret, "close", msg);
263 });
264 return;
265 }
266
267 // If the close was successful, we still want to emit a process warning
268 // to notify that the file descriptor was gc'd. We want to be noisy about
269 // this because not explicitly closing the FileHandle is a bug.
270
271 env()->SetImmediate([detail](Environment* env) {
272 ProcessEmitWarning(env,
273 "Closing file descriptor %d on garbage collection",
274 detail.fd);
275 if (env->filehandle_close_warning()) {
276 env->set_filehandle_close_warning(false);
277 ProcessEmitDeprecationWarning(
278 env,
279 "Closing a FileHandle object on garbage collection is deprecated. "
280 "Please close FileHandle objects explicitly using "
281 "FileHandle.prototype.close(). In the future, an error will be "
282 "thrown if a file descriptor is closed during garbage collection.",
283 "DEP0137").IsNothing();
284 }
285 }, CallbackFlags::kUnrefed);
286 }
287
Resolve()288 void FileHandle::CloseReq::Resolve() {
289 Isolate* isolate = env()->isolate();
290 HandleScope scope(isolate);
291 Context::Scope context_scope(env()->context());
292 InternalCallbackScope callback_scope(this);
293 Local<Promise> promise = promise_.Get(isolate);
294 Local<Promise::Resolver> resolver = promise.As<Promise::Resolver>();
295 resolver->Resolve(env()->context(), Undefined(isolate)).Check();
296 }
297
Reject(Local<Value> reason)298 void FileHandle::CloseReq::Reject(Local<Value> reason) {
299 Isolate* isolate = env()->isolate();
300 HandleScope scope(isolate);
301 Context::Scope context_scope(env()->context());
302 InternalCallbackScope callback_scope(this);
303 Local<Promise> promise = promise_.Get(isolate);
304 Local<Promise::Resolver> resolver = promise.As<Promise::Resolver>();
305 resolver->Reject(env()->context(), reason).Check();
306 }
307
file_handle()308 FileHandle* FileHandle::CloseReq::file_handle() {
309 Isolate* isolate = env()->isolate();
310 HandleScope scope(isolate);
311 Local<Value> val = ref_.Get(isolate);
312 Local<Object> obj = val.As<Object>();
313 return Unwrap<FileHandle>(obj);
314 }
315
CloseReq(Environment * env,Local<Object> obj,Local<Promise> promise,Local<Value> ref)316 FileHandle::CloseReq::CloseReq(Environment* env,
317 Local<Object> obj,
318 Local<Promise> promise,
319 Local<Value> ref)
320 : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) {
321 promise_.Reset(env->isolate(), promise);
322 ref_.Reset(env->isolate(), ref);
323 }
324
~CloseReq()325 FileHandle::CloseReq::~CloseReq() {
326 uv_fs_req_cleanup(req());
327 promise_.Reset();
328 ref_.Reset();
329 }
330
MemoryInfo(MemoryTracker * tracker) const331 void FileHandle::CloseReq::MemoryInfo(MemoryTracker* tracker) const {
332 tracker->TrackField("promise", promise_);
333 tracker->TrackField("ref", ref_);
334 }
335
336
337
338 // Closes this FileHandle asynchronously and returns a Promise that will be
339 // resolved when the callback is invoked, or rejects with a UVException if
340 // there was a problem closing the fd. This is the preferred mechanism for
341 // closing the FD object even tho the object will attempt to close
342 // automatically on gc.
ClosePromise()343 MaybeLocal<Promise> FileHandle::ClosePromise() {
344 Isolate* isolate = env()->isolate();
345 EscapableHandleScope scope(isolate);
346 Local<Context> context = env()->context();
347 auto maybe_resolver = Promise::Resolver::New(context);
348 CHECK(!maybe_resolver.IsEmpty());
349 Local<Promise::Resolver> resolver = maybe_resolver.ToLocalChecked();
350 Local<Promise> promise = resolver.As<Promise>();
351 CHECK(!reading_);
352 if (!closed_ && !closing_) {
353 closing_ = true;
354 Local<Object> close_req_obj;
355 if (!env()
356 ->fdclose_constructor_template()
357 ->NewInstance(env()->context())
358 .ToLocal(&close_req_obj)) {
359 return MaybeLocal<Promise>();
360 }
361 CloseReq* req = new CloseReq(env(), close_req_obj, promise, object());
362 auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) {
363 std::unique_ptr<CloseReq> close(CloseReq::from_req(req));
364 CHECK_NOT_NULL(close);
365 close->file_handle()->AfterClose();
366 Isolate* isolate = close->env()->isolate();
367 if (req->result < 0) {
368 HandleScope handle_scope(isolate);
369 close->Reject(UVException(isolate, req->result, "close"));
370 } else {
371 close->Resolve();
372 }
373 }};
374 int ret = req->Dispatch(uv_fs_close, fd_, AfterClose);
375 if (ret < 0) {
376 req->Reject(UVException(isolate, ret, "close"));
377 delete req;
378 }
379 } else {
380 // Already closed. Just reject the promise immediately
381 resolver->Reject(context, UVException(isolate, UV_EBADF, "close"))
382 .Check();
383 }
384 return scope.Escape(promise);
385 }
386
Close(const FunctionCallbackInfo<Value> & args)387 void FileHandle::Close(const FunctionCallbackInfo<Value>& args) {
388 FileHandle* fd;
389 ASSIGN_OR_RETURN_UNWRAP(&fd, args.Holder());
390 Local<Promise> ret;
391 if (!fd->ClosePromise().ToLocal(&ret)) return;
392 args.GetReturnValue().Set(ret);
393 }
394
395
ReleaseFD(const FunctionCallbackInfo<Value> & args)396 void FileHandle::ReleaseFD(const FunctionCallbackInfo<Value>& args) {
397 FileHandle* fd;
398 ASSIGN_OR_RETURN_UNWRAP(&fd, args.Holder());
399 // Just act as if this FileHandle has been closed.
400 fd->AfterClose();
401 }
402
403
AfterClose()404 void FileHandle::AfterClose() {
405 closing_ = false;
406 closed_ = true;
407 fd_ = -1;
408 if (reading_ && !persistent().IsEmpty())
409 EmitRead(UV_EOF);
410 }
411
MemoryInfo(MemoryTracker * tracker) const412 void FileHandleReadWrap::MemoryInfo(MemoryTracker* tracker) const {
413 tracker->TrackField("buffer", buffer_);
414 tracker->TrackField("file_handle", this->file_handle_);
415 }
416
FileHandleReadWrap(FileHandle * handle,Local<Object> obj)417 FileHandleReadWrap::FileHandleReadWrap(FileHandle* handle, Local<Object> obj)
418 : ReqWrap(handle->env(), obj, AsyncWrap::PROVIDER_FSREQCALLBACK),
419 file_handle_(handle) {}
420
ReadStart()421 int FileHandle::ReadStart() {
422 if (!IsAlive() || IsClosing())
423 return UV_EOF;
424
425 reading_ = true;
426
427 if (current_read_)
428 return 0;
429
430 BaseObjectPtr<FileHandleReadWrap> read_wrap;
431
432 if (read_length_ == 0) {
433 EmitRead(UV_EOF);
434 return 0;
435 }
436
437 {
438 // Create a new FileHandleReadWrap or re-use one.
439 // Either way, we need these two scopes for AsyncReset() or otherwise
440 // for creating the new instance.
441 HandleScope handle_scope(env()->isolate());
442 AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(this);
443
444 auto& freelist = binding_data_->file_handle_read_wrap_freelist;
445 if (freelist.size() > 0) {
446 read_wrap = std::move(freelist.back());
447 freelist.pop_back();
448 // Use a fresh async resource.
449 // Lifetime is ensured via AsyncWrap::resource_.
450 Local<Object> resource = Object::New(env()->isolate());
451 USE(resource->Set(
452 env()->context(), env()->handle_string(), read_wrap->object()));
453 read_wrap->AsyncReset(resource);
454 read_wrap->file_handle_ = this;
455 } else {
456 Local<Object> wrap_obj;
457 if (!env()
458 ->filehandlereadwrap_template()
459 ->NewInstance(env()->context())
460 .ToLocal(&wrap_obj)) {
461 return UV_EBUSY;
462 }
463 read_wrap = MakeDetachedBaseObject<FileHandleReadWrap>(this, wrap_obj);
464 }
465 }
466 int64_t recommended_read = 65536;
467 if (read_length_ >= 0 && read_length_ <= recommended_read)
468 recommended_read = read_length_;
469
470 read_wrap->buffer_ = EmitAlloc(recommended_read);
471
472 current_read_ = std::move(read_wrap);
473
474 current_read_->Dispatch(uv_fs_read,
475 fd_,
476 ¤t_read_->buffer_,
477 1,
478 read_offset_,
479 uv_fs_callback_t{[](uv_fs_t* req) {
480 FileHandle* handle;
481 {
482 FileHandleReadWrap* req_wrap = FileHandleReadWrap::from_req(req);
483 handle = req_wrap->file_handle_;
484 CHECK_EQ(handle->current_read_.get(), req_wrap);
485 }
486
487 // ReadStart() checks whether current_read_ is set to determine whether
488 // a read is in progress. Moving it into a local variable makes sure that
489 // the ReadStart() call below doesn't think we're still actively reading.
490 BaseObjectPtr<FileHandleReadWrap> read_wrap =
491 std::move(handle->current_read_);
492
493 int result = req->result;
494 uv_buf_t buffer = read_wrap->buffer_;
495
496 uv_fs_req_cleanup(req);
497
498 // Push the read wrap back to the freelist, or let it be destroyed
499 // once we’re exiting the current scope.
500 constexpr size_t kWantedFreelistFill = 100;
501 auto& freelist = handle->binding_data_->file_handle_read_wrap_freelist;
502 if (freelist.size() < kWantedFreelistFill) {
503 read_wrap->Reset();
504 freelist.emplace_back(std::move(read_wrap));
505 }
506
507 if (result >= 0) {
508 // Read at most as many bytes as we originally planned to.
509 if (handle->read_length_ >= 0 && handle->read_length_ < result)
510 result = handle->read_length_;
511
512 // If we read data and we have an expected length, decrease it by
513 // how much we have read.
514 if (handle->read_length_ >= 0)
515 handle->read_length_ -= result;
516
517 // If we have an offset, increase it by how much we have read.
518 if (handle->read_offset_ >= 0)
519 handle->read_offset_ += result;
520 }
521
522 // Reading 0 bytes from a file always means EOF, or that we reached
523 // the end of the requested range.
524 if (result == 0)
525 result = UV_EOF;
526
527 handle->EmitRead(result, buffer);
528
529 // Start over, if EmitRead() didn’t tell us to stop.
530 if (handle->reading_)
531 handle->ReadStart();
532 }});
533
534 return 0;
535 }
536
ReadStop()537 int FileHandle::ReadStop() {
538 reading_ = false;
539 return 0;
540 }
541
542 typedef SimpleShutdownWrap<ReqWrap<uv_fs_t>> FileHandleCloseWrap;
543
CreateShutdownWrap(Local<Object> object)544 ShutdownWrap* FileHandle::CreateShutdownWrap(Local<Object> object) {
545 return new FileHandleCloseWrap(this, object);
546 }
547
DoShutdown(ShutdownWrap * req_wrap)548 int FileHandle::DoShutdown(ShutdownWrap* req_wrap) {
549 FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(req_wrap);
550 closing_ = true;
551 wrap->Dispatch(uv_fs_close, fd_, uv_fs_callback_t{[](uv_fs_t* req) {
552 FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(
553 FileHandleCloseWrap::from_req(req));
554 FileHandle* handle = static_cast<FileHandle*>(wrap->stream());
555 handle->AfterClose();
556
557 int result = req->result;
558 uv_fs_req_cleanup(req);
559 wrap->Done(result);
560 }});
561
562 return 0;
563 }
564
565
Reject(Local<Value> reject)566 void FSReqCallback::Reject(Local<Value> reject) {
567 MakeCallback(env()->oncomplete_string(), 1, &reject);
568 }
569
ResolveStat(const uv_stat_t * stat)570 void FSReqCallback::ResolveStat(const uv_stat_t* stat) {
571 Resolve(FillGlobalStatsArray(binding_data(), use_bigint(), stat));
572 }
573
Resolve(Local<Value> value)574 void FSReqCallback::Resolve(Local<Value> value) {
575 Local<Value> argv[2] {
576 Null(env()->isolate()),
577 value
578 };
579 MakeCallback(env()->oncomplete_string(),
580 value->IsUndefined() ? 1 : arraysize(argv),
581 argv);
582 }
583
SetReturnValue(const FunctionCallbackInfo<Value> & args)584 void FSReqCallback::SetReturnValue(const FunctionCallbackInfo<Value>& args) {
585 args.GetReturnValue().SetUndefined();
586 }
587
NewFSReqCallback(const FunctionCallbackInfo<Value> & args)588 void NewFSReqCallback(const FunctionCallbackInfo<Value>& args) {
589 CHECK(args.IsConstructCall());
590 BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
591 new FSReqCallback(binding_data, args.This(), args[0]->IsTrue());
592 }
593
FSReqAfterScope(FSReqBase * wrap,uv_fs_t * req)594 FSReqAfterScope::FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req)
595 : wrap_(wrap),
596 req_(req),
597 handle_scope_(wrap->env()->isolate()),
598 context_scope_(wrap->env()->context()) {
599 CHECK_EQ(wrap_->req(), req);
600 }
601
~FSReqAfterScope()602 FSReqAfterScope::~FSReqAfterScope() {
603 Clear();
604 }
605
Clear()606 void FSReqAfterScope::Clear() {
607 if (!wrap_) return;
608
609 uv_fs_req_cleanup(wrap_->req());
610 wrap_->Detach();
611 wrap_.reset();
612 }
613
614 // TODO(joyeecheung): create a normal context object, and
615 // construct the actual errors in the JS land using the context.
616 // The context should include fds for some fs APIs, currently they are
617 // missing in the error messages. The path, dest, syscall, fd, .etc
618 // can be put into the context before the binding is even invoked,
619 // the only information that has to come from the C++ layer is the
620 // error number (and possibly the syscall for abstraction),
621 // which is also why the errors should have been constructed
622 // in JS for more flexibility.
Reject(uv_fs_t * req)623 void FSReqAfterScope::Reject(uv_fs_t* req) {
624 BaseObjectPtr<FSReqBase> wrap { wrap_ };
625 Local<Value> exception =
626 UVException(wrap_->env()->isolate(),
627 req->result,
628 wrap_->syscall(),
629 nullptr,
630 req->path,
631 wrap_->data());
632 Clear();
633 wrap->Reject(exception);
634 }
635
Proceed()636 bool FSReqAfterScope::Proceed() {
637 if (req_->result < 0) {
638 Reject(req_);
639 return false;
640 }
641 return true;
642 }
643
AfterNoArgs(uv_fs_t * req)644 void AfterNoArgs(uv_fs_t* req) {
645 FSReqBase* req_wrap = FSReqBase::from_req(req);
646 FSReqAfterScope after(req_wrap, req);
647
648 if (after.Proceed())
649 req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
650 }
651
AfterStat(uv_fs_t * req)652 void AfterStat(uv_fs_t* req) {
653 FSReqBase* req_wrap = FSReqBase::from_req(req);
654 FSReqAfterScope after(req_wrap, req);
655
656 if (after.Proceed()) {
657 req_wrap->ResolveStat(&req->statbuf);
658 }
659 }
660
AfterInteger(uv_fs_t * req)661 void AfterInteger(uv_fs_t* req) {
662 FSReqBase* req_wrap = FSReqBase::from_req(req);
663 FSReqAfterScope after(req_wrap, req);
664
665 if (req->result >= 0 && req_wrap->is_plain_open())
666 req_wrap->env()->AddUnmanagedFd(req->result);
667
668 if (after.Proceed())
669 req_wrap->Resolve(Integer::New(req_wrap->env()->isolate(), req->result));
670 }
671
AfterOpenFileHandle(uv_fs_t * req)672 void AfterOpenFileHandle(uv_fs_t* req) {
673 FSReqBase* req_wrap = FSReqBase::from_req(req);
674 FSReqAfterScope after(req_wrap, req);
675
676 if (after.Proceed()) {
677 FileHandle* fd = FileHandle::New(req_wrap->binding_data(), req->result);
678 if (fd == nullptr) return;
679 req_wrap->Resolve(fd->object());
680 }
681 }
682
683 // Reverse the logic applied by path.toNamespacedPath() to create a
684 // namespace-prefixed path.
FromNamespacedPath(std::string * path)685 void FromNamespacedPath(std::string* path) {
686 #ifdef _WIN32
687 if (path->compare(0, 8, "\\\\?\\UNC\\", 8) == 0) {
688 *path = path->substr(8);
689 path->insert(0, "\\\\");
690 } else if (path->compare(0, 4, "\\\\?\\", 4) == 0) {
691 *path = path->substr(4);
692 }
693 #endif
694 }
695
AfterMkdirp(uv_fs_t * req)696 void AfterMkdirp(uv_fs_t* req) {
697 FSReqBase* req_wrap = FSReqBase::from_req(req);
698 FSReqAfterScope after(req_wrap, req);
699
700 MaybeLocal<Value> path;
701 Local<Value> error;
702
703 if (after.Proceed()) {
704 if (!req_wrap->continuation_data()->first_path().empty()) {
705 std::string first_path(req_wrap->continuation_data()->first_path());
706 FromNamespacedPath(&first_path);
707 path = StringBytes::Encode(req_wrap->env()->isolate(), first_path.c_str(),
708 req_wrap->encoding(),
709 &error);
710 if (path.IsEmpty())
711 req_wrap->Reject(error);
712 else
713 req_wrap->Resolve(path.ToLocalChecked());
714 } else {
715 req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
716 }
717 }
718 }
719
AfterStringPath(uv_fs_t * req)720 void AfterStringPath(uv_fs_t* req) {
721 FSReqBase* req_wrap = FSReqBase::from_req(req);
722 FSReqAfterScope after(req_wrap, req);
723
724 MaybeLocal<Value> link;
725 Local<Value> error;
726
727 if (after.Proceed()) {
728 link = StringBytes::Encode(req_wrap->env()->isolate(),
729 req->path,
730 req_wrap->encoding(),
731 &error);
732 if (link.IsEmpty())
733 req_wrap->Reject(error);
734 else
735 req_wrap->Resolve(link.ToLocalChecked());
736 }
737 }
738
AfterStringPtr(uv_fs_t * req)739 void AfterStringPtr(uv_fs_t* req) {
740 FSReqBase* req_wrap = FSReqBase::from_req(req);
741 FSReqAfterScope after(req_wrap, req);
742
743 MaybeLocal<Value> link;
744 Local<Value> error;
745
746 if (after.Proceed()) {
747 link = StringBytes::Encode(req_wrap->env()->isolate(),
748 static_cast<const char*>(req->ptr),
749 req_wrap->encoding(),
750 &error);
751 if (link.IsEmpty())
752 req_wrap->Reject(error);
753 else
754 req_wrap->Resolve(link.ToLocalChecked());
755 }
756 }
757
AfterScanDir(uv_fs_t * req)758 void AfterScanDir(uv_fs_t* req) {
759 FSReqBase* req_wrap = FSReqBase::from_req(req);
760 FSReqAfterScope after(req_wrap, req);
761
762 if (!after.Proceed()) {
763 return;
764 }
765 Environment* env = req_wrap->env();
766 Local<Value> error;
767 int r;
768 std::vector<Local<Value>> name_v;
769
770 for (;;) {
771 uv_dirent_t ent;
772
773 r = uv_fs_scandir_next(req, &ent);
774 if (r == UV_EOF)
775 break;
776 if (r != 0) {
777 return req_wrap->Reject(UVException(
778 env->isolate(), r, nullptr, req_wrap->syscall(), req->path));
779 }
780
781 MaybeLocal<Value> filename =
782 StringBytes::Encode(env->isolate(),
783 ent.name,
784 req_wrap->encoding(),
785 &error);
786 if (filename.IsEmpty())
787 return req_wrap->Reject(error);
788
789 name_v.push_back(filename.ToLocalChecked());
790 }
791
792 req_wrap->Resolve(Array::New(env->isolate(), name_v.data(), name_v.size()));
793 }
794
AfterScanDirWithTypes(uv_fs_t * req)795 void AfterScanDirWithTypes(uv_fs_t* req) {
796 FSReqBase* req_wrap = FSReqBase::from_req(req);
797 FSReqAfterScope after(req_wrap, req);
798
799 if (!after.Proceed()) {
800 return;
801 }
802
803 Environment* env = req_wrap->env();
804 Isolate* isolate = env->isolate();
805 Local<Value> error;
806 int r;
807
808 std::vector<Local<Value>> name_v;
809 std::vector<Local<Value>> type_v;
810
811 for (;;) {
812 uv_dirent_t ent;
813
814 r = uv_fs_scandir_next(req, &ent);
815 if (r == UV_EOF)
816 break;
817 if (r != 0) {
818 return req_wrap->Reject(
819 UVException(isolate, r, nullptr, req_wrap->syscall(), req->path));
820 }
821
822 MaybeLocal<Value> filename =
823 StringBytes::Encode(isolate,
824 ent.name,
825 req_wrap->encoding(),
826 &error);
827 if (filename.IsEmpty())
828 return req_wrap->Reject(error);
829
830 name_v.push_back(filename.ToLocalChecked());
831 type_v.emplace_back(Integer::New(isolate, ent.type));
832 }
833
834 Local<Value> result[] = {
835 Array::New(isolate, name_v.data(), name_v.size()),
836 Array::New(isolate, type_v.data(), type_v.size())
837 };
838 req_wrap->Resolve(Array::New(isolate, result, arraysize(result)));
839 }
840
Access(const FunctionCallbackInfo<Value> & args)841 void Access(const FunctionCallbackInfo<Value>& args) {
842 Environment* env = Environment::GetCurrent(args);
843 Isolate* isolate = env->isolate();
844 HandleScope scope(isolate);
845
846 const int argc = args.Length();
847 CHECK_GE(argc, 2);
848
849 CHECK(args[1]->IsInt32());
850 int mode = args[1].As<Int32>()->Value();
851
852 BufferValue path(isolate, args[0]);
853 CHECK_NOT_NULL(*path);
854
855 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
856 if (req_wrap_async != nullptr) { // access(path, mode, req)
857 AsyncCall(env, req_wrap_async, args, "access", UTF8, AfterNoArgs,
858 uv_fs_access, *path, mode);
859 } else { // access(path, mode, undefined, ctx)
860 CHECK_EQ(argc, 4);
861 FSReqWrapSync req_wrap_sync;
862 FS_SYNC_TRACE_BEGIN(access);
863 SyncCall(env, args[3], &req_wrap_sync, "access", uv_fs_access, *path, mode);
864 FS_SYNC_TRACE_END(access);
865 }
866 }
867
868
Close(const FunctionCallbackInfo<Value> & args)869 void Close(const FunctionCallbackInfo<Value>& args) {
870 Environment* env = Environment::GetCurrent(args);
871
872 const int argc = args.Length();
873 CHECK_GE(argc, 2);
874
875 CHECK(args[0]->IsInt32());
876 int fd = args[0].As<Int32>()->Value();
877 env->RemoveUnmanagedFd(fd);
878
879 FSReqBase* req_wrap_async = GetReqWrap(args, 1);
880 if (req_wrap_async != nullptr) { // close(fd, req)
881 AsyncCall(env, req_wrap_async, args, "close", UTF8, AfterNoArgs,
882 uv_fs_close, fd);
883 } else { // close(fd, undefined, ctx)
884 CHECK_EQ(argc, 3);
885 FSReqWrapSync req_wrap_sync;
886 FS_SYNC_TRACE_BEGIN(close);
887 SyncCall(env, args[2], &req_wrap_sync, "close", uv_fs_close, fd);
888 FS_SYNC_TRACE_END(close);
889 }
890 }
891
892
893 // Used to speed up module loading. Returns an array [string, boolean]
InternalModuleReadJSON(const FunctionCallbackInfo<Value> & args)894 static void InternalModuleReadJSON(const FunctionCallbackInfo<Value>& args) {
895 Environment* env = Environment::GetCurrent(args);
896 Isolate* isolate = env->isolate();
897 uv_loop_t* loop = env->event_loop();
898
899 CHECK(args[0]->IsString());
900 node::Utf8Value path(isolate, args[0]);
901
902 if (strlen(*path) != path.length()) {
903 args.GetReturnValue().Set(Array::New(isolate));
904 return; // Contains a nul byte.
905 }
906 uv_fs_t open_req;
907 const int fd = uv_fs_open(loop, &open_req, *path, O_RDONLY, 0, nullptr);
908 uv_fs_req_cleanup(&open_req);
909
910 if (fd < 0) {
911 args.GetReturnValue().Set(Array::New(isolate));
912 return;
913 }
914
915 auto defer_close = OnScopeLeave([fd, loop]() {
916 uv_fs_t close_req;
917 CHECK_EQ(0, uv_fs_close(loop, &close_req, fd, nullptr));
918 uv_fs_req_cleanup(&close_req);
919 });
920
921 const size_t kBlockSize = 32 << 10;
922 std::vector<char> chars;
923 int64_t offset = 0;
924 ssize_t numchars;
925 do {
926 const size_t start = chars.size();
927 chars.resize(start + kBlockSize);
928
929 uv_buf_t buf;
930 buf.base = &chars[start];
931 buf.len = kBlockSize;
932
933 uv_fs_t read_req;
934 numchars = uv_fs_read(loop, &read_req, fd, &buf, 1, offset, nullptr);
935 uv_fs_req_cleanup(&read_req);
936
937 if (numchars < 0) {
938 args.GetReturnValue().Set(Array::New(isolate));
939 return;
940 }
941 offset += numchars;
942 } while (static_cast<size_t>(numchars) == kBlockSize);
943
944 size_t start = 0;
945 if (offset >= 3 && 0 == memcmp(&chars[0], "\xEF\xBB\xBF", 3)) {
946 start = 3; // Skip UTF-8 BOM.
947 }
948
949 const size_t size = offset - start;
950 char* p = &chars[start];
951 char* pe = &chars[size];
952 char* pos[2];
953 char** ppos = &pos[0];
954
955 while (p < pe) {
956 char c = *p++;
957 if (c == '\\' && p < pe && *p == '"') p++;
958 if (c != '"') continue;
959 *ppos++ = p;
960 if (ppos < &pos[2]) continue;
961 ppos = &pos[0];
962
963 char* s = &pos[0][0];
964 char* se = &pos[1][-1]; // Exclude quote.
965 size_t n = se - s;
966
967 if (n == 4) {
968 if (0 == memcmp(s, "main", 4)) break;
969 if (0 == memcmp(s, "name", 4)) break;
970 if (0 == memcmp(s, "type", 4)) break;
971 } else if (n == 7) {
972 if (0 == memcmp(s, "exports", 7)) break;
973 if (0 == memcmp(s, "imports", 7)) break;
974 }
975 }
976
977
978 Local<Value> return_value[] = {
979 String::NewFromUtf8(isolate,
980 &chars[start],
981 v8::NewStringType::kNormal,
982 size).ToLocalChecked(),
983 Boolean::New(isolate, p < pe ? true : false)
984 };
985 args.GetReturnValue().Set(
986 Array::New(isolate, return_value, arraysize(return_value)));
987 }
988
989 // Used to speed up module loading. Returns 0 if the path refers to
990 // a file, 1 when it's a directory or < 0 on error (usually -ENOENT.)
991 // The speedup comes from not creating thousands of Stat and Error objects.
InternalModuleStat(const FunctionCallbackInfo<Value> & args)992 static void InternalModuleStat(const FunctionCallbackInfo<Value>& args) {
993 Environment* env = Environment::GetCurrent(args);
994
995 CHECK(args[0]->IsString());
996 node::Utf8Value path(env->isolate(), args[0]);
997
998 uv_fs_t req;
999 int rc = uv_fs_stat(env->event_loop(), &req, *path, nullptr);
1000 if (rc == 0) {
1001 const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
1002 rc = !!(s->st_mode & S_IFDIR);
1003 }
1004 uv_fs_req_cleanup(&req);
1005
1006 args.GetReturnValue().Set(rc);
1007 }
1008
Stat(const FunctionCallbackInfo<Value> & args)1009 static void Stat(const FunctionCallbackInfo<Value>& args) {
1010 BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
1011 Environment* env = binding_data->env();
1012
1013 const int argc = args.Length();
1014 CHECK_GE(argc, 2);
1015
1016 BufferValue path(env->isolate(), args[0]);
1017 CHECK_NOT_NULL(*path);
1018
1019 bool use_bigint = args[1]->IsTrue();
1020 FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1021 if (req_wrap_async != nullptr) { // stat(path, use_bigint, req)
1022 AsyncCall(env, req_wrap_async, args, "stat", UTF8, AfterStat,
1023 uv_fs_stat, *path);
1024 } else { // stat(path, use_bigint, undefined, ctx)
1025 CHECK_EQ(argc, 4);
1026 FSReqWrapSync req_wrap_sync;
1027 FS_SYNC_TRACE_BEGIN(stat);
1028 int err = SyncCall(env, args[3], &req_wrap_sync, "stat", uv_fs_stat, *path);
1029 FS_SYNC_TRACE_END(stat);
1030 if (err != 0) {
1031 return; // error info is in ctx
1032 }
1033
1034 Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
1035 static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
1036 args.GetReturnValue().Set(arr);
1037 }
1038 }
1039
LStat(const FunctionCallbackInfo<Value> & args)1040 static void LStat(const FunctionCallbackInfo<Value>& args) {
1041 BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
1042 Environment* env = binding_data->env();
1043
1044 const int argc = args.Length();
1045 CHECK_GE(argc, 3);
1046
1047 BufferValue path(env->isolate(), args[0]);
1048 CHECK_NOT_NULL(*path);
1049
1050 bool use_bigint = args[1]->IsTrue();
1051 FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1052 if (req_wrap_async != nullptr) { // lstat(path, use_bigint, req)
1053 AsyncCall(env, req_wrap_async, args, "lstat", UTF8, AfterStat,
1054 uv_fs_lstat, *path);
1055 } else { // lstat(path, use_bigint, undefined, ctx)
1056 CHECK_EQ(argc, 4);
1057 FSReqWrapSync req_wrap_sync;
1058 FS_SYNC_TRACE_BEGIN(lstat);
1059 int err = SyncCall(env, args[3], &req_wrap_sync, "lstat", uv_fs_lstat,
1060 *path);
1061 FS_SYNC_TRACE_END(lstat);
1062 if (err != 0) {
1063 return; // error info is in ctx
1064 }
1065
1066 Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
1067 static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
1068 args.GetReturnValue().Set(arr);
1069 }
1070 }
1071
FStat(const FunctionCallbackInfo<Value> & args)1072 static void FStat(const FunctionCallbackInfo<Value>& args) {
1073 BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
1074 Environment* env = binding_data->env();
1075
1076 const int argc = args.Length();
1077 CHECK_GE(argc, 2);
1078
1079 CHECK(args[0]->IsInt32());
1080 int fd = args[0].As<Int32>()->Value();
1081
1082 bool use_bigint = args[1]->IsTrue();
1083 FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1084 if (req_wrap_async != nullptr) { // fstat(fd, use_bigint, req)
1085 AsyncCall(env, req_wrap_async, args, "fstat", UTF8, AfterStat,
1086 uv_fs_fstat, fd);
1087 } else { // fstat(fd, use_bigint, undefined, ctx)
1088 CHECK_EQ(argc, 4);
1089 FSReqWrapSync req_wrap_sync;
1090 FS_SYNC_TRACE_BEGIN(fstat);
1091 int err = SyncCall(env, args[3], &req_wrap_sync, "fstat", uv_fs_fstat, fd);
1092 FS_SYNC_TRACE_END(fstat);
1093 if (err != 0) {
1094 return; // error info is in ctx
1095 }
1096
1097 Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
1098 static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
1099 args.GetReturnValue().Set(arr);
1100 }
1101 }
1102
Symlink(const FunctionCallbackInfo<Value> & args)1103 static void Symlink(const FunctionCallbackInfo<Value>& args) {
1104 Environment* env = Environment::GetCurrent(args);
1105 Isolate* isolate = env->isolate();
1106
1107 const int argc = args.Length();
1108 CHECK_GE(argc, 4);
1109
1110 BufferValue target(isolate, args[0]);
1111 CHECK_NOT_NULL(*target);
1112 BufferValue path(isolate, args[1]);
1113 CHECK_NOT_NULL(*path);
1114
1115 CHECK(args[2]->IsInt32());
1116 int flags = args[2].As<Int32>()->Value();
1117
1118 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1119 if (req_wrap_async != nullptr) { // symlink(target, path, flags, req)
1120 AsyncDestCall(env, req_wrap_async, args, "symlink", *path, path.length(),
1121 UTF8, AfterNoArgs, uv_fs_symlink, *target, *path, flags);
1122 } else { // symlink(target, path, flags, undefinec, ctx)
1123 CHECK_EQ(argc, 5);
1124 FSReqWrapSync req_wrap_sync;
1125 FS_SYNC_TRACE_BEGIN(symlink);
1126 SyncCall(env, args[4], &req_wrap_sync, "symlink",
1127 uv_fs_symlink, *target, *path, flags);
1128 FS_SYNC_TRACE_END(symlink);
1129 }
1130 }
1131
Link(const FunctionCallbackInfo<Value> & args)1132 static void Link(const FunctionCallbackInfo<Value>& args) {
1133 Environment* env = Environment::GetCurrent(args);
1134 Isolate* isolate = env->isolate();
1135
1136 const int argc = args.Length();
1137 CHECK_GE(argc, 3);
1138
1139 BufferValue src(isolate, args[0]);
1140 CHECK_NOT_NULL(*src);
1141
1142 BufferValue dest(isolate, args[1]);
1143 CHECK_NOT_NULL(*dest);
1144
1145 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1146 if (req_wrap_async != nullptr) { // link(src, dest, req)
1147 AsyncDestCall(env, req_wrap_async, args, "link", *dest, dest.length(), UTF8,
1148 AfterNoArgs, uv_fs_link, *src, *dest);
1149 } else { // link(src, dest)
1150 CHECK_EQ(argc, 4);
1151 FSReqWrapSync req_wrap_sync;
1152 FS_SYNC_TRACE_BEGIN(link);
1153 SyncCall(env, args[3], &req_wrap_sync, "link",
1154 uv_fs_link, *src, *dest);
1155 FS_SYNC_TRACE_END(link);
1156 }
1157 }
1158
ReadLink(const FunctionCallbackInfo<Value> & args)1159 static void ReadLink(const FunctionCallbackInfo<Value>& args) {
1160 Environment* env = Environment::GetCurrent(args);
1161 Isolate* isolate = env->isolate();
1162
1163 const int argc = args.Length();
1164 CHECK_GE(argc, 3);
1165
1166 BufferValue path(isolate, args[0]);
1167 CHECK_NOT_NULL(*path);
1168
1169 const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
1170
1171 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1172 if (req_wrap_async != nullptr) { // readlink(path, encoding, req)
1173 AsyncCall(env, req_wrap_async, args, "readlink", encoding, AfterStringPtr,
1174 uv_fs_readlink, *path);
1175 } else {
1176 CHECK_EQ(argc, 4);
1177 FSReqWrapSync req_wrap_sync;
1178 FS_SYNC_TRACE_BEGIN(readlink);
1179 int err = SyncCall(env, args[3], &req_wrap_sync, "readlink",
1180 uv_fs_readlink, *path);
1181 FS_SYNC_TRACE_END(readlink);
1182 if (err < 0) {
1183 return; // syscall failed, no need to continue, error info is in ctx
1184 }
1185 const char* link_path = static_cast<const char*>(req_wrap_sync.req.ptr);
1186
1187 Local<Value> error;
1188 MaybeLocal<Value> rc = StringBytes::Encode(isolate,
1189 link_path,
1190 encoding,
1191 &error);
1192 if (rc.IsEmpty()) {
1193 Local<Object> ctx = args[3].As<Object>();
1194 ctx->Set(env->context(), env->error_string(), error).Check();
1195 return;
1196 }
1197
1198 args.GetReturnValue().Set(rc.ToLocalChecked());
1199 }
1200 }
1201
Rename(const FunctionCallbackInfo<Value> & args)1202 static void Rename(const FunctionCallbackInfo<Value>& args) {
1203 Environment* env = Environment::GetCurrent(args);
1204 Isolate* isolate = env->isolate();
1205
1206 const int argc = args.Length();
1207 CHECK_GE(argc, 3);
1208
1209 BufferValue old_path(isolate, args[0]);
1210 CHECK_NOT_NULL(*old_path);
1211 BufferValue new_path(isolate, args[1]);
1212 CHECK_NOT_NULL(*new_path);
1213
1214 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1215 if (req_wrap_async != nullptr) {
1216 AsyncDestCall(env, req_wrap_async, args, "rename", *new_path,
1217 new_path.length(), UTF8, AfterNoArgs, uv_fs_rename,
1218 *old_path, *new_path);
1219 } else {
1220 CHECK_EQ(argc, 4);
1221 FSReqWrapSync req_wrap_sync;
1222 FS_SYNC_TRACE_BEGIN(rename);
1223 SyncCall(env, args[3], &req_wrap_sync, "rename", uv_fs_rename,
1224 *old_path, *new_path);
1225 FS_SYNC_TRACE_END(rename);
1226 }
1227 }
1228
FTruncate(const FunctionCallbackInfo<Value> & args)1229 static void FTruncate(const FunctionCallbackInfo<Value>& args) {
1230 Environment* env = Environment::GetCurrent(args);
1231
1232 const int argc = args.Length();
1233 CHECK_GE(argc, 3);
1234
1235 CHECK(args[0]->IsInt32());
1236 const int fd = args[0].As<Int32>()->Value();
1237
1238 CHECK(IsSafeJsInt(args[1]));
1239 const int64_t len = args[1].As<Integer>()->Value();
1240
1241 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1242 if (req_wrap_async != nullptr) {
1243 AsyncCall(env, req_wrap_async, args, "ftruncate", UTF8, AfterNoArgs,
1244 uv_fs_ftruncate, fd, len);
1245 } else {
1246 CHECK_EQ(argc, 4);
1247 FSReqWrapSync req_wrap_sync;
1248 FS_SYNC_TRACE_BEGIN(ftruncate);
1249 SyncCall(env, args[3], &req_wrap_sync, "ftruncate", uv_fs_ftruncate, fd,
1250 len);
1251 FS_SYNC_TRACE_END(ftruncate);
1252 }
1253 }
1254
Fdatasync(const FunctionCallbackInfo<Value> & args)1255 static void Fdatasync(const FunctionCallbackInfo<Value>& args) {
1256 Environment* env = Environment::GetCurrent(args);
1257
1258 const int argc = args.Length();
1259 CHECK_GE(argc, 2);
1260
1261 CHECK(args[0]->IsInt32());
1262 const int fd = args[0].As<Int32>()->Value();
1263
1264 FSReqBase* req_wrap_async = GetReqWrap(args, 1);
1265 if (req_wrap_async != nullptr) {
1266 AsyncCall(env, req_wrap_async, args, "fdatasync", UTF8, AfterNoArgs,
1267 uv_fs_fdatasync, fd);
1268 } else {
1269 CHECK_EQ(argc, 3);
1270 FSReqWrapSync req_wrap_sync;
1271 FS_SYNC_TRACE_BEGIN(fdatasync);
1272 SyncCall(env, args[2], &req_wrap_sync, "fdatasync", uv_fs_fdatasync, fd);
1273 FS_SYNC_TRACE_END(fdatasync);
1274 }
1275 }
1276
Fsync(const FunctionCallbackInfo<Value> & args)1277 static void Fsync(const FunctionCallbackInfo<Value>& args) {
1278 Environment* env = Environment::GetCurrent(args);
1279
1280 const int argc = args.Length();
1281 CHECK_GE(argc, 2);
1282
1283 CHECK(args[0]->IsInt32());
1284 const int fd = args[0].As<Int32>()->Value();
1285
1286 FSReqBase* req_wrap_async = GetReqWrap(args, 1);
1287 if (req_wrap_async != nullptr) {
1288 AsyncCall(env, req_wrap_async, args, "fsync", UTF8, AfterNoArgs,
1289 uv_fs_fsync, fd);
1290 } else {
1291 CHECK_EQ(argc, 3);
1292 FSReqWrapSync req_wrap_sync;
1293 FS_SYNC_TRACE_BEGIN(fsync);
1294 SyncCall(env, args[2], &req_wrap_sync, "fsync", uv_fs_fsync, fd);
1295 FS_SYNC_TRACE_END(fsync);
1296 }
1297 }
1298
Unlink(const FunctionCallbackInfo<Value> & args)1299 static void Unlink(const FunctionCallbackInfo<Value>& args) {
1300 Environment* env = Environment::GetCurrent(args);
1301
1302 const int argc = args.Length();
1303 CHECK_GE(argc, 2);
1304
1305 BufferValue path(env->isolate(), args[0]);
1306 CHECK_NOT_NULL(*path);
1307
1308 FSReqBase* req_wrap_async = GetReqWrap(args, 1);
1309 if (req_wrap_async != nullptr) {
1310 AsyncCall(env, req_wrap_async, args, "unlink", UTF8, AfterNoArgs,
1311 uv_fs_unlink, *path);
1312 } else {
1313 CHECK_EQ(argc, 3);
1314 FSReqWrapSync req_wrap_sync;
1315 FS_SYNC_TRACE_BEGIN(unlink);
1316 SyncCall(env, args[2], &req_wrap_sync, "unlink", uv_fs_unlink, *path);
1317 FS_SYNC_TRACE_END(unlink);
1318 }
1319 }
1320
RMDir(const FunctionCallbackInfo<Value> & args)1321 static void RMDir(const FunctionCallbackInfo<Value>& args) {
1322 Environment* env = Environment::GetCurrent(args);
1323
1324 const int argc = args.Length();
1325 CHECK_GE(argc, 2);
1326
1327 BufferValue path(env->isolate(), args[0]);
1328 CHECK_NOT_NULL(*path);
1329
1330 FSReqBase* req_wrap_async = GetReqWrap(args, 1); // rmdir(path, req)
1331 if (req_wrap_async != nullptr) {
1332 AsyncCall(env, req_wrap_async, args, "rmdir", UTF8, AfterNoArgs,
1333 uv_fs_rmdir, *path);
1334 } else { // rmdir(path, undefined, ctx)
1335 CHECK_EQ(argc, 3);
1336 FSReqWrapSync req_wrap_sync;
1337 FS_SYNC_TRACE_BEGIN(rmdir);
1338 SyncCall(env, args[2], &req_wrap_sync, "rmdir",
1339 uv_fs_rmdir, *path);
1340 FS_SYNC_TRACE_END(rmdir);
1341 }
1342 }
1343
MKDirpSync(uv_loop_t * loop,uv_fs_t * req,const std::string & path,int mode,uv_fs_cb cb)1344 int MKDirpSync(uv_loop_t* loop,
1345 uv_fs_t* req,
1346 const std::string& path,
1347 int mode,
1348 uv_fs_cb cb) {
1349 FSReqWrapSync* req_wrap = ContainerOf(&FSReqWrapSync::req, req);
1350
1351 // on the first iteration of algorithm, stash state information.
1352 if (req_wrap->continuation_data() == nullptr) {
1353 req_wrap->set_continuation_data(
1354 std::make_unique<FSContinuationData>(req, mode, cb));
1355 req_wrap->continuation_data()->PushPath(std::move(path));
1356 }
1357
1358 while (req_wrap->continuation_data()->paths().size() > 0) {
1359 std::string next_path = req_wrap->continuation_data()->PopPath();
1360 int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr);
1361 while (true) {
1362 switch (err) {
1363 // Note: uv_fs_req_cleanup in terminal paths will be called by
1364 // ~FSReqWrapSync():
1365 case 0:
1366 req_wrap->continuation_data()->MaybeSetFirstPath(next_path);
1367 if (req_wrap->continuation_data()->paths().size() == 0) {
1368 return 0;
1369 }
1370 break;
1371 case UV_EACCES:
1372 case UV_ENOTDIR:
1373 case UV_EPERM: {
1374 return err;
1375 }
1376 case UV_ENOENT: {
1377 std::string dirname = next_path.substr(0,
1378 next_path.find_last_of(kPathSeparator));
1379 if (dirname != next_path) {
1380 req_wrap->continuation_data()->PushPath(std::move(next_path));
1381 req_wrap->continuation_data()->PushPath(std::move(dirname));
1382 } else if (req_wrap->continuation_data()->paths().size() == 0) {
1383 err = UV_EEXIST;
1384 continue;
1385 }
1386 break;
1387 }
1388 default:
1389 uv_fs_req_cleanup(req);
1390 int orig_err = err;
1391 err = uv_fs_stat(loop, req, next_path.c_str(), nullptr);
1392 if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) {
1393 uv_fs_req_cleanup(req);
1394 if (orig_err == UV_EEXIST &&
1395 req_wrap->continuation_data()->paths().size() > 0) {
1396 return UV_ENOTDIR;
1397 }
1398 return UV_EEXIST;
1399 }
1400 if (err < 0) return err;
1401 break;
1402 }
1403 break;
1404 }
1405 uv_fs_req_cleanup(req);
1406 }
1407
1408 return 0;
1409 }
1410
MKDirpAsync(uv_loop_t * loop,uv_fs_t * req,const char * path,int mode,uv_fs_cb cb)1411 int MKDirpAsync(uv_loop_t* loop,
1412 uv_fs_t* req,
1413 const char* path,
1414 int mode,
1415 uv_fs_cb cb) {
1416 FSReqBase* req_wrap = FSReqBase::from_req(req);
1417 // on the first iteration of algorithm, stash state information.
1418 if (req_wrap->continuation_data() == nullptr) {
1419 req_wrap->set_continuation_data(
1420 std::make_unique<FSContinuationData>(req, mode, cb));
1421 req_wrap->continuation_data()->PushPath(std::move(path));
1422 }
1423
1424 // on each iteration of algorithm, mkdir directory on top of stack.
1425 std::string next_path = req_wrap->continuation_data()->PopPath();
1426 int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode,
1427 uv_fs_callback_t{[](uv_fs_t* req) {
1428 FSReqBase* req_wrap = FSReqBase::from_req(req);
1429 Environment* env = req_wrap->env();
1430 uv_loop_t* loop = env->event_loop();
1431 std::string path = req->path;
1432 int err = req->result;
1433
1434 while (true) {
1435 switch (err) {
1436 // Note: uv_fs_req_cleanup in terminal paths will be called by
1437 // FSReqAfterScope::~FSReqAfterScope()
1438 case 0: {
1439 if (req_wrap->continuation_data()->paths().size() == 0) {
1440 req_wrap->continuation_data()->MaybeSetFirstPath(path);
1441 req_wrap->continuation_data()->Done(0);
1442 } else {
1443 req_wrap->continuation_data()->MaybeSetFirstPath(path);
1444 uv_fs_req_cleanup(req);
1445 MKDirpAsync(loop, req, path.c_str(),
1446 req_wrap->continuation_data()->mode(), nullptr);
1447 }
1448 break;
1449 }
1450 case UV_EACCES:
1451 case UV_ENOTDIR:
1452 case UV_EPERM: {
1453 req_wrap->continuation_data()->Done(err);
1454 break;
1455 }
1456 case UV_ENOENT: {
1457 std::string dirname = path.substr(0,
1458 path.find_last_of(kPathSeparator));
1459 if (dirname != path) {
1460 req_wrap->continuation_data()->PushPath(std::move(path));
1461 req_wrap->continuation_data()->PushPath(std::move(dirname));
1462 } else if (req_wrap->continuation_data()->paths().size() == 0) {
1463 err = UV_EEXIST;
1464 continue;
1465 }
1466 uv_fs_req_cleanup(req);
1467 MKDirpAsync(loop, req, path.c_str(),
1468 req_wrap->continuation_data()->mode(), nullptr);
1469 break;
1470 }
1471 default:
1472 uv_fs_req_cleanup(req);
1473 // Stash err for use in the callback.
1474 req->data = reinterpret_cast<void*>(static_cast<intptr_t>(err));
1475 int err = uv_fs_stat(loop, req, path.c_str(),
1476 uv_fs_callback_t{[](uv_fs_t* req) {
1477 FSReqBase* req_wrap = FSReqBase::from_req(req);
1478 int err = req->result;
1479 if (reinterpret_cast<intptr_t>(req->data) == UV_EEXIST &&
1480 req_wrap->continuation_data()->paths().size() > 0) {
1481 if (err == 0 && S_ISDIR(req->statbuf.st_mode)) {
1482 Environment* env = req_wrap->env();
1483 uv_loop_t* loop = env->event_loop();
1484 std::string path = req->path;
1485 uv_fs_req_cleanup(req);
1486 MKDirpAsync(loop, req, path.c_str(),
1487 req_wrap->continuation_data()->mode(), nullptr);
1488 return;
1489 }
1490 err = UV_ENOTDIR;
1491 }
1492 // verify that the path pointed to is actually a directory.
1493 if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST;
1494 req_wrap->continuation_data()->Done(err);
1495 }});
1496 if (err < 0) req_wrap->continuation_data()->Done(err);
1497 break;
1498 }
1499 break;
1500 }
1501 }});
1502
1503 return err;
1504 }
1505
CallMKDirpSync(Environment * env,const FunctionCallbackInfo<Value> & args,FSReqWrapSync * req_wrap,const char * path,int mode)1506 int CallMKDirpSync(Environment* env, const FunctionCallbackInfo<Value>& args,
1507 FSReqWrapSync* req_wrap, const char* path, int mode) {
1508 env->PrintSyncTrace();
1509 int err = MKDirpSync(env->event_loop(), &req_wrap->req, path, mode,
1510 nullptr);
1511 if (err < 0) {
1512 v8::Local<v8::Context> context = env->context();
1513 v8::Local<v8::Object> ctx_obj = args[4].As<v8::Object>();
1514 v8::Isolate* isolate = env->isolate();
1515 ctx_obj->Set(context,
1516 env->errno_string(),
1517 v8::Integer::New(isolate, err)).Check();
1518 ctx_obj->Set(context,
1519 env->syscall_string(),
1520 OneByteString(isolate, "mkdir")).Check();
1521 }
1522 return err;
1523 }
1524
MKDir(const FunctionCallbackInfo<Value> & args)1525 static void MKDir(const FunctionCallbackInfo<Value>& args) {
1526 Environment* env = Environment::GetCurrent(args);
1527
1528 const int argc = args.Length();
1529 CHECK_GE(argc, 4);
1530
1531 BufferValue path(env->isolate(), args[0]);
1532 CHECK_NOT_NULL(*path);
1533
1534 CHECK(args[1]->IsInt32());
1535 const int mode = args[1].As<Int32>()->Value();
1536
1537 CHECK(args[2]->IsBoolean());
1538 bool mkdirp = args[2]->IsTrue();
1539
1540 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1541 if (req_wrap_async != nullptr) { // mkdir(path, mode, req)
1542 AsyncCall(env, req_wrap_async, args, "mkdir", UTF8,
1543 mkdirp ? AfterMkdirp : AfterNoArgs,
1544 mkdirp ? MKDirpAsync : uv_fs_mkdir, *path, mode);
1545 } else { // mkdir(path, mode, undefined, ctx)
1546 CHECK_EQ(argc, 5);
1547 FSReqWrapSync req_wrap_sync;
1548 FS_SYNC_TRACE_BEGIN(mkdir);
1549 if (mkdirp) {
1550 int err = CallMKDirpSync(env, args, &req_wrap_sync, *path, mode);
1551 if (err == 0 &&
1552 !req_wrap_sync.continuation_data()->first_path().empty()) {
1553 Local<Value> error;
1554 std::string first_path(req_wrap_sync.continuation_data()->first_path());
1555 FromNamespacedPath(&first_path);
1556 MaybeLocal<Value> path = StringBytes::Encode(env->isolate(),
1557 first_path.c_str(),
1558 UTF8, &error);
1559 if (path.IsEmpty()) {
1560 Local<Object> ctx = args[4].As<Object>();
1561 ctx->Set(env->context(), env->error_string(), error).Check();
1562 return;
1563 }
1564 args.GetReturnValue().Set(path.ToLocalChecked());
1565 }
1566 } else {
1567 SyncCall(env, args[4], &req_wrap_sync, "mkdir",
1568 uv_fs_mkdir, *path, mode);
1569 }
1570 FS_SYNC_TRACE_END(mkdir);
1571 }
1572 }
1573
RealPath(const FunctionCallbackInfo<Value> & args)1574 static void RealPath(const FunctionCallbackInfo<Value>& args) {
1575 Environment* env = Environment::GetCurrent(args);
1576 Isolate* isolate = env->isolate();
1577
1578 const int argc = args.Length();
1579 CHECK_GE(argc, 3);
1580
1581 BufferValue path(isolate, args[0]);
1582 CHECK_NOT_NULL(*path);
1583
1584 const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
1585
1586 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1587 if (req_wrap_async != nullptr) { // realpath(path, encoding, req)
1588 AsyncCall(env, req_wrap_async, args, "realpath", encoding, AfterStringPtr,
1589 uv_fs_realpath, *path);
1590 } else { // realpath(path, encoding, undefined, ctx)
1591 CHECK_EQ(argc, 4);
1592 FSReqWrapSync req_wrap_sync;
1593 FS_SYNC_TRACE_BEGIN(realpath);
1594 int err = SyncCall(env, args[3], &req_wrap_sync, "realpath",
1595 uv_fs_realpath, *path);
1596 FS_SYNC_TRACE_END(realpath);
1597 if (err < 0) {
1598 return; // syscall failed, no need to continue, error info is in ctx
1599 }
1600
1601 const char* link_path = static_cast<const char*>(req_wrap_sync.req.ptr);
1602
1603 Local<Value> error;
1604 MaybeLocal<Value> rc = StringBytes::Encode(isolate,
1605 link_path,
1606 encoding,
1607 &error);
1608 if (rc.IsEmpty()) {
1609 Local<Object> ctx = args[3].As<Object>();
1610 ctx->Set(env->context(), env->error_string(), error).Check();
1611 return;
1612 }
1613
1614 args.GetReturnValue().Set(rc.ToLocalChecked());
1615 }
1616 }
1617
ReadDir(const FunctionCallbackInfo<Value> & args)1618 static void ReadDir(const FunctionCallbackInfo<Value>& args) {
1619 Environment* env = Environment::GetCurrent(args);
1620 Isolate* isolate = env->isolate();
1621
1622 const int argc = args.Length();
1623 CHECK_GE(argc, 3);
1624
1625 BufferValue path(isolate, args[0]);
1626 CHECK_NOT_NULL(*path);
1627
1628 const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
1629
1630 bool with_types = args[2]->IsTrue();
1631
1632 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1633 if (req_wrap_async != nullptr) { // readdir(path, encoding, withTypes, req)
1634 if (with_types) {
1635 AsyncCall(env, req_wrap_async, args, "scandir", encoding,
1636 AfterScanDirWithTypes, uv_fs_scandir, *path, 0 /*flags*/);
1637 } else {
1638 AsyncCall(env, req_wrap_async, args, "scandir", encoding,
1639 AfterScanDir, uv_fs_scandir, *path, 0 /*flags*/);
1640 }
1641 } else { // readdir(path, encoding, withTypes, undefined, ctx)
1642 CHECK_EQ(argc, 5);
1643 FSReqWrapSync req_wrap_sync;
1644 FS_SYNC_TRACE_BEGIN(readdir);
1645 int err = SyncCall(env, args[4], &req_wrap_sync, "scandir",
1646 uv_fs_scandir, *path, 0 /*flags*/);
1647 FS_SYNC_TRACE_END(readdir);
1648 if (err < 0) {
1649 return; // syscall failed, no need to continue, error info is in ctx
1650 }
1651
1652 CHECK_GE(req_wrap_sync.req.result, 0);
1653 int r;
1654 std::vector<Local<Value>> name_v;
1655 std::vector<Local<Value>> type_v;
1656
1657 for (;;) {
1658 uv_dirent_t ent;
1659
1660 r = uv_fs_scandir_next(&(req_wrap_sync.req), &ent);
1661 if (r == UV_EOF)
1662 break;
1663 if (r != 0) {
1664 Local<Object> ctx = args[4].As<Object>();
1665 ctx->Set(env->context(), env->errno_string(),
1666 Integer::New(isolate, r)).Check();
1667 ctx->Set(env->context(), env->syscall_string(),
1668 OneByteString(isolate, "readdir")).Check();
1669 return;
1670 }
1671
1672 Local<Value> error;
1673 MaybeLocal<Value> filename = StringBytes::Encode(isolate,
1674 ent.name,
1675 encoding,
1676 &error);
1677
1678 if (filename.IsEmpty()) {
1679 Local<Object> ctx = args[4].As<Object>();
1680 ctx->Set(env->context(), env->error_string(), error).Check();
1681 return;
1682 }
1683
1684 name_v.push_back(filename.ToLocalChecked());
1685
1686 if (with_types) {
1687 type_v.emplace_back(Integer::New(isolate, ent.type));
1688 }
1689 }
1690
1691
1692 Local<Array> names = Array::New(isolate, name_v.data(), name_v.size());
1693 if (with_types) {
1694 Local<Value> result[] = {
1695 names,
1696 Array::New(isolate, type_v.data(), type_v.size())
1697 };
1698 args.GetReturnValue().Set(Array::New(isolate, result, arraysize(result)));
1699 } else {
1700 args.GetReturnValue().Set(names);
1701 }
1702 }
1703 }
1704
Open(const FunctionCallbackInfo<Value> & args)1705 static void Open(const FunctionCallbackInfo<Value>& args) {
1706 Environment* env = Environment::GetCurrent(args);
1707
1708 const int argc = args.Length();
1709 CHECK_GE(argc, 3);
1710
1711 BufferValue path(env->isolate(), args[0]);
1712 CHECK_NOT_NULL(*path);
1713
1714 CHECK(args[1]->IsInt32());
1715 const int flags = args[1].As<Int32>()->Value();
1716
1717 CHECK(args[2]->IsInt32());
1718 const int mode = args[2].As<Int32>()->Value();
1719
1720 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1721 if (req_wrap_async != nullptr) { // open(path, flags, mode, req)
1722 req_wrap_async->set_is_plain_open(true);
1723 AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterInteger,
1724 uv_fs_open, *path, flags, mode);
1725 } else { // open(path, flags, mode, undefined, ctx)
1726 CHECK_EQ(argc, 5);
1727 FSReqWrapSync req_wrap_sync;
1728 FS_SYNC_TRACE_BEGIN(open);
1729 int result = SyncCall(env, args[4], &req_wrap_sync, "open",
1730 uv_fs_open, *path, flags, mode);
1731 FS_SYNC_TRACE_END(open);
1732 if (result >= 0) env->AddUnmanagedFd(result);
1733 args.GetReturnValue().Set(result);
1734 }
1735 }
1736
OpenFileHandle(const FunctionCallbackInfo<Value> & args)1737 static void OpenFileHandle(const FunctionCallbackInfo<Value>& args) {
1738 BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
1739 Environment* env = binding_data->env();
1740 Isolate* isolate = env->isolate();
1741
1742 const int argc = args.Length();
1743 CHECK_GE(argc, 3);
1744
1745 BufferValue path(isolate, args[0]);
1746 CHECK_NOT_NULL(*path);
1747
1748 CHECK(args[1]->IsInt32());
1749 const int flags = args[1].As<Int32>()->Value();
1750
1751 CHECK(args[2]->IsInt32());
1752 const int mode = args[2].As<Int32>()->Value();
1753
1754 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1755 if (req_wrap_async != nullptr) { // openFileHandle(path, flags, mode, req)
1756 AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterOpenFileHandle,
1757 uv_fs_open, *path, flags, mode);
1758 } else { // openFileHandle(path, flags, mode, undefined, ctx)
1759 CHECK_EQ(argc, 5);
1760 FSReqWrapSync req_wrap_sync;
1761 FS_SYNC_TRACE_BEGIN(open);
1762 int result = SyncCall(env, args[4], &req_wrap_sync, "open",
1763 uv_fs_open, *path, flags, mode);
1764 FS_SYNC_TRACE_END(open);
1765 if (result < 0) {
1766 return; // syscall failed, no need to continue, error info is in ctx
1767 }
1768 FileHandle* fd = FileHandle::New(binding_data, result);
1769 if (fd == nullptr) return;
1770 args.GetReturnValue().Set(fd->object());
1771 }
1772 }
1773
CopyFile(const FunctionCallbackInfo<Value> & args)1774 static void CopyFile(const FunctionCallbackInfo<Value>& args) {
1775 Environment* env = Environment::GetCurrent(args);
1776 Isolate* isolate = env->isolate();
1777
1778 const int argc = args.Length();
1779 CHECK_GE(argc, 3);
1780
1781 BufferValue src(isolate, args[0]);
1782 CHECK_NOT_NULL(*src);
1783
1784 BufferValue dest(isolate, args[1]);
1785 CHECK_NOT_NULL(*dest);
1786
1787 CHECK(args[2]->IsInt32());
1788 const int flags = args[2].As<Int32>()->Value();
1789
1790 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1791 if (req_wrap_async != nullptr) { // copyFile(src, dest, flags, req)
1792 AsyncDestCall(env, req_wrap_async, args, "copyfile",
1793 *dest, dest.length(), UTF8, AfterNoArgs,
1794 uv_fs_copyfile, *src, *dest, flags);
1795 } else { // copyFile(src, dest, flags, undefined, ctx)
1796 CHECK_EQ(argc, 5);
1797 FSReqWrapSync req_wrap_sync;
1798 FS_SYNC_TRACE_BEGIN(copyfile);
1799 SyncCall(env, args[4], &req_wrap_sync, "copyfile",
1800 uv_fs_copyfile, *src, *dest, flags);
1801 FS_SYNC_TRACE_END(copyfile);
1802 }
1803 }
1804
1805
1806 // Wrapper for write(2).
1807 //
1808 // bytesWritten = write(fd, buffer, offset, length, position, callback)
1809 // 0 fd integer. file descriptor
1810 // 1 buffer the data to write
1811 // 2 offset where in the buffer to start from
1812 // 3 length how much to write
1813 // 4 position if integer, position to write at in the file.
1814 // if null, write from the current position
WriteBuffer(const FunctionCallbackInfo<Value> & args)1815 static void WriteBuffer(const FunctionCallbackInfo<Value>& args) {
1816 Environment* env = Environment::GetCurrent(args);
1817
1818 const int argc = args.Length();
1819 CHECK_GE(argc, 4);
1820
1821 CHECK(args[0]->IsInt32());
1822 const int fd = args[0].As<Int32>()->Value();
1823
1824 CHECK(Buffer::HasInstance(args[1]));
1825 Local<Object> buffer_obj = args[1].As<Object>();
1826 char* buffer_data = Buffer::Data(buffer_obj);
1827 size_t buffer_length = Buffer::Length(buffer_obj);
1828
1829 CHECK(IsSafeJsInt(args[2]));
1830 const int64_t off_64 = args[2].As<Integer>()->Value();
1831 CHECK_GE(off_64, 0);
1832 CHECK_LE(static_cast<uint64_t>(off_64), buffer_length);
1833 const size_t off = static_cast<size_t>(off_64);
1834
1835 CHECK(args[3]->IsInt32());
1836 const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
1837 CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
1838 CHECK_LE(len, buffer_length);
1839 CHECK_GE(off + len, off);
1840
1841 const int64_t pos = GetOffset(args[4]);
1842
1843 char* buf = buffer_data + off;
1844 uv_buf_t uvbuf = uv_buf_init(buf, len);
1845
1846 FSReqBase* req_wrap_async = GetReqWrap(args, 5);
1847 if (req_wrap_async != nullptr) { // write(fd, buffer, off, len, pos, req)
1848 AsyncCall(env, req_wrap_async, args, "write", UTF8, AfterInteger,
1849 uv_fs_write, fd, &uvbuf, 1, pos);
1850 } else { // write(fd, buffer, off, len, pos, undefined, ctx)
1851 CHECK_EQ(argc, 7);
1852 FSReqWrapSync req_wrap_sync;
1853 FS_SYNC_TRACE_BEGIN(write);
1854 int bytesWritten = SyncCall(env, args[6], &req_wrap_sync, "write",
1855 uv_fs_write, fd, &uvbuf, 1, pos);
1856 FS_SYNC_TRACE_END(write, "bytesWritten", bytesWritten);
1857 args.GetReturnValue().Set(bytesWritten);
1858 }
1859 }
1860
1861
1862 // Wrapper for writev(2).
1863 //
1864 // bytesWritten = writev(fd, chunks, position, callback)
1865 // 0 fd integer. file descriptor
1866 // 1 chunks array of buffers to write
1867 // 2 position if integer, position to write at in the file.
1868 // if null, write from the current position
WriteBuffers(const FunctionCallbackInfo<Value> & args)1869 static void WriteBuffers(const FunctionCallbackInfo<Value>& args) {
1870 Environment* env = Environment::GetCurrent(args);
1871
1872 const int argc = args.Length();
1873 CHECK_GE(argc, 3);
1874
1875 CHECK(args[0]->IsInt32());
1876 const int fd = args[0].As<Int32>()->Value();
1877
1878 CHECK(args[1]->IsArray());
1879 Local<Array> chunks = args[1].As<Array>();
1880
1881 int64_t pos = GetOffset(args[2]);
1882
1883 MaybeStackBuffer<uv_buf_t> iovs(chunks->Length());
1884
1885 for (uint32_t i = 0; i < iovs.length(); i++) {
1886 Local<Value> chunk = chunks->Get(env->context(), i).ToLocalChecked();
1887 CHECK(Buffer::HasInstance(chunk));
1888 iovs[i] = uv_buf_init(Buffer::Data(chunk), Buffer::Length(chunk));
1889 }
1890
1891 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1892 if (req_wrap_async != nullptr) { // writeBuffers(fd, chunks, pos, req)
1893 AsyncCall(env, req_wrap_async, args, "write", UTF8, AfterInteger,
1894 uv_fs_write, fd, *iovs, iovs.length(), pos);
1895 } else { // writeBuffers(fd, chunks, pos, undefined, ctx)
1896 CHECK_EQ(argc, 5);
1897 FSReqWrapSync req_wrap_sync;
1898 FS_SYNC_TRACE_BEGIN(write);
1899 int bytesWritten = SyncCall(env, args[4], &req_wrap_sync, "write",
1900 uv_fs_write, fd, *iovs, iovs.length(), pos);
1901 FS_SYNC_TRACE_END(write, "bytesWritten", bytesWritten);
1902 args.GetReturnValue().Set(bytesWritten);
1903 }
1904 }
1905
1906
1907 // Wrapper for write(2).
1908 //
1909 // bytesWritten = write(fd, string, position, enc, callback)
1910 // 0 fd integer. file descriptor
1911 // 1 string non-buffer values are converted to strings
1912 // 2 position if integer, position to write at in the file.
1913 // if null, write from the current position
1914 // 3 enc encoding of string
WriteString(const FunctionCallbackInfo<Value> & args)1915 static void WriteString(const FunctionCallbackInfo<Value>& args) {
1916 Environment* env = Environment::GetCurrent(args);
1917 Isolate* isolate = env->isolate();
1918
1919 const int argc = args.Length();
1920 CHECK_GE(argc, 4);
1921
1922 CHECK(args[0]->IsInt32());
1923 const int fd = args[0].As<Int32>()->Value();
1924
1925 const int64_t pos = GetOffset(args[2]);
1926
1927 const auto enc = ParseEncoding(isolate, args[3], UTF8);
1928
1929 Local<Value> value = args[1];
1930 char* buf = nullptr;
1931 size_t len;
1932
1933 FSReqBase* req_wrap_async = GetReqWrap(args, 4);
1934 const bool is_async = req_wrap_async != nullptr;
1935
1936 // Avoid copying the string when it is externalized but only when:
1937 // 1. The target encoding is compatible with the string's encoding, and
1938 // 2. The write is synchronous, otherwise the string might get neutered
1939 // while the request is in flight, and
1940 // 3. For UCS2, when the host system is little-endian. Big-endian systems
1941 // need to call StringBytes::Write() to ensure proper byte swapping.
1942 // The const_casts are conceptually sound: memory is read but not written.
1943 if (!is_async && value->IsString()) {
1944 auto string = value.As<String>();
1945 if ((enc == ASCII || enc == LATIN1) && string->IsExternalOneByte()) {
1946 auto ext = string->GetExternalOneByteStringResource();
1947 buf = const_cast<char*>(ext->data());
1948 len = ext->length();
1949 } else if (enc == UCS2 && IsLittleEndian() && string->IsExternal()) {
1950 auto ext = string->GetExternalStringResource();
1951 buf = reinterpret_cast<char*>(const_cast<uint16_t*>(ext->data()));
1952 len = ext->length() * sizeof(*ext->data());
1953 }
1954 }
1955
1956 if (is_async) { // write(fd, string, pos, enc, req)
1957 CHECK_NOT_NULL(req_wrap_async);
1958 if (!StringBytes::StorageSize(isolate, value, enc).To(&len)) return;
1959 FSReqBase::FSReqBuffer& stack_buffer =
1960 req_wrap_async->Init("write", len, enc);
1961 // StorageSize may return too large a char, so correct the actual length
1962 // by the write size
1963 len = StringBytes::Write(isolate, *stack_buffer, len, args[1], enc);
1964 stack_buffer.SetLengthAndZeroTerminate(len);
1965 uv_buf_t uvbuf = uv_buf_init(*stack_buffer, len);
1966 int err = req_wrap_async->Dispatch(uv_fs_write,
1967 fd,
1968 &uvbuf,
1969 1,
1970 pos,
1971 AfterInteger);
1972 if (err < 0) {
1973 uv_fs_t* uv_req = req_wrap_async->req();
1974 uv_req->result = err;
1975 uv_req->path = nullptr;
1976 AfterInteger(uv_req); // after may delete req_wrap_async if there is
1977 // an error
1978 } else {
1979 req_wrap_async->SetReturnValue(args);
1980 }
1981 } else { // write(fd, string, pos, enc, undefined, ctx)
1982 CHECK_EQ(argc, 6);
1983 FSReqWrapSync req_wrap_sync;
1984 FSReqBase::FSReqBuffer stack_buffer;
1985 if (buf == nullptr) {
1986 if (!StringBytes::StorageSize(isolate, value, enc).To(&len))
1987 return;
1988 stack_buffer.AllocateSufficientStorage(len + 1);
1989 // StorageSize may return too large a char, so correct the actual length
1990 // by the write size
1991 len = StringBytes::Write(isolate, *stack_buffer,
1992 len, args[1], enc);
1993 stack_buffer.SetLengthAndZeroTerminate(len);
1994 buf = *stack_buffer;
1995 }
1996 uv_buf_t uvbuf = uv_buf_init(buf, len);
1997 FS_SYNC_TRACE_BEGIN(write);
1998 int bytesWritten = SyncCall(env, args[5], &req_wrap_sync, "write",
1999 uv_fs_write, fd, &uvbuf, 1, pos);
2000 FS_SYNC_TRACE_END(write, "bytesWritten", bytesWritten);
2001 args.GetReturnValue().Set(bytesWritten);
2002 }
2003 }
2004
2005
2006 /*
2007 * Wrapper for read(2).
2008 *
2009 * bytesRead = fs.read(fd, buffer, offset, length, position)
2010 *
2011 * 0 fd int32. file descriptor
2012 * 1 buffer instance of Buffer
2013 * 2 offset int64. offset to start reading into inside buffer
2014 * 3 length int32. length to read
2015 * 4 position int64. file position - -1 for current position
2016 */
Read(const FunctionCallbackInfo<Value> & args)2017 static void Read(const FunctionCallbackInfo<Value>& args) {
2018 Environment* env = Environment::GetCurrent(args);
2019
2020 const int argc = args.Length();
2021 CHECK_GE(argc, 5);
2022
2023 CHECK(args[0]->IsInt32());
2024 const int fd = args[0].As<Int32>()->Value();
2025
2026 CHECK(Buffer::HasInstance(args[1]));
2027 Local<Object> buffer_obj = args[1].As<Object>();
2028 char* buffer_data = Buffer::Data(buffer_obj);
2029 size_t buffer_length = Buffer::Length(buffer_obj);
2030
2031 CHECK(IsSafeJsInt(args[2]));
2032 const int64_t off_64 = args[2].As<Integer>()->Value();
2033 CHECK_GE(off_64, 0);
2034 CHECK_LT(static_cast<uint64_t>(off_64), buffer_length);
2035 const size_t off = static_cast<size_t>(off_64);
2036
2037 CHECK(args[3]->IsInt32());
2038 const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
2039 CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
2040
2041 CHECK(IsSafeJsInt(args[4]) || args[4]->IsBigInt());
2042 const int64_t pos = args[4]->IsNumber() ?
2043 args[4].As<Integer>()->Value() :
2044 args[4].As<BigInt>()->Int64Value();
2045
2046 char* buf = buffer_data + off;
2047 uv_buf_t uvbuf = uv_buf_init(buf, len);
2048
2049 FSReqBase* req_wrap_async = GetReqWrap(args, 5);
2050 if (req_wrap_async != nullptr) { // read(fd, buffer, offset, len, pos, req)
2051 AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger,
2052 uv_fs_read, fd, &uvbuf, 1, pos);
2053 } else { // read(fd, buffer, offset, len, pos, undefined, ctx)
2054 CHECK_EQ(argc, 7);
2055 FSReqWrapSync req_wrap_sync;
2056 FS_SYNC_TRACE_BEGIN(read);
2057 const int bytesRead = SyncCall(env, args[6], &req_wrap_sync, "read",
2058 uv_fs_read, fd, &uvbuf, 1, pos);
2059 FS_SYNC_TRACE_END(read, "bytesRead", bytesRead);
2060 args.GetReturnValue().Set(bytesRead);
2061 }
2062 }
2063
2064
2065 // Wrapper for readv(2).
2066 //
2067 // bytesRead = fs.readv(fd, buffers[, position], callback)
2068 // 0 fd integer. file descriptor
2069 // 1 buffers array of buffers to read
2070 // 2 position if integer, position to read at in the file.
2071 // if null, read from the current position
ReadBuffers(const FunctionCallbackInfo<Value> & args)2072 static void ReadBuffers(const FunctionCallbackInfo<Value>& args) {
2073 Environment* env = Environment::GetCurrent(args);
2074
2075 const int argc = args.Length();
2076 CHECK_GE(argc, 3);
2077
2078 CHECK(args[0]->IsInt32());
2079 const int fd = args[0].As<Int32>()->Value();
2080
2081 CHECK(args[1]->IsArray());
2082 Local<Array> buffers = args[1].As<Array>();
2083
2084 int64_t pos = GetOffset(args[2]); // -1 if not a valid JS int
2085
2086 MaybeStackBuffer<uv_buf_t> iovs(buffers->Length());
2087
2088 // Init uv buffers from ArrayBufferViews
2089 for (uint32_t i = 0; i < iovs.length(); i++) {
2090 Local<Value> buffer = buffers->Get(env->context(), i).ToLocalChecked();
2091 CHECK(Buffer::HasInstance(buffer));
2092 iovs[i] = uv_buf_init(Buffer::Data(buffer), Buffer::Length(buffer));
2093 }
2094
2095 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2096 if (req_wrap_async != nullptr) { // readBuffers(fd, buffers, pos, req)
2097 AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger,
2098 uv_fs_read, fd, *iovs, iovs.length(), pos);
2099 } else { // readBuffers(fd, buffers, undefined, ctx)
2100 CHECK_EQ(argc, 5);
2101 FSReqWrapSync req_wrap_sync;
2102 FS_SYNC_TRACE_BEGIN(read);
2103 int bytesRead = SyncCall(env, /* ctx */ args[4], &req_wrap_sync, "read",
2104 uv_fs_read, fd, *iovs, iovs.length(), pos);
2105 FS_SYNC_TRACE_END(read, "bytesRead", bytesRead);
2106 args.GetReturnValue().Set(bytesRead);
2107 }
2108 }
2109
2110
2111 /* fs.chmod(path, mode);
2112 * Wrapper for chmod(1) / EIO_CHMOD
2113 */
Chmod(const FunctionCallbackInfo<Value> & args)2114 static void Chmod(const FunctionCallbackInfo<Value>& args) {
2115 Environment* env = Environment::GetCurrent(args);
2116
2117 const int argc = args.Length();
2118 CHECK_GE(argc, 2);
2119
2120 BufferValue path(env->isolate(), args[0]);
2121 CHECK_NOT_NULL(*path);
2122
2123 CHECK(args[1]->IsInt32());
2124 int mode = args[1].As<Int32>()->Value();
2125
2126 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
2127 if (req_wrap_async != nullptr) { // chmod(path, mode, req)
2128 AsyncCall(env, req_wrap_async, args, "chmod", UTF8, AfterNoArgs,
2129 uv_fs_chmod, *path, mode);
2130 } else { // chmod(path, mode, undefined, ctx)
2131 CHECK_EQ(argc, 4);
2132 FSReqWrapSync req_wrap_sync;
2133 FS_SYNC_TRACE_BEGIN(chmod);
2134 SyncCall(env, args[3], &req_wrap_sync, "chmod",
2135 uv_fs_chmod, *path, mode);
2136 FS_SYNC_TRACE_END(chmod);
2137 }
2138 }
2139
2140
2141 /* fs.fchmod(fd, mode);
2142 * Wrapper for fchmod(1) / EIO_FCHMOD
2143 */
FChmod(const FunctionCallbackInfo<Value> & args)2144 static void FChmod(const FunctionCallbackInfo<Value>& args) {
2145 Environment* env = Environment::GetCurrent(args);
2146
2147 const int argc = args.Length();
2148 CHECK_GE(argc, 2);
2149
2150 CHECK(args[0]->IsInt32());
2151 const int fd = args[0].As<Int32>()->Value();
2152
2153 CHECK(args[1]->IsInt32());
2154 const int mode = args[1].As<Int32>()->Value();
2155
2156 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
2157 if (req_wrap_async != nullptr) { // fchmod(fd, mode, req)
2158 AsyncCall(env, req_wrap_async, args, "fchmod", UTF8, AfterNoArgs,
2159 uv_fs_fchmod, fd, mode);
2160 } else { // fchmod(fd, mode, undefined, ctx)
2161 CHECK_EQ(argc, 4);
2162 FSReqWrapSync req_wrap_sync;
2163 FS_SYNC_TRACE_BEGIN(fchmod);
2164 SyncCall(env, args[3], &req_wrap_sync, "fchmod",
2165 uv_fs_fchmod, fd, mode);
2166 FS_SYNC_TRACE_END(fchmod);
2167 }
2168 }
2169
2170
2171 /* fs.chown(path, uid, gid);
2172 * Wrapper for chown(1) / EIO_CHOWN
2173 */
Chown(const FunctionCallbackInfo<Value> & args)2174 static void Chown(const FunctionCallbackInfo<Value>& args) {
2175 Environment* env = Environment::GetCurrent(args);
2176
2177 const int argc = args.Length();
2178 CHECK_GE(argc, 3);
2179
2180 BufferValue path(env->isolate(), args[0]);
2181 CHECK_NOT_NULL(*path);
2182
2183 CHECK(IsSafeJsInt(args[1]));
2184 const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Integer>()->Value());
2185
2186 CHECK(IsSafeJsInt(args[2]));
2187 const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Integer>()->Value());
2188
2189 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2190 if (req_wrap_async != nullptr) { // chown(path, uid, gid, req)
2191 AsyncCall(env, req_wrap_async, args, "chown", UTF8, AfterNoArgs,
2192 uv_fs_chown, *path, uid, gid);
2193 } else { // chown(path, uid, gid, undefined, ctx)
2194 CHECK_EQ(argc, 5);
2195 FSReqWrapSync req_wrap_sync;
2196 FS_SYNC_TRACE_BEGIN(chown);
2197 SyncCall(env, args[4], &req_wrap_sync, "chown",
2198 uv_fs_chown, *path, uid, gid);
2199 FS_SYNC_TRACE_END(chown);
2200 }
2201 }
2202
2203
2204 /* fs.fchown(fd, uid, gid);
2205 * Wrapper for fchown(1) / EIO_FCHOWN
2206 */
FChown(const FunctionCallbackInfo<Value> & args)2207 static void FChown(const FunctionCallbackInfo<Value>& args) {
2208 Environment* env = Environment::GetCurrent(args);
2209
2210 const int argc = args.Length();
2211 CHECK_GE(argc, 3);
2212
2213 CHECK(args[0]->IsInt32());
2214 const int fd = args[0].As<Int32>()->Value();
2215
2216 CHECK(IsSafeJsInt(args[1]));
2217 const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Integer>()->Value());
2218
2219 CHECK(IsSafeJsInt(args[2]));
2220 const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Integer>()->Value());
2221
2222 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2223 if (req_wrap_async != nullptr) { // fchown(fd, uid, gid, req)
2224 AsyncCall(env, req_wrap_async, args, "fchown", UTF8, AfterNoArgs,
2225 uv_fs_fchown, fd, uid, gid);
2226 } else { // fchown(fd, uid, gid, undefined, ctx)
2227 CHECK_EQ(argc, 5);
2228 FSReqWrapSync req_wrap_sync;
2229 FS_SYNC_TRACE_BEGIN(fchown);
2230 SyncCall(env, args[4], &req_wrap_sync, "fchown",
2231 uv_fs_fchown, fd, uid, gid);
2232 FS_SYNC_TRACE_END(fchown);
2233 }
2234 }
2235
2236
LChown(const FunctionCallbackInfo<Value> & args)2237 static void LChown(const FunctionCallbackInfo<Value>& args) {
2238 Environment* env = Environment::GetCurrent(args);
2239
2240 const int argc = args.Length();
2241 CHECK_GE(argc, 3);
2242
2243 BufferValue path(env->isolate(), args[0]);
2244 CHECK_NOT_NULL(*path);
2245
2246 CHECK(IsSafeJsInt(args[1]));
2247 const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Integer>()->Value());
2248
2249 CHECK(IsSafeJsInt(args[2]));
2250 const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Integer>()->Value());
2251
2252 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2253 if (req_wrap_async != nullptr) { // lchown(path, uid, gid, req)
2254 AsyncCall(env, req_wrap_async, args, "lchown", UTF8, AfterNoArgs,
2255 uv_fs_lchown, *path, uid, gid);
2256 } else { // lchown(path, uid, gid, undefined, ctx)
2257 CHECK_EQ(argc, 5);
2258 FSReqWrapSync req_wrap_sync;
2259 FS_SYNC_TRACE_BEGIN(lchown);
2260 SyncCall(env, args[4], &req_wrap_sync, "lchown",
2261 uv_fs_lchown, *path, uid, gid);
2262 FS_SYNC_TRACE_END(lchown);
2263 }
2264 }
2265
2266
UTimes(const FunctionCallbackInfo<Value> & args)2267 static void UTimes(const FunctionCallbackInfo<Value>& args) {
2268 Environment* env = Environment::GetCurrent(args);
2269
2270 const int argc = args.Length();
2271 CHECK_GE(argc, 3);
2272
2273 BufferValue path(env->isolate(), args[0]);
2274 CHECK_NOT_NULL(*path);
2275
2276 CHECK(args[1]->IsNumber());
2277 const double atime = args[1].As<Number>()->Value();
2278
2279 CHECK(args[2]->IsNumber());
2280 const double mtime = args[2].As<Number>()->Value();
2281
2282 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2283 if (req_wrap_async != nullptr) { // utimes(path, atime, mtime, req)
2284 AsyncCall(env, req_wrap_async, args, "utime", UTF8, AfterNoArgs,
2285 uv_fs_utime, *path, atime, mtime);
2286 } else { // utimes(path, atime, mtime, undefined, ctx)
2287 CHECK_EQ(argc, 5);
2288 FSReqWrapSync req_wrap_sync;
2289 FS_SYNC_TRACE_BEGIN(utimes);
2290 SyncCall(env, args[4], &req_wrap_sync, "utime",
2291 uv_fs_utime, *path, atime, mtime);
2292 FS_SYNC_TRACE_END(utimes);
2293 }
2294 }
2295
FUTimes(const FunctionCallbackInfo<Value> & args)2296 static void FUTimes(const FunctionCallbackInfo<Value>& args) {
2297 Environment* env = Environment::GetCurrent(args);
2298
2299 const int argc = args.Length();
2300 CHECK_GE(argc, 3);
2301
2302 CHECK(args[0]->IsInt32());
2303 const int fd = args[0].As<Int32>()->Value();
2304
2305 CHECK(args[1]->IsNumber());
2306 const double atime = args[1].As<Number>()->Value();
2307
2308 CHECK(args[2]->IsNumber());
2309 const double mtime = args[2].As<Number>()->Value();
2310
2311 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2312 if (req_wrap_async != nullptr) { // futimes(fd, atime, mtime, req)
2313 AsyncCall(env, req_wrap_async, args, "futime", UTF8, AfterNoArgs,
2314 uv_fs_futime, fd, atime, mtime);
2315 } else { // futimes(fd, atime, mtime, undefined, ctx)
2316 CHECK_EQ(argc, 5);
2317 FSReqWrapSync req_wrap_sync;
2318 FS_SYNC_TRACE_BEGIN(futimes);
2319 SyncCall(env, args[4], &req_wrap_sync, "futime",
2320 uv_fs_futime, fd, atime, mtime);
2321 FS_SYNC_TRACE_END(futimes);
2322 }
2323 }
2324
LUTimes(const FunctionCallbackInfo<Value> & args)2325 static void LUTimes(const FunctionCallbackInfo<Value>& args) {
2326 Environment* env = Environment::GetCurrent(args);
2327
2328 const int argc = args.Length();
2329 CHECK_GE(argc, 3);
2330
2331 BufferValue path(env->isolate(), args[0]);
2332 CHECK_NOT_NULL(*path);
2333
2334 CHECK(args[1]->IsNumber());
2335 const double atime = args[1].As<Number>()->Value();
2336
2337 CHECK(args[2]->IsNumber());
2338 const double mtime = args[2].As<Number>()->Value();
2339
2340 FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2341 if (req_wrap_async != nullptr) { // lutimes(path, atime, mtime, req)
2342 AsyncCall(env, req_wrap_async, args, "lutime", UTF8, AfterNoArgs,
2343 uv_fs_lutime, *path, atime, mtime);
2344 } else { // lutimes(path, atime, mtime, undefined, ctx)
2345 CHECK_EQ(argc, 5);
2346 FSReqWrapSync req_wrap_sync;
2347 FS_SYNC_TRACE_BEGIN(lutimes);
2348 SyncCall(env, args[4], &req_wrap_sync, "lutime",
2349 uv_fs_lutime, *path, atime, mtime);
2350 FS_SYNC_TRACE_END(lutimes);
2351 }
2352 }
2353
Mkdtemp(const FunctionCallbackInfo<Value> & args)2354 static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
2355 Environment* env = Environment::GetCurrent(args);
2356 Isolate* isolate = env->isolate();
2357
2358 const int argc = args.Length();
2359 CHECK_GE(argc, 2);
2360
2361 BufferValue tmpl(isolate, args[0]);
2362 CHECK_NOT_NULL(*tmpl);
2363
2364 const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
2365
2366 FSReqBase* req_wrap_async = GetReqWrap(args, 2);
2367 if (req_wrap_async != nullptr) { // mkdtemp(tmpl, encoding, req)
2368 AsyncCall(env, req_wrap_async, args, "mkdtemp", encoding, AfterStringPath,
2369 uv_fs_mkdtemp, *tmpl);
2370 } else { // mkdtemp(tmpl, encoding, undefined, ctx)
2371 CHECK_EQ(argc, 4);
2372 FSReqWrapSync req_wrap_sync;
2373 FS_SYNC_TRACE_BEGIN(mkdtemp);
2374 SyncCall(env, args[3], &req_wrap_sync, "mkdtemp",
2375 uv_fs_mkdtemp, *tmpl);
2376 FS_SYNC_TRACE_END(mkdtemp);
2377 const char* path = req_wrap_sync.req.path;
2378
2379 Local<Value> error;
2380 MaybeLocal<Value> rc =
2381 StringBytes::Encode(isolate, path, encoding, &error);
2382 if (rc.IsEmpty()) {
2383 Local<Object> ctx = args[3].As<Object>();
2384 ctx->Set(env->context(), env->error_string(), error).Check();
2385 return;
2386 }
2387 args.GetReturnValue().Set(rc.ToLocalChecked());
2388 }
2389 }
2390
MemoryInfo(MemoryTracker * tracker) const2391 void BindingData::MemoryInfo(MemoryTracker* tracker) const {
2392 tracker->TrackField("stats_field_array", stats_field_array);
2393 tracker->TrackField("stats_field_bigint_array", stats_field_bigint_array);
2394 tracker->TrackField("file_handle_read_wrap_freelist",
2395 file_handle_read_wrap_freelist);
2396 }
2397
2398 // TODO(addaleax): Remove once we're on C++17.
2399 constexpr FastStringKey BindingData::type_name;
2400
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)2401 void Initialize(Local<Object> target,
2402 Local<Value> unused,
2403 Local<Context> context,
2404 void* priv) {
2405 Environment* env = Environment::GetCurrent(context);
2406 Isolate* isolate = env->isolate();
2407 BindingData* const binding_data =
2408 env->AddBindingData<BindingData>(context, target);
2409 if (binding_data == nullptr) return;
2410
2411 env->SetMethod(target, "access", Access);
2412 env->SetMethod(target, "close", Close);
2413 env->SetMethod(target, "open", Open);
2414 env->SetMethod(target, "openFileHandle", OpenFileHandle);
2415 env->SetMethod(target, "read", Read);
2416 env->SetMethod(target, "readBuffers", ReadBuffers);
2417 env->SetMethod(target, "fdatasync", Fdatasync);
2418 env->SetMethod(target, "fsync", Fsync);
2419 env->SetMethod(target, "rename", Rename);
2420 env->SetMethod(target, "ftruncate", FTruncate);
2421 env->SetMethod(target, "rmdir", RMDir);
2422 env->SetMethod(target, "mkdir", MKDir);
2423 env->SetMethod(target, "readdir", ReadDir);
2424 env->SetMethod(target, "internalModuleReadJSON", InternalModuleReadJSON);
2425 env->SetMethod(target, "internalModuleStat", InternalModuleStat);
2426 env->SetMethod(target, "stat", Stat);
2427 env->SetMethod(target, "lstat", LStat);
2428 env->SetMethod(target, "fstat", FStat);
2429 env->SetMethod(target, "link", Link);
2430 env->SetMethod(target, "symlink", Symlink);
2431 env->SetMethod(target, "readlink", ReadLink);
2432 env->SetMethod(target, "unlink", Unlink);
2433 env->SetMethod(target, "writeBuffer", WriteBuffer);
2434 env->SetMethod(target, "writeBuffers", WriteBuffers);
2435 env->SetMethod(target, "writeString", WriteString);
2436 env->SetMethod(target, "realpath", RealPath);
2437 env->SetMethod(target, "copyFile", CopyFile);
2438
2439 env->SetMethod(target, "chmod", Chmod);
2440 env->SetMethod(target, "fchmod", FChmod);
2441
2442 env->SetMethod(target, "chown", Chown);
2443 env->SetMethod(target, "fchown", FChown);
2444 env->SetMethod(target, "lchown", LChown);
2445
2446 env->SetMethod(target, "utimes", UTimes);
2447 env->SetMethod(target, "futimes", FUTimes);
2448 env->SetMethod(target, "lutimes", LUTimes);
2449
2450 env->SetMethod(target, "mkdtemp", Mkdtemp);
2451
2452 target
2453 ->Set(context,
2454 FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
2455 Integer::New(
2456 isolate,
2457 static_cast<int32_t>(FsStatsOffset::kFsStatsFieldsNumber)))
2458 .Check();
2459
2460 target->Set(context,
2461 FIXED_ONE_BYTE_STRING(isolate, "statValues"),
2462 binding_data->stats_field_array.GetJSArray()).Check();
2463
2464 target->Set(context,
2465 FIXED_ONE_BYTE_STRING(isolate, "bigintStatValues"),
2466 binding_data->stats_field_bigint_array.GetJSArray()).Check();
2467
2468 StatWatcher::Initialize(env, target);
2469
2470 // Create FunctionTemplate for FSReqCallback
2471 Local<FunctionTemplate> fst = env->NewFunctionTemplate(NewFSReqCallback);
2472 fst->InstanceTemplate()->SetInternalFieldCount(
2473 FSReqBase::kInternalFieldCount);
2474 fst->Inherit(AsyncWrap::GetConstructorTemplate(env));
2475 env->SetConstructorFunction(target, "FSReqCallback", fst);
2476
2477 // Create FunctionTemplate for FileHandleReadWrap. There’s no need
2478 // to do anything in the constructor, so we only store the instance template.
2479 Local<FunctionTemplate> fh_rw = FunctionTemplate::New(isolate);
2480 fh_rw->InstanceTemplate()->SetInternalFieldCount(
2481 FSReqBase::kInternalFieldCount);
2482 fh_rw->Inherit(AsyncWrap::GetConstructorTemplate(env));
2483 Local<String> fhWrapString =
2484 FIXED_ONE_BYTE_STRING(isolate, "FileHandleReqWrap");
2485 fh_rw->SetClassName(fhWrapString);
2486 env->set_filehandlereadwrap_template(
2487 fst->InstanceTemplate());
2488
2489 // Create Function Template for FSReqPromise
2490 Local<FunctionTemplate> fpt = FunctionTemplate::New(isolate);
2491 fpt->Inherit(AsyncWrap::GetConstructorTemplate(env));
2492 Local<String> promiseString =
2493 FIXED_ONE_BYTE_STRING(isolate, "FSReqPromise");
2494 fpt->SetClassName(promiseString);
2495 Local<ObjectTemplate> fpo = fpt->InstanceTemplate();
2496 fpo->SetInternalFieldCount(FSReqBase::kInternalFieldCount);
2497 env->set_fsreqpromise_constructor_template(fpo);
2498
2499 // Create FunctionTemplate for FileHandle
2500 Local<FunctionTemplate> fd = env->NewFunctionTemplate(FileHandle::New);
2501 fd->Inherit(AsyncWrap::GetConstructorTemplate(env));
2502 env->SetProtoMethod(fd, "close", FileHandle::Close);
2503 env->SetProtoMethod(fd, "releaseFD", FileHandle::ReleaseFD);
2504 Local<ObjectTemplate> fdt = fd->InstanceTemplate();
2505 fdt->SetInternalFieldCount(StreamBase::kInternalFieldCount);
2506 StreamBase::AddMethods(env, fd);
2507 env->SetConstructorFunction(target, "FileHandle", fd);
2508 env->set_fd_constructor_template(fdt);
2509
2510 // Create FunctionTemplate for FileHandle::CloseReq
2511 Local<FunctionTemplate> fdclose = FunctionTemplate::New(isolate);
2512 fdclose->SetClassName(FIXED_ONE_BYTE_STRING(isolate,
2513 "FileHandleCloseReq"));
2514 fdclose->Inherit(AsyncWrap::GetConstructorTemplate(env));
2515 Local<ObjectTemplate> fdcloset = fdclose->InstanceTemplate();
2516 fdcloset->SetInternalFieldCount(FSReqBase::kInternalFieldCount);
2517 env->set_fdclose_constructor_template(fdcloset);
2518
2519 Local<Symbol> use_promises_symbol =
2520 Symbol::New(isolate,
2521 FIXED_ONE_BYTE_STRING(isolate, "use promises"));
2522 env->set_fs_use_promises_symbol(use_promises_symbol);
2523 target->Set(context,
2524 FIXED_ONE_BYTE_STRING(isolate, "kUsePromises"),
2525 use_promises_symbol).Check();
2526 }
2527
binding_data()2528 BindingData* FSReqBase::binding_data() {
2529 return binding_data_.get();
2530 }
2531 } // namespace fs
2532
2533 } // end namespace node
2534
2535 NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs, node::fs::Initialize)
2536