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: 159 179 88.8 %
Date: 2020-02-19 22:14:06 Branches: 59 114 51.8 %

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::Function;
32
using v8::FunctionCallbackInfo;
33
using v8::FunctionTemplate;
34
using v8::HandleScope;
35
using v8::Integer;
36
using v8::Isolate;
37
using v8::Local;
38
using v8::MaybeLocal;
39
using v8::Null;
40
using v8::Number;
41
using v8::Object;
42
using v8::ObjectTemplate;
43
using v8::String;
44
using v8::Value;
45
46
#define TRACE_NAME(name) "fs_dir.sync." #name
47
#define GET_TRACE_ENABLED                                                      \
48
  (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED                                 \
49
  (TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)
50
#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...)                                  \
51
  if (GET_TRACE_ENABLED)                                                       \
52
  TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \
53
  ##__VA_ARGS__);
54
#define FS_DIR_SYNC_TRACE_END(syscall, ...)                                    \
55
  if (GET_TRACE_ENABLED)                                                       \
56
  TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall),   \
57
  ##__VA_ARGS__);
58
59
20
DirHandle::DirHandle(Environment* env, Local<Object> obj, uv_dir_t* dir)
60
    : AsyncWrap(env, obj, AsyncWrap::PROVIDER_DIRHANDLE),
61
20
      dir_(dir) {
62
20
  MakeWeak();
63
64
20
  dir_->nentries = 0;
65
20
  dir_->dirents = nullptr;
66
20
}
67
68
20
DirHandle* DirHandle::New(Environment* env, uv_dir_t* dir) {
69
  Local<Object> obj;
70
60
  if (!env->dir_instance_template()
71
60
          ->NewInstance(env->context())
72
20
          .ToLocal(&obj)) {
73
    return nullptr;
74
  }
75
76
20
  return new DirHandle(env, obj, dir);
77
}
78
79
void DirHandle::New(const FunctionCallbackInfo<Value>& args) {
80
  CHECK(args.IsConstructCall());
81
}
82
83
60
DirHandle::~DirHandle() {
84
20
  CHECK(!closing_);  // We should not be deleting while explicitly closing!
85
20
  GCClose();         // Close synchronously and emit warning
86
20
  CHECK(closed_);    // We have to be closed at the point
87
40
}
88
89
void DirHandle::MemoryInfo(MemoryTracker* tracker) const {
90
  tracker->TrackFieldWithSize("dir", sizeof(*dir_));
91
}
92
93
// Close the directory handle if it hasn't already been closed. A process
94
// warning will be emitted using a SetImmediate to avoid calling back to
95
// JS during GC. If closing the fd fails at this point, a fatal exception
96
// will crash the process immediately.
97
20
inline void DirHandle::GCClose() {
98
29
  if (closed_) return;
99
  uv_fs_t req;
100
11
  int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr);
101
11
  uv_fs_req_cleanup(&req);
102
11
  closing_ = false;
103
11
  closed_ = true;
104
105
  struct err_detail { int ret; };
106
107
11
  err_detail detail { ret };
108
109
11
  if (ret < 0) {
110
    // Do not unref this
111
    env()->SetImmediate([detail](Environment* env) {
112
      char msg[70];
113
      snprintf(msg, arraysize(msg),
114
              "Closing directory handle on garbage collection failed");
115
      // This exception will end up being fatal for the process because
116
      // it is being thrown from within the SetImmediate handler and
117
      // there is no JS stack to bubble it to. In other words, tearing
118
      // down the process is the only reasonable thing we can do here.
119
      HandleScope handle_scope(env->isolate());
120
      env->ThrowUVException(detail.ret, "close", msg);
121
    });
122
    return;
123
  }
124
125
  // If the close was successful, we still want to emit a process warning
126
  // to notify that the file descriptor was gc'd. We want to be noisy about
127
  // this because not explicitly closing the DirHandle is a bug.
128
129
33
  env()->SetUnrefImmediate([](Environment* env) {
130
    ProcessEmitWarning(env,
131
11
                       "Closing directory handle on garbage collection");
132
22
  });
