Coverage Report

Created: 2025-08-28 09:57

/src/node/src/node_file.cc
Line
Count
Source (jump to first uncovered line)
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 "ada.h"
23
#include "aliased_buffer-inl.h"
24
#include "memory_tracker-inl.h"
25
#include "node_buffer.h"
26
#include "node_errors.h"
27
#include "node_external_reference.h"
28
#include "node_file-inl.h"
29
#include "node_metadata.h"
30
#include "node_process-inl.h"
31
#include "node_stat_watcher.h"
32
#include "node_url.h"
33
#include "permission/permission.h"
34
#include "util-inl.h"
35
36
#include "tracing/trace_event.h"
37
38
#include "req_wrap-inl.h"
39
#include "stream_base-inl.h"
40
#include "string_bytes.h"
41
#include "uv.h"
42
43
#if defined(__MINGW32__) || defined(_MSC_VER)
44
# include <io.h>
45
#endif
46
47
namespace node {
48
49
namespace fs {
50
51
using v8::Array;
52
using v8::BigInt;
53
using v8::Context;
54
using v8::EscapableHandleScope;
55
using v8::Function;
56
using v8::FunctionCallbackInfo;
57
using v8::FunctionTemplate;
58
using v8::HandleScope;
59
using v8::Int32;
60
using v8::Integer;
61
using v8::Isolate;
62
using v8::JustVoid;
63
using v8::Local;
64
using v8::Maybe;
65
using v8::MaybeLocal;
66
using v8::Nothing;
67
using v8::Number;
68
using v8::Object;
69
using v8::ObjectTemplate;
70
using v8::Promise;
71
using v8::String;
72
using v8::Undefined;
73
using v8::Value;
74
75
#ifndef S_ISDIR
76
# define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
77
#endif
78
79
#ifdef __POSIX__
80
constexpr char kPathSeparator = '/';
81
#else
82
const char* const kPathSeparator = "\\/";
83
#endif
84
85
0
std::string Basename(const std::string& str, const std::string& extension) {
86
  // Remove everything leading up to and including the final path separator.
87
0
  std::string::size_type pos = str.find_last_of(kPathSeparator);
88
89
  // Starting index for the resulting string
90
0
  std::size_t start_pos = 0;
91
  // String size to return
92
0
  std::size_t str_size = str.size();
93
0
  if (pos != std::string::npos) {
94
0
    start_pos = pos + 1;
95
0
    str_size -= start_pos;
96
0
  }
97
98
  // Strip away the extension, if any.
99
0
  if (str_size >= extension.size() &&
100
0
      str.compare(str.size() - extension.size(),
101
0
        extension.size(), extension) == 0) {
102
0
    str_size -= extension.size();
103
0
  }
104
105
0
  return str.substr(start_pos, str_size);
106
0
}
107
108
0
inline int64_t GetOffset(Local<Value> value) {
109
0
  return IsSafeJsInt(value) ? value.As<Integer>()->Value() : -1;
110
0
}
111
112
0
static const char* get_fs_func_name_by_type(uv_fs_type req_type) {
113
0
  switch (req_type) {
114
0
#define FS_TYPE_TO_NAME(type, name)                                            \
115
0
  case UV_FS_##type:                                                           \
116
0
    return name;
117
0
    FS_TYPE_TO_NAME(OPEN, "open")
118
0
    FS_TYPE_TO_NAME(CLOSE, "close")
119
0
    FS_TYPE_TO_NAME(READ, "read")
120
0
    FS_TYPE_TO_NAME(WRITE, "write")
121
0
    FS_TYPE_TO_NAME(SENDFILE, "sendfile")
122
0
    FS_TYPE_TO_NAME(STAT, "stat")
123
0
    FS_TYPE_TO_NAME(LSTAT, "lstat")
124
0
    FS_TYPE_TO_NAME(FSTAT, "fstat")
125
0
    FS_TYPE_TO_NAME(FTRUNCATE, "ftruncate")
126
0
    FS_TYPE_TO_NAME(UTIME, "utime")
127
0
    FS_TYPE_TO_NAME(FUTIME, "futime")
128
0
    FS_TYPE_TO_NAME(ACCESS, "access")
129
0
    FS_TYPE_TO_NAME(CHMOD, "chmod")
130
0
    FS_TYPE_TO_NAME(FCHMOD, "fchmod")
131
0
    FS_TYPE_TO_NAME(FSYNC, "fsync")
132
0
    FS_TYPE_TO_NAME(FDATASYNC, "fdatasync")
133
0
    FS_TYPE_TO_NAME(UNLINK, "unlink")
134
0
    FS_TYPE_TO_NAME(RMDIR, "rmdir")
135
0
    FS_TYPE_TO_NAME(MKDIR, "mkdir")
136
0
    FS_TYPE_TO_NAME(MKDTEMP, "mkdtemp")
137
0
    FS_TYPE_TO_NAME(RENAME, "rename")
138
0
    FS_TYPE_TO_NAME(SCANDIR, "scandir")
139
0
    FS_TYPE_TO_NAME(LINK, "link")
140
0
    FS_TYPE_TO_NAME(SYMLINK, "symlink")
141
0
    FS_TYPE_TO_NAME(READLINK, "readlink")
142
0
    FS_TYPE_TO_NAME(CHOWN, "chown")
143
0
    FS_TYPE_TO_NAME(FCHOWN, "fchown")
144
0
    FS_TYPE_TO_NAME(REALPATH, "realpath")
145
0
    FS_TYPE_TO_NAME(COPYFILE, "copyfile")
146
0
    FS_TYPE_TO_NAME(LCHOWN, "lchown")
147
0
    FS_TYPE_TO_NAME(STATFS, "statfs")
148
0
    FS_TYPE_TO_NAME(MKSTEMP, "mkstemp")
149
0
    FS_TYPE_TO_NAME(LUTIME, "lutime")
150
0
#undef FS_TYPE_TO_NAME
151
0
    default:
152
0
      return "unknown";
153
0
  }
154
0
}
155
156
#define TRACE_NAME(name) "fs.sync." #name
157
#define GET_TRACE_ENABLED                                                      \
158
14.0k
  (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(                                \
159
14.0k
       TRACING_CATEGORY_NODE2(fs, sync)) != 0)
160
#define FS_SYNC_TRACE_BEGIN(syscall, ...)                                      \
161
7.00k
  if (GET_TRACE_ENABLED)                                                       \
162
7.00k
    TRACE_EVENT_BEGIN(                                                         \
163
7.00k
        TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__);
164
#define FS_SYNC_TRACE_END(syscall, ...)                                        \
165
7.00k
  if (GET_TRACE_ENABLED)                                                       \
166
7.00k
    TRACE_EVENT_END(                                                           \
167
7.00k
        TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__);
168
169
#define FS_ASYNC_TRACE_BEGIN0(fs_type, id)                                     \
170
0
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs, async),         \
171
0
                                    get_fs_func_name_by_type(fs_type),         \
172
0
                                    id);
173
174
#define FS_ASYNC_TRACE_END0(fs_type, id)                                       \
175
  TRACE_EVENT_NESTABLE_ASYNC_END0(TRACING_CATEGORY_NODE2(fs, async),           \
176
                                  get_fs_func_name_by_type(fs_type),           \
177
                                  id);
178
179
#define FS_ASYNC_TRACE_BEGIN1(fs_type, id, name, value)                        \
180
3.88k
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE2(fs, async),         \
181
3.88k
                                    get_fs_func_name_by_type(fs_type),         \
182
3.88k
                                    id,                                        \
183
3.88k
                                    name,                                      \
184
3.88k
                                    value);
185
186
#define FS_ASYNC_TRACE_END1(fs_type, id, name, value)                          \
187
3.88k
  TRACE_EVENT_NESTABLE_ASYNC_END1(TRACING_CATEGORY_NODE2(fs, async),           \
188
3.88k
                                  get_fs_func_name_by_type(fs_type),           \
189
3.88k
                                  id,                                          \
190
3.88k
                                  name,                                        \
191
3.88k
                                  value);
192
193
#define FS_ASYNC_TRACE_BEGIN2(fs_type, id, name1, value1, name2, value2)       \
194
0
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(TRACING_CATEGORY_NODE2(fs, async),         \
195
0
                                    get_fs_func_name_by_type(fs_type),         \
196
0
                                    id,                                        \
197
0
                                    name1,                                     \
198
0
                                    value1,                                    \
199
0
                                    name2,                                     \
200
0
                                    value2);
201
202
#define FS_ASYNC_TRACE_END2(fs_type, id, name1, value1, name2, value2)         \
203
  TRACE_EVENT_NESTABLE_ASYNC_END2(TRACING_CATEGORY_NODE2(fs, async),           \
204
                                  get_fs_func_name_by_type(fs_type),           \
205
                                  id,                                          \
206
                                  name1,                                       \
207
                                  value1,                                      \
208
                                  name2,                                       \
209
                                  value2);
210
211
// We sometimes need to convert a C++ lambda function to a raw C-style function.
212
// This is helpful, because ReqWrap::Dispatch() does not recognize lambda
213
// functions, and thus does not wrap them properly.
214
typedef void(*uv_fs_callback_t)(uv_fs_t*);
215
216
217
0
void FSContinuationData::MemoryInfo(MemoryTracker* tracker) const {
218
0
  tracker->TrackField("paths", paths_);
219
0
}
220
221
0
FileHandleReadWrap::~FileHandleReadWrap() = default;
222
223
3.88k
FSReqBase::~FSReqBase() = default;
224
225
0
void FSReqBase::MemoryInfo(MemoryTracker* tracker) const {
226
0
  tracker->TrackField("continuation_data", continuation_data_);
227
0
}
228
229
// The FileHandle object wraps a file descriptor and will close it on garbage
230
// collection if necessary. If that happens, a process warning will be
231
// emitted (or a fatal exception will occur if the fd cannot be closed.)
232
FileHandle::FileHandle(BindingData* binding_data,
233
                       Local<Object> obj, int fd)
234
0
    : AsyncWrap(binding_data->env(), obj, AsyncWrap::PROVIDER_FILEHANDLE),
235
0
      StreamBase(env()),
236
0
      fd_(fd),
237
0
      binding_data_(binding_data) {
238
0
  MakeWeak();
239
0
  StreamBase::AttachToObject(GetObject());
240
0
}
241
242
FileHandle* FileHandle::New(BindingData* binding_data,
243
                            int fd,
244
                            Local<Object> obj,
245
                            std::optional<int64_t> maybeOffset,
246
0
                            std::optional<int64_t> maybeLength) {
247
0
  Environment* env = binding_data->env();
248
0
  if (obj.IsEmpty() && !env->fd_constructor_template()
249
0
                            ->NewInstance(env->context())
250
0
                            .ToLocal(&obj)) {
251
0
    return nullptr;
252
0
  }
253
0
  auto handle = new FileHandle(binding_data, obj, fd);
254
0
  if (maybeOffset.has_value()) handle->read_offset_ = maybeOffset.value();
255
0
  if (maybeLength.has_value()) handle->read_length_ = maybeLength.value();
256
0
  return handle;
257
0
}
258
259
0
void FileHandle::New(const FunctionCallbackInfo<Value>& args) {
260
0
  CHECK(args.IsConstructCall());
261
0
  CHECK(args[0]->IsInt32());
262
0
  Realm* realm = Realm::GetCurrent(args);
263
0
  BindingData* binding_data = realm->GetBindingData<BindingData>();
264
265
0
  std::optional<int64_t> maybeOffset = std::nullopt;
266
0
  std::optional<int64_t> maybeLength = std::nullopt;
267
0
  if (args[1]->IsNumber())
268
0
    maybeOffset = args[1]->IntegerValue(realm->context()).FromJust();
269
0
  if (args[2]->IsNumber())
270
0
    maybeLength = args[2]->IntegerValue(realm->context()).FromJust();
271
272
0
  FileHandle::New(binding_data,
273
0
                  args[0].As<Int32>()->Value(),
274
0
                  args.This(),
275
0
                  maybeOffset,
276
0
                  maybeLength);
277
0
}
278
279
0
FileHandle::~FileHandle() {
280
0
  CHECK(!closing_);  // We should not be deleting while explicitly closing!
281
0
  Close();           // Close synchronously and emit warning
282
0
  CHECK(closed_);    // We have to be closed at the point
283
0
}
284
285
int FileHandle::DoWrite(WriteWrap* w,
286
                        uv_buf_t* bufs,
287
                        size_t count,
288
0
                        uv_stream_t* send_handle) {
289
0
  return UV_ENOSYS;  // Not implemented (yet).
290
0
}
291
292
0
void FileHandle::MemoryInfo(MemoryTracker* tracker) const {
293
0
  tracker->TrackField("current_read", current_read_);
294
0
}
295
296
0
BaseObject::TransferMode FileHandle::GetTransferMode() const {
297
0
  return reading_ || closing_ || closed_
298
0
             ? TransferMode::kDisallowCloneAndTransfer
299
0
             : TransferMode::kTransferable;
300
0
}
301
302
0
std::unique_ptr<worker::TransferData> FileHandle::TransferForMessaging() {
303
0
  CHECK_NE(GetTransferMode(), TransferMode::kDisallowCloneAndTransfer);
304
0
  auto ret = std::make_unique<TransferData>(fd_);
305
0
  closed_ = true;
306
0
  return ret;
307
0
}
308
309
0
FileHandle::TransferData::TransferData(int fd) : fd_(fd) {}
310
311
0
FileHandle::TransferData::~TransferData() {
312
0
  if (fd_ > 0) {
313
0
    uv_fs_t close_req;
314
0
    CHECK_NE(fd_, -1);
315
0
    FS_SYNC_TRACE_BEGIN(close);
316
0
    CHECK_EQ(0, uv_fs_close(nullptr, &close_req, fd_, nullptr));
317
0
    FS_SYNC_TRACE_END(close);
318
0
    uv_fs_req_cleanup(&close_req);
319
0
  }
320
0
}
321
322
BaseObjectPtr<BaseObject> FileHandle::TransferData::Deserialize(
323
    Environment* env,
324
    v8::Local<v8::Context> context,
325
0
    std::unique_ptr<worker::TransferData> self) {
326
0
  BindingData* bd = Realm::GetBindingData<BindingData>(context);
327
0
  if (bd == nullptr) return {};
328
329
0
  int fd = fd_;
330
0
  fd_ = -1;
331
0
  return BaseObjectPtr<BaseObject> { FileHandle::New(bd, fd) };
332
0
}
333
334
// Close the file descriptor if it hasn't already been closed. A process
335
// warning will be emitted using a SetImmediate to avoid calling back to
336
// JS during GC. If closing the fd fails at this point, a fatal exception
337
// will crash the process immediately.
338
0
inline void FileHandle::Close() {
339
0
  if (closed_ || closing_) return;
340
0
  uv_fs_t req;
341
0
  CHECK_NE(fd_, -1);
342
0
  FS_SYNC_TRACE_BEGIN(close);
343
0
  int ret = uv_fs_close(env()->event_loop(), &req, fd_, nullptr);
344
0
  FS_SYNC_TRACE_END(close);
345
0
  uv_fs_req_cleanup(&req);
346
347
0
  struct err_detail { int ret; int fd; };
348
349
0
  err_detail detail { ret, fd_ };
350
351
0
  AfterClose();
352
353
0
  if (ret < 0) {
354
    // Do not unref this
355
0
    env()->SetImmediate([detail](Environment* env) {
356
0
      char msg[70];
357
0
      snprintf(msg, arraysize(msg),
358
0
              "Closing file descriptor %d on garbage collection failed",
359
0
              detail.fd);
360
      // This exception will end up being fatal for the process because
361
      // it is being thrown from within the SetImmediate handler and
362
      // there is no JS stack to bubble it to. In other words, tearing
363
      // down the process is the only reasonable thing we can do here.
364
0
      HandleScope handle_scope(env->isolate());
365
0
      env->ThrowUVException(detail.ret, "close", msg);
366
0
    });
367
0
    return;
368
0
  }
369
370
  // If the close was successful, we still want to emit a process warning
371
  // to notify that the file descriptor was gc'd. We want to be noisy about
372
  // this because not explicitly closing the FileHandle is a bug.
373
374
0
  env()->SetImmediate([detail](Environment* env) {
375
0
    ProcessEmitWarning(env,
376
0
                       "Closing file descriptor %d on garbage collection",
377
0
                       detail.fd);
378
0
    if (env->filehandle_close_warning()) {
379
0
      env->set_filehandle_close_warning(false);
380
0
      USE(ProcessEmitDeprecationWarning(
381
0
          env,
382
0
          "Closing a FileHandle object on garbage collection is deprecated. "
383
0
          "Please close FileHandle objects explicitly using "
384
0
          "FileHandle.prototype.close(). In the future, an error will be "
385
0
          "thrown if a file descriptor is closed during garbage collection.",
386
0
          "DEP0137"));
387
0
    }
388
0
  }, CallbackFlags::kUnrefed);
389
0
}
390
391
0
void FileHandle::CloseReq::Resolve() {
392
0
  Isolate* isolate = env()->isolate();
393
0
  HandleScope scope(isolate);
394
0
  Context::Scope context_scope(env()->context());
395
0
  InternalCallbackScope callback_scope(this);
396
0
  Local<Promise> promise = promise_.Get(isolate);
397
0
  Local<Promise::Resolver> resolver = promise.As<Promise::Resolver>();
398
0
  resolver->Resolve(env()->context(), Undefined(isolate)).Check();
399
0
}
400
401
0
void FileHandle::CloseReq::Reject(Local<Value> reason) {
402
0
  Isolate* isolate = env()->isolate();
403
0
  HandleScope scope(isolate);
404
0
  Context::Scope context_scope(env()->context());
405
0
  InternalCallbackScope callback_scope(this);
406
0
  Local<Promise> promise = promise_.Get(isolate);
407
0
  Local<Promise::Resolver> resolver = promise.As<Promise::Resolver>();
408
0
  resolver->Reject(env()->context(), reason).Check();
409
0
}
410
411
0
FileHandle* FileHandle::CloseReq::file_handle() {
412
0
  Isolate* isolate = env()->isolate();
413
0
  HandleScope scope(isolate);
414
0
  Local<Value> val = ref_.Get(isolate);
415
0
  Local<Object> obj = val.As<Object>();
416
0
  return Unwrap<FileHandle>(obj);
417
0
}
418
419
FileHandle::CloseReq::CloseReq(Environment* env,
420
                               Local<Object> obj,
421
                               Local<Promise> promise,
422
                               Local<Value> ref)
