• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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