133
}
134
135
8
void AfterClose(uv_fs_t* req) {
136
8
  FSReqBase* req_wrap = FSReqBase::from_req(req);
137
16
  FSReqAfterScope after(req_wrap, req);
138
139
8
  if (after.Proceed())
140
16
    req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
141
8
}
142
143
9
void DirHandle::Close(const FunctionCallbackInfo<Value>& args) {
144
9
  Environment* env = Environment::GetCurrent(args);
145
146
9
  const int argc = args.Length();
147
9
  CHECK_GE(argc, 1);
148
149
  DirHandle* dir;
150
9
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
151
152
9
  dir->closing_ = false;
153
9
  dir->closed_ = true;
154
155
9
  FSReqBase* req_wrap_async = GetReqWrap(env, args[0]);
156
9
  if (req_wrap_async != nullptr) {  // close(req)
157
8
    AsyncCall(env, req_wrap_async, args, "closedir", UTF8, AfterClose,
158
8
              uv_fs_closedir, dir->dir());
159
  } else {  // close(undefined, ctx)
160
1
    CHECK_EQ(argc, 2);
161
2
    FSReqWrapSync req_wrap_sync;
162

1
    FS_DIR_SYNC_TRACE_BEGIN(closedir);
163
1
    SyncCall(env, args[1], &req_wrap_sync, "closedir", uv_fs_closedir,
164
1
             dir->dir());
165

1
    FS_DIR_SYNC_TRACE_END(closedir);
166
  }
167
}
168
169
8
static MaybeLocal<Array> DirentListToArray(
170
    Environment* env,
171
    uv_dirent_t* ents,
172
    int num,
173
    enum encoding encoding,
174
    Local<Value>* err_out) {
175
16
  MaybeStackBuffer<Local<Value>, 64> entries(num * 2);
176
177
  // Return an array of all read filenames.
178
8
  int j = 0;
179
48
  for (int i = 0; i < num; i++) {
180
    Local<Value> filename;
181
    Local<Value> error;
182
40
    const size_t namelen = strlen(ents[i].name);
183
80
    if (!StringBytes::Encode(env->isolate(),
184
40
                             ents[i].name,
185
                             namelen,
186
                             encoding,
187
80
                             &error).ToLocal(&filename)) {
188
      *err_out = error;
189
      return MaybeLocal<Array>();
190
    }
191
192
40
    entries[j++] = filename;
193
80
    entries[j++] = Integer::New(env->isolate(), ents[i].type);
194
  }
195
196
16
  return Array::New(env->isolate(), entries.out(), j);
197
}
198
199
8
static void AfterDirRead(uv_fs_t* req) {
200
8
  FSReqBase* req_wrap = FSReqBase::from_req(req);
201
14
  FSReqAfterScope after(req_wrap, req);
202
203
8
  if (!after.Proceed()) {
204
    return;
205
  }
206
207
8
  Environment* env = req_wrap->env();
208
8
  Isolate* isolate = env->isolate();
209
210
8
  if (req->result == 0) {
211
    // Done
212
    Local<Value> done = Null(isolate);
213
2
    req_wrap->Resolve(done);
214
2
    return;
215
  }
216
217
6
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
218
6
  req->ptr = nullptr;
219
220
  Local<Value> error;
221
  Local<Array> js_array;
222
12
  if (!DirentListToArray(env,
223
                         dir->dirents,
224
6
                         req->result,
225
                         req_wrap->encoding(),
226
18
                         &error).ToLocal(&js_array)) {
227
    return req_wrap->Reject(error);
228
  }
229
230
12
  req_wrap->Resolve(js_array);
231
}
232
233
234
11
void DirHandle::Read(const FunctionCallbackInfo<Value>& args) {
235
11
  Environment* env = Environment::GetCurrent(args);
236
11
  Isolate* isolate = env->isolate();
237
238
11
  const int argc = args.Length();
239
11
  CHECK_GE(argc, 3);
240
241
11
  const enum encoding encoding = ParseEncoding(isolate, args[0], UTF8);
242
243
  DirHandle* dir;
244
12
  ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
245
246
22
  CHECK(args[1]->IsNumber());
247
33
  uint64_t buffer_size = args[1].As<Number>()->Value();
248
249
11
  if (buffer_size != dir->dirents_.size()) {
250
8
    dir->dirents_.resize(buffer_size);
251
8
    dir->dir_->nentries = buffer_size;
252
8
    dir->dir_->dirents = dir->dirents_.data();
253
  }
254
255
11
  FSReqBase* req_wrap_async = GetReqWrap(env, args[2]);
256
11
  if (req_wrap_async != nullptr) {  // dir.read(encoding, bufferSize, req)
257
8
    AsyncCall(env, req_wrap_async, args, "readdir", encoding,
258
8
              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
9
void AfterOpenDir(uv_fs_t* req) {
296
9
  FSReqBase* req_wrap = FSReqBase::from_req(req);
297
17
  FSReqAfterScope after(req_wrap, req);
298
299
9
  if (!after.Proceed()) {
300
1
    return;
301
  }
302
303
8
  Environment* env = req_wrap->env();
304
305
8
  uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
306
8
  DirHandle* handle = DirHandle::New(env, dir);
307
308
16
  req_wrap->Resolve(handle->object().As<Value>());
309
}
310
311
22
static void OpenDir(const FunctionCallbackInfo<Value>& args) {
312
22
  Environment* env = Environment::GetCurrent(args);
313
22
  Isolate* isolate = env->isolate();
314
315
22
  const int argc = args.Length();
316
22
  CHECK_GE(argc, 3);
317
318
43
  BufferValue path(isolate, args[0]);
319
22
  CHECK_NOT_NULL(*path);
320
321
22
  const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
322
323
22
  FSReqBase* req_wrap_async = GetReqWrap(env, args[2]);
324
22
  if (req_wrap_async != nullptr) {  // openDir(path, encoding, req)
325
9
    AsyncCall(env, req_wrap_async, args, "opendir", encoding, AfterOpenDir,
326
9
              uv_fs_opendir, *path);
327
  } else {  // openDir(path, encoding, undefined, ctx)
328
13
    CHECK_EQ(argc, 4);
329
25
    FSReqWrapSync req_wrap_sync;
330

13
    FS_DIR_SYNC_TRACE_BEGIN(opendir);
331
13
    int result = SyncCall(env, args[3], &req_wrap_sync, "opendir",
332
13
                          uv_fs_opendir, *path);
333

13
    FS_DIR_SYNC_TRACE_END(opendir);
334
13
    if (result < 0) {
335
1
      return;  // syscall failed, no need to continue, error info is in ctx
336
    }
337
338
12
    uv_fs_t* req = &req_wrap_sync.req;
339
12
    uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
340
12
    DirHandle* handle = DirHandle::New(env, dir);
341
342
36
    args.GetReturnValue().Set(handle->object().As<Value>());
343
  }
344
}
345
346
4377
void Initialize(Local<Object> target,
347
                Local<Value> unused,
348
                Local<Context> context,
349
                void* priv) {
350
4377
  Environment* env = Environment::GetCurrent(context);
351
4377
  Isolate* isolate = env->isolate();
352
353
4377
  env->SetMethod(target, "opendir", OpenDir);
354
355
  // Create FunctionTemplate for DirHandle
356
4377
  Local<FunctionTemplate> dir = env->NewFunctionTemplate(DirHandle::New);
357
8754
  dir->Inherit(AsyncWrap::GetConstructorTemplate(env));
358
4377
  env->SetProtoMethod(dir, "read", DirHandle::Read);
359
4377
  env->SetProtoMethod(dir, "close", DirHandle::Close);
360
4377
  Local<ObjectTemplate> dirt = dir->InstanceTemplate();
361
4377
  dirt->SetInternalFieldCount(DirHandle::kDirHandleFieldCount);
362
  Local<String> handleString =
363
4377
       FIXED_ONE_BYTE_STRING(isolate, "DirHandle");
364
4377
  dir->SetClassName(handleString);
365
  target
366
8754
      ->Set(context, handleString,
367
17508
            dir->GetFunction(env->context()).ToLocalChecked())
368
      .FromJust();
369
4377
  env->set_dir_instance_template(dirt);
370
4377
}
371
372
}  // namespace fs_dir
373
374
}  // end namespace node
375
376

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