423
0
  : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) {
424
0
  promise_.Reset(env->isolate(), promise);
425
0
  ref_.Reset(env->isolate(), ref);
426
0
}
427
428
0
FileHandle::CloseReq::~CloseReq() {
429
0
  uv_fs_req_cleanup(req());
430
0
  promise_.Reset();
431
0
  ref_.Reset();
432
0
}
433
434
0
void FileHandle::CloseReq::MemoryInfo(MemoryTracker* tracker) const {
435
0
  tracker->TrackField("promise", promise_);
436
0
  tracker->TrackField("ref", ref_);
437
0
}
438
439
440
441
// Closes this FileHandle asynchronously and returns a Promise that will be
442
// resolved when the callback is invoked, or rejects with a UVException if
443
// there was a problem closing the fd. This is the preferred mechanism for
444
// closing the FD object even tho the object will attempt to close
445
// automatically on gc.
446
0
MaybeLocal<Promise> FileHandle::ClosePromise() {
447
0
  Isolate* isolate = env()->isolate();
448
0
  EscapableHandleScope scope(isolate);
449
0
  Local<Context> context = env()->context();
450
451
0
  Local<Value> close_resolver =
452
0
      object()->GetInternalField(FileHandle::kClosingPromiseSlot).As<Value>();
453
0
  if (close_resolver->IsPromise()) {
454
0
    return close_resolver.As<Promise>();
455
0
  }
456
457
0
  CHECK(!closed_);
458
0
  CHECK(!closing_);
459
0
  CHECK(!reading_);
460
461
0
  auto maybe_resolver = Promise::Resolver::New(context);
462
0
  CHECK(!maybe_resolver.IsEmpty());
463
0
  Local<Promise::Resolver> resolver = maybe_resolver.ToLocalChecked();
464
0
  Local<Promise> promise = resolver.As<Promise>();
465
466
0
  Local<Object> close_req_obj;
467
0
  if (!env()->fdclose_constructor_template()
468
0
          ->NewInstance(env()->context()).ToLocal(&close_req_obj)) {
469
0
    return MaybeLocal<Promise>();
470
0
  }
471
0
  closing_ = true;
472
0
  object()->SetInternalField(FileHandle::kClosingPromiseSlot, promise);
473
474
0
  CloseReq* req = new CloseReq(env(), close_req_obj, promise, object());
475
0
  auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) {
476
0
    CloseReq* req_wrap = CloseReq::from_req(req);
477
0
    FS_ASYNC_TRACE_END1(
478
0
        req->fs_type, req_wrap, "result", static_cast<int>(req->result))
479
0
    BaseObjectPtr<CloseReq> close(req_wrap);
480
0
    CHECK(close);
481
0
    close->file_handle()->AfterClose();
482
0
    if (!close->env()->can_call_into_js()) return;
483
0
    Isolate* isolate = close->env()->isolate();
484
0
    if (req->result < 0) {
485
0
      HandleScope handle_scope(isolate);
486
0
      close->Reject(
487
0
          UVException(isolate, static_cast<int>(req->result), "close"));
488
0
    } else {
489
0
      close->Resolve();
490
0
    }
491
0
  }};
492
0
  CHECK_NE(fd_, -1);
493
0
  FS_ASYNC_TRACE_BEGIN0(UV_FS_CLOSE, req)
494
0
  int ret = req->Dispatch(uv_fs_close, fd_, AfterClose);
495
0
  if (ret < 0) {
496
0
    req->Reject(UVException(isolate, ret, "close"));
497
0
    delete req;
498
0
  }
499
500
0
  return scope.Escape(promise);
501
0
}
502
503
0
void FileHandle::Close(const FunctionCallbackInfo<Value>& args) {
504
0
  FileHandle* fd;
505
0
  ASSIGN_OR_RETURN_UNWRAP(&fd, args.Holder());
506
0
  Local<Promise> ret;
507
0
  if (!fd->ClosePromise().ToLocal(&ret)) return;
508
0
  args.GetReturnValue().Set(ret);
509
0
}
510
511
512
0
void FileHandle::ReleaseFD(const FunctionCallbackInfo<Value>& args) {
513
0
  FileHandle* fd;
514
0
  ASSIGN_OR_RETURN_UNWRAP(&fd, args.Holder());
515
0
  fd->Release();
516
0
}
517
518
0
int FileHandle::Release() {
519
0
  int fd = GetFD();
520
  // Just pretend that Close was called and we're all done.
521
0
  AfterClose();
522
0
  return fd;
523
0
}
524
525
0
void FileHandle::AfterClose() {
526
0
  closing_ = false;
527
0
  closed_ = true;
528
0
  fd_ = -1;
529
0
  if (reading_ && !persistent().IsEmpty())
530
0
    EmitRead(UV_EOF);
531
0
}
532
533
0
void FileHandleReadWrap::MemoryInfo(MemoryTracker* tracker) const {
534
0
  tracker->TrackField("buffer", buffer_);
535
0
  tracker->TrackField("file_handle", this->file_handle_);
536
0
}
537
538
FileHandleReadWrap::FileHandleReadWrap(FileHandle* handle, Local<Object> obj)
539
0
  : ReqWrap(handle->env(), obj, AsyncWrap::PROVIDER_FSREQCALLBACK),
540
0
    file_handle_(handle) {}
541
542
0
int FileHandle::ReadStart() {
543
0
  if (!IsAlive() || IsClosing())
544
0
    return UV_EOF;
545
546
0
  reading_ = true;
547
548
0
  if (current_read_)
549
0
    return 0;
550
551
0
  BaseObjectPtr<FileHandleReadWrap> read_wrap;
552
553
0
  if (read_length_ == 0) {
554
0
    EmitRead(UV_EOF);
555
0
    return 0;
556
0
  }
557
558
0
  {
559
    // Create a new FileHandleReadWrap or re-use one.
560
    // Either way, we need these two scopes for AsyncReset() or otherwise
561
    // for creating the new instance.
562
0
    HandleScope handle_scope(env()->isolate());
563
0
    AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(this);
564
565
0
    auto& freelist = binding_data_->file_handle_read_wrap_freelist;
566
0
    if (freelist.size() > 0) {
567
0
      read_wrap = std::move(freelist.back());
568
0
      freelist.pop_back();
569
      // Use a fresh async resource.
570
      // Lifetime is ensured via AsyncWrap::resource_.
571
0
      Local<Object> resource = Object::New(env()->isolate());
572
0
      USE(resource->Set(
573
0
          env()->context(), env()->handle_string(), read_wrap->object()));
574
0
      read_wrap->AsyncReset(resource);
575
0
      read_wrap->file_handle_ = this;
576
0
    } else {
577
0
      Local<Object> wrap_obj;
578
0
      if (!env()
579
0
               ->filehandlereadwrap_template()
580
0
               ->NewInstance(env()->context())
581
0
               .ToLocal(&wrap_obj)) {
582
0
        return UV_EBUSY;
583
0
      }
584
0
      read_wrap = MakeDetachedBaseObject<FileHandleReadWrap>(this, wrap_obj);
585
0
    }
586
0
  }
587
0
  int64_t recommended_read = 65536;
588
0
  if (read_length_ >= 0 && read_length_ <= recommended_read)
589
0
    recommended_read = read_length_;
590
591
0
  read_wrap->buffer_ = EmitAlloc(recommended_read);
592
593
0
  current_read_ = std::move(read_wrap);
594
0
  FS_ASYNC_TRACE_BEGIN0(UV_FS_READ, current_read_.get())
595
0
  current_read_->Dispatch(uv_fs_read,
596
0
                          fd_,
597
0
                          &current_read_->buffer_,
598
0
                          1,
599
0
                          read_offset_,
600
0
                          uv_fs_callback_t{[](uv_fs_t* req) {
601
0
    FileHandle* handle;
602
0
    {
603
0
      FileHandleReadWrap* req_wrap = FileHandleReadWrap::from_req(req);
604
0
      FS_ASYNC_TRACE_END1(
605
0
          req->fs_type, req_wrap, "result", static_cast<int>(req->result))
606
0
      handle = req_wrap->file_handle_;
607
0
      CHECK_EQ(handle->current_read_.get(), req_wrap);
608
0
    }
609
610
    // ReadStart() checks whether current_read_ is set to determine whether
611
    // a read is in progress. Moving it into a local variable makes sure that
612
    // the ReadStart() call below doesn't think we're still actively reading.
613
0
    BaseObjectPtr<FileHandleReadWrap> read_wrap =
614
0
        std::move(handle->current_read_);
615
616
0
    ssize_t result = req->result;
617
0
    uv_buf_t buffer = read_wrap->buffer_;
618
619
0
    uv_fs_req_cleanup(req);
620
621
    // Push the read wrap back to the freelist, or let it be destroyed
622
    // once we’re exiting the current scope.
623
0
    constexpr size_t kWantedFreelistFill = 100;
624
0
    auto& freelist = handle->binding_data_->file_handle_read_wrap_freelist;
625
0
    if (freelist.size() < kWantedFreelistFill) {
626
0
      read_wrap->Reset();
627
0
      freelist.emplace_back(std::move(read_wrap));
628
0
    }
629
630
0
    if (result >= 0) {
631
      // Read at most as many bytes as we originally planned to.
632
0
      if (handle->read_length_ >= 0 && handle->read_length_ < result)
633
0
        result = handle->read_length_;
634
635
      // If we read data and we have an expected length, decrease it by
636
      // how much we have read.
637
0
      if (handle->read_length_ >= 0)
638
0
        handle->read_length_ -= result;
639
640
      // If we have an offset, increase it by how much we have read.
641
0
      if (handle->read_offset_ >= 0)
642
0
        handle->read_offset_ += result;
643
0
    }
644
645
    // Reading 0 bytes from a file always means EOF, or that we reached
646
    // the end of the requested range.
647
0
    if (result == 0)
648
0
      result = UV_EOF;
649
650
0
    handle->EmitRead(result, buffer);
651
652
    // Start over, if EmitRead() didn’t tell us to stop.
653
0
    if (handle->reading_)
654
0
      handle->ReadStart();
655
0
  }});
656
657
0
  return 0;
658
0
}
659
660
0
int FileHandle::ReadStop() {
661
0
  reading_ = false;
662
0
  return 0;
663
0
}
664
665
typedef SimpleShutdownWrap<ReqWrap<uv_fs_t>> FileHandleCloseWrap;
666
667
0
ShutdownWrap* FileHandle::CreateShutdownWrap(Local<Object> object) {
668
0
  return new FileHandleCloseWrap(this, object);
669
0
}
670
671
0
int FileHandle::DoShutdown(ShutdownWrap* req_wrap) {
672
0
  if (closing_ || closed_) {
673
0
    req_wrap->Done(0);
674
0
    return 1;
675
0
  }
676
0
  FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(req_wrap);
677
0
  closing_ = true;
678
0
  CHECK_NE(fd_, -1);
679
0
  FS_ASYNC_TRACE_BEGIN0(UV_FS_CLOSE, wrap)
680
0
  wrap->Dispatch(uv_fs_close, fd_, uv_fs_callback_t{[](uv_fs_t* req) {
681
0
    FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(
682
0
        FileHandleCloseWrap::from_req(req));
683
0
    FS_ASYNC_TRACE_END1(
684
0
        req->fs_type, wrap, "result", static_cast<int>(req->result))
685
0
    FileHandle* handle = static_cast<FileHandle*>(wrap->stream());
686
0
    handle->AfterClose();
687
688
0
    int result = static_cast<int>(req->result);
689
0
    uv_fs_req_cleanup(req);
690
0
    wrap->Done(result);
691
0
  }});
692
693
0
  return 0;
694
0
}
695
696
697
0
void FSReqCallback::Reject(Local<Value> reject) {
698
0
  MakeCallback(env()->oncomplete_string(), 1, &reject);
699
0
}
700
701
0
void FSReqCallback::ResolveStat(const uv_stat_t* stat) {
702
0
  Resolve(FillGlobalStatsArray(binding_data(), use_bigint(), stat));
703
0
}
704
705
0
void FSReqCallback::ResolveStatFs(const uv_statfs_t* stat) {
706
0
  Resolve(FillGlobalStatFsArray(binding_data(), use_bigint(), stat));
707
0
}
708
709
0
void FSReqCallback::Resolve(Local<Value> value) {
710
0
  Local<Value> argv[2] {
711
0
    Null(env()->isolate()),
712
0
    value
713
0
  };
714
0
  MakeCallback(env()->oncomplete_string(),
715
0
               value->IsUndefined() ? 1 : arraysize(argv),
716
0
               argv);
717
0
}
718
719
3.88k
void FSReqCallback::SetReturnValue(const FunctionCallbackInfo<Value>& args) {
720
3.88k
  args.GetReturnValue().SetUndefined();
721
3.88k
}
722
723
3.88k
void NewFSReqCallback(const FunctionCallbackInfo<Value>& args) {
724
3.88k
  CHECK(args.IsConstructCall());
725
3.88k
  BindingData* binding_data = Realm::GetBindingData<BindingData>(args);
726
3.88k
  new FSReqCallback(binding_data, args.This(), args[0]->IsTrue());
727
3.88k
}
728
729
FSReqAfterScope::FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req)
730
3.88k
    : wrap_(wrap),
731
3.88k
      req_(req),
732
3.88k
      handle_scope_(wrap->env()->isolate()),
