GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_dir.cc Lines: 13 178 7.3 %
Date: 2021-02-19 04:08:54 Branches: 2 116 1.7 %

Line Branch Exec Source
1
#include "node_dir.h"
2
#include "node_file-inl.h"
3
#include "node_process.h"
4
#include "memory_tracker-inl.h"
5
#include "util.h"
6
7
#include "tracing/trace_event.h"
8
9
#include "string_bytes.h"
10
11
#include <fcntl.h>
12
#include <sys/types.h>
13
#include <sys/stat.h>
14
#include <cstring>
15
#include <cerrno>
16
#include <climits>
17
18
#include <memory>
19
20
namespace node {
21
22
namespace fs_dir {
23
24
using fs::FSReqAfterScope;
25
using fs::FSReqBase;
26
using fs::FSReqWrapSync;
27
using fs::GetReqWrap;
28
29
using v8::Array;
30
using v8::Context;
31
using v8::FunctionCallbackInfo;
32
using v8::FunctionTemplate;
33
using v8::HandleScope;
34
using v8::Integer;
35
using v8::Isolate;
36
using v8::Local;
37
using v8::MaybeLocal;
38
using v8::Null;
39
using v8::Number;
40
using v8::Object;
41
using v8::ObjectTemplate;
42
using v8::Value;
43
44
#define TRACE_NAME(name) "fs_dir.sync." #name
45
#define GET_TRACE_ENABLED                                                      \
46
  (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED                                 \
47
  (TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)
48
#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...)                                  \
49
  if (GET_TRACE_ENABLED)                                                       \
50
  TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \
51
  ##__VA_ARGS__);
52
#define FS_DIR_SYNC_TRACE_END(syscall, ...)                                    \
53
  if (GET_TRACE_ENABLED)                                                       \
54
  TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall),   \
55
  ##__VA_ARGS__);
56
57
DirHandle::DirHandle(Environment* env, Local<Object> obj, uv_dir_t* dir)
58
    : AsyncWrap(env, obj, AsyncWrap::PROVIDER_DIRHANDLE),
59
      dir_(dir) {
60
  MakeWeak();
61
62
  dir_->nentries = 0;
63
  dir_->dirents = nullptr;
64
}
65
66
DirHandle* DirHandle::New(Environment* env, uv_dir_t* dir) {
67
  Local<Object> obj;
68
  if (!env->dir_instance_template()
69
          ->NewInstance(env->context())
70
          .ToLocal(&obj)) {
71
    return nullptr;
72
  }
73
74
  return new DirHandle(env, obj, dir);
75
}
76
77
void DirHandle::New(const FunctionCallbackInfo<Value>& args) {
78
  CHECK(args.IsConstructCall());
79
}
80
81
DirHandle::~DirHandle() {
82
  CHECK(!closing_);  // We should not be deleting while explicitly closing!
83
  GCClose();         // Close synchronously and emit warning
84
  CHECK(closed_);    // We have to be closed at the point
85
}
86
87
void DirHandle::MemoryInfo(MemoryTracker* tracker) const {
88
  tracker->TrackFieldWithSize("dir", sizeof(*dir_));
89
}
90
91
// Close the directory handle if it hasn't already been closed. A process
92
// warning will be emitted using a SetImmediate to avoid calling back to
93
// JS during GC. If closing the fd fails at this point, a fatal exception
94
// will crash the process immediately.
95
inline void DirHandle::GCClose() {
96
  if (closed_) return;
97
  uv_fs_t req;
98
  int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr);
99
  uv_fs_req_cleanup(&req);
100
  closing_ = false;
101
  closed_ = true;
102
103
  struct err_detail { int ret; };
104
105
  err_detail detail { ret };
106
107
  if (ret < 0) {
108
    // Do not unref this
109
    env()->SetImmediate([detail](Environment* env) {
110
      const char* msg = "Closing directory handle on garbage collection failed";
111
      // This exception will end up being fatal for the process because
112
      // it is being thrown from within the SetImmediate handler and
113
      // there is no JS stack to bubble it to. In other words, tearing
114
      // down the process is the only reasonable thing we can do here.
115
      HandleScope handle_scope(env->isolate());
116
      env->ThrowUVException(detail.ret, "close", msg);
117
    });
118
    return;
119
  }
120
121
  // If the close was successful, we still want to emit a process warning
122
  // to notify that the file descriptor was gc'd. We want to be noisy about
123
  // this because not explicitly closing the DirHandle is a bug.
124
125
  env()->SetImmediate([](Environment* env) {
126
    ProcessEmitWarning(env,
127
                       "Closing directory handle on garbage collection");
128
  }, CallbackFlags::kUnrefed);
129
}
130
131
void AfterClose(uv_fs_t* req) {
132
  FSReqBase* req_wrap = FSReqBase::from_req(req);
133
  FSReqAfterScope after(req_wrap, req);
134
135
  if (after.Proceed())
136
    req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
137
}
138
139
void DirHandle::Close(const FunctionCallbackInfo<Value>& args) {
140
  Environment* env = Environment::GetCurrent(args);
141
142
  const int argc = args.Length();
143
  CHECK_GE(argc, 1);
144
145
  DirHandle* dir;
146
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
147
148
  dir->closing_ = false;
149
  dir->closed_ = true;
150
151
  FSReqBase* req_wrap_async = GetReqWrap(args, 0);
152
  if (req_wrap_async != nullptr) {  // close(req)
153
    AsyncCall(env, req_wrap_async, args, "closedir", UTF8, AfterClose,
154
              uv_fs_closedir, dir->dir());
155
  } else {  // close(undefined, ctx)
156
    CHECK_EQ(argc, 2);
157
    FSReqWrapSync req_wrap_sync;
158
    FS_DIR_SYNC_TRACE_BEGIN(closedir);
159
    SyncCall(env, args[1], &req_wrap_sync, "closedir", uv_fs_closedir,
160
             dir->dir());
161
    FS_DIR_SYNC_TRACE_END(closedir);
162
  }
163
}
164
165
static MaybeLocal<Array> DirentListToArray(
166
    Environment* env,
167
    uv_dirent_t* ents,
168
    int num,
169
    enum encoding encoding,
170
    Local<Value>* err_out) {
171
  MaybeStackBuffer<Local<Value>, 64> entries(num * 2);
172
173
  // Return an array of all read filenames.
174
  int j = 0;
175
  for (int i = 0; i < num; i++) {
176
    Local<Value> filename;
177
    Local<Value> error;
178
    const size_t namelen = strlen(ents[i].name);
179
    if (!StringBytes::Encode(env->isolate(),
180
                             ents[i].name,
181
                             namelen,
182
                             encoding,
183
                             &error).ToLocal(&filename)) {
184
      *err_out = error;
185
      return MaybeLocal<Array>();
186
    }
187
188
    entries[j++] = filename;
189
    entries[j++] = Integer::New(env->isolate(), ents[i].type);
190
  }
191
192
  return Array::New(env->isolate(), entries.out(), j);
193
}
194
195
static void AfterDirRead(uv_fs_t* req) {
196
  BaseObjectPtr<FSReqBase> req_wrap { FSReqBase::from_req(req) };
197
  FSReqAfterScope after(req_wrap.get(), req);
198
199
  if (!after.Proceed()) {
200
    return;
201
  }
202
203
  Environment* env = req_wrap->env();
204
  Isolate* isolate = env->isolate();
205
206
  if (req->result == 0) {
207
    // Done
208
    Local<Value> done = Null(isolate);
209
    after.Clear();
210
    req_wrap->Resolve(done);
211
    return;
212
  }
213
214
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
215
216
  Local<Value> error;
217
  Local<Array> js_array;
218
  if (!DirentListToArray(env,
219
                         dir->dirents,
220
                         static_cast<int>(req->result),
221
                         req_wrap->encoding(),
222
                         &error)
223
           .ToLocal(&js_array)) {
224
    // Clear libuv resources *before* delivering results to JS land because
225
    // that can schedule another operation on the same uv_dir_t. Ditto below.
226
    after.Clear();
227
    return req_wrap->Reject(error);
228
  }
229
230
  after.Clear();
231
  req_wrap->Resolve(js_array);
232
}
233
234
235
void DirHandle::Read(const FunctionCallbackInfo<Value>& args) {
236
  Environment* env = Environment::GetCurrent(args);
237
  Isolate* isolate = env->isolate();
238
239
  const int argc = args.Length();
240
  CHECK_GE(argc, 3);
241
242
  const enum encoding encoding = ParseEncoding(isolate, args[0], UTF8);
243
244
  DirHandle* dir;
245
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
246
247
  CHECK(args[1]->IsNumber());
248
  uint64_t buffer_size = static_cast<uint64_t>(args[1].As<Number>()->Value());
249
250
  if (buffer_size != dir->dirents_.size()) {
251
    dir->dirents_.resize(buffer_size);
252
    dir->dir_->nentries = buffer_size;
253
    dir->dir_->dirents = dir->dirents_.data();
254
  }
255
256
  FSReqBase* req_wrap_async = GetReqWrap(args, 2);
257
  if (req_wrap_async != nullptr) {  // dir.read(encoding, bufferSize, req)
258
    AsyncCall(env, req_wrap_async, args, "readdir", encoding,
259
              AfterDirRead, uv_fs_readdir, dir->dir());
260
  } else {  // dir.read(encoding, bufferSize, undefined, ctx)
261
    CHECK_EQ(argc, 4);
262
    FSReqWrapSync req_wrap_sync;
263
    FS_DIR_SYNC_TRACE_BEGIN(readdir);
264
    int err = SyncCall(env, args[3], &req_wrap_sync, "readdir", uv_fs_readdir,
265
                       dir->dir());
266
    FS_DIR_SYNC_TRACE_END(readdir);
267
    if (err < 0) {
268
      return;  // syscall failed, no need to continue, error info is in ctx
269
    }
270
271
    if (req_wrap_sync.req.result == 0) {
272
      // Done
273
      Local<Value> done = Null(isolate);
274
      args.GetReturnValue().Set(done);
275
      return;
276
    }
277
278
    CHECK_GE(req_wrap_sync.req.result, 0);
279
280
    Local<Value> error;
281
    Local<Array> js_array;
282
    if (!DirentListToArray(env,
283
                           dir->dir()->dirents,
284
                           static_cast<int>(req_wrap_sync.req.result),
285
                           encoding,
286
                           &error)
287
             .ToLocal(&js_array)) {
288
      Local<Object> ctx = args[2].As<Object>();
289
      USE(ctx->Set(env->context(), env->error_string(), error));
290
      return;
291
    }
292
293
    args.GetReturnValue().Set(js_array);
294
  }
295
}
296
297
void AfterOpenDir(uv_fs_t* req) {
298
  FSReqBase* req_wrap = FSReqBase::from_req(req);
299
  FSReqAfterScope after(req_wrap, req);
300
301
  if (!after.Proceed()) {
302
    return;
303
  }
304
305
  Environment* env = req_wrap->env();
306
307
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
308
  DirHandle* handle = DirHandle::New(env, dir);
309
310
  req_wrap->Resolve(handle->object().As<Value>());
311
}
312
313
static void OpenDir(const FunctionCallbackInfo<Value>& args) {
314
  Environment* env = Environment::GetCurrent(args);
315
  Isolate* isolate = env->isolate();
316
317
  const int argc = args.Length();
318
  CHECK_GE(argc, 3);
319
320
  BufferValue path(isolate, args[0]);
321
  CHECK_NOT_NULL(*path);
322
323
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
324
325
  FSReqBase* req_wrap_async = GetReqWrap(args, 2);
326
  if (req_wrap_async != nullptr) {  // openDir(path, encoding, req)
327
    AsyncCall(env, req_wrap_async, args, "opendir", encoding, AfterOpenDir,
328
              uv_fs_opendir, *path);
329
  } else {  // openDir(path, encoding, undefined, ctx)
330
    CHECK_EQ(argc, 4);
331
    FSReqWrapSync req_wrap_sync;
332
    FS_DIR_SYNC_TRACE_BEGIN(opendir);
333
    int result = SyncCall(env, args[3], &req_wrap_sync, "opendir",
334
                          uv_fs_opendir, *path);
335
    FS_DIR_SYNC_TRACE_END(opendir);
336
    if (result < 0) {
337
      return;  // syscall failed, no need to continue, error info is in ctx
338
    }
339
340
    uv_fs_t* req = &req_wrap_sync.req;
341
    uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
342
    DirHandle* handle = DirHandle::New(env, dir);
343
344
    args.GetReturnValue().Set(handle->object().As<Value>());
345
  }
346
}
347
348
120
void Initialize(Local<Object> target,
349
                Local<Value> unused,
350
                Local<Context> context,
351
                void* priv) {
352
120
  Environment* env = Environment::GetCurrent(context);
353
354
120
  env->SetMethod(target, "opendir", OpenDir);
355
356
  // Create FunctionTemplate for DirHandle
357
120
  Local<FunctionTemplate> dir = env->NewFunctionTemplate(DirHandle::New);
358
240
  dir->Inherit(AsyncWrap::GetConstructorTemplate(env));
359
120
  env->SetProtoMethod(dir, "read", DirHandle::Read);
360
120
  env->SetProtoMethod(dir, "close", DirHandle::Close);
361
120
  Local<ObjectTemplate> dirt = dir->InstanceTemplate();
362
120
  dirt->SetInternalFieldCount(DirHandle::kInternalFieldCount);
363
120
  env->SetConstructorFunction(target, "DirHandle", dir);
364
120
  env->set_dir_instance_template(dirt);
365
120
}
366
367
}  // namespace fs_dir
368
369
}  // end namespace node
370
371

481
NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs_dir, node::fs_dir::Initialize)