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: 156 179 87.2 %
Date: 2021-01-16 04:10:54 Branches: 61 116 52.6 %

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
26
DirHandle::DirHandle(Environment* env, Local<Object> obj, uv_dir_t* dir)
58
    : AsyncWrap(env, obj, AsyncWrap::PROVIDER_DIRHANDLE),
59
26
      dir_(dir) {
60
26
  MakeWeak();
61
62
26
  dir_->nentries = 0;
63
26
  dir_->dirents = nullptr;
64
26
}
65
66
26
DirHandle* DirHandle::New(Environment* env, uv_dir_t* dir) {
67
  Local<Object> obj;
68
78
  if (!env->dir_instance_template()
69
78
          ->NewInstance(env->context())
70
26
          .ToLocal(&obj)) {
71
    return nullptr;
72
  }
73
74
26
  return new DirHandle(env, obj, dir);
75
}
76
77
void DirHandle::New(const FunctionCallbackInfo<Value>& args) {
78
  CHECK(args.IsConstructCall());
79
}
80
81
78
DirHandle::~DirHandle() {
82
26
  CHECK(!closing_);  // We should not be deleting while explicitly closing!
83
26
  GCClose();         // Close synchronously and emit warning
84
26
  CHECK(closed_);    // We have to be closed at the point
85
52
}
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
26
inline void DirHandle::GCClose() {
96
40
  if (closed_) return;
97
  uv_fs_t req;
98
12
  int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr);
99
12
  uv_fs_req_cleanup(&req);
100
12
  closing_ = false;
101
12
  closed_ = true;
102
103
  struct err_detail { int ret; };
104
105
12
  err_detail detail { ret };
106
107
12
  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
25
  env()->SetImmediate([](Environment* env) {
126
    ProcessEmitWarning(env,
127
1
                       "Closing directory handle on garbage collection");
128
13
  }, CallbackFlags::kUnrefed);
129
}
130
131
9
void AfterClose(uv_fs_t* req) {
132
9
  FSReqBase* req_wrap = FSReqBase::from_req(req);
133
18
  FSReqAfterScope after(req_wrap, req);
134
135
9
  if (after.Proceed())
136
18
    req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
137
9
}
138
139
14
void DirHandle::Close(const FunctionCallbackInfo<Value>& args) {
140
14
  Environment* env = Environment::GetCurrent(args);
141
142
14
  const int argc = args.Length();
143
14
  CHECK_GE(argc, 1);
144
145
  DirHandle* dir;
146
14
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
147
148
14
  dir->closing_ = false;
149
14
  dir->closed_ = true;
150
151
14
  FSReqBase* req_wrap_async = GetReqWrap(args, 0);
152
14
  if (req_wrap_async != nullptr) {  // close(req)
153
9
    AsyncCall(env, req_wrap_async, args, "closedir", UTF8, AfterClose,
154
9
              uv_fs_closedir, dir->dir());
155
  } else {  // close(undefined, ctx)
156
5
    CHECK_EQ(argc, 2);
157
10
    FSReqWrapSync req_wrap_sync;
158
5
    FS_DIR_SYNC_TRACE_BEGIN(closedir);
159
5
    SyncCall(env, args[1], &req_wrap_sync, "closedir", uv_fs_closedir,
160
5
             dir->dir());
161

5
    FS_DIR_SYNC_TRACE_END(closedir);
162
  }
163
}
164
165
11
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
22
  MaybeStackBuffer<Local<Value>, 64> entries(num * 2);
172
173
  // Return an array of all read filenames.
174
11
  int j = 0;
175
66
  for (int i = 0; i < num; i++) {
176
    Local<Value> filename;
177
    Local<Value> error;
178
55
    const size_t namelen = strlen(ents[i].name);
179
110
    if (!StringBytes::Encode(env->isolate(),
180
55
                             ents[i].name,
181
                             namelen,
182
                             encoding,
183
110
                             &error).ToLocal(&filename)) {
184
      *err_out = error;
185
      return MaybeLocal<Array>();
186
    }
187
188
55
    entries[j++] = filename;
189
110
    entries[j++] = Integer::New(env->isolate(), ents[i].type);
190
  }
191
192
22
  return Array::New(env->isolate(), entries.out(), j);