733
3.88k
      context_scope_(wrap->env()->context()) {
734
3.88k
  CHECK_EQ(wrap_->req(), req);
735
3.88k
}
736
737
3.88k
FSReqAfterScope::~FSReqAfterScope() {
738
3.88k
  Clear();
739
3.88k
}
740
741
3.88k
void FSReqAfterScope::Clear() {
742
3.88k
  if (!wrap_) return;
743
744
3.88k
  uv_fs_req_cleanup(wrap_->req());
745
3.88k
  wrap_->Detach();
746
3.88k
  wrap_.reset();
747
3.88k
}
748
749
// TODO(joyeecheung): create a normal context object, and
750
// construct the actual errors in the JS land using the context.
751
// The context should include fds for some fs APIs, currently they are
752
// missing in the error messages. The path, dest, syscall, fd, .etc
753
// can be put into the context before the binding is even invoked,
754
// the only information that has to come from the C++ layer is the
755
// error number (and possibly the syscall for abstraction),
756
// which is also why the errors should have been constructed
757
// in JS for more flexibility.
758
0
void FSReqAfterScope::Reject(uv_fs_t* req) {
759
0
  BaseObjectPtr<FSReqBase> wrap { wrap_ };
760
0
  Local<Value> exception = UVException(wrap_->env()->isolate(),
761
0
                                       static_cast<int>(req->result),
762
0
                                       wrap_->syscall(),
763
0
                                       nullptr,
764
0
                                       req->path,
765
0
                                       wrap_->data());
766
0
  Clear();
767
0
  wrap->Reject(exception);
768
0
}
769
770
3.88k
bool FSReqAfterScope::Proceed() {
771
3.88k
  if (!wrap_->env()->can_call_into_js()) {
772
3.88k
    return false;
773
3.88k
  }
774
775
0
  if (req_->result < 0) {
776
0
    Reject(req_);
777
0
    return false;
778
0
  }
779
0
  return true;
780
0
}
781
782
0
void AfterNoArgs(uv_fs_t* req) {
783
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
784
0
  FSReqAfterScope after(req_wrap, req);
785
0
  FS_ASYNC_TRACE_END1(
786
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
787
0
  if (after.Proceed())
788
0
    req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
789
0
}
790
791
755
void AfterStat(uv_fs_t* req) {
792
755
  FSReqBase* req_wrap = FSReqBase::from_req(req);
793
755
  FSReqAfterScope after(req_wrap, req);
794
755
  FS_ASYNC_TRACE_END1(
795
755
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
796
755
  if (after.Proceed()) {
797
0
    req_wrap->ResolveStat(&req->statbuf);
798
0
  }
799
755
}
800
801
0
void AfterStatFs(uv_fs_t* req) {
802
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
803
0
  FSReqAfterScope after(req_wrap, req);
804
0
  FS_ASYNC_TRACE_END1(
805
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
806
0
  if (after.Proceed()) {
807
0
    req_wrap->ResolveStatFs(static_cast<uv_statfs_t*>(req->ptr));
808
0
  }
809
0
}
810
811
3.13k
void AfterInteger(uv_fs_t* req) {
812
3.13k
  FSReqBase* req_wrap = FSReqBase::from_req(req);
813
3.13k
  FSReqAfterScope after(req_wrap, req);
814
3.13k
  FS_ASYNC_TRACE_END1(
815
3.13k
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
816
3.13k
  int result = static_cast<int>(req->result);
817
3.13k
  if (result >= 0 && req_wrap->is_plain_open())
818
1.30k
    req_wrap->env()->AddUnmanagedFd(result);
819
820
3.13k
  if (after.Proceed())
821
0
    req_wrap->Resolve(Integer::New(req_wrap->env()->isolate(), result));
822
3.13k
}
823
824
0
void AfterOpenFileHandle(uv_fs_t* req) {
825
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
826
0
  FSReqAfterScope after(req_wrap, req);
827
0
  FS_ASYNC_TRACE_END1(
828
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
829
0
  if (after.Proceed()) {
830
0
    FileHandle* fd = FileHandle::New(req_wrap->binding_data(),
831
0
                                     static_cast<int>(req->result));
832
0
    if (fd == nullptr) return;
833
0
    req_wrap->Resolve(fd->object());
834
0
  }
835
0
}
836
837
0
void AfterMkdirp(uv_fs_t* req) {
838
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
839
0
  FSReqAfterScope after(req_wrap, req);
840
0
  FS_ASYNC_TRACE_END1(
841
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
842
0
  if (after.Proceed()) {
843
0
    std::string first_path(req_wrap->continuation_data()->first_path());
844
0
    if (first_path.empty())
845
0
      return req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
846
0
    node::url::FromNamespacedPath(&first_path);
847
0
    Local<Value> path;
848
0
    Local<Value> error;
849
0
    if (!StringBytes::Encode(req_wrap->env()->isolate(), first_path.c_str(),
850
0
                             req_wrap->encoding(),
851
0
                             &error).ToLocal(&path)) {
852
0
      return req_wrap->Reject(error);
853
0
    }
854
0
    return req_wrap->Resolve(path);
855
0
  }
856
0
}
857
858
0
void AfterStringPath(uv_fs_t* req) {
859
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
860
0
  FSReqAfterScope after(req_wrap, req);
861
0
  FS_ASYNC_TRACE_END1(
862
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
863
0
  MaybeLocal<Value> link;
864
0
  Local<Value> error;
865
866
0
  if (after.Proceed()) {
867
0
    link = StringBytes::Encode(req_wrap->env()->isolate(),
868
0
                               req->path,
869
0
                               req_wrap->encoding(),
870
0
                               &error);
871
0
    if (link.IsEmpty())
872
0
      req_wrap->Reject(error);
873
0
    else
874
0
      req_wrap->Resolve(link.ToLocalChecked());
875
0
  }
876
0
}
877
878
0
void AfterStringPtr(uv_fs_t* req) {
879
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
880
0
  FSReqAfterScope after(req_wrap, req);
881
0
  FS_ASYNC_TRACE_END1(
882
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
883
0
  MaybeLocal<Value> link;
884
0
  Local<Value> error;
885
886
0
  if (after.Proceed()) {
887
0
    link = StringBytes::Encode(req_wrap->env()->isolate(),
888
0
                               static_cast<const char*>(req->ptr),
889
0
                               req_wrap->encoding(),
890
0
                               &error);
891
0
    if (link.IsEmpty())
892
0
      req_wrap->Reject(error);
893
0
    else
894
0
      req_wrap->Resolve(link.ToLocalChecked());
895
0
  }
896
0
}
897
898
0
void AfterScanDir(uv_fs_t* req) {
899
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
900
0
  FSReqAfterScope after(req_wrap, req);
901
0
  FS_ASYNC_TRACE_END1(
902
0
      req->fs_type, req_wrap, "result", static_cast<int>(req->result))
903
0
  if (!after.Proceed()) {
904
0
    return;
905
0
  }
906
907
0
  Environment* env = req_wrap->env();
908
0
  Isolate* isolate = env->isolate();
909
0
  Local<Value> error;
910
0
  int r;
911
912
0
  std::vector<Local<Value>> name_v;
913
0
  std::vector<Local<Value>> type_v;
914
915
0
  const bool with_file_types = req_wrap->with_file_types();
916
917
0
  for (;;) {
918
0
    uv_dirent_t ent;
919
920
0
    r = uv_fs_scandir_next(req, &ent);
921
0
    if (r == UV_EOF)
922
0
      break;
923
0
    if (r != 0) {
924
0
      return req_wrap->Reject(
925
0
          UVException(isolate, r, nullptr, req_wrap->syscall(), req->path));
926
0
    }
927
928
0
    Local<Value> filename;
929
0
    if (!StringBytes::Encode(isolate, ent.name, req_wrap->encoding(), &error)
930
0
             .ToLocal(&filename)) {
931
0
      return req_wrap->Reject(error);
932
0
    }
933
0
    name_v.push_back(filename);
934
935
0
    if (with_file_types) type_v.emplace_back(Integer::New(isolate, ent.type));
936
0
  }
937
938
0
  if (with_file_types) {
939
0
    Local<Value> result[] = {Array::New(isolate, name_v.data(), name_v.size()),
940
0
                             Array::New(isolate, type_v.data(), type_v.size())};
941
0
    req_wrap->Resolve(Array::New(isolate, result, arraysize(result)));
942
0
  } else {
943
0
    req_wrap->Resolve(Array::New(isolate, name_v.data(), name_v.size()));
944
0
  }
945
0
}
946
947
0
void Access(const FunctionCallbackInfo<Value>& args) {
948
0
  Environment* env = Environment::GetCurrent(args);
949
950
0
  Isolate* isolate = env->isolate();
951
0
  HandleScope scope(isolate);
952
953
0
  const int argc = args.Length();
954
0
  CHECK_GE(argc, 2);  // path, mode
955
956
0
  int mode;
957
0
  if (!GetValidFileMode(env, args[1], UV_FS_ACCESS).To(&mode)) {
958
0
    return;
959
0
  }
960
961
0
  BufferValue path(isolate, args[0]);
962
0
  CHECK_NOT_NULL(*path);
963
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
964
0
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
965
966
0
  if (argc > 2) {  // access(path, mode, req)
967
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
968
0
    CHECK_NOT_NULL(req_wrap_async);
969
0
    FS_ASYNC_TRACE_BEGIN1(
970
0
        UV_FS_ACCESS, req_wrap_async, "path", TRACE_STR_COPY(*path))
971
0
    AsyncCall(env, req_wrap_async, args, "access", UTF8, AfterNoArgs,
972
0
              uv_fs_access, *path, mode);
973
0
  } else {  // access(path, mode)
974
0
    FSReqWrapSync req_wrap_sync("access", *path);
975
0
    FS_SYNC_TRACE_BEGIN(access);
976
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_access, *path, mode);
977
0
    FS_SYNC_TRACE_END(access);
978
0
  }
979
0
}
980
981
1
void Close(const FunctionCallbackInfo<Value>& args) {
982
1
  Environment* env = Environment::GetCurrent(args);
983
984
1
  const int argc = args.Length();
985
1
  CHECK_GE(argc, 1);
986
987
1
  int fd;
988
1
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
989
0
    return;
990
0
  }
991
1
  env->RemoveUnmanagedFd(fd);
992
993
1
  if (argc > 1) {  // close(fd, req)
994
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 1);
995
0
    CHECK_NOT_NULL(req_wrap_async);
996
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_CLOSE, req_wrap_async)
997
0
    AsyncCall(env, req_wrap_async, args, "close", UTF8, AfterNoArgs,
998
0
              uv_fs_close, fd);
999
1
  } else {  // close(fd)
1000
1
    FSReqWrapSync req_wrap_sync("close");
1001
1
    FS_SYNC_TRACE_BEGIN(close);
1002
1
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_close, fd);
1003
1
    FS_SYNC_TRACE_END(close);
1004
1
  }
1005
1
}
1006
1007
0
static void ExistsSync(const FunctionCallbackInfo<Value>& args) {
1008
0
  Environment* env = Environment::GetCurrent(args);
1009
0
  Isolate* isolate = env->isolate();
1010
0
  CHECK_GE(args.Length(), 1);
1011
1012
0
  BufferValue path(isolate, args[0]);
1013
0
  CHECK_NOT_NULL(*path);
1014
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1015
0
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
1016
1017
0
  uv_fs_t req;
1018
0
  auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
1019
0
  FS_SYNC_TRACE_BEGIN(access);
1020
0
  int err = uv_fs_access(nullptr, &req, path.out(), 0, nullptr);
1021
0
  FS_SYNC_TRACE_END(access);
1022
1023
#ifdef _WIN32
1024
  // In case of an invalid symlink, `uv_fs_access` on win32
1025
  // will **not** return an error and is therefore not enough.
1026
  // Double check with `uv_fs_stat()`.
1027
  if (err == 0) {
1028
    FS_SYNC_TRACE_BEGIN(stat);
1029
    err = uv_fs_stat(nullptr, &req, path.out(), nullptr);
1030
    FS_SYNC_TRACE_END(stat);
1031
  }
1032
#endif  // _WIN32
1033
1034
0
  args.GetReturnValue().Set(err == 0);
1035
0
}
1036
1037
// Used to speed up module loading.  Returns 0 if the path refers to
1038
// a file, 1 when it's a directory or < 0 on error (usually -ENOENT.)
1039
// The speedup comes from not creating thousands of Stat and Error objects.
1040
5
static void InternalModuleStat(const FunctionCallbackInfo<Value>& args) {
1041
5
  Environment* env = Environment::GetCurrent(args);
1042
1043
5
  CHECK(args[0]->IsString());
1044
5
  node::Utf8Value path(env->isolate(), args[0]);
1045
5
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1046
5
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
1047
1048
5
  uv_fs_t req;
1049
5
  int rc = uv_fs_stat(env->event_loop(), &req, *path, nullptr);
1050
5
  if (rc == 0) {
1051
1
    const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
1052
1
    rc = !!(s->st_mode & S_IFDIR);
1053
1
  }
1054
5
  uv_fs_req_cleanup(&req);
1055
1056
5
  args.GetReturnValue().Set(rc);
1057
5
}
1058
1059
0
constexpr bool is_uv_error_except_no_entry(int result) {
1060
0
  return result < 0 && result != UV_ENOENT;
1061
0
}
1062
1063
0
static void Stat(const FunctionCallbackInfo<Value>& args) {
1064
0
  Realm* realm = Realm::GetCurrent(args);
1065
0
  BindingData* binding_data = realm->GetBindingData<BindingData>();
1066
0
  Environment* env = realm->env();
1067
1068
0
  const int argc = args.Length();
1069
0
  CHECK_GE(argc, 3);
1070
1071
0
  BufferValue path(realm->isolate(), args[0]);
1072
0
  CHECK_NOT_NULL(*path);
1073
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1074
0
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
1075
1076
0
  bool use_bigint = args[1]->IsTrue();
1077
0
  if (!args[2]->IsUndefined()) {  // stat(path, use_bigint, req)
1078
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1079
0
    CHECK_NOT_NULL(req_wrap_async);
1080
0
    FS_ASYNC_TRACE_BEGIN1(
1081
0
        UV_FS_STAT, req_wrap_async, "path", TRACE_STR_COPY(*path))
1082
0
    AsyncCall(env, req_wrap_async, args, "stat", UTF8, AfterStat,
1083
0
              uv_fs_stat, *path);
1084
0
  } else {  // stat(path, use_bigint, undefined, do_not_throw_if_no_entry)
1085
0
    bool do_not_throw_if_no_entry = args[3]->IsFalse();
1086
0
    FSReqWrapSync req_wrap_sync("stat", *path);
1087
0
    FS_SYNC_TRACE_BEGIN(stat);
1088
0
    int result;
1089
0
    if (do_not_throw_if_no_entry) {
1090
0
      result = SyncCallAndThrowIf(
1091
0
          is_uv_error_except_no_entry, env, &req_wrap_sync, uv_fs_stat, *path);
1092
0
    } else {
1093
0
      result = SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_stat, *path);
1094
0
    }
1095
0
    FS_SYNC_TRACE_END(stat);
1096
0
    if (is_uv_error(result)) {
1097
0
      return;
1098
0
    }
1099
0
    Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
1100
0
        static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
1101
0
    args.GetReturnValue().Set(arr);
1102
0
  }
1103
0
}
1104
1105
755
static void LStat(const FunctionCallbackInfo<Value>& args) {
1106
755
  Realm* realm = Realm::GetCurrent(args);
1107
755
  BindingData* binding_data = realm->GetBindingData<BindingData>();
1108
755
  Environment* env = realm->env();
1109
1110
755
  const int argc = args.Length();
1111
755
  CHECK_GE(argc, 3);
1112
1113
755
  BufferValue path(realm->isolate(), args[0]);
1114
755
  CHECK_NOT_NULL(*path);
1115
1116
755
  bool use_bigint = args[1]->IsTrue();
1117
755
  if (!args[2]->IsUndefined()) {  // lstat(path, use_bigint, req)
1118
755
    FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1119
755
    FS_ASYNC_TRACE_BEGIN1(
1120
755
        UV_FS_LSTAT, req_wrap_async, "path", TRACE_STR_COPY(*path))
1121
755
    AsyncCall(env, req_wrap_async, args, "lstat", UTF8, AfterStat,
1122
755
              uv_fs_lstat, *path);
1123
755
  } else {  // lstat(path, use_bigint, undefined, throw_if_no_entry)
1124
0
    bool do_not_throw_if_no_entry = args[3]->IsFalse();
1125
0
    FSReqWrapSync req_wrap_sync("lstat", *path);
1126
0
    FS_SYNC_TRACE_BEGIN(lstat);
1127
0
    int result;
1128
0
    if (do_not_throw_if_no_entry) {
1129
0
      result = SyncCallAndThrowIf(
1130
0
          is_uv_error_except_no_entry, env, &req_wrap_sync, uv_fs_lstat, *path);
1131
0
    } else {
1132
0
      result = SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_lstat, *path);
1133
0
    }
1134
0
    FS_SYNC_TRACE_END(lstat);
1135
0
    if (is_uv_error(result)) {
1136
0
      return;
1137
0
    }
1138
1139
0
    Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
1140
0
        static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
1141
0
    args.GetReturnValue().Set(arr);
1142
0
  }
1143
755
}
1144
1145
0
static void FStat(const FunctionCallbackInfo<Value>& args) {
1146
0
  Realm* realm = Realm::GetCurrent(args);
1147
0
  BindingData* binding_data = realm->GetBindingData<BindingData>();
1148
0
  Environment* env = realm->env();
1149
1150
0
  const int argc = args.Length();
1151
0
  CHECK_GE(argc, 2);
1152
1153
0
  int fd;
1154
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
1155
0
    return;
1156
0
  }
1157
1158
0
  bool use_bigint = args[1]->IsTrue();
1159
0
  if (!args[2]->IsUndefined()) {  // fstat(fd, use_bigint, req)
1160
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1161
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FSTAT, req_wrap_async)
1162
0
    AsyncCall(env, req_wrap_async, args, "fstat", UTF8, AfterStat,
1163
0
              uv_fs_fstat, fd);
1164
0
  } else {  // fstat(fd, use_bigint, undefined, do_not_throw_error)
1165
0
    bool do_not_throw_error = args[2]->IsTrue();
1166
0
    const auto should_throw = [do_not_throw_error](int result) {
1167
0
      return is_uv_error(result) && !do_not_throw_error;
1168
0
    };
1169
0
    FSReqWrapSync req_wrap_sync("fstat");
1170
0
    FS_SYNC_TRACE_BEGIN(fstat);
1171
0
    int err =
1172
0
        SyncCallAndThrowIf(should_throw, env, &req_wrap_sync, uv_fs_fstat, fd);
1173
0
    FS_SYNC_TRACE_END(fstat);
1174
0
    if (is_uv_error(err)) {
1175
0
      return;
1176
0
    }
1177
1178
0
    Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
1179
0
        static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
1180
0
    args.GetReturnValue().Set(arr);
1181
0
  }
1182
0
}
1183
1184
0
static void StatFs(const FunctionCallbackInfo<Value>& args) {
1185
0
  Realm* realm = Realm::GetCurrent(args);
1186
0
  BindingData* binding_data = realm->GetBindingData<BindingData>();
1187
0
  Environment* env = realm->env();
1188
1189
0
  const int argc = args.Length();
1190
0
  CHECK_GE(argc, 2);
1191
1192
0
  BufferValue path(realm->isolate(), args[0]);
1193
0
  CHECK_NOT_NULL(*path);
1194
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1195
0
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
1196
1197
0
  bool use_bigint = args[1]->IsTrue();
1198
0
  if (argc > 2) {  // statfs(path, use_bigint, req)
1199
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1200
0
    CHECK_NOT_NULL(req_wrap_async);
1201
0
    FS_ASYNC_TRACE_BEGIN1(
1202
0
        UV_FS_STATFS, req_wrap_async, "path", TRACE_STR_COPY(*path))
1203
0
    AsyncCall(env,
1204
0
              req_wrap_async,
1205
0
              args,
1206
0
              "statfs",
1207
0
              UTF8,
1208
0
              AfterStatFs,
1209
0
              uv_fs_statfs,
1210
0
              *path);
1211
0
  } else {  // statfs(path, use_bigint)
1212
0
    FSReqWrapSync req_wrap_sync("statfs", *path);
1213
0
    FS_SYNC_TRACE_BEGIN(statfs);
1214
0
    int result =
1215
0
        SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_statfs, *path);
1216
0
    FS_SYNC_TRACE_END(statfs);
1217
0
    if (is_uv_error(result)) {
1218
0
      return;
1219
0
    }
1220
1221
0
    Local<Value> arr = FillGlobalStatFsArray(
1222
0
        binding_data,
1223
0
        use_bigint,
1224
0
        static_cast<const uv_statfs_t*>(req_wrap_sync.req.ptr));
1225
0
    args.GetReturnValue().Set(arr);
1226
0
  }
1227
0
}
1228
1229
0
static void Symlink(const FunctionCallbackInfo<Value>& args) {
1230
0
  Environment* env = Environment::GetCurrent(args);
1231
0
  Isolate* isolate = env->isolate();
1232
1233
0
  const int argc = args.Length();
1234
0
  CHECK_GE(argc, 3);
1235
1236
0
  BufferValue target(isolate, args[0]);
1237
0
  CHECK_NOT_NULL(*target);
1238
0
  auto target_view = target.ToStringView();
1239
  // To avoid bypass the symlink target should be allowed to read and write
1240
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1241
0
      env, permission::PermissionScope::kFileSystemRead, target_view);
1242
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1243
0
      env, permission::PermissionScope::kFileSystemWrite, target_view);
1244
1245
0
  BufferValue path(isolate, args[1]);
1246
0
  CHECK_NOT_NULL(*path);
1247
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1248
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
1249
1250
0
  CHECK(args[2]->IsInt32());
1251
0
  int flags = args[2].As<Int32>()->Value();
1252
1253
0
  if (argc > 3) {  // symlink(target, path, flags, req)
1254
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1255
0
    FS_ASYNC_TRACE_BEGIN2(UV_FS_SYMLINK,
1256
0
                          req_wrap_async,
1257
0
                          "target",
1258
0
                          TRACE_STR_COPY(*target),
1259
0
                          "path",
1260
0
                          TRACE_STR_COPY(*path))
1261
0
    AsyncDestCall(env, req_wrap_async, args, "symlink", *path, path.length(),
1262
0
                  UTF8, AfterNoArgs, uv_fs_symlink, *target, *path, flags);
1263
0
  } else {  // symlink(target, path, flags, undefined, ctx)
1264
0
    FSReqWrapSync req_wrap_sync("symlink", *target, *path);
1265
0
    FS_SYNC_TRACE_BEGIN(symlink);
1266
0
    SyncCallAndThrowOnError(
1267
0
        env, &req_wrap_sync, uv_fs_symlink, *target, *path, flags);
1268
0
    FS_SYNC_TRACE_END(symlink);
1269
0
  }
1270
0
}
1271
1272
0
static void Link(const FunctionCallbackInfo<Value>& args) {
1273
0
  Environment* env = Environment::GetCurrent(args);
1274
0
  Isolate* isolate = env->isolate();
1275
1276
0
  const int argc = args.Length();
1277
0
  CHECK_GE(argc, 2);
1278
1279
0
  BufferValue src(isolate, args[0]);
1280
0
  CHECK_NOT_NULL(*src);
1281
1282
0
  const auto src_view = src.ToStringView();
1283
  // To avoid bypass the link target should be allowed to read and write
1284
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1285
0
      env, permission::PermissionScope::kFileSystemRead, src_view);
1286
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1287
0
      env, permission::PermissionScope::kFileSystemWrite, src_view);
1288
1289
0
  BufferValue dest(isolate, args[1]);
1290
0
  CHECK_NOT_NULL(*dest);
1291
0
  const auto dest_view = dest.ToStringView();
1292
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1293
0
      env, permission::PermissionScope::kFileSystemWrite, dest_view);
1294
1295
0
  if (argc > 2) {  // link(src, dest, req)
1296
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1297
0
    FS_ASYNC_TRACE_BEGIN2(UV_FS_LINK,
1298
0
                          req_wrap_async,
1299
0
                          "src",
1300
0
                          TRACE_STR_COPY(*src),
1301
0
                          "dest",
1302
0
                          TRACE_STR_COPY(*dest))
1303
0
    AsyncDestCall(env, req_wrap_async, args, "link", *dest, dest.length(), UTF8,
1304
0
                  AfterNoArgs, uv_fs_link, *src, *dest);
1305
0
  } else {  // link(src, dest)
1306
0
    FSReqWrapSync req_wrap_sync("link", *src, *dest);
1307
0
    FS_SYNC_TRACE_BEGIN(link);
1308
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_link, *src, *dest);
1309
0
    FS_SYNC_TRACE_END(link);
