summaryrefslogtreecommitdiff
path: root/src/node_file.cc
diff options
context:
space:
mode:
authorBenjamin Coe <ben@npmjs.com>2018-08-09 16:52:41 -0700
committerBenjamin Coe <ben@npmjs.com>2018-08-11 12:07:32 -0700
commitbdef1b1eb45e2953e1ff68f0cc9a68ec83573e57 (patch)
treecf12dfaaa414432980c139066aa977ffc0724760 /src/node_file.cc
parente0395247c899af101f8a1f76a8554be1ff14040a (diff)
downloadandroid-node-v8-bdef1b1eb45e2953e1ff68f0cc9a68ec83573e57.tar.gz
android-node-v8-bdef1b1eb45e2953e1ff68f0cc9a68ec83573e57.tar.bz2
android-node-v8-bdef1b1eb45e2953e1ff68f0cc9a68ec83573e57.zip
fs: implement mkdir recursive (mkdirp)
Implements mkdirp functionality in node_file.cc. The Benefit of implementing in C++ layer is that the logic is more easily shared between the Promise and callback implementation and there are notable performance improvements. This commit is part of the Tooling Group Initiative. Refs: https://github.com/nodejs/user-feedback/pull/70 PR-URL: https://github.com/nodejs/node/pull/21875 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Jon Moss <me@jonathanmoss.me> Reviewed-By: Ron Korving <ron@ronkorving.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Sam Ruby <rubys@intertwingly.net> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Diffstat (limited to 'src/node_file.cc')
-rw-r--r--src/node_file.cc158
1 files changed, 151 insertions, 7 deletions
diff --git a/src/node_file.cc b/src/node_file.cc
index b0effaff7b..714dec157b 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -76,6 +76,16 @@ using v8::Value;
# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
+#ifndef S_ISDIR
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifdef __POSIX__
+const char* kPathSeparator = "/";
+#else
+const char* kPathSeparator = "\\/";
+#endif
+
#define GET_OFFSET(a) ((a)->IsNumber() ? (a).As<Integer>()->Value() : -1)
#define TRACE_NAME(name) "fs.sync." #name
#define GET_TRACE_ENABLED \
@@ -1148,11 +1158,137 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) {
}
}
+int MKDirpSync(uv_loop_t* loop, uv_fs_t* req, const std::string& path, int mode,
+ uv_fs_cb cb = nullptr) {
+ FSContinuationData continuation_data(req, mode, cb);
+ continuation_data.PushPath(std::move(path));
+
+ while (continuation_data.paths.size() > 0) {
+ std::string next_path = continuation_data.PopPath();
+ int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr);
+ while (true) {
+ switch (err) {
+ case 0:
+ if (continuation_data.paths.size() == 0) {
+ return 0;
+ }
+ break;
+ case UV_ENOENT: {
+ std::string dirname = next_path.substr(0,
+ next_path.find_last_of(kPathSeparator));
+ if (dirname != next_path) {
+ continuation_data.PushPath(std::move(next_path));
+ continuation_data.PushPath(std::move(dirname));
+ } else if (continuation_data.paths.size() == 0) {
+ err = UV_EEXIST;
+ continue;
+ }
+ break;
+ }
+ case UV_EPERM: {
+ return err;
+ }
+ default:
+ uv_fs_req_cleanup(req);
+ err = uv_fs_stat(loop, req, next_path.c_str(), nullptr);
+ if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) return UV_EEXIST;
+ if (err < 0) return err;
+ break;
+ }
+ break;
+ }
+ uv_fs_req_cleanup(req);
+ }
+
+ return 0;
+}
+
+int MKDirpAsync(uv_loop_t* loop,
+ uv_fs_t* req,
+ const char* path,
+ int mode,
+ uv_fs_cb cb) {
+ FSReqBase* req_wrap = FSReqBase::from_req(req);
+ // on the first iteration of algorithm, stash state information.
+ if (req_wrap->continuation_data == nullptr) {
+ req_wrap->continuation_data = std::unique_ptr<FSContinuationData>{
+ new FSContinuationData(req, mode, cb)};
+ req_wrap->continuation_data->PushPath(std::move(path));
+ }
+
+ // on each iteration of algorithm, mkdir directory on top of stack.
+ std::string next_path = req_wrap->continuation_data->PopPath();
+ int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode,
+ uv_fs_callback_t{[](uv_fs_t* req) {
+ FSReqBase* req_wrap = FSReqBase::from_req(req);
+ Environment* env = req_wrap->env();
+ uv_loop_t* loop = env->event_loop();
+ std::string path = req->path;
+ int err = req->result;
+
+ while (true) {
+ switch (err) {
+ case 0: {
+ if (req_wrap->continuation_data->paths.size() == 0) {
+ req_wrap->continuation_data->Done(0);
+ } else {
+ uv_fs_req_cleanup(req);
+ MKDirpAsync(loop, req, path.c_str(),
+ req_wrap->continuation_data->mode, nullptr);
+ }
+ break;
+ }
+ case UV_ENOENT: {
+ std::string dirname = path.substr(0,
+ path.find_last_of(kPathSeparator));
+ if (dirname != path) {
+ req_wrap->continuation_data->PushPath(std::move(path));
+ req_wrap->continuation_data->PushPath(std::move(dirname));
+ } else if (req_wrap->continuation_data->paths.size() == 0) {
+ err = UV_EEXIST;
+ continue;
+ }
+ uv_fs_req_cleanup(req);
+ MKDirpAsync(loop, req, path.c_str(),
+ req_wrap->continuation_data->mode, nullptr);
+ break;
+ }
+ case UV_EPERM: {
+ req_wrap->continuation_data->Done(err);
+ break;
+ }
+ default:
+ if (err == UV_EEXIST &&
+ req_wrap->continuation_data->paths.size() > 0) {
+ uv_fs_req_cleanup(req);
+ MKDirpAsync(loop, req, path.c_str(),
+ req_wrap->continuation_data->mode, nullptr);
+ } else {
+ // verify that the path pointed to is actually a directory.
+ uv_fs_req_cleanup(req);
+ int err = uv_fs_stat(loop, req, path.c_str(),
+ uv_fs_callback_t{[](uv_fs_t* req) {
+ FSReqBase* req_wrap = FSReqBase::from_req(req);
+ int err = req->result;
+ if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST;
+ req_wrap->continuation_data->Done(err);
+ }});
+ if (err < 0) req_wrap->continuation_data->Done(err);
+ }
+ break;
+ }
+ break;
+ }
+ }});
+
+ return err;
+}
+
static void MKDir(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
const int argc = args.Length();
- CHECK_GE(argc, 3);
+ CHECK_GE(argc, 4);
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
@@ -1160,16 +1296,24 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
CHECK(args[1]->IsInt32());
const int mode = args[1].As<Int32>()->Value();
- FSReqBase* req_wrap_async = GetReqWrap(env, args[2]);
+ CHECK(args[2]->IsBoolean());
+ bool mkdirp = args[2]->IsTrue();
+
+ FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
if (req_wrap_async != nullptr) { // mkdir(path, mode, req)
- AsyncCall(env, req_wrap_async, args, "mkdir", UTF8, AfterNoArgs,
- uv_fs_mkdir, *path, mode);
+ AsyncCall(env, req_wrap_async, args, "mkdir", UTF8,
+ AfterNoArgs, mkdirp ? MKDirpAsync : uv_fs_mkdir, *path, mode);
} else { // mkdir(path, mode, undefined, ctx)
- CHECK_EQ(argc, 4);
+ CHECK_EQ(argc, 5);
FSReqWrapSync req_wrap_sync;
FS_SYNC_TRACE_BEGIN(mkdir);
- SyncCall(env, args[3], &req_wrap_sync, "mkdir",
- uv_fs_mkdir, *path, mode);
+ if (mkdirp) {
+ SyncCall(env, args[4], &req_wrap_sync, "mkdir",
+ MKDirpSync, *path, mode);
+ } else {
+ SyncCall(env, args[4], &req_wrap_sync, "mkdir",
+ uv_fs_mkdir, *path, mode);
+ }
FS_SYNC_TRACE_END(mkdir);
}
}