193
}
194
195
11
static void AfterDirRead(uv_fs_t* req) {
196
20
  BaseObjectPtr<FSReqBase> req_wrap { FSReqBase::from_req(req) };
197
20
  FSReqAfterScope after(req_wrap.get(), req);
198
199
11
  if (!after.Proceed()) {
200
    return;
201
  }
202
203
11
  Environment* env = req_wrap->env();
204
11
  Isolate* isolate = env->isolate();
205
206
11
  if (req->result == 0) {
207
    // Done
208
    Local<Value> done = Null(isolate);
209
2
    after.Clear();
210
2
    req_wrap->Resolve(done);
211
2
    return;
212
  }
213
214
9
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
215
216
  Local<Value> error;
217
  Local<Array> js_array;
218
18
  if (!DirentListToArray(env,
219
                         dir->dirents,
220
9
                         req->result,
221
                         req_wrap->encoding(),
222
27
                         &error).ToLocal(&js_array)) {
223
    // Clear libuv resources *before* delivering results to JS land because
224
    // that can schedule another operation on the same uv_dir_t. Ditto below.
225
    after.Clear();
226
    return req_wrap->Reject(error);
227
  }
228
229
9
  after.Clear();
230
18
  req_wrap->Resolve(js_array);
231
}
232
233
234
14
void DirHandle::Read(const FunctionCallbackInfo<Value>& args) {
235
14
  Environment* env = Environment::GetCurrent(args);
236
14
  Isolate* isolate = env->isolate();
237
238
14
  const int argc = args.Length();
239
14
  CHECK_GE(argc, 3);
240
241
14
  const enum encoding encoding = ParseEncoding(isolate, args[0], UTF8);
242
243
  DirHandle* dir;
244
15
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
245
246
28
  CHECK(args[1]->IsNumber());
247
42
  uint64_t buffer_size = args[1].As<Number>()->Value();
248
249
14
  if (buffer_size != dir->dirents_.size()) {
250
11
    dir->dirents_.resize(buffer_size);
251
11
    dir->dir_->nentries = buffer_size;
252
11
    dir->dir_->dirents = dir->dirents_.data();
253
  }
254
255
14
  FSReqBase* req_wrap_async = GetReqWrap(args, 2);
256
14
  if (req_wrap_async != nullptr) {  // dir.read(encoding, bufferSize, req)
257
11
    AsyncCall(env, req_wrap_async, args, "readdir", encoding,
258
11
              AfterDirRead, uv_fs_readdir, dir->dir());
259
  } else {  // dir.read(encoding, bufferSize, undefined, ctx)
260
3
    CHECK_EQ(argc, 4);
261
5
    FSReqWrapSync req_wrap_sync;
262
3
    FS_DIR_SYNC_TRACE_BEGIN(readdir);
263
3
    int err = SyncCall(env, args[3], &req_wrap_sync, "readdir", uv_fs_readdir,
264
3
                       dir->dir());
265

3
    FS_DIR_SYNC_TRACE_END(readdir);
266
3
    if (err < 0) {
267
      return;  // syscall failed, no need to continue, error info is in ctx
268
    }
269
270

3
    if (req_wrap_sync.req.result == 0) {
271
      // Done
272
      Local<Value> done = Null(isolate);
273
2
      args.GetReturnValue().Set(done);
274
1
      return;
275
    }
276
277
2
    CHECK_GE(req_wrap_sync.req.result, 0);
278
279
    Local<Value> error;
280
    Local<Array> js_array;
281
4
    if (!DirentListToArray(env,
282
2
                           dir->dir()->dirents,
283
2
                           req_wrap_sync.req.result,
284
                           encoding,
285
6
                           &error).ToLocal(&js_array)) {
286
      Local<Object> ctx = args[2].As<Object>();
287
      USE(ctx->Set(env->context(), env->error_string(), error));
288
      return;
289
    }
290
291
4
    args.GetReturnValue().Set(js_array);
292
  }
293
}
294
295
12
void AfterOpenDir(uv_fs_t* req) {
296
12
  FSReqBase* req_wrap = FSReqBase::from_req(req);
297
23
  FSReqAfterScope after(req_wrap, req);
298
299
12
  if (!after.Proceed()) {
300
1
    return;
301
  }
302
303
11
  Environment* env = req_wrap->env();
304
305
11
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
306
11
  DirHandle* handle = DirHandle::New(env, dir);
307
308
22
  req_wrap->Resolve(handle->object().As<Value>());
309
}
310
311
28
static void OpenDir(const FunctionCallbackInfo<Value>& args) {
312
28
  Environment* env = Environment::GetCurrent(args);
313
28
  Isolate* isolate = env->isolate();
314
315
28
  const int argc = args.Length();
316
28
  CHECK_GE(argc, 3);
317
318
55
  BufferValue path(isolate, args[0]);
319
28
  CHECK_NOT_NULL(*path);
320
321
28
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
322
323
28
  FSReqBase* req_wrap_async = GetReqWrap(args, 2);
324
28
  if (req_wrap_async != nullptr) {  // openDir(path, encoding, req)
325
12
    AsyncCall(env, req_wrap_async, args, "opendir", encoding, AfterOpenDir,
326
12
              uv_fs_opendir, *path);
327
  } else {  // openDir(path, encoding, undefined, ctx)
328
16
    CHECK_EQ(argc, 4);
329
31
    FSReqWrapSync req_wrap_sync;
330

16
    FS_DIR_SYNC_TRACE_BEGIN(opendir);
331
16
    int result = SyncCall(env, args[3], &req_wrap_sync, "opendir",
332
16
                          uv_fs_opendir, *path);
333
16
    FS_DIR_SYNC_TRACE_END(opendir);
334
16
    if (result < 0) {
335
1
      return;  // syscall failed, no need to continue, error info is in ctx
336
    }
337
338
15
    uv_fs_t* req = &req_wrap_sync.req;
339
15
    uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
340
15
    DirHandle* handle = DirHandle::New(env, dir);
341
342
45
    args.GetReturnValue().Set(handle->object().As<Value>());
343
  }
344
}
345
346
4989
void Initialize(Local<Object> target,
347
                Local<Value> unused,
348
                Local<Context> context,
349
                void* priv) {
350
4989
  Environment* env = Environment::GetCurrent(context);
351
352
4989
  env->SetMethod(target, "opendir", OpenDir);
353
354
  // Create FunctionTemplate for DirHandle
355
4989
  Local<FunctionTemplate> dir = env->NewFunctionTemplate(DirHandle::New);
356
9978
  dir->Inherit(AsyncWrap::GetConstructorTemplate(env));
357
4989
  env->SetProtoMethod(dir, "read", DirHandle::Read);
358
4989
  env->SetProtoMethod(dir, "close", DirHandle::Close);
359
4989
  Local<ObjectTemplate> dirt = dir->InstanceTemplate();
360
4989
  dirt->SetInternalFieldCount(DirHandle::kInternalFieldCount);
361
4989
  env->SetConstructorFunction(target, "DirHandle", dir);
362
4989
  env->set_dir_instance_template(dirt);
363
4989
}
364
365
}  // namespace fs_dir
366
367
}  // end namespace node
368
369

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