1310
0
  }
1311
0
}
1312
1313
0
static void ReadLink(const FunctionCallbackInfo<Value>& args) {
1314
0
  Environment* env = Environment::GetCurrent(args);
1315
0
  Isolate* isolate = env->isolate();
1316
1317
0
  const int argc = args.Length();
1318
0
  CHECK_GE(argc, 2);
1319
1320
0
  BufferValue path(isolate, args[0]);
1321
0
  CHECK_NOT_NULL(*path);
1322
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1323
0
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
1324
1325
0
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
1326
1327
0
  if (argc > 2) {  // readlink(path, encoding, req)
1328
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1329
0
    FS_ASYNC_TRACE_BEGIN1(
1330
0
        UV_FS_READLINK, req_wrap_async, "path", TRACE_STR_COPY(*path))
1331
0
    AsyncCall(env, req_wrap_async, args, "readlink", encoding, AfterStringPtr,
1332
0
              uv_fs_readlink, *path);
1333
0
  } else {  // readlink(path, encoding)
1334
0
    FSReqWrapSync req_wrap_sync("readlink", *path);
1335
0
    FS_SYNC_TRACE_BEGIN(readlink);
1336
0
    int err =
1337
0
        SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_readlink, *path);
1338
0
    FS_SYNC_TRACE_END(readlink);
1339
0
    if (err < 0) {
1340
0
      return;
1341
0
    }
1342
0
    const char* link_path = static_cast<const char*>(req_wrap_sync.req.ptr);
1343
1344
0
    Local<Value> error;
1345
0
    MaybeLocal<Value> rc = StringBytes::Encode(isolate,
1346
0
                                               link_path,
1347
0
                                               encoding,
1348
0
                                               &error);
1349
0
    if (rc.IsEmpty()) {
1350
0
      env->isolate()->ThrowException(error);
1351
0
      return;
1352
0
    }
1353
1354
0
    args.GetReturnValue().Set(rc.ToLocalChecked());
1355
0
  }
1356
0
}
1357
1358
0
static void Rename(const FunctionCallbackInfo<Value>& args) {
1359
0
  Environment* env = Environment::GetCurrent(args);
1360
0
  Isolate* isolate = env->isolate();
1361
1362
0
  const int argc = args.Length();
1363
0
  CHECK_GE(argc, 2);
1364
1365
0
  BufferValue old_path(isolate, args[0]);
1366
0
  CHECK_NOT_NULL(*old_path);
1367
0
  auto view_old_path = old_path.ToStringView();
1368
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1369
0
      env, permission::PermissionScope::kFileSystemRead, view_old_path);
1370
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1371
0
      env, permission::PermissionScope::kFileSystemWrite, view_old_path);
1372
1373
0
  BufferValue new_path(isolate, args[1]);
1374
0
  CHECK_NOT_NULL(*new_path);
1375
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1376
0
      env,
1377
0
      permission::PermissionScope::kFileSystemWrite,
1378
0
      new_path.ToStringView());
1379
1380
0
  if (argc > 2) {  // rename(old_path, new_path, req)
1381
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1382
0
    FS_ASYNC_TRACE_BEGIN2(UV_FS_RENAME,
1383
0
                          req_wrap_async,
1384
0
                          "old_path",
1385
0
                          TRACE_STR_COPY(*old_path),
1386
0
                          "new_path",
1387
0
                          TRACE_STR_COPY(*new_path))
1388
0
    AsyncDestCall(env, req_wrap_async, args, "rename", *new_path,
1389
0
                  new_path.length(), UTF8, AfterNoArgs, uv_fs_rename,
1390
0
                  *old_path, *new_path);
1391
0
  } else {  // rename(old_path, new_path)
1392
0
    FSReqWrapSync req_wrap_sync("rename", *old_path, *new_path);
1393
0
    FS_SYNC_TRACE_BEGIN(rename);
1394
0
    SyncCallAndThrowOnError(
1395
0
        env, &req_wrap_sync, uv_fs_rename, *old_path, *new_path);
1396
0
    FS_SYNC_TRACE_END(rename);
1397
0
  }
1398
0
}
1399
1400
0
static void FTruncate(const FunctionCallbackInfo<Value>& args) {
1401
0
  Environment* env = Environment::GetCurrent(args);
1402
1403
0
  const int argc = args.Length();
1404
0
  CHECK_GE(argc, 2);
1405
1406
0
  int fd;
1407
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
1408
0
    return;
1409
0
  }
1410
1411
0
  CHECK(IsSafeJsInt(args[1]));
1412
0
  const int64_t len = args[1].As<Integer>()->Value();
1413
1414
0
  if (argc > 2) {  // ftruncate(fd, len, req)
1415
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1416
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FTRUNCATE, req_wrap_async)
1417
0
    AsyncCall(env, req_wrap_async, args, "ftruncate", UTF8, AfterNoArgs,
1418
0
              uv_fs_ftruncate, fd, len);
1419
0
  } else {  // ftruncate(fd, len)
1420
0
    FSReqWrapSync req_wrap_sync("ftruncate");
1421
0
    FS_SYNC_TRACE_BEGIN(ftruncate);
1422
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_ftruncate, fd, len);
1423
0
    FS_SYNC_TRACE_END(ftruncate);
1424
0
  }
1425
0
}
1426
1427
0
static void Fdatasync(const FunctionCallbackInfo<Value>& args) {
1428
0
  Environment* env = Environment::GetCurrent(args);
1429
1430
0
  const int argc = args.Length();
1431
0
  CHECK_GE(argc, 1);
1432
1433
0
  int fd;
1434
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
1435
0
    return;
1436
0
  }
1437
1438
0
  if (argc > 1) {  // fdatasync(fd, req)
1439
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 1);
1440
0
    CHECK_NOT_NULL(req_wrap_async);
1441
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FDATASYNC, req_wrap_async)
1442
0
    AsyncCall(env, req_wrap_async, args, "fdatasync", UTF8, AfterNoArgs,
1443
0
              uv_fs_fdatasync, fd);
1444
0
  } else {  // fdatasync(fd)
1445
0
    FSReqWrapSync req_wrap_sync("fdatasync");
1446
0
    FS_SYNC_TRACE_BEGIN(fdatasync);
1447
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_fdatasync, fd);
1448
0
    FS_SYNC_TRACE_END(fdatasync);
1449
0
  }
1450
0
}
1451
1452
0
static void Fsync(const FunctionCallbackInfo<Value>& args) {
1453
0
  Environment* env = Environment::GetCurrent(args);
1454
1455
0
  const int argc = args.Length();
1456
0
  CHECK_GE(argc, 1);
1457
1458
0
  int fd;
1459
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
1460
0
    return;
1461
0
  }
1462
1463
0
  if (argc > 1) {
1464
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 1);
1465
0
    CHECK_NOT_NULL(req_wrap_async);
1466
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FSYNC, req_wrap_async)
1467
0
    AsyncCall(env, req_wrap_async, args, "fsync", UTF8, AfterNoArgs,
1468
0
              uv_fs_fsync, fd);
1469
0
  } else {
1470
0
    FSReqWrapSync req_wrap_sync("fsync");
1471
0
    FS_SYNC_TRACE_BEGIN(fsync);
1472
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_fsync, fd);
1473
0
    FS_SYNC_TRACE_END(fsync);
1474
0
  }
1475
0
}
1476
1477
0
static void Unlink(const FunctionCallbackInfo<Value>& args) {
1478
0
  Environment* env = Environment::GetCurrent(args);
1479
1480
0
  const int argc = args.Length();
1481
0
  CHECK_GE(argc, 1);
1482
1483
0
  BufferValue path(env->isolate(), args[0]);
1484
0
  CHECK_NOT_NULL(*path);
1485
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1486
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
1487
1488
0
  if (argc > 1) {  // unlink(path, req)
1489
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 1);
1490
0
    CHECK_NOT_NULL(req_wrap_async);
1491
0
    FS_ASYNC_TRACE_BEGIN1(
1492
0
        UV_FS_UNLINK, req_wrap_async, "path", TRACE_STR_COPY(*path))
1493
0
    AsyncCall(env, req_wrap_async, args, "unlink", UTF8, AfterNoArgs,
1494
0
              uv_fs_unlink, *path);
1495
0
  } else {  // unlink(path)
1496
0
    FSReqWrapSync req_wrap_sync("unlink", *path);
1497
0
    FS_SYNC_TRACE_BEGIN(unlink);
1498
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_unlink, *path);
1499
0
    FS_SYNC_TRACE_END(unlink);
1500
0
  }
1501
0
}
1502
1503
0
static void RMDir(const FunctionCallbackInfo<Value>& args) {
1504
0
  Environment* env = Environment::GetCurrent(args);
1505
1506
0
  const int argc = args.Length();
1507
0
  CHECK_GE(argc, 1);
1508
1509
0
  BufferValue path(env->isolate(), args[0]);
1510
0
  CHECK_NOT_NULL(*path);
1511
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1512
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
1513
1514
0
  if (argc > 1) {
1515
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 1);  // rmdir(path, req)
1516
0
    FS_ASYNC_TRACE_BEGIN1(
1517
0
        UV_FS_RMDIR, req_wrap_async, "path", TRACE_STR_COPY(*path))
1518
0
    AsyncCall(env, req_wrap_async, args, "rmdir", UTF8, AfterNoArgs,
1519
0
              uv_fs_rmdir, *path);
1520
0
  } else {  // rmdir(path)
1521
0
    FSReqWrapSync req_wrap_sync("rmdir", *path);
1522
0
    FS_SYNC_TRACE_BEGIN(rmdir);
1523
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_rmdir, *path);
1524
0
    FS_SYNC_TRACE_END(rmdir);
1525
0
  }
1526
0
}
1527
1528
int MKDirpSync(uv_loop_t* loop,
1529
               uv_fs_t* req,
1530
               const std::string& path,
1531
               int mode,
1532
0
               uv_fs_cb cb) {
1533
0
  FSReqWrapSync* req_wrap = ContainerOf(&FSReqWrapSync::req, req);
1534
1535
  // on the first iteration of algorithm, stash state information.
1536
0
  if (req_wrap->continuation_data() == nullptr) {
1537
0
    req_wrap->set_continuation_data(
1538
0
        std::make_unique<FSContinuationData>(req, mode, cb));
1539
0
    req_wrap->continuation_data()->PushPath(std::move(path));
1540
0
  }
1541
1542
0
  while (req_wrap->continuation_data()->paths().size() > 0) {
1543
0
    std::string next_path = req_wrap->continuation_data()->PopPath();
1544
0
    int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr);
1545
0
    while (true) {
1546
0
      switch (err) {
1547
        // Note: uv_fs_req_cleanup in terminal paths will be called by
1548
        // ~FSReqWrapSync():
1549
0
        case 0:
1550
0
          req_wrap->continuation_data()->MaybeSetFirstPath(next_path);
1551
0
          if (req_wrap->continuation_data()->paths().size() == 0) {
1552
0
            return 0;
1553
0
          }
1554
0
          break;
1555
0
        case UV_EACCES:
1556
0
        case UV_ENOSPC:
1557
0
        case UV_ENOTDIR:
1558
0
        case UV_EPERM: {
1559
0
          return err;
1560
0
        }
1561
0
        case UV_ENOENT: {
1562
0
          std::string dirname = next_path.substr(0,
1563
0
                                        next_path.find_last_of(kPathSeparator));
1564
0
          if (dirname != next_path) {
1565
0
            req_wrap->continuation_data()->PushPath(std::move(next_path));
1566
0
            req_wrap->continuation_data()->PushPath(std::move(dirname));
1567
0
          } else if (req_wrap->continuation_data()->paths().size() == 0) {
1568
0
            err = UV_EEXIST;
1569
0
            continue;
1570
0
          }
1571
0
          break;
1572
0
        }
1573
0
        default:
1574
0
          uv_fs_req_cleanup(req);
1575
0
          int orig_err = err;
1576
0
          err = uv_fs_stat(loop, req, next_path.c_str(), nullptr);
1577
0
          if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) {
1578
0
            uv_fs_req_cleanup(req);
1579
0
            if (orig_err == UV_EEXIST &&
1580
0
              req_wrap->continuation_data()->paths().size() > 0) {
1581
0
              return UV_ENOTDIR;
1582
0
            }
1583
0
            return UV_EEXIST;
1584
0
          }
1585
0
          if (err < 0) return err;
1586
0
          break;
1587
0
      }
1588
0
      break;
1589
0
    }
1590
0
    uv_fs_req_cleanup(req);
1591
0
  }
1592
1593
0
  return 0;
1594
0
}
1595
1596
int MKDirpAsync(uv_loop_t* loop,
1597
                uv_fs_t* req,
1598
                const char* path,
1599
                int mode,
1600
0
                uv_fs_cb cb) {
1601
0
  FSReqBase* req_wrap = FSReqBase::from_req(req);
1602
  // on the first iteration of algorithm, stash state information.
1603
0
  if (req_wrap->continuation_data() == nullptr) {
1604
0
    req_wrap->set_continuation_data(
1605
0
        std::make_unique<FSContinuationData>(req, mode, cb));
1606
0
    req_wrap->continuation_data()->PushPath(std::move(path));
1607
0
  }
1608
1609
  // on each iteration of algorithm, mkdir directory on top of stack.
1610
0
  std::string next_path = req_wrap->continuation_data()->PopPath();
1611
0
  int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode,
1612
0
                        uv_fs_callback_t{[](uv_fs_t* req) {
1613
0
    FSReqBase* req_wrap = FSReqBase::from_req(req);
1614
0
    Environment* env = req_wrap->env();
1615
0
    uv_loop_t* loop = env->event_loop();
1616
0
    std::string path = req->path;
1617
0
    int err = static_cast<int>(req->result);
1618
1619
0
    while (true) {
1620
0
      switch (err) {
1621
        // Note: uv_fs_req_cleanup in terminal paths will be called by
1622
        // FSReqAfterScope::~FSReqAfterScope()
1623
0
        case 0: {
1624
0
          if (req_wrap->continuation_data()->paths().size() == 0) {
1625
0
            req_wrap->continuation_data()->MaybeSetFirstPath(path);
1626
0
            req_wrap->continuation_data()->Done(0);
1627
0
          } else {
1628
0
            req_wrap->continuation_data()->MaybeSetFirstPath(path);
1629
0
            uv_fs_req_cleanup(req);
1630
0
            MKDirpAsync(loop, req, path.c_str(),
1631
0
                        req_wrap->continuation_data()->mode(), nullptr);
1632
0
          }
1633
0
          break;
1634
0
        }
1635
0
        case UV_EACCES:
1636
0
        case UV_ENOTDIR:
1637
0
        case UV_EPERM: {
1638
0
          req_wrap->continuation_data()->Done(err);
1639
0
          break;
1640
0
        }
1641
0
        case UV_ENOENT: {
1642
0
          std::string dirname = path.substr(0,
1643
0
                                            path.find_last_of(kPathSeparator));
1644
0
          if (dirname != path) {
1645
0
            req_wrap->continuation_data()->PushPath(path);
1646
0
            req_wrap->continuation_data()->PushPath(std::move(dirname));
1647
0
          } else if (req_wrap->continuation_data()->paths().size() == 0) {
1648
0
            err = UV_EEXIST;
1649
0
            continue;
1650
0
          }
1651
0
          uv_fs_req_cleanup(req);
1652
0
          MKDirpAsync(loop, req, path.c_str(),
1653
0
                      req_wrap->continuation_data()->mode(), nullptr);
1654
0
          break;
1655
0
        }
1656
0
        default:
1657
0
          uv_fs_req_cleanup(req);
1658
          // Stash err for use in the callback.
1659
0
          req->data = reinterpret_cast<void*>(static_cast<intptr_t>(err));
1660
0
          int err = uv_fs_stat(loop, req, path.c_str(),
1661
0
                               uv_fs_callback_t{[](uv_fs_t* req) {
1662
0
            FSReqBase* req_wrap = FSReqBase::from_req(req);
1663
0
            int err = static_cast<int>(req->result);
1664
0
            if (reinterpret_cast<intptr_t>(req->data) == UV_EEXIST &&
1665
0
                  req_wrap->continuation_data()->paths().size() > 0) {
1666
0
              if (err == 0 && S_ISDIR(req->statbuf.st_mode)) {
1667
0
                Environment* env = req_wrap->env();
1668
0
                uv_loop_t* loop = env->event_loop();
1669
0
                std::string path = req->path;
1670
0
                uv_fs_req_cleanup(req);
1671
0
                MKDirpAsync(loop, req, path.c_str(),
1672
0
                            req_wrap->continuation_data()->mode(), nullptr);
1673
0
                return;
1674
0
              }
1675
0
              err = UV_ENOTDIR;
1676
0
            }
1677
            // verify that the path pointed to is actually a directory.
1678
0
            if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST;
1679
0
            req_wrap->continuation_data()->Done(err);
1680
0
          }});
1681
0
          if (err < 0) req_wrap->continuation_data()->Done(err);
1682
0
          break;
1683
0
      }
1684
0
      break;
1685
0
    }
1686
0
  }});
1687
1688
0
  return err;
1689
0
}
1690
1691
0
static void MKDir(const FunctionCallbackInfo<Value>& args) {
1692
0
  Environment* env = Environment::GetCurrent(args);
1693
1694
0
  const int argc = args.Length();
1695
0
  CHECK_GE(argc, 3);
1696
1697
0
  BufferValue path(env->isolate(), args[0]);
1698
0
  CHECK_NOT_NULL(*path);
1699
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1700
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
1701
1702
0
  CHECK(args[1]->IsInt32());
1703
0
  const int mode = args[1].As<Int32>()->Value();
1704
1705
0
  CHECK(args[2]->IsBoolean());
1706
0
  bool mkdirp = args[2]->IsTrue();
1707
1708
0
  if (argc > 3) {  // mkdir(path, mode, recursive, req)
1709
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1710
0
    FS_ASYNC_TRACE_BEGIN1(
1711
0
        UV_FS_UNLINK, req_wrap_async, "path", TRACE_STR_COPY(*path))
1712
0
    AsyncCall(env, req_wrap_async, args, "mkdir", UTF8,
1713
0
              mkdirp ? AfterMkdirp : AfterNoArgs,
1714
0
              mkdirp ? MKDirpAsync : uv_fs_mkdir, *path, mode);
1715
0
  } else {  // mkdir(path, mode, recursive)
1716
0
    FSReqWrapSync req_wrap_sync("mkdir", *path);
1717
0
    FS_SYNC_TRACE_BEGIN(mkdir);
1718
0
    if (mkdirp) {
1719
0
      env->PrintSyncTrace();
1720
0
      int err = MKDirpSync(
1721
0
          env->event_loop(), &req_wrap_sync.req, *path, mode, nullptr);
1722
0
      if (is_uv_error(err)) {
1723
0
        env->ThrowUVException(err, "mkdir", nullptr, *path);
1724
0
        return;
1725
0
      }
1726
0
      if (!req_wrap_sync.continuation_data()->first_path().empty()) {
1727
0
        Local<Value> error;
1728
0
        std::string first_path(req_wrap_sync.continuation_data()->first_path());
1729
0
        node::url::FromNamespacedPath(&first_path);
1730
0
        MaybeLocal<Value> path = StringBytes::Encode(env->isolate(),
1731
0
                                                     first_path.c_str(),
1732
0
                                                     UTF8, &error);
1733
0
        if (path.IsEmpty()) {
1734
0
          env->isolate()->ThrowException(error);
1735
0
          return;
1736
0
        }
1737
0
        args.GetReturnValue().Set(path.ToLocalChecked());
1738
0
      }
1739
0
    } else {
1740
0
      SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_mkdir, *path, mode);
1741
0
    }
1742
0
    FS_SYNC_TRACE_END(mkdir);
1743
0
  }
1744
0
}
1745
1746
0
static void RealPath(const FunctionCallbackInfo<Value>& args) {
1747
0
  Environment* env = Environment::GetCurrent(args);
1748
0
  Isolate* isolate = env->isolate();
1749
1750
0
  const int argc = args.Length();
1751
0
  CHECK_GE(argc, 2);
1752
1753
0
  BufferValue path(isolate, args[0]);
1754
0
  CHECK_NOT_NULL(*path);
1755
1756
0
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
1757
1758
0
  if (argc > 2) {  // realpath(path, encoding, req)
1759
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
1760
0
    FS_ASYNC_TRACE_BEGIN1(
1761
0
        UV_FS_REALPATH, req_wrap_async, "path", TRACE_STR_COPY(*path))
1762
0
    AsyncCall(env, req_wrap_async, args, "realpath", encoding, AfterStringPtr,
1763
0
              uv_fs_realpath, *path);
1764
0
  } else {  // realpath(path, encoding, undefined, ctx)
1765
0
    FSReqWrapSync req_wrap_sync("realpath", *path);
1766
0
    FS_SYNC_TRACE_BEGIN(realpath);
1767
0
    int err =
1768
0
        SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_realpath, *path);
1769
0
    FS_SYNC_TRACE_END(realpath);
1770
0
    if (err < 0) {
1771
0
      return;
1772
0
    }
1773
1774
0
    const char* link_path = static_cast<const char*>(req_wrap_sync.req.ptr);
1775
1776
0
    Local<Value> error;
1777
0
    MaybeLocal<Value> rc = StringBytes::Encode(isolate,
1778
0
                                               link_path,
1779
0
                                               encoding,
1780
0
                                               &error);
1781
0
    if (rc.IsEmpty()) {
1782
0
      env->isolate()->ThrowException(error);
1783
0
      return;
1784
0
    }
1785
1786
0
    args.GetReturnValue().Set(rc.ToLocalChecked());
1787
0
  }
1788
0
}
1789
1790
0
static void ReadDir(const FunctionCallbackInfo<Value>& args) {
1791
0
  Environment* env = Environment::GetCurrent(args);
1792
0
  Isolate* isolate = env->isolate();
1793
1794
0
  const int argc = args.Length();
1795
0
  CHECK_GE(argc, 3);
1796
1797
0
  BufferValue path(isolate, args[0]);
1798
0
  CHECK_NOT_NULL(*path);
1799
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
1800
0
      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
1801
1802
0
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
1803
1804
0
  bool with_types = args[2]->IsTrue();
1805
1806
0
  if (argc > 3) {  // readdir(path, encoding, withTypes, req)
1807
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1808
0
    req_wrap_async->set_with_file_types(with_types);
1809
0
    FS_ASYNC_TRACE_BEGIN1(
1810
0
        UV_FS_SCANDIR, req_wrap_async, "path", TRACE_STR_COPY(*path))
1811
0
    AsyncCall(env,
1812
0
              req_wrap_async,
1813
0
              args,
1814
0
              "scandir",
1815
0
              encoding,
1816
0
              AfterScanDir,
1817
0
              uv_fs_scandir,
1818
0
              *path,
1819
0
              0 /*flags*/);
1820
0
  } else {  // readdir(path, encoding, withTypes)
1821
0
    FSReqWrapSync req_wrap_sync("scandir", *path);
1822
0
    FS_SYNC_TRACE_BEGIN(readdir);
1823
0
    int err = SyncCallAndThrowOnError(
1824
0
        env, &req_wrap_sync, uv_fs_scandir, *path, 0 /*flags*/);
1825
0
    FS_SYNC_TRACE_END(readdir);
1826
0
    if (is_uv_error(err)) {
1827
0
      return;
1828
0
    }
1829
1830
0
    int r;
1831
0
    std::vector<Local<Value>> name_v;
1832
0
    std::vector<Local<Value>> type_v;
1833
1834
0
    for (;;) {
1835
0
      uv_dirent_t ent;
1836
1837
0
      r = uv_fs_scandir_next(&(req_wrap_sync.req), &ent);
1838
0
      if (r == UV_EOF)
1839
0
        break;
1840
0
      if (is_uv_error(r)) {
1841
0
        env->ThrowUVException(r, "scandir", nullptr, *path);
1842
0
        return;
1843
0
      }
1844
1845
0
      Local<Value> error;
1846
0
      MaybeLocal<Value> filename = StringBytes::Encode(isolate,
1847
0
                                                       ent.name,
1848
0
                                                       encoding,
1849
0
                                                       &error);
1850
1851
0
      if (filename.IsEmpty()) {
1852
0
        isolate->ThrowException(error);
1853
0
        return;
1854
0
      }
1855
1856
0
      name_v.push_back(filename.ToLocalChecked());
1857
1858
0
      if (with_types) {
1859
0
        type_v.emplace_back(Integer::New(isolate, ent.type));
1860
0
      }
1861
0
    }
1862
1863
1864
0
    Local<Array> names = Array::New(isolate, name_v.data(), name_v.size());
1865
0
    if (with_types) {
1866
0
      Local<Value> result[] = {
1867
0
        names,
1868
0
        Array::New(isolate, type_v.data(), type_v.size())
1869
0
      };
1870
0
      args.GetReturnValue().Set(Array::New(isolate, result, arraysize(result)));
1871
0
    } else {
1872
0
      args.GetReturnValue().Set(names);
1873
0
    }
1874
0
  }
1875
0
}
1876
1877
static inline Maybe<void> CheckOpenPermissions(Environment* env,
1878
                                               const BufferValue& path,
1879
5.46k
                                               int flags) {
1880
  // These flags capture the intention of the open() call.
1881
5.46k
  const int rwflags = flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
1882
1883
  // These flags have write-like side effects even with O_RDONLY, at least on
1884
  // some operating systems. On Windows, for example, O_RDONLY | O_TEMPORARY
1885
  // can be used to delete a file. Bizarre.
1886
5.46k
  const int write_as_side_effect = flags & (UV_FS_O_APPEND | UV_FS_O_CREAT |
1887
5.46k
                                            UV_FS_O_TRUNC | UV_FS_O_TEMPORARY);
1888
1889
5.46k
  auto pathView = path.ToStringView();
1890
5.46k
  if (rwflags != UV_FS_O_WRONLY) {
1891
2.34k
    THROW_IF_INSUFFICIENT_PERMISSIONS(
1892
2.34k
        env,
1893
2.34k
        permission::PermissionScope::kFileSystemRead,
1894
2.34k
        pathView,
1895
2.34k
        Nothing<void>());
1896
2.34k
  }
1897
5.46k
  if (rwflags != UV_FS_O_RDONLY || write_as_side_effect) {
1898
3.88k
    THROW_IF_INSUFFICIENT_PERMISSIONS(
1899
3.88k
        env,
1900
3.88k
        permission::PermissionScope::kFileSystemWrite,
1901
3.88k
        pathView,
1902
3.88k
        Nothing<void>());
1903
3.88k
  }
1904
5.46k
  return JustVoid();
1905
5.46k
}
1906
1907
3.13k
static void Open(const FunctionCallbackInfo<Value>& args) {
1908
3.13k
  Environment* env = Environment::GetCurrent(args);
1909
1910
3.13k
  const int argc = args.Length();
1911
3.13k
  CHECK_GE(argc, 3);
1912
1913
3.13k
  BufferValue path(env->isolate(), args[0]);
1914
3.13k
  CHECK_NOT_NULL(*path);
1915
1916
3.13k
  CHECK(args[1]->IsInt32());
1917
3.13k
  const int flags = args[1].As<Int32>()->Value();
1918
1919
3.13k
  CHECK(args[2]->IsInt32());
1920
3.13k
  const int mode = args[2].As<Int32>()->Value();
1921
1922
3.13k
  if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
1923
1924
3.13k
  if (argc > 3) {  // open(path, flags, mode, req)
1925
3.13k
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1926
3.13k
    CHECK_NOT_NULL(req_wrap_async);
1927
3.13k
    req_wrap_async->set_is_plain_open(true);
1928
3.13k
    FS_ASYNC_TRACE_BEGIN1(
1929
3.13k
        UV_FS_OPEN, req_wrap_async, "path", TRACE_STR_COPY(*path))
1930
3.13k
    AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterInteger,
1931
3.13k
              uv_fs_open, *path, flags, mode);
1932
3.13k
  } else {  // open(path, flags, mode)
1933
1
    FSReqWrapSync req_wrap_sync("open", *path);
1934
1
    FS_SYNC_TRACE_BEGIN(open);
1935
1
    int result = SyncCallAndThrowOnError(
1936
1
        env, &req_wrap_sync, uv_fs_open, *path, flags, mode);
1937
1
    FS_SYNC_TRACE_END(open);
1938
1
    if (is_uv_error(result)) return;
1939
1
    env->AddUnmanagedFd(result);
1940
1
    args.GetReturnValue().Set(result);
1941
1
  }
1942
3.13k
}
1943
1944
0
static void OpenFileHandle(const FunctionCallbackInfo<Value>& args) {
1945
0
  Realm* realm = Realm::GetCurrent(args);
1946
0
  BindingData* binding_data = realm->GetBindingData<BindingData>();
1947
0
  Environment* env = realm->env();
1948
1949
0
  const int argc = args.Length();
1950
0
  CHECK_GE(argc, 3);
1951
1952
0
  BufferValue path(realm->isolate(), args[0]);
1953
0
  CHECK_NOT_NULL(*path);
1954
1955
0
  CHECK(args[1]->IsInt32());
1956
0
  const int flags = args[1].As<Int32>()->Value();
1957
1958
0
  CHECK(args[2]->IsInt32());
1959
0
  const int mode = args[2].As<Int32>()->Value();
1960
1961
0
  if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
1962
1963
0
  FSReqBase* req_wrap_async = GetReqWrap(args, 3);
1964
0
  if (req_wrap_async != nullptr) {  // openFileHandle(path, flags, mode, req)
1965
0
    FS_ASYNC_TRACE_BEGIN1(
1966
0
        UV_FS_OPEN, req_wrap_async, "path", TRACE_STR_COPY(*path))
1967
0
    AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterOpenFileHandle,
1968
0
              uv_fs_open, *path, flags, mode);
1969
0
  } else {  // openFileHandle(path, flags, mode, undefined, ctx)
1970
0
    CHECK_EQ(argc, 5);
1971
0
    FSReqWrapSync req_wrap_sync;
1972
0
    FS_SYNC_TRACE_BEGIN(open);
1973
0
    int result = SyncCall(env, args[4], &req_wrap_sync, "open",
1974
0
                          uv_fs_open, *path, flags, mode);
1975
0
    FS_SYNC_TRACE_END(open);
1976
0
    if (result < 0) {
1977
0
      return;  // syscall failed, no need to continue, error info is in ctx
1978
0
    }
1979
0
    FileHandle* fd = FileHandle::New(binding_data, result);
1980
0
    if (fd == nullptr) return;
1981
0
    args.GetReturnValue().Set(fd->object());
1982
0
  }
1983
0
}
1984
1985
0
static void CopyFile(const FunctionCallbackInfo<Value>& args) {
1986
0
  Environment* env = Environment::GetCurrent(args);
1987
0
  Isolate* isolate = env->isolate();
1988
1989
0
  const int argc = args.Length();
1990
0
  CHECK_GE(argc, 3);  // src, dest, flags
1991
1992
0
  int flags;
1993
0
  if (!GetValidFileMode(env, args[2], UV_FS_COPYFILE).To(&flags)) {
1994
0
    return;
1995
0
  }
1996
1997
0
  BufferValue src(isolate, args[0]);
1998
0
  CHECK_NOT_NULL(*src);
1999
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2000
0
      env, permission::PermissionScope::kFileSystemRead, src.ToStringView());
2001
2002
0
  BufferValue dest(isolate, args[1]);
2003
0
  CHECK_NOT_NULL(*dest);
2004
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2005
0
      env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());
2006
2007
0
  if (argc > 3) {  // copyFile(src, dest, flags, req)
2008
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2009
0
    FS_ASYNC_TRACE_BEGIN2(UV_FS_COPYFILE,
2010
0
                          req_wrap_async,
2011
0
                          "src",
2012
0
                          TRACE_STR_COPY(*src),
2013
0
                          "dest",
2014
0
                          TRACE_STR_COPY(*dest))
2015
0
    AsyncDestCall(env, req_wrap_async, args, "copyfile",
2016
0
                  *dest, dest.length(), UTF8, AfterNoArgs,
2017
0
                  uv_fs_copyfile, *src, *dest, flags);
2018
0
  } else {  // copyFile(src, dest, flags)
2019
0
    FSReqWrapSync req_wrap_sync("copyfile", *src, *dest);
2020
0
    FS_SYNC_TRACE_BEGIN(copyfile);
2021
0
    SyncCallAndThrowOnError(
2022
0
        env, &req_wrap_sync, uv_fs_copyfile, *src, *dest, flags);
2023
0
    FS_SYNC_TRACE_END(copyfile);
2024
0
  }
2025
0
}
2026
2027
// Wrapper for write(2).
2028
//
2029
// bytesWritten = write(fd, buffer, offset, length, position, callback)
2030
// 0 fd        integer. file descriptor
2031
// 1 buffer    the data to write
2032
// 2 offset    where in the buffer to start from
2033
// 3 length    how much to write
2034
// 4 position  if integer, position to write at in the file.
2035
//             if null, write from the current position
2036
0
static void WriteBuffer(const FunctionCallbackInfo<Value>& args) {
2037
0
  Environment* env = Environment::GetCurrent(args);
2038
2039
0
  const int argc = args.Length();
2040
0
  CHECK_GE(argc, 4);
2041
2042
0
  CHECK(args[0]->IsInt32());
2043
0
  const int fd = args[0].As<Int32>()->Value();
2044
2045
0
  CHECK(Buffer::HasInstance(args[1]));
2046
0
  Local<Object> buffer_obj = args[1].As<Object>();
2047
0
  char* buffer_data = Buffer::Data(buffer_obj);
2048
0
  size_t buffer_length = Buffer::Length(buffer_obj);
2049
2050
0
  CHECK(IsSafeJsInt(args[2]));
2051
0
  const int64_t off_64 = args[2].As<Integer>()->Value();
2052
0
  CHECK_GE(off_64, 0);
2053
0
  CHECK_LE(static_cast<uint64_t>(off_64), buffer_length);
2054
0
  const size_t off = static_cast<size_t>(off_64);
2055
2056
0
  CHECK(args[3]->IsInt32());
2057
0
  const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
2058
0
  CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
2059
0
  CHECK_LE(len, buffer_length);
2060
0
  CHECK_GE(off + len, off);
2061
2062
0
  const int64_t pos = GetOffset(args[4]);
2063
2064
0
  char* buf = buffer_data + off;
2065
0
  uv_buf_t uvbuf = uv_buf_init(buf, len);
2066
2067
0
  FSReqBase* req_wrap_async = GetReqWrap(args, 5);
2068
0
  if (req_wrap_async != nullptr) {  // write(fd, buffer, off, len, pos, req)
2069
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_WRITE, req_wrap_async)
2070
0
    AsyncCall(env, req_wrap_async, args, "write", UTF8, AfterInteger,
2071
0
              uv_fs_write, fd, &uvbuf, 1, pos);
2072
0
  } else {  // write(fd, buffer, off, len, pos, undefined, ctx)
2073
0
    CHECK_EQ(argc, 7);
2074
0
    FSReqWrapSync req_wrap_sync;
2075
0
    FS_SYNC_TRACE_BEGIN(write);
2076
0
    int bytesWritten = SyncCall(env, args[6], &req_wrap_sync, "write",
2077
0
                                uv_fs_write, fd, &uvbuf, 1, pos);
2078
0
    FS_SYNC_TRACE_END(write, "bytesWritten", bytesWritten);
2079
0
    args.GetReturnValue().Set(bytesWritten);
2080
0
  }
2081
0
}
2082
2083
2084
// Wrapper for writev(2).
2085
//
2086
// bytesWritten = writev(fd, chunks, position, callback)
2087
// 0 fd        integer. file descriptor
2088
// 1 chunks    array of buffers to write
2089
// 2 position  if integer, position to write at in the file.
2090
//             if null, write from the current position
2091
0
static void WriteBuffers(const FunctionCallbackInfo<Value>& args) {
2092
0
  Environment* env = Environment::GetCurrent(args);
2093
2094
0
  const int argc = args.Length();
2095
0
  CHECK_GE(argc, 3);
2096
2097
0
  CHECK(args[0]->IsInt32());
2098
0
  const int fd = args[0].As<Int32>()->Value();
2099
2100
0
  CHECK(args[1]->IsArray());
2101
0
  Local<Array> chunks = args[1].As<Array>();
2102
2103
0
  int64_t pos = GetOffset(args[2]);
2104
2105
0
  MaybeStackBuffer<uv_buf_t> iovs(chunks->Length());
2106
2107
0
  for (uint32_t i = 0; i < iovs.length(); i++) {
2108
0
    Local<Value> chunk = chunks->Get(env->context(), i).ToLocalChecked();
2109
0
    CHECK(Buffer::HasInstance(chunk));
2110
0
    iovs[i] = uv_buf_init(Buffer::Data(chunk), Buffer::Length(chunk));
2111
0
  }
2112
2113
0
  if (argc > 3) {  // writeBuffers(fd, chunks, pos, req)
2114
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2115
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_WRITE, req_wrap_async)
2116
0
    AsyncCall(env,
2117
0
              req_wrap_async,
2118
0
              args,
2119
0
              "write",
2120
0
              UTF8,
2121
0
              AfterInteger,
2122
0
              uv_fs_write,
2123
0
              fd,
2124
0
              *iovs,
2125
0
              iovs.length(),
2126
0
              pos);
2127
0
  } else {  // writeBuffers(fd, chunks, pos)
2128
0
    FSReqWrapSync req_wrap_sync("write");
2129
0
    FS_SYNC_TRACE_BEGIN(write);
2130
0
    int bytesWritten = SyncCallAndThrowOnError(
2131
0
        env, &req_wrap_sync, uv_fs_write, fd, *iovs, iovs.length(), pos);
2132
0
    FS_SYNC_TRACE_END(write, "bytesWritten", bytesWritten);
2133
0
    if (is_uv_error(bytesWritten)) {
2134
0
      return;
2135
0
    }
2136
0
    args.GetReturnValue().Set(bytesWritten);
2137
0
  }
