fs: return first folder made by mkdir recursive · nodejs/node@ff58854
@@ -586,6 +586,43 @@ void AfterOpenFileHandle(uv_fs_t* req) {
586586 }
587587}
588588589+// Reverse the logic applied by path.toNamespacedPath() to create a
590+// namespace-prefixed path.
591+void FromNamespacedPath(std::string* path) {
592+#ifdef _WIN32
593+if (path->compare(0, 8, "\\\\?\\UNC\\", 8) == 0) {
594+ *path = path->substr(8);
595+ path->insert(0, "\\\\");
596+ } else if (path->compare(0, 4, "\\\\?\\", 4) == 0) {
597+ *path = path->substr(4);
598+ }
599+#endif
600+}
601+602+void AfterMkdirp(uv_fs_t* req) {
603+ FSReqBase* req_wrap = FSReqBase::from_req(req);
604+ FSReqAfterScope after(req_wrap, req);
605+606+ MaybeLocal<Value> path;
607+ Local<Value> error;
608+609+if (after.Proceed()) {
610+if (!req_wrap->continuation_data()->first_path().empty()) {
611+ std::string first_path(req_wrap->continuation_data()->first_path());
612+FromNamespacedPath(&first_path);
613+ path = StringBytes::Encode(req_wrap->env()->isolate(), first_path.c_str(),
614+ req_wrap->encoding(),
615+&error);
616+if (path.IsEmpty())
617+ req_wrap->Reject(error);
618+else
619+ req_wrap->Resolve(path.ToLocalChecked());
620+ } else {
621+ req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
622+ }
623+ }
624+}
625+589626void AfterStringPath(uv_fs_t* req) {
590627 FSReqBase* req_wrap = FSReqBase::from_req(req);
591628 FSReqAfterScope after(req_wrap, req);
@@ -1213,18 +1250,25 @@ int MKDirpSync(uv_loop_t* loop,
12131250const std::string& path,
12141251int mode,
12151252 uv_fs_cb cb) {
1216- FSContinuationData continuation_data(req, mode, cb);
1217- continuation_data.PushPath(std::move(path));
1253+ FSReqWrapSync* req_wrap = ContainerOf(&FSReqWrapSync::req, req);
1254+1255+// on the first iteration of algorithm, stash state information.
1256+if (req_wrap->continuation_data() == nullptr) {
1257+ req_wrap->set_continuation_data(
1258+ std::make_unique<FSContinuationData>(req, mode, cb));
1259+ req_wrap->continuation_data()->PushPath(std::move(path));
1260+ }
121812611219-while (continuation_data.paths().size() > 0) {
1220- std::string next_path = continuation_data.PopPath();
1262+while (req_wrap->continuation_data()->paths().size() > 0) {
1263+ std::string next_path = req_wrap->continuation_data()->PopPath();
12211264int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr);
12221265while (true) {
12231266switch (err) {
12241267// Note: uv_fs_req_cleanup in terminal paths will be called by
12251268// ~FSReqWrapSync():
12261269case 0:
1227-if (continuation_data.paths().size() == 0) {
1270+ req_wrap->continuation_data()->MaybeSetFirstPath(next_path);
1271+if (req_wrap->continuation_data()->paths().size() == 0) {
12281272return 0;
12291273 }
12301274break;
@@ -1237,9 +1281,9 @@ int MKDirpSync(uv_loop_t* loop,
12371281 std::string dirname = next_path.substr(0,
12381282 next_path.find_last_of(kPathSeparator));
12391283if (dirname != next_path) {
1240- continuation_data.PushPath(std::move(next_path));
1241- continuation_data.PushPath(std::move(dirname));
1242- } else if (continuation_data.paths().size() == 0) {
1284+req_wrap->continuation_data()->PushPath(std::move(next_path));
1285+req_wrap->continuation_data()->PushPath(std::move(dirname));
1286+ } else if (req_wrap->continuation_data()->paths().size() == 0) {
12431287 err = UV_EEXIST;
12441288continue;
12451289 }
@@ -1251,7 +1295,8 @@ int MKDirpSync(uv_loop_t* loop,
12511295 err = uv_fs_stat(loop, req, next_path.c_str(), nullptr);
12521296if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) {
12531297uv_fs_req_cleanup(req);
1254-if (orig_err == UV_EEXIST && continuation_data.paths().size() > 0) {
1298+if (orig_err == UV_EEXIST &&
1299+ req_wrap->continuation_data()->paths().size() > 0) {
12551300return UV_ENOTDIR;
12561301 }
12571302return UV_EEXIST;
@@ -1296,8 +1341,10 @@ int MKDirpAsync(uv_loop_t* loop,
12961341// FSReqAfterScope::~FSReqAfterScope()
12971342case 0: {
12981343if (req_wrap->continuation_data()->paths().size() == 0) {
1344+ req_wrap->continuation_data()->MaybeSetFirstPath(path);
12991345 req_wrap->continuation_data()->Done(0);
13001346 } else {
1347+ req_wrap->continuation_data()->MaybeSetFirstPath(path);
13011348uv_fs_req_cleanup(req);
13021349MKDirpAsync(loop, req, path.c_str(),
13031350 req_wrap->continuation_data()->mode(), nullptr);
@@ -1360,6 +1407,25 @@ int MKDirpAsync(uv_loop_t* loop,
13601407return err;
13611408}
136214091410+int CallMKDirpSync(Environment* env, const FunctionCallbackInfo<Value>& args,
1411+ FSReqWrapSync* req_wrap, const char* path, int mode) {
1412+ env->PrintSyncTrace();
1413+int err = MKDirpSync(env->event_loop(), &req_wrap->req, path, mode,
1414+nullptr);
1415+if (err < 0) {
1416+ v8::Local<v8::Context> context = env->context();
1417+ v8::Local<v8::Object> ctx_obj = args[4].As<v8::Object>();
1418+ v8::Isolate* isolate = env->isolate();
1419+ ctx_obj->Set(context,
1420+ env->errno_string(),
1421+v8::Integer::New(isolate, err)).Check();
1422+ ctx_obj->Set(context,
1423+ env->syscall_string(),
1424+OneByteString(isolate, "mkdir")).Check();
1425+ }
1426+return err;
1427+}
1428+13631429static void MKDir(const FunctionCallbackInfo<Value>& args) {
13641430 Environment* env = Environment::GetCurrent(args);
13651431@@ -1378,14 +1444,29 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
13781444 FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
13791445if (req_wrap_async != nullptr) { // mkdir(path, mode, req)
13801446AsyncCall(env, req_wrap_async, args, "mkdir", UTF8,
1381- AfterNoArgs, mkdirp ? MKDirpAsync : uv_fs_mkdir, *path, mode);
1447+ mkdirp ? AfterMkdirp : AfterNoArgs,
1448+ mkdirp ? MKDirpAsync : uv_fs_mkdir, *path, mode);
13821449 } else { // mkdir(path, mode, undefined, ctx)
13831450CHECK_EQ(argc, 5);
13841451 FSReqWrapSync req_wrap_sync;
13851452FS_SYNC_TRACE_BEGIN(mkdir);
13861453if (mkdirp) {
1387-SyncCall(env, args[4], &req_wrap_sync, "mkdir",
1388- MKDirpSync, *path, mode);
1454+int err = CallMKDirpSync(env, args, &req_wrap_sync, *path, mode);
1455+if (err == 0 &&
1456+ !req_wrap_sync.continuation_data()->first_path().empty()) {
1457+ Local<Value> error;
1458+ std::string first_path(req_wrap_sync.continuation_data()->first_path());
1459+FromNamespacedPath(&first_path);
1460+ MaybeLocal<Value> path = StringBytes::Encode(env->isolate(),
1461+ first_path.c_str(),
1462+ UTF8, &error);
1463+if (path.IsEmpty()) {
1464+ Local<Object> ctx = args[4].As<Object>();
1465+ ctx->Set(env->context(), env->error_string(), error).Check();
1466+return;
1467+ }
1468+ args.GetReturnValue().Set(path.ToLocalChecked());
1469+ }
13891470 } else {
13901471SyncCall(env, args[4], &req_wrap_sync, "mkdir",
13911472 uv_fs_mkdir, *path, mode);