1 #ifndef SRC_NODE_FILE_INL_H_
2 #define SRC_NODE_FILE_INL_H_
3
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6 #include "node_file.h"
7 #include "req_wrap-inl.h"
8
9 namespace node {
10 namespace fs {
11
FSContinuationData(uv_fs_t * req,int mode,uv_fs_cb done_cb)12 FSContinuationData::FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb)
13 : done_cb_(done_cb), req_(req), mode_(mode) {
14 }
15
PushPath(std::string && path)16 void FSContinuationData::PushPath(std::string&& path) {
17 paths_.emplace_back(std::move(path));
18 }
19
PushPath(const std::string & path)20 void FSContinuationData::PushPath(const std::string& path) {
21 paths_.push_back(path);
22 }
23
MaybeSetFirstPath(const std::string & path)24 void FSContinuationData::MaybeSetFirstPath(const std::string& path) {
25 if (first_path_.empty()) {
26 first_path_ = path;
27 }
28 }
29
PopPath()30 std::string FSContinuationData::PopPath() {
31 CHECK(!paths_.empty());
32 std::string path = std::move(paths_.back());
33 paths_.pop_back();
34 return path;
35 }
36
Done(int result)37 void FSContinuationData::Done(int result) {
38 req_->result = result;
39 done_cb_(req_);
40 }
41
FSReqBase(BindingData * binding_data,v8::Local<v8::Object> req,AsyncWrap::ProviderType type,bool use_bigint)42 FSReqBase::FSReqBase(BindingData* binding_data,
43 v8::Local<v8::Object> req,
44 AsyncWrap::ProviderType type,
45 bool use_bigint)
46 : ReqWrap(binding_data->env(), req, type),
47 use_bigint_(use_bigint),
48 binding_data_(binding_data) {
49 }
50
Init(const char * syscall,const char * data,size_t len,enum encoding encoding)51 void FSReqBase::Init(const char* syscall,
52 const char* data,
53 size_t len,
54 enum encoding encoding) {
55 syscall_ = syscall;
56 encoding_ = encoding;
57
58 if (data != nullptr) {
59 CHECK(!has_data_);
60 buffer_.AllocateSufficientStorage(len + 1);
61 buffer_.SetLengthAndZeroTerminate(len);
62 memcpy(*buffer_, data, len);
63 has_data_ = true;
64 }
65 }
66
67 FSReqBase::FSReqBuffer&
Init(const char * syscall,size_t len,enum encoding encoding)68 FSReqBase::Init(const char* syscall, size_t len, enum encoding encoding) {
69 syscall_ = syscall;
70 encoding_ = encoding;
71
72 buffer_.AllocateSufficientStorage(len + 1);
73 has_data_ = false; // so that the data does not show up in error messages
74 return buffer_;
75 }
76
FSReqCallback(BindingData * binding_data,v8::Local<v8::Object> req,bool use_bigint)77 FSReqCallback::FSReqCallback(BindingData* binding_data,
78 v8::Local<v8::Object> req,
79 bool use_bigint)
80 : FSReqBase(binding_data,
81 req,
82 AsyncWrap::PROVIDER_FSREQCALLBACK,
83 use_bigint) {}
84
85 template <typename NativeT, typename V8T>
FillStatsArray(AliasedBufferBase<NativeT,V8T> * fields,const uv_stat_t * s,const size_t offset)86 void FillStatsArray(AliasedBufferBase<NativeT, V8T>* fields,
87 const uv_stat_t* s,
88 const size_t offset) {
89 #define SET_FIELD_WITH_STAT(stat_offset, stat) \
90 fields->SetValue(offset + static_cast<size_t>(FsStatsOffset::stat_offset), \
91 static_cast<NativeT>(stat))
92
93 // On win32, time is stored in uint64_t and starts from 1601-01-01.
94 // libuv calculates tv_sec and tv_nsec from it and converts to signed long,
95 // which causes Y2038 overflow. On the other platforms it is safe to treat
96 // negative values as pre-epoch time.
97 #ifdef _WIN32
98 #define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \
99 /* NOLINTNEXTLINE(runtime/int) */ \
100 SET_FIELD_WITH_STAT(stat_offset, static_cast<unsigned long>(stat))
101 #else
102 #define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \
103 SET_FIELD_WITH_STAT(stat_offset, static_cast<double>(stat))
104 #endif // _WIN32
105
106 SET_FIELD_WITH_STAT(kDev, s->st_dev);
107 SET_FIELD_WITH_STAT(kMode, s->st_mode);
108 SET_FIELD_WITH_STAT(kNlink, s->st_nlink);
109 SET_FIELD_WITH_STAT(kUid, s->st_uid);
110 SET_FIELD_WITH_STAT(kGid, s->st_gid);
111 SET_FIELD_WITH_STAT(kRdev, s->st_rdev);
112 SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize);
113 SET_FIELD_WITH_STAT(kIno, s->st_ino);
114 SET_FIELD_WITH_STAT(kSize, s->st_size);
115 SET_FIELD_WITH_STAT(kBlocks, s->st_blocks);
116
117 SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec);
118 SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec);
119 SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec);
120 SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec);
121 SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec);
122 SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec);
123 SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec);
124 SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec);
125
126 #undef SET_FIELD_WITH_TIME_STAT
127 #undef SET_FIELD_WITH_STAT
128 }
129
FillGlobalStatsArray(BindingData * binding_data,const bool use_bigint,const uv_stat_t * s,const bool second)130 v8::Local<v8::Value> FillGlobalStatsArray(BindingData* binding_data,
131 const bool use_bigint,
132 const uv_stat_t* s,
133 const bool second) {
134 const ptrdiff_t offset =
135 second ? static_cast<ptrdiff_t>(FsStatsOffset::kFsStatsFieldsNumber) : 0;
136 if (use_bigint) {
137 auto* const arr = &binding_data->stats_field_bigint_array;
138 FillStatsArray(arr, s, offset);
139 return arr->GetJSArray();
140 } else {
141 auto* const arr = &binding_data->stats_field_array;
142 FillStatsArray(arr, s, offset);
143 return arr->GetJSArray();
144 }
145 }
146
147 template <typename NativeT, typename V8T>
FillStatFsArray(AliasedBufferBase<NativeT,V8T> * fields,const uv_statfs_t * s)148 void FillStatFsArray(AliasedBufferBase<NativeT, V8T>* fields,
149 const uv_statfs_t* s) {
150 #define SET_FIELD(field, stat) \
151 fields->SetValue(static_cast<size_t>(FsStatFsOffset::field), \
152 static_cast<NativeT>(stat))
153
154 SET_FIELD(kType, s->f_type);
155 SET_FIELD(kBSize, s->f_bsize);
156 SET_FIELD(kBlocks, s->f_blocks);
157 SET_FIELD(kBFree, s->f_bfree);
158 SET_FIELD(kBAvail, s->f_bavail);
159 SET_FIELD(kFiles, s->f_files);
160 SET_FIELD(kFFree, s->f_ffree);
161
162 #undef SET_FIELD
163 }
164
FillGlobalStatFsArray(BindingData * binding_data,const bool use_bigint,const uv_statfs_t * s)165 v8::Local<v8::Value> FillGlobalStatFsArray(BindingData* binding_data,
166 const bool use_bigint,
167 const uv_statfs_t* s) {
168 if (use_bigint) {
169 auto* const arr = &binding_data->statfs_field_bigint_array;
170 FillStatFsArray(arr, s);
171 return arr->GetJSArray();
172 } else {
173 auto* const arr = &binding_data->statfs_field_array;
174 FillStatFsArray(arr, s);
175 return arr->GetJSArray();
176 }
177 }
178
179 template <typename AliasedBufferT>
180 FSReqPromise<AliasedBufferT>*
New(BindingData * binding_data,bool use_bigint)181 FSReqPromise<AliasedBufferT>::New(BindingData* binding_data,
182 bool use_bigint) {
183 Environment* env = binding_data->env();
184 v8::Local<v8::Object> obj;
185 if (!env->fsreqpromise_constructor_template()
186 ->NewInstance(env->context())
187 .ToLocal(&obj)) {
188 return nullptr;
189 }
190 v8::Local<v8::Promise::Resolver> resolver;
191 if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) ||
192 obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) {
193 return nullptr;
194 }
195 return new FSReqPromise(binding_data, obj, use_bigint);
196 }
197
198 template <typename AliasedBufferT>
~FSReqPromise()199 FSReqPromise<AliasedBufferT>::~FSReqPromise() {
200 // Validate that the promise was explicitly resolved or rejected but only if
201 // the Isolate is not terminating because in this case the promise might have
202 // not finished.
203 CHECK_IMPLIES(!finished_, !env()->can_call_into_js());
204 }
205
206 template <typename AliasedBufferT>
FSReqPromise(BindingData * binding_data,v8::Local<v8::Object> obj,bool use_bigint)207 FSReqPromise<AliasedBufferT>::FSReqPromise(BindingData* binding_data,
208 v8::Local<v8::Object> obj,
209 bool use_bigint)
210 : FSReqBase(
211 binding_data, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint),
212 stats_field_array_(
213 env()->isolate(),
214 static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber)),
215 statfs_field_array_(
216 env()->isolate(),
217 static_cast<size_t>(FsStatFsOffset::kFsStatFsFieldsNumber)) {}
218
219 template <typename AliasedBufferT>
Reject(v8::Local<v8::Value> reject)220 void FSReqPromise<AliasedBufferT>::Reject(v8::Local<v8::Value> reject) {
221 finished_ = true;
222 v8::HandleScope scope(env()->isolate());
223 InternalCallbackScope callback_scope(this);
224 v8::Local<v8::Value> value =
225 object()->Get(env()->context(),
226 env()->promise_string()).ToLocalChecked();
227 v8::Local<v8::Promise::Resolver> resolver = value.As<v8::Promise::Resolver>();
228 USE(resolver->Reject(env()->context(), reject).FromJust());
229 }
230
231 template <typename AliasedBufferT>
Resolve(v8::Local<v8::Value> value)232 void FSReqPromise<AliasedBufferT>::Resolve(v8::Local<v8::Value> value) {
233 finished_ = true;
234 v8::HandleScope scope(env()->isolate());
235 InternalCallbackScope callback_scope(this);
236 v8::Local<v8::Value> val =
237 object()->Get(env()->context(),
238 env()->promise_string()).ToLocalChecked();
239 v8::Local<v8::Promise::Resolver> resolver = val.As<v8::Promise::Resolver>();
240 USE(resolver->Resolve(env()->context(), value).FromJust());
241 }
242
243 template <typename AliasedBufferT>
ResolveStat(const uv_stat_t * stat)244 void FSReqPromise<AliasedBufferT>::ResolveStat(const uv_stat_t* stat) {
245 FillStatsArray(&stats_field_array_, stat);
246 Resolve(stats_field_array_.GetJSArray());
247 }
248
249 template <typename AliasedBufferT>
ResolveStatFs(const uv_statfs_t * stat)250 void FSReqPromise<AliasedBufferT>::ResolveStatFs(const uv_statfs_t* stat) {
251 FillStatFsArray(&statfs_field_array_, stat);
252 Resolve(statfs_field_array_.GetJSArray());
253 }
254
255 template <typename AliasedBufferT>
SetReturnValue(const v8::FunctionCallbackInfo<v8::Value> & args)256 void FSReqPromise<AliasedBufferT>::SetReturnValue(
257 const v8::FunctionCallbackInfo<v8::Value>& args) {
258 v8::Local<v8::Value> val =
259 object()->Get(env()->context(),
260 env()->promise_string()).ToLocalChecked();
261 v8::Local<v8::Promise::Resolver> resolver = val.As<v8::Promise::Resolver>();
262 args.GetReturnValue().Set(resolver->GetPromise());
263 }
264
265 template <typename AliasedBufferT>
MemoryInfo(MemoryTracker * tracker)266 void FSReqPromise<AliasedBufferT>::MemoryInfo(MemoryTracker* tracker) const {
267 FSReqBase::MemoryInfo(tracker);
268 tracker->TrackField("stats_field_array", stats_field_array_);
269 tracker->TrackField("statfs_field_array", statfs_field_array_);
270 }
271
GetReqWrap(const v8::FunctionCallbackInfo<v8::Value> & args,int index,bool use_bigint)272 FSReqBase* GetReqWrap(const v8::FunctionCallbackInfo<v8::Value>& args,
273 int index,
274 bool use_bigint) {
275 v8::Local<v8::Value> value = args[index];
276 if (value->IsObject()) {
277 return Unwrap<FSReqBase>(value.As<v8::Object>());
278 }
279
280 BindingData* binding_data = Realm::GetBindingData<BindingData>(args);
281 Environment* env = binding_data->env();
282 if (value->StrictEquals(env->fs_use_promises_symbol())) {
283 if (use_bigint) {
284 return FSReqPromise<AliasedBigInt64Array>::New(binding_data, use_bigint);
285 } else {
286 return FSReqPromise<AliasedFloat64Array>::New(binding_data, use_bigint);
287 }
288 }
289 return nullptr;
290 }
291
292 // Returns nullptr if the operation fails from the start.
293 template <typename Func, typename... Args>
AsyncDestCall(Environment * env,FSReqBase * req_wrap,const v8::FunctionCallbackInfo<v8::Value> & args,const char * syscall,const char * dest,size_t len,enum encoding enc,uv_fs_cb after,Func fn,Args...fn_args)294 FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap,
295 const v8::FunctionCallbackInfo<v8::Value>& args,
296 const char* syscall, const char* dest,
297 size_t len, enum encoding enc, uv_fs_cb after,
298 Func fn, Args... fn_args) {
299 CHECK_NOT_NULL(req_wrap);
300 req_wrap->Init(syscall, dest, len, enc);
301 int err = req_wrap->Dispatch(fn, fn_args..., after);
302 if (err < 0) {
303 uv_fs_t* uv_req = req_wrap->req();
304 uv_req->result = err;
305 uv_req->path = nullptr;
306 after(uv_req); // after may delete req_wrap if there is an error
307 req_wrap = nullptr;
308 } else {
309 req_wrap->SetReturnValue(args);
310 }
311
312 return req_wrap;
313 }
314
315 // Returns nullptr if the operation fails from the start.
316 template <typename Func, typename... Args>
AsyncCall(Environment * env,FSReqBase * req_wrap,const v8::FunctionCallbackInfo<v8::Value> & args,const char * syscall,enum encoding enc,uv_fs_cb after,Func fn,Args...fn_args)317 FSReqBase* AsyncCall(Environment* env,
318 FSReqBase* req_wrap,
319 const v8::FunctionCallbackInfo<v8::Value>& args,
320 const char* syscall, enum encoding enc,
321 uv_fs_cb after, Func fn, Args... fn_args) {
322 return AsyncDestCall(env, req_wrap, args,
323 syscall, nullptr, 0, enc,
324 after, fn, fn_args...);
325 }
326
327 // Template counterpart of SYNC_CALL, except that it only puts
328 // the error number and the syscall in the context instead of
329 // creating an error in the C++ land.
330 // ctx must be checked using value->IsObject() before being passed.
331 template <typename Func, typename... Args>
SyncCall(Environment * env,v8::Local<v8::Value> ctx,FSReqWrapSync * req_wrap,const char * syscall,Func fn,Args...args)332 int SyncCall(Environment* env, v8::Local<v8::Value> ctx,
333 FSReqWrapSync* req_wrap, const char* syscall,
334 Func fn, Args... args) {
335 env->PrintSyncTrace();
336 int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr);
337 if (err < 0) {
338 v8::Local<v8::Context> context = env->context();
339 v8::Local<v8::Object> ctx_obj = ctx.As<v8::Object>();
340 v8::Isolate* isolate = env->isolate();
341 ctx_obj->Set(context,
342 env->errno_string(),
343 v8::Integer::New(isolate, err)).Check();
344 ctx_obj->Set(context,
345 env->syscall_string(),
346 OneByteString(isolate, syscall)).Check();
347 }
348 return err;
349 }
350
351 } // namespace fs
352 } // namespace node
353
354 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
355
356 #endif // SRC_NODE_FILE_INL_H_
357