2138
0
}
2139
2140
2141
// Wrapper for write(2).
2142
//
2143
// bytesWritten = write(fd, string, position, enc, callback)
2144
// 0 fd        integer. file descriptor
2145
// 1 string    non-buffer values are converted to strings
2146
// 2 position  if integer, position to write at in the file.
2147
//             if null, write from the current position
2148
// 3 enc       encoding of string
2149
0
static void WriteString(const FunctionCallbackInfo<Value>& args) {
2150
0
  Environment* env = Environment::GetCurrent(args);
2151
0
  Isolate* isolate = env->isolate();
2152
2153
0
  const int argc = args.Length();
2154
0
  CHECK_GE(argc, 4);
2155
0
  CHECK(args[0]->IsInt32());
2156
0
  const int fd = args[0].As<Int32>()->Value();
2157
2158
0
  const int64_t pos = GetOffset(args[2]);
2159
2160
0
  const auto enc = ParseEncoding(isolate, args[3], UTF8);
2161
2162
0
  Local<Value> value = args[1];
2163
0
  char* buf = nullptr;
2164
0
  size_t len;
2165
2166
0
  FSReqBase* req_wrap_async = GetReqWrap(args, 4);
2167
0
  const bool is_async = req_wrap_async != nullptr;
2168
2169
  // Avoid copying the string when it is externalized but only when:
2170
  // 1. The target encoding is compatible with the string's encoding, and
2171
  // 2. The write is synchronous, otherwise the string might get neutered
2172
  //    while the request is in flight, and
2173
  // 3. For UCS2, when the host system is little-endian.  Big-endian systems
2174
  //    need to call StringBytes::Write() to ensure proper byte swapping.
2175
  // The const_casts are conceptually sound: memory is read but not written.
2176
0
  if (!is_async && value->IsString()) {
2177
0
    auto string = value.As<String>();
2178
0
    if ((enc == ASCII || enc == LATIN1) && string->IsExternalOneByte()) {
2179
0
      auto ext = string->GetExternalOneByteStringResource();
2180
0
      buf = const_cast<char*>(ext->data());
2181
0
      len = ext->length();
2182
0
    } else if (enc == UCS2 && IsLittleEndian() && string->IsExternalTwoByte()) {
2183
0
      auto ext = string->GetExternalStringResource();
2184
0
      buf = reinterpret_cast<char*>(const_cast<uint16_t*>(ext->data()));
2185
0
      len = ext->length() * sizeof(*ext->data());
2186
0
    }
2187
0
  }
2188
2189
0
  if (is_async) {  // write(fd, string, pos, enc, req)
2190
0
    CHECK_NOT_NULL(req_wrap_async);
2191
0
    if (!StringBytes::StorageSize(isolate, value, enc).To(&len)) return;
2192
0
    FSReqBase::FSReqBuffer& stack_buffer =
2193
0
        req_wrap_async->Init("write", len, enc);
2194
    // StorageSize may return too large a char, so correct the actual length
2195
    // by the write size
2196
0
    len = StringBytes::Write(isolate, *stack_buffer, len, args[1], enc);
2197
0
    stack_buffer.SetLengthAndZeroTerminate(len);
2198
0
    uv_buf_t uvbuf = uv_buf_init(*stack_buffer, len);
2199
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_WRITE, req_wrap_async)
2200
0
    int err = req_wrap_async->Dispatch(uv_fs_write,
2201
0
                                       fd,
2202
0
                                       &uvbuf,
2203
0
                                       1,
2204
0
                                       pos,
2205
0
                                       AfterInteger);
2206
0
    if (err < 0) {
2207
0
      uv_fs_t* uv_req = req_wrap_async->req();
2208
0
      uv_req->result = err;
2209
0
      uv_req->path = nullptr;
2210
0
      AfterInteger(uv_req);  // after may delete req_wrap_async if there is
2211
                             // an error
2212
0
    } else {
2213
0
      req_wrap_async->SetReturnValue(args);
2214
0
    }
2215
0
  } else {  // write(fd, string, pos, enc, undefined, ctx)
2216
0
    CHECK_EQ(argc, 6);
2217
0
    FSReqWrapSync req_wrap_sync;
2218
0
    FSReqBase::FSReqBuffer stack_buffer;
2219
0
    if (buf == nullptr) {
2220
0
      if (!StringBytes::StorageSize(isolate, value, enc).To(&len))
2221
0
        return;
2222
0
      stack_buffer.AllocateSufficientStorage(len + 1);
2223
      // StorageSize may return too large a char, so correct the actual length
2224
      // by the write size
2225
0
      len = StringBytes::Write(isolate, *stack_buffer,
2226
0
                               len, args[1], enc);
2227
0
      stack_buffer.SetLengthAndZeroTerminate(len);
2228
0
      buf = *stack_buffer;
2229
0
    }
2230
0
    uv_buf_t uvbuf = uv_buf_init(buf, len);
2231
0
    FS_SYNC_TRACE_BEGIN(write);
2232
0
    int bytesWritten = SyncCall(env, args[5], &req_wrap_sync, "write",
2233
0
                                uv_fs_write, fd, &uvbuf, 1, pos);
2234
0
    FS_SYNC_TRACE_END(write, "bytesWritten", bytesWritten);
2235
0
    args.GetReturnValue().Set(bytesWritten);
2236
0
  }
2237
0
}
2238
2239
1.54k
static void WriteFileUtf8(const FunctionCallbackInfo<Value>& args) {
2240
  // Fast C++ path for fs.writeFileSync(path, data) with utf8 encoding
2241
  // (file, data, options.flag, options.mode)
2242
2243
1.54k
  Environment* env = Environment::GetCurrent(args);
2244
1.54k
  auto isolate = env->isolate();
2245
2246
1.54k
  CHECK_EQ(args.Length(), 4);
2247
2248
1.54k
  BufferValue value(isolate, args[1]);
2249
1.54k
  CHECK_NOT_NULL(*value);
2250
2251
1.54k
  CHECK(args[2]->IsInt32());
2252
1.54k
  const int flags = args[2].As<Int32>()->Value();
2253
2254
1.54k
  CHECK(args[3]->IsInt32());
2255
1.54k
  const int mode = args[3].As<Int32>()->Value();
2256
2257
1.54k
  uv_file file;
2258
2259
1.54k
  bool is_fd = args[0]->IsInt32();
2260
2261
  // Check for file descriptor
2262
1.54k
  if (is_fd) {
2263
0
    file = args[0].As<Int32>()->Value();
2264
1.54k
  } else {
2265
1.54k
    BufferValue path(isolate, args[0]);
2266
1.54k
    CHECK_NOT_NULL(*path);
2267
1.54k
    if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
2268
2269
1.54k
    FSReqWrapSync req_open("open", *path);
2270
2271
1.54k
    FS_SYNC_TRACE_BEGIN(open);
2272
1.54k
    file =
2273
1.54k
        SyncCallAndThrowOnError(env, &req_open, uv_fs_open, *path, flags, mode);
2274
1.54k
    FS_SYNC_TRACE_END(open);
2275
2276
1.54k
    if (is_uv_error(file)) {
2277
0
      return;
2278
0
    }
2279
1.54k
  }
2280
2281
1.54k
  int bytesWritten = 0;
2282
1.54k
  uint32_t offset = 0;
2283
2284
1.54k
  const size_t length = value.length();
2285
1.54k
  uv_buf_t uvbuf = uv_buf_init(value.out(), length);
2286
2287
1.54k
  FS_SYNC_TRACE_BEGIN(write);
2288
3.08k
  while (offset < length) {
2289
1.54k
    FSReqWrapSync req_write("write");
2290
1.54k
    bytesWritten = SyncCallAndThrowOnError(
2291
1.54k
        env, &req_write, uv_fs_write, file, &uvbuf, 1, -1);
2292
2293
    // Write errored out
2294
1.54k
    if (bytesWritten < 0) {
2295
0
      break;
2296
0
    }
2297
2298
1.54k
    offset += bytesWritten;
2299
1.54k
    DCHECK_LE(offset, length);
2300
1.54k
    uvbuf.base += bytesWritten;
2301
1.54k
    uvbuf.len -= bytesWritten;
2302
1.54k
  }
2303
1.54k
  FS_SYNC_TRACE_END(write);
2304
2305
1.54k
  if (!is_fd) {
2306
1.54k
    FSReqWrapSync req_close("close");
2307
2308
1.54k
    FS_SYNC_TRACE_BEGIN(close);
2309
1.54k
    int result = SyncCallAndThrowOnError(env, &req_close, uv_fs_close, file);
2310
1.54k
    FS_SYNC_TRACE_END(close);
2311
2312
1.54k
    if (is_uv_error(result)) {
2313
0
      return;
2314
0
    }
2315
1.54k
  }
2316
1.54k
}
2317
2318
/*
2319
 * Wrapper for read(2).
2320
 *
2321
 * bytesRead = fs.read(fd, buffer, offset, length, position)
2322
 *
2323
 * 0 fd        int32. file descriptor
2324
 * 1 buffer    instance of Buffer
2325
 * 2 offset    int64. offset to start reading into inside buffer
2326
 * 3 length    int32. length to read
2327
 * 4 position  int64. file position - -1 for current position
2328
 */
2329
3
static void Read(const FunctionCallbackInfo<Value>& args) {
2330
3
  Environment* env = Environment::GetCurrent(args);
2331
2332
3
  const int argc = args.Length();
2333
3
  CHECK_GE(argc, 5);
2334
2335
3
  CHECK(args[0]->IsInt32());
2336
3
  const int fd = args[0].As<Int32>()->Value();
2337
2338
3
  CHECK(Buffer::HasInstance(args[1]));
2339
3
  Local<Object> buffer_obj = args[1].As<Object>();
2340
3
  char* buffer_data = Buffer::Data(buffer_obj);
2341
3
  size_t buffer_length = Buffer::Length(buffer_obj);
2342
2343
3
  CHECK(IsSafeJsInt(args[2]));
2344
3
  const int64_t off_64 = args[2].As<Integer>()->Value();
2345
3
  CHECK_GE(off_64, 0);
2346
3
  CHECK_LT(static_cast<uint64_t>(off_64), buffer_length);
2347
3
  const size_t off = static_cast<size_t>(off_64);
2348
2349
3
  CHECK(args[3]->IsInt32());
2350
3
  const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
2351
3
  CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
2352
2353
3
  CHECK(IsSafeJsInt(args[4]) || args[4]->IsBigInt());
2354
3
  const int64_t pos = args[4]->IsNumber() ?
2355
3
                      args[4].As<Integer>()->Value() :
2356
3
                      args[4].As<BigInt>()->Int64Value();
2357
2358
3
  char* buf = buffer_data + off;
2359
3
  uv_buf_t uvbuf = uv_buf_init(buf, len);
2360
2361
3
  if (argc > 5) {  // read(fd, buffer, offset, len, pos, req)
2362
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 5);
2363
0
    CHECK_NOT_NULL(req_wrap_async);
2364
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_READ, req_wrap_async)
2365
0
    AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger,
2366
0
              uv_fs_read, fd, &uvbuf, 1, pos);
2367
3
  } else {  // read(fd, buffer, offset, len, pos)
2368
3
    FSReqWrapSync req_wrap_sync("read");
2369
3
    FS_SYNC_TRACE_BEGIN(read);
2370
3
    const int bytesRead = SyncCallAndThrowOnError(
2371
3
        env, &req_wrap_sync, uv_fs_read, fd, &uvbuf, 1, pos);
2372
3
    FS_SYNC_TRACE_END(read, "bytesRead", bytesRead);
2373
2374
3
    if (is_uv_error(bytesRead)) {
2375
0
      return;
2376
0
    }
2377
2378
3
    args.GetReturnValue().Set(bytesRead);
2379
3
  }
2380
3
}
2381
2382
789
static void ReadFileUtf8(const FunctionCallbackInfo<Value>& args) {
2383
789
  Environment* env = Environment::GetCurrent(args);
2384
789
  auto isolate = env->isolate();
2385
2386
789
  CHECK_GE(args.Length(), 2);
2387
2388
789
  CHECK(args[1]->IsInt32());
2389
789
  const int flags = args[1].As<Int32>()->Value();
2390
2391
789
  uv_file file;
2392
789
  uv_fs_t req;
2393
2394
789
  bool is_fd = args[0]->IsInt32();
2395
2396
  // Check for file descriptor
2397
789
  if (is_fd) {
2398
0
    file = args[0].As<Int32>()->Value();
2399
789
  } else {
2400
789
    BufferValue path(env->isolate(), args[0]);
2401
789
    CHECK_NOT_NULL(*path);
2402
789
    if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
2403
2404
789
    FS_SYNC_TRACE_BEGIN(open);
2405
789
    file = uv_fs_open(nullptr, &req, *path, flags, 0666, nullptr);
2406
789
    FS_SYNC_TRACE_END(open);
2407
789
    if (req.result < 0) {
2408
0
      uv_fs_req_cleanup(&req);
2409
      // req will be cleaned up by scope leave.
2410
0
      return env->ThrowUVException(req.result, "open", nullptr, path.out());
2411
0
    }
2412
789
  }
2413
2414
789
  auto defer_close = OnScopeLeave([file, is_fd, &req]() {
2415
789
    if (!is_fd) {
2416
789
      FS_SYNC_TRACE_BEGIN(close);
2417
789
      CHECK_EQ(0, uv_fs_close(nullptr, &req, file, nullptr));
2418
789
      FS_SYNC_TRACE_END(close);
2419
789
    }
2420
789
    uv_fs_req_cleanup(&req);
2421
789
  });
2422
2423
789
  std::string result{};
2424
789
  char buffer[8192];
2425
789
  uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
2426
2427
789
  FS_SYNC_TRACE_BEGIN(read);
2428
12.1k
  while (true) {
2429
12.1k
    auto r = uv_fs_read(nullptr, &req, file, &buf, 1, -1, nullptr);
2430
12.1k
    if (req.result < 0) {
2431
0
      FS_SYNC_TRACE_END(read);
2432
      // req will be cleaned up by scope leave.
2433
0
      return env->ThrowUVException(req.result, "read", nullptr);
2434
0
    }
2435
12.1k
    if (r <= 0) {
2436
789
      break;
2437
789
    }
2438
11.3k
    result.append(buf.base, r);
2439
11.3k
  }
2440
789
  FS_SYNC_TRACE_END(read);
2441
2442
789
  args.GetReturnValue().Set(
2443
789
      ToV8Value(env->context(), result, isolate).ToLocalChecked());
2444
789
}
2445
2446
// Wrapper for readv(2).
2447
//
2448
// bytesRead = fs.readv(fd, buffers[, position], callback)
2449
// 0 fd        integer. file descriptor
2450
// 1 buffers   array of buffers to read
2451
// 2 position  if integer, position to read at in the file.
2452
//             if null, read from the current position
2453
0
static void ReadBuffers(const FunctionCallbackInfo<Value>& args) {
2454
0
  Environment* env = Environment::GetCurrent(args);
2455
2456
0
  const int argc = args.Length();
2457
0
  CHECK_GE(argc, 3);
2458
2459
0
  CHECK(args[0]->IsInt32());
2460
0
  const int fd = args[0].As<Int32>()->Value();
2461
2462
0
  CHECK(args[1]->IsArray());
2463
0
  Local<Array> buffers = args[1].As<Array>();
2464
2465
0
  int64_t pos = GetOffset(args[2]);  // -1 if not a valid JS int
2466
2467
0
  MaybeStackBuffer<uv_buf_t> iovs(buffers->Length());
2468
2469
  // Init uv buffers from ArrayBufferViews
2470
0
  for (uint32_t i = 0; i < iovs.length(); i++) {
2471
0
    Local<Value> buffer = buffers->Get(env->context(), i).ToLocalChecked();
2472
0
    CHECK(Buffer::HasInstance(buffer));
2473
0
    iovs[i] = uv_buf_init(Buffer::Data(buffer), Buffer::Length(buffer));
2474
0
  }
2475
2476
0
  if (argc > 3) {  // readBuffers(fd, buffers, pos, req)
2477
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2478
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_READ, req_wrap_async)
2479
0
    AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger,
2480
0
              uv_fs_read, fd, *iovs, iovs.length(), pos);
2481
0
  } else {  // readBuffers(fd, buffers, undefined, ctx)
2482
0
    FSReqWrapSync req_wrap_sync("read");
2483
0
    FS_SYNC_TRACE_BEGIN(read);
2484
0
    int bytesRead = SyncCallAndThrowOnError(
2485
0
        env, &req_wrap_sync, uv_fs_read, fd, *iovs, iovs.length(), pos);
2486
0
    FS_SYNC_TRACE_END(read, "bytesRead", bytesRead);
2487
0
    if (is_uv_error(bytesRead)) {
2488
0
      return;
2489
0
    }
2490
0
    args.GetReturnValue().Set(bytesRead);
2491
0
  }
2492
0
}
2493
2494
2495
/* fs.chmod(path, mode);
2496
 * Wrapper for chmod(1) / EIO_CHMOD
2497
 */
2498
0
static void Chmod(const FunctionCallbackInfo<Value>& args) {
2499
0
  Environment* env = Environment::GetCurrent(args);
2500
2501
0
  const int argc = args.Length();
2502
0
  CHECK_GE(argc, 2);
2503
2504
0
  BufferValue path(env->isolate(), args[0]);
2505
0
  CHECK_NOT_NULL(*path);
2506
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2507
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
2508
2509
0
  CHECK(args[1]->IsInt32());
2510
0
  int mode = args[1].As<Int32>()->Value();
2511
2512
0
  if (argc > 2) {  // chmod(path, mode, req)
2513
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
2514
0
    FS_ASYNC_TRACE_BEGIN1(
2515
0
        UV_FS_CHMOD, req_wrap_async, "path", TRACE_STR_COPY(*path))
2516
0
    AsyncCall(env, req_wrap_async, args, "chmod", UTF8, AfterNoArgs,
2517
0
              uv_fs_chmod, *path, mode);
2518
0
  } else {  // chmod(path, mode)
2519
0
    FSReqWrapSync req_wrap_sync("chmod", *path);
2520
0
    FS_SYNC_TRACE_BEGIN(chmod);
2521
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_chmod, *path, mode);
2522
0
    FS_SYNC_TRACE_END(chmod);
2523
0
  }
2524
0
}
2525
2526
2527
/* fs.fchmod(fd, mode);
2528
 * Wrapper for fchmod(1) / EIO_FCHMOD
2529
 */
2530
0
static void FChmod(const FunctionCallbackInfo<Value>& args) {
2531
0
  Environment* env = Environment::GetCurrent(args);
2532
2533
0
  const int argc = args.Length();
2534
0
  CHECK_GE(argc, 2);
2535
2536
0
  int fd;
2537
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
2538
0
    return;
2539
0
  }
2540
2541
0
  CHECK(args[1]->IsInt32());
2542
0
  const int mode = args[1].As<Int32>()->Value();
2543
2544
0
  if (argc > 2) {  // fchmod(fd, mode, req)
2545
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
2546
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FCHMOD, req_wrap_async)
2547
0
    AsyncCall(env, req_wrap_async, args, "fchmod", UTF8, AfterNoArgs,
2548
0
              uv_fs_fchmod, fd, mode);
2549
0
  } else {  // fchmod(fd, mode)
2550
0
    FSReqWrapSync req_wrap_sync("fchmod");
2551
0
    FS_SYNC_TRACE_BEGIN(fchmod);
2552
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_fchmod, fd, mode);
2553
0
    FS_SYNC_TRACE_END(fchmod);
2554
0
  }
2555
0
}
2556
2557
/* fs.chown(path, uid, gid);
2558
 * Wrapper for chown(1) / EIO_CHOWN
2559
 */
2560
0
static void Chown(const FunctionCallbackInfo<Value>& args) {
2561
0
  Environment* env = Environment::GetCurrent(args);
2562
2563
0
  const int argc = args.Length();
2564
0
  CHECK_GE(argc, 3);
2565
2566
0
  BufferValue path(env->isolate(), args[0]);
2567
0
  CHECK_NOT_NULL(*path);
2568
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2569
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
2570
2571
0
  CHECK(IsSafeJsInt(args[1]));
2572
0
  const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Integer>()->Value());
2573
2574
0
  CHECK(IsSafeJsInt(args[2]));
2575
0
  const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Integer>()->Value());
2576
2577
0
  if (argc > 3) {  // chown(path, uid, gid, req)
2578
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2579
0
    FS_ASYNC_TRACE_BEGIN1(
2580
0
        UV_FS_CHOWN, req_wrap_async, "path", TRACE_STR_COPY(*path))
2581
0
    AsyncCall(env, req_wrap_async, args, "chown", UTF8, AfterNoArgs,
2582
0
              uv_fs_chown, *path, uid, gid);
2583
0
  } else {  // chown(path, uid, gid)
2584
0
    FSReqWrapSync req_wrap_sync("chown", *path);
2585
0
    FS_SYNC_TRACE_BEGIN(chown);
2586
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_chown, *path, uid, gid);
2587
0
    FS_SYNC_TRACE_END(chown);
2588
0
  }
2589
0
}
2590
2591
2592
/* fs.fchown(fd, uid, gid);
2593
 * Wrapper for fchown(1) / EIO_FCHOWN
2594
 */
2595
0
static void FChown(const FunctionCallbackInfo<Value>& args) {
2596
0
  Environment* env = Environment::GetCurrent(args);
2597
2598
0
  const int argc = args.Length();
2599
0
  CHECK_GE(argc, 3);
2600
2601
0
  int fd;
2602
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
2603
0
    return;
2604
0
  }
2605
2606
0
  CHECK(IsSafeJsInt(args[1]));
2607
0
  const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Integer>()->Value());
2608
2609
0
  CHECK(IsSafeJsInt(args[2]));
2610
0
  const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Integer>()->Value());
2611
2612
0
  if (argc > 3) {  // fchown(fd, uid, gid, req)
2613
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2614
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FCHOWN, req_wrap_async)
2615
0
    AsyncCall(env, req_wrap_async, args, "fchown", UTF8, AfterNoArgs,
2616
0
              uv_fs_fchown, fd, uid, gid);
2617
0
  } else {  // fchown(fd, uid, gid)
2618
0
    FSReqWrapSync req_wrap_sync("fchown");
2619
0
    FS_SYNC_TRACE_BEGIN(fchown);
2620
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_fchown, fd, uid, gid);
2621
0
    FS_SYNC_TRACE_END(fchown);
2622
0
  }
2623
0
}
2624
2625
2626
0
static void LChown(const FunctionCallbackInfo<Value>& args) {
2627
0
  Environment* env = Environment::GetCurrent(args);
2628
2629
0
  const int argc = args.Length();
2630
0
  CHECK_GE(argc, 3);
2631
2632
0
  BufferValue path(env->isolate(), args[0]);
2633
0
  CHECK_NOT_NULL(*path);
2634
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2635
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
2636
2637
0
  CHECK(IsSafeJsInt(args[1]));
2638
0
  const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Integer>()->Value());
2639
2640
0
  CHECK(IsSafeJsInt(args[2]));
2641
0
  const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Integer>()->Value());
2642
2643
0
  if (argc > 3) {  // lchown(path, uid, gid, req)
2644
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2645
0
    FS_ASYNC_TRACE_BEGIN1(
2646
0
        UV_FS_LCHOWN, req_wrap_async, "path", TRACE_STR_COPY(*path))
2647
0
    AsyncCall(env, req_wrap_async, args, "lchown", UTF8, AfterNoArgs,
2648
0
              uv_fs_lchown, *path, uid, gid);
2649
0
  } else {  // lchown(path, uid, gid)
2650
0
    FSReqWrapSync req_wrap_sync("lchown", *path);
2651
0
    FS_SYNC_TRACE_BEGIN(lchown);
2652
0
    SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_lchown, *path, uid, gid);
2653
0
    FS_SYNC_TRACE_END(lchown);
2654
0
  }
2655
0
}
2656
2657
2658
0
static void UTimes(const FunctionCallbackInfo<Value>& args) {
2659
0
  Environment* env = Environment::GetCurrent(args);
2660
2661
0
  const int argc = args.Length();
2662
0
  CHECK_GE(argc, 3);
2663
2664
0
  BufferValue path(env->isolate(), args[0]);
2665
0
  CHECK_NOT_NULL(*path);
2666
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2667
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
2668
2669
0
  CHECK(args[1]->IsNumber());
2670
0
  const double atime = args[1].As<Number>()->Value();
2671
2672
0
  CHECK(args[2]->IsNumber());
2673
0
  const double mtime = args[2].As<Number>()->Value();
2674
2675
0
  if (argc > 3) {  // utimes(path, atime, mtime, req)
2676
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2677
0
    FS_ASYNC_TRACE_BEGIN1(
2678
0
        UV_FS_UTIME, req_wrap_async, "path", TRACE_STR_COPY(*path))
2679
0
    AsyncCall(env, req_wrap_async, args, "utime", UTF8, AfterNoArgs,
2680
0
              uv_fs_utime, *path, atime, mtime);
2681
0
  } else {  // utimes(path, atime, mtime)
2682
0
    FSReqWrapSync req_wrap_sync("utime", *path);
2683
0
    FS_SYNC_TRACE_BEGIN(utimes);
2684
0
    SyncCallAndThrowOnError(
2685
0
        env, &req_wrap_sync, uv_fs_utime, *path, atime, mtime);
2686
0
    FS_SYNC_TRACE_END(utimes);
2687
0
  }
2688
0
}
2689
2690
0
static void FUTimes(const FunctionCallbackInfo<Value>& args) {
2691
0
  Environment* env = Environment::GetCurrent(args);
2692
2693
0
  const int argc = args.Length();
2694
0
  CHECK_GE(argc, 3);
2695
2696
0
  int fd;
2697
0
  if (!GetValidatedFd(env, args[0]).To(&fd)) {
2698
0
    return;
2699
0
  }
2700
2701
0
  CHECK(args[1]->IsNumber());
2702
0
  const double atime = args[1].As<Number>()->Value();
2703
2704
0
  CHECK(args[2]->IsNumber());
2705
0
  const double mtime = args[2].As<Number>()->Value();
2706
2707
0
  if (argc > 3) {  // futimes(fd, atime, mtime, req)
2708
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2709
0
    FS_ASYNC_TRACE_BEGIN0(UV_FS_FUTIME, req_wrap_async)
2710
0
    AsyncCall(env, req_wrap_async, args, "futime", UTF8, AfterNoArgs,
2711
0
              uv_fs_futime, fd, atime, mtime);
2712
0
  } else {  // futimes(fd, atime, mtime)
2713
0
    FSReqWrapSync req_wrap_sync("futime");
2714
0
    FS_SYNC_TRACE_BEGIN(futimes);
2715
0
    SyncCallAndThrowOnError(
2716
0
        env, &req_wrap_sync, uv_fs_futime, fd, atime, mtime);
2717
0
    FS_SYNC_TRACE_END(futimes);
2718
0
  }
2719
0
}
2720
2721
0
static void LUTimes(const FunctionCallbackInfo<Value>& args) {
2722
0
  Environment* env = Environment::GetCurrent(args);
2723
2724
0
  const int argc = args.Length();
2725
0
  CHECK_GE(argc, 3);
2726
2727
0
  BufferValue path(env->isolate(), args[0]);
2728
0
  CHECK_NOT_NULL(*path);
2729
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2730
0
      env, permission::PermissionScope::kFileSystemWrite, path.ToStringView());
2731
2732
0
  CHECK(args[1]->IsNumber());
2733
0
  const double atime = args[1].As<Number>()->Value();
2734
2735
0
  CHECK(args[2]->IsNumber());
2736
0
  const double mtime = args[2].As<Number>()->Value();
2737
2738
0
  if (argc > 3) {  // lutimes(path, atime, mtime, req)
2739
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2740
0
    FS_ASYNC_TRACE_BEGIN1(
2741
0
        UV_FS_LUTIME, req_wrap_async, "path", TRACE_STR_COPY(*path))
2742
0
    AsyncCall(env, req_wrap_async, args, "lutime", UTF8, AfterNoArgs,
2743
0
              uv_fs_lutime, *path, atime, mtime);
2744
0
  } else {  // lutimes(path, atime, mtime)
2745
0
    FSReqWrapSync req_wrap_sync("lutime", *path);
2746
0
    FS_SYNC_TRACE_BEGIN(lutimes);
2747
0
    SyncCallAndThrowOnError(
2748
0
        env, &req_wrap_sync, uv_fs_lutime, *path, atime, mtime);
2749
0
    FS_SYNC_TRACE_END(lutimes);
2750
0
  }
2751
0
}
2752
2753
0
static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
2754
0
  Environment* env = Environment::GetCurrent(args);
2755
0
  Isolate* isolate = env->isolate();
2756
2757
0
  const int argc = args.Length();
2758
0
  CHECK_GE(argc, 2);
2759
2760
0
  BufferValue tmpl(isolate, args[0]);
2761
0
  static constexpr const char* const suffix = "XXXXXX";
2762
0
  const auto length = tmpl.length();
2763
0
  tmpl.AllocateSufficientStorage(length + strlen(suffix));
2764
0
  snprintf(tmpl.out() + length, tmpl.length(), "%s", suffix);
2765
2766
0
  CHECK_NOT_NULL(*tmpl);
2767
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2768
0
      env, permission::PermissionScope::kFileSystemWrite, tmpl.ToStringView());
2769
2770
0
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
2771
2772
0
  if (argc > 2) {  // mkdtemp(tmpl, encoding, req)
2773
0
    FSReqBase* req_wrap_async = GetReqWrap(args, 2);
2774
0
    FS_ASYNC_TRACE_BEGIN1(
2775
0
        UV_FS_MKDTEMP, req_wrap_async, "path", TRACE_STR_COPY(*tmpl))
2776
0
    AsyncCall(env, req_wrap_async, args, "mkdtemp", encoding, AfterStringPath,
2777
0
              uv_fs_mkdtemp, *tmpl);
2778
0
  } else {  // mkdtemp(tmpl, encoding)
2779
0
    FSReqWrapSync req_wrap_sync("mkdtemp", *tmpl);
2780
0
    FS_SYNC_TRACE_BEGIN(mkdtemp);
2781
0
    int result =
2782
0
        SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_mkdtemp, *tmpl);
2783
0
    FS_SYNC_TRACE_END(mkdtemp);
2784
0
    if (is_uv_error(result)) {
2785
0
      return;
2786
0
    }
2787
0
    Local<Value> error;
2788
0
    MaybeLocal<Value> rc =
2789
0
        StringBytes::Encode(isolate, req_wrap_sync.req.path, encoding, &error);
2790
0
    if (rc.IsEmpty()) {
2791
0
      env->isolate()->ThrowException(error);
2792
0
      return;
2793
0
    }
2794
0
    args.GetReturnValue().Set(rc.ToLocalChecked());
2795
0
  }
2796
0
}
2797
2798
static void GetFormatOfExtensionlessFile(
2799
0
    const FunctionCallbackInfo<Value>& args) {
2800
0
  CHECK_EQ(args.Length(), 1);
2801
0
  CHECK(args[0]->IsString());
2802
2803
0
  Environment* env = Environment::GetCurrent(args);
2804
0
  node::Utf8Value input(args.GetIsolate(), args[0]);
2805
2806
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2807
0
      env, permission::PermissionScope::kFileSystemRead, input.ToStringView());
2808
2809
0
  uv_fs_t req;
2810
0
  FS_SYNC_TRACE_BEGIN(open)
2811
0
  uv_file file = uv_fs_open(nullptr, &req, input.out(), O_RDONLY, 0, nullptr);
2812
0
  FS_SYNC_TRACE_END(open);
2813
2814
0
  if (req.result < 0) {
2815
0
    return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2816
0
  }
2817
2818
0
  auto cleanup = OnScopeLeave([&req, &file]() {
2819
0
    FS_SYNC_TRACE_BEGIN(close);
2820
0
    CHECK_EQ(0, uv_fs_close(nullptr, &req, file, nullptr));
2821
0
    FS_SYNC_TRACE_END(close);
2822
0
    uv_fs_req_cleanup(&req);
2823
0
  });
2824
2825
0
  char buffer[4];
2826
0
  uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
2827
0
  int err = uv_fs_read(nullptr, &req, file, &buf, 1, 0, nullptr);
2828
2829
0
  if (err < 0) {
2830
0
    return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2831
0
  }
2832
2833
  // We do this by taking advantage of the fact that all Wasm files start with
2834
  // the header `0x00 0x61 0x73 0x6d`
2835
0
  if (buffer[0] == 0x00 && buffer[1] == 0x61 && buffer[2] == 0x73 &&
2836
0
      buffer[3] == 0x6d) {
2837
0
    return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_WASM);
2838
0
  }
2839
2840
0
  return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2841
0
}
2842
2843
BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile(
2844
0
    Environment* env, const std::string& file_path) {
2845
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
2846
0
      env,
2847
0
      permission::PermissionScope::kFileSystemRead,
2848
0
      file_path,
2849
0
      BindingData::FilePathIsFileReturnType::kThrowInsufficientPermissions);
2850
2851
0
  uv_fs_t req;
2852
2853
0
  int rc = uv_fs_stat(env->event_loop(), &req, file_path.c_str(), nullptr);
2854
2855
0
  if (rc == 0) {
2856
0
    const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
2857
0
    rc = !!(s->st_mode & S_IFDIR);
2858
0
  }
2859
2860
0
  uv_fs_req_cleanup(&req);
2861
2862
  // rc is 0 if the path refers to a file
2863
0
  if (rc == 0) return BindingData::FilePathIsFileReturnType::kIsFile;
2864
2865
0
  return BindingData::FilePathIsFileReturnType::kIsNotFile;
2866
0
}
2867
2868
namespace {
2869
2870
// define the final index of the algorithm resolution
2871
// when packageConfig.main is defined.
2872
constexpr uint8_t legacy_main_extensions_with_main_end = 7;
2873
// define the final index of the algorithm resolution
2874
// when packageConfig.main is NOT defined
2875
constexpr uint8_t legacy_main_extensions_package_fallback_end = 10;
2876
// the possible file extensions that should be tested
2877
// 0-6: when packageConfig.main is defined
2878
// 7-9: when packageConfig.main is NOT defined,
2879
//      or when the previous case didn't found the file
2880
constexpr std::array<std::string_view, 10> legacy_main_extensions = {
2881
    "",
2882
    ".js",
2883
    ".json",
2884
    ".node",
2885
    "/index.js",
2886
    "/index.json",
2887
    "/index.node",
2888
    ".js",
2889
    ".json",
2890
    ".node"};
2891
2892
}  // namespace
2893
2894
0
void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
2895
0
  CHECK_GE(args.Length(), 1);
2896
0
  CHECK(args[0]->IsString());
2897
2898
0
  Environment* env = Environment::GetCurrent(args);
2899
0
  auto isolate = env->isolate();
2900
2901
0
  Utf8Value utf8_package_json_url(isolate, args[0]);
2902
0
  auto package_json_url =
2903
0
      ada::parse<ada::url_aggregator>(utf8_package_json_url.ToStringView());
2904
2905
0
  if (!package_json_url) {
2906
0
    THROW_ERR_INVALID_URL(isolate, "Invalid URL");
2907
0
    return;
2908
0
  }
2909
2910
0
  ada::result<ada::url_aggregator> file_path_url;
2911
0
  std::optional<std::string> initial_file_path;
2912
0
  std::string file_path;
2913
2914
0
  if (args.Length() >= 2 && args[1]->IsString()) {
2915
0
    auto package_config_main = Utf8Value(isolate, args[1]).ToString();
2916
2917
0
    file_path_url = ada::parse<ada::url_aggregator>(
2918
0
        std::string("./") + package_config_main, &package_json_url.value());
2919
2920
0
    if (!file_path_url) {
2921
0
      THROW_ERR_INVALID_URL(isolate, "Invalid URL");
2922
0
      return;
2923
0
    }
2924
2925
0
    initial_file_path = node::url::FileURLToPath(env, *file_path_url);
2926
0
    if (!initial_file_path.has_value()) {
2927
0
      return;
2928
0
    }
2929
2930
0
    node::url::FromNamespacedPath(&initial_file_path.value());
2931
2932
0
    for (int i = 0; i < legacy_main_extensions_with_main_end; i++) {
2933
0
      file_path = *initial_file_path + std::string(legacy_main_extensions[i]);
2934
2935
0
      switch (FilePathIsFile(env, file_path)) {
2936
0
        case BindingData::FilePathIsFileReturnType::kIsFile:
2937
0
          return args.GetReturnValue().Set(i);
2938
0
        case BindingData::FilePathIsFileReturnType::kIsNotFile:
2939
0
          continue;
2940
0
        case BindingData::FilePathIsFileReturnType::
2941
0
            kThrowInsufficientPermissions:
2942
          // the default behavior when do not have permission is to return
2943
          // and exit the execution of the method as soon as possible
2944
          // the internal function will throw the exception
2945
0
          return;
2946
0
        default:
2947
0
          UNREACHABLE();
2948
0
      }
2949
0
    }
2950
0
  }
2951
2952
0
  file_path_url =
2953
0
      ada::parse<ada::url_aggregator>("./index", &package_json_url.value());
2954
2955
0
  if (!file_path_url) {
2956
0
    THROW_ERR_INVALID_URL(isolate, "Invalid URL");
2957
0
    return;
2958
0
  }
2959
2960
0
  initial_file_path = node::url::FileURLToPath(env, *file_path_url);
2961
0
  if (!initial_file_path.has_value()) {
2962
0
    return;
2963
0
  }
2964
2965
0
  node::url::FromNamespacedPath(&initial_file_path.value());
2966
2967
0
  for (int i = legacy_main_extensions_with_main_end;
2968
0
       i < legacy_main_extensions_package_fallback_end;
2969
0
       i++) {
2970
0
    file_path = *initial_file_path + std::string(legacy_main_extensions[i]);
2971
2972
0
    switch (FilePathIsFile(env, file_path)) {
2973
0
      case BindingData::FilePathIsFileReturnType::kIsFile:
2974
0
        return args.GetReturnValue().Set(i);
2975
0
      case BindingData::FilePathIsFileReturnType::kIsNotFile:
2976
0
        continue;
2977
0
      case BindingData::FilePathIsFileReturnType::kThrowInsufficientPermissions:
2978
        // the default behavior when do not have permission is to return
2979
        // and exit the execution of the method as soon as possible
2980
        // the internal function will throw the exception
2981
0
        return;
2982
0
      default:
2983
0
        UNREACHABLE();
2984
0
    }
2985
0
  }
2986
2987
0
  std::optional<std::string> module_path =
2988
0
      node::url::FileURLToPath(env, *package_json_url);
2989
0
  std::optional<std::string> module_base;
2990
2991
0
  if (!module_path.has_value()) {
2992
0
    return;
2993
0
  }
2994
2995
0
  if (args.Length() >= 3 && args[2]->IsString()) {
2996
0
    Utf8Value utf8_base_path(isolate, args[2]);
2997
0
    auto base_url =
2998
0
        ada::parse<ada::url_aggregator>(utf8_base_path.ToStringView());
2999
3000
0
    if (!base_url) {
3001
0
      THROW_ERR_INVALID_URL(isolate, "Invalid URL");
3002
0
      return;
3003
0
    }
3004
3005
0
    module_base = node::url::FileURLToPath(env, *base_url);
3006
0
    if (!module_base.has_value()) {
3007
0
      return;
3008
0
    }
3009
0
  } else {
3010
0
    THROW_ERR_INVALID_ARG_TYPE(
3011
0
        isolate,
3012
0
        "The \"base\" argument must be of type string or an instance of URL.");
3013
0
    return;
3014
0
  }
3015
3016
0
  THROW_ERR_MODULE_NOT_FOUND(isolate,
3017
0
                             "Cannot find package '%s' imported from %s",
3018
0
                             *module_path,
3019
0
                             *module_base);
3020
0
}
3021
3022
0
void BindingData::MemoryInfo(MemoryTracker* tracker) const {
3023
0
  tracker->TrackField("stats_field_array", stats_field_array);
3024
0
  tracker->TrackField("stats_field_bigint_array", stats_field_bigint_array);
3025
0
  tracker->TrackField("statfs_field_array", statfs_field_array);
3026
0
  tracker->TrackField("statfs_field_bigint_array", statfs_field_bigint_array);
3027
0
  tracker->TrackField("file_handle_read_wrap_freelist",
3028
0
                      file_handle_read_wrap_freelist);
3029
0
}
3030
3031
BindingData::BindingData(Realm* realm,
3032
                         v8::Local<v8::Object> wrap,
3033
                         InternalFieldInfo* info)
3034
122k
    : SnapshotableObject(realm, wrap, type_int),
3035
122k
      stats_field_array(realm->isolate(),
3036
122k
                        kFsStatsBufferLength,
3037
122k
                        MAYBE_FIELD_PTR(info, stats_field_array)),
3038
122k
      stats_field_bigint_array(realm->isolate(),
3039
122k
                               kFsStatsBufferLength,
3040
122k
                               MAYBE_FIELD_PTR(info, stats_field_bigint_array)),
3041
122k
      statfs_field_array(realm->isolate(),
3042
122k
                         kFsStatFsBufferLength,
3043
122k
                         MAYBE_FIELD_PTR(info, statfs_field_array)),
3044
122k
      statfs_field_bigint_array(
3045
122k
          realm->isolate(),
3046
122k
          kFsStatFsBufferLength,
3047
122k
          MAYBE_FIELD_PTR(info, statfs_field_bigint_array)) {
3048
122k
  Isolate* isolate = realm->isolate();
3049
122k
  Local<Context> context = realm->context();
3050
3051
122k
  if (info == nullptr) {
3052
122k
    wrap->Set(context,
3053
122k
              FIXED_ONE_BYTE_STRING(isolate, "statValues"),
3054
122k
              stats_field_array.GetJSArray())
3055
122k
        .Check();
3056
3057
122k
    wrap->Set(context,
3058
122k
              FIXED_ONE_BYTE_STRING(isolate, "bigintStatValues"),
3059
122k
              stats_field_bigint_array.GetJSArray())
3060
122k
        .Check();
3061
3062
122k
    wrap->Set(context,
3063
122k
              FIXED_ONE_BYTE_STRING(isolate, "statFsValues"),
3064
122k
              statfs_field_array.GetJSArray())
3065
122k
        .Check();
3066
3067
122k
    wrap->Set(context,
3068
122k
              FIXED_ONE_BYTE_STRING(isolate, "bigintStatFsValues"),
3069
122k
              statfs_field_bigint_array.GetJSArray())
3070
122k
        .Check();
3071
122k
  } else {
3072
0
    stats_field_array.Deserialize(realm->context());
3073
0
    stats_field_bigint_array.Deserialize(realm->context());
3074
0
    statfs_field_array.Deserialize(realm->context());
3075
0
    statfs_field_bigint_array.Deserialize(realm->context());
3076
0
  }
3077
122k
  stats_field_array.MakeWeak();
3078
122k
  stats_field_bigint_array.MakeWeak();
3079
122k
  statfs_field_array.MakeWeak();
3080
122k
  statfs_field_bigint_array.MakeWeak();
3081
122k
}
3082
3083
void BindingData::Deserialize(Local<Context> context,
3084
                              Local<Object> holder,
3085
                              int index,
3086
0
                              InternalFieldInfoBase* info) {
3087
0
  DCHECK_IS_SNAPSHOT_SLOT(index);
3088
0
  HandleScope scope(context->GetIsolate());
3089
0
  Realm* realm = Realm::GetCurrent(context);
3090
0
  InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
3091
0
  BindingData* binding =
3092
0
      realm->AddBindingData<BindingData>(holder, casted_info);
3093
0
  CHECK_NOT_NULL(binding);
3094
0
}
3095
3096
bool BindingData::PrepareForSerialization(Local<Context> context,
3097
0
                                          v8::SnapshotCreator* creator) {
3098
0
  CHECK(file_handle_read_wrap_freelist.empty());
3099
0
  DCHECK_NULL(internal_field_info_);
3100
0
  internal_field_info_ = InternalFieldInfoBase::New<InternalFieldInfo>(type());
3101
0
  internal_field_info_->stats_field_array =
3102
0
      stats_field_array.Serialize(context, creator);
3103
0
  internal_field_info_->stats_field_bigint_array =
3104
0
      stats_field_bigint_array.Serialize(context, creator);
3105
0
  internal_field_info_->statfs_field_array =
3106
0
      statfs_field_array.Serialize(context, creator);
3107
0
  internal_field_info_->statfs_field_bigint_array =
3108
0
      statfs_field_bigint_array.Serialize(context, creator);
3109
  // Return true because we need to maintain the reference to the binding from
3110
  // JS land.
3111
0
  return true;
3112
0
}
3113
3114
0
InternalFieldInfoBase* BindingData::Serialize(int index) {
3115
0
  DCHECK_IS_SNAPSHOT_SLOT(index);
3116
0
  InternalFieldInfo* info = internal_field_info_;
3117
0
  internal_field_info_ = nullptr;
3118
0
  return info;
3119
0
}
3120
3121
void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
3122
122k
                                             Local<ObjectTemplate> target) {
3123
122k
  Isolate* isolate = isolate_data->isolate();
3124
3125
122k
  SetMethod(
3126
122k
      isolate, target, "legacyMainResolve", BindingData::LegacyMainResolve);
3127
122k
}
3128
3129
void BindingData::RegisterExternalReferences(
3130
0
    ExternalReferenceRegistry* registry) {
3131
0
  registry->Register(BindingData::LegacyMainResolve);
3132
0
}
3133
3134
static void CreatePerIsolateProperties(IsolateData* isolate_data,
3135
122k
                                       Local<ObjectTemplate> target) {
3136
122k
  Isolate* isolate = isolate_data->isolate();
3137
3138
122k
  SetMethod(isolate,
3139
122k
            target,
3140
122k
            "getFormatOfExtensionlessFile",
3141
122k
            GetFormatOfExtensionlessFile);
3142
122k
  SetMethod(isolate, target, "access", Access);
3143
122k
  SetMethod(isolate, target, "close", Close);
3144
122k
  SetMethod(isolate, target, "existsSync", ExistsSync);
3145
122k
  SetMethod(isolate, target, "open", Open);
3146
122k
  SetMethod(isolate, target, "openFileHandle", OpenFileHandle);
3147
122k
  SetMethod(isolate, target, "read", Read);
3148
122k
  SetMethod(isolate, target, "readFileUtf8", ReadFileUtf8);
3149
122k
  SetMethod(isolate, target, "readBuffers", ReadBuffers);
3150
122k
  SetMethod(isolate, target, "fdatasync", Fdatasync);
3151
122k
  SetMethod(isolate, target, "fsync", Fsync);
3152
122k
  SetMethod(isolate, target, "rename", Rename);
3153
122k
  SetMethod(isolate, target, "ftruncate", FTruncate);
3154
122k
  SetMethod(isolate, target, "rmdir", RMDir);
3155
122k
  SetMethod(isolate, target, "mkdir", MKDir);
3156
122k
  SetMethod(isolate, target, "readdir", ReadDir);
3157
122k
  SetMethod(isolate, target, "internalModuleStat", InternalModuleStat);
3158
122k
  SetMethod(isolate, target, "stat", Stat);
3159
122k
  SetMethod(isolate, target, "lstat", LStat);
3160
122k
  SetMethod(isolate, target, "fstat", FStat);
3161
122k
  SetMethod(isolate, target, "statfs", StatFs);
3162
122k
  SetMethod(isolate, target, "link", Link);
3163
122k
  SetMethod(isolate, target, "symlink", Symlink);
3164
122k
  SetMethod(isolate, target, "readlink", ReadLink);
3165
122k
  SetMethod(isolate, target, "unlink", Unlink);
3166
122k
  SetMethod(isolate, target, "writeBuffer", WriteBuffer);
3167
122k
  SetMethod(isolate, target, "writeBuffers", WriteBuffers);
3168
122k
  SetMethod(isolate, target, "writeString", WriteString);
3169
122k
  SetMethod(isolate, target, "writeFileUtf8", WriteFileUtf8);
3170
122k
  SetMethod(isolate, target, "realpath", RealPath);
3171
122k
  SetMethod(isolate, target, "copyFile", CopyFile);
3172
3173
122k
  SetMethod(isolate, target, "chmod", Chmod);
3174
122k
  SetMethod(isolate, target, "fchmod", FChmod);
3175
3176
122k
  SetMethod(isolate, target, "chown", Chown);
3177
122k
  SetMethod(isolate, target, "fchown", FChown);
3178
122k
  SetMethod(isolate, target, "lchown", LChown);
3179
3180
122k
  SetMethod(isolate, target, "utimes", UTimes);
3181
122k
  SetMethod(isolate, target, "futimes", FUTimes);
3182
122k
  SetMethod(isolate, target, "lutimes", LUTimes);
3183
3184
122k
  SetMethod(isolate, target, "mkdtemp", Mkdtemp);
3185
3186
122k
  StatWatcher::CreatePerIsolateProperties(isolate_data, target);
3187
122k
  BindingData::CreatePerIsolateProperties(isolate_data, target);
3188
3189
122k
  target->Set(
3190
122k
      FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
3191
122k
      Integer::New(isolate,
3192
122k
                   static_cast<int32_t>(FsStatsOffset::kFsStatsFieldsNumber)));
3193
3194
  // Create FunctionTemplate for FSReqCallback
3195
122k
  Local<FunctionTemplate> fst = NewFunctionTemplate(isolate, NewFSReqCallback);
3196
122k
  fst->InstanceTemplate()->SetInternalFieldCount(
3197
122k
      FSReqBase::kInternalFieldCount);
3198
122k
  fst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
3199
122k
  SetConstructorFunction(isolate, target, "FSReqCallback", fst);
3200
3201
  // Create FunctionTemplate for FileHandleReadWrap. There’s no need
3202
  // to do anything in the constructor, so we only store the instance template.
3203
122k
  Local<FunctionTemplate> fh_rw = FunctionTemplate::New(isolate);
3204
122k
  fh_rw->InstanceTemplate()->SetInternalFieldCount(
3205
122k
      FSReqBase::kInternalFieldCount);
3206
122k
  fh_rw->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
3207
122k
  Local<String> fhWrapString =
3208
122k
      FIXED_ONE_BYTE_STRING(isolate, "FileHandleReqWrap");
3209
122k
  fh_rw->SetClassName(fhWrapString);
3210
122k
  isolate_data->set_filehandlereadwrap_template(fst->InstanceTemplate());
3211
3212
  // Create Function Template for FSReqPromise
3213
122k
  Local<FunctionTemplate> fpt = FunctionTemplate::New(isolate);
3214
122k
  fpt->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
3215
122k
  Local<String> promiseString =
3216
122k
      FIXED_ONE_BYTE_STRING(isolate, "FSReqPromise");
3217
122k
  fpt->SetClassName(promiseString);
3218
122k
  Local<ObjectTemplate> fpo = fpt->InstanceTemplate();
3219
122k
  fpo->SetInternalFieldCount(FSReqBase::kInternalFieldCount);
3220
122k
  isolate_data->set_fsreqpromise_constructor_template(fpo);
3221
3222
  // Create FunctionTemplate for FileHandle
3223
122k
  Local<FunctionTemplate> fd = NewFunctionTemplate(isolate, FileHandle::New);
3224
122k
  fd->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
3225
122k
  SetProtoMethod(isolate, fd, "close", FileHandle::Close);
3226
122k
  SetProtoMethod(isolate, fd, "releaseFD", FileHandle::ReleaseFD);
3227
122k
  Local<ObjectTemplate> fdt = fd->InstanceTemplate();
3228
122k
  fdt->SetInternalFieldCount(FileHandle::kInternalFieldCount);
3229
122k
  StreamBase::AddMethods(isolate_data, fd);
3230
122k
  SetConstructorFunction(isolate, target, "FileHandle", fd);
3231
122k
  isolate_data->set_fd_constructor_template(fdt);
3232
3233
  // Create FunctionTemplate for FileHandle::CloseReq
3234
122k
  Local<FunctionTemplate> fdclose = FunctionTemplate::New(isolate);
3235
122k
  fdclose->SetClassName(FIXED_ONE_BYTE_STRING(isolate,
3236
122k
                        "FileHandleCloseReq"));
3237
122k
  fdclose->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
3238
122k
  Local<ObjectTemplate> fdcloset = fdclose->InstanceTemplate();
3239
122k
  fdcloset->SetInternalFieldCount(FSReqBase::kInternalFieldCount);
3240
122k
  isolate_data->set_fdclose_constructor_template(fdcloset);
3241
3242
122k
  target->Set(isolate, "kUsePromises", isolate_data->fs_use_promises_symbol());
3243
122k
}
3244
3245
static void CreatePerContextProperties(Local<Object> target,
3246
                                       Local<Value> unused,
3247
                                       Local<Context> context,
3248
122k
                                       void* priv) {
3249
122k
  Realm* realm = Realm::GetCurrent(context);
3250
122k
  realm->AddBindingData<BindingData>(target);
3251
122k
}
3252
3253
0
BindingData* FSReqBase::binding_data() {
3254
0
  return binding_data_.get();
3255
0
}
3256
3257
0
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
3258
0
  registry->Register(Access);
3259
0
  StatWatcher::RegisterExternalReferences(registry);
3260
0
  BindingData::RegisterExternalReferences(registry);
3261
3262
0
  registry->Register(GetFormatOfExtensionlessFile);
3263
0
  registry->Register(Close);
3264
0
  registry->Register(ExistsSync);
3265
0
  registry->Register(Open);
3266
0
  registry->Register(OpenFileHandle);
3267
0
  registry->Register(Read);
3268
0
  registry->Register(ReadFileUtf8);
3269
0
  registry->Register(ReadBuffers);
3270
0
  registry->Register(Fdatasync);
3271
0
  registry->Register(Fsync);
3272
0
  registry->Register(Rename);
3273
0
  registry->Register(FTruncate);
3274
0
  registry->Register(RMDir);
3275
0
  registry->Register(MKDir);
3276
0
  registry->Register(ReadDir);
3277
0
  registry->Register(InternalModuleStat);
3278
0
  registry->Register(Stat);
3279
0
  registry->Register(LStat);
3280
0
  registry->Register(FStat);
3281
0
  registry->Register(StatFs);
3282
0
  registry->Register(Link);
3283
0
  registry->Register(Symlink);
3284
0
  registry->Register(ReadLink);
3285
0
  registry->Register(Unlink);
3286
0
  registry->Register(WriteBuffer);
3287
0
  registry->Register(WriteBuffers);
3288
0
  registry->Register(WriteString);
3289
0
  registry->Register(WriteFileUtf8);
3290
0
  registry->Register(RealPath);
3291
0
  registry->Register(CopyFile);
3292
3293
0
  registry->Register(Chmod);
3294
0
  registry->Register(FChmod);
3295
3296
0
  registry->Register(Chown);
3297
0
  registry->Register(FChown);
3298
0
  registry->Register(LChown);
3299
3300
0
  registry->Register(UTimes);
3301
0
  registry->Register(FUTimes);
3302
0
  registry->Register(LUTimes);
3303
3304
0
  registry->Register(Mkdtemp);
3305
0
  registry->Register(NewFSReqCallback);
3306
3307
0
  registry->Register(FileHandle::New);
3308
0
  registry->Register(FileHandle::Close);
3309
0
  registry->Register(FileHandle::ReleaseFD);
3310
0
  StreamBase::RegisterExternalReferences(registry);
3311
0
}
3312
3313
}  // namespace fs
3314
3315
}  // end namespace node
3316
3317
NODE_BINDING_CONTEXT_AWARE_INTERNAL(fs, node::fs::CreatePerContextProperties)
3318
NODE_BINDING_PER_ISOLATE_INIT(fs, node::fs::CreatePerIsolateProperties)
3319
NODE_BINDING_EXTERNAL_REFERENCE(fs, node::fs::RegisterExternalReferences)