@@ -76,6 +76,16 @@ using v8::Value;
7676# define MIN (a, b ) ((a) < (b) ? (a) : (b))
7777#endif
7878
79+ #ifndef S_ISDIR
80+ # define S_ISDIR (mode ) (((mode) & S_IFMT) == S_IFDIR)
81+ #endif
82+
83+ #ifdef __POSIX__
84+ const char * kPathSeparator = " /" ;
85+ #else
86+ const char * kPathSeparator = " \\ /" ;
87+ #endif
88+
7989#define GET_OFFSET (a ) ((a)->IsNumber () ? (a).As<Integer>()->Value() : -1)
8090#define TRACE_NAME (name ) " fs.sync." #name
8191#define GET_TRACE_ENABLED \
@@ -1148,28 +1158,162 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) {
11481158 }
11491159}
11501160
1161+ int MKDirpSync (uv_loop_t * loop, uv_fs_t * req, const std::string& path, int mode,
1162+ uv_fs_cb cb = nullptr ) {
1163+ FSContinuationData continuation_data (req, mode, cb);
1164+ continuation_data.PushPath (std::move (path));
1165+
1166+ while (continuation_data.paths .size () > 0 ) {
1167+ std::string next_path = continuation_data.PopPath ();
1168+ int err = uv_fs_mkdir (loop, req, next_path.c_str (), mode, nullptr );
1169+ while (true ) {
1170+ switch (err) {
1171+ case 0 :
1172+ if (continuation_data.paths .size () == 0 ) {
1173+ return 0 ;
1174+ }
1175+ break ;
1176+ case UV_ENOENT: {
1177+ std::string dirname = next_path.substr (0 ,
1178+ next_path.find_last_of (kPathSeparator ));
1179+ if (dirname != next_path) {
1180+ continuation_data.PushPath (std::move (next_path));
1181+ continuation_data.PushPath (std::move (dirname));
1182+ } else if (continuation_data.paths .size () == 0 ) {
1183+ err = UV_EEXIST;
1184+ continue ;
1185+ }
1186+ break ;
1187+ }
1188+ case UV_EPERM: {
1189+ return err;
1190+ }
1191+ default :
1192+ uv_fs_req_cleanup (req);
1193+ err = uv_fs_stat (loop, req, next_path.c_str (), nullptr );
1194+ if (err == 0 && !S_ISDIR (req->statbuf .st_mode )) return UV_EEXIST;
1195+ if (err < 0 ) return err;
1196+ break ;
1197+ }
1198+ break ;
1199+ }
1200+ uv_fs_req_cleanup (req);
1201+ }
1202+
1203+ return 0 ;
1204+ }
1205+
1206+ int MKDirpAsync (uv_loop_t * loop,
1207+ uv_fs_t * req,
1208+ const char * path,
1209+ int mode,
1210+ uv_fs_cb cb) {
1211+ FSReqBase* req_wrap = FSReqBase::from_req (req);
1212+ // on the first iteration of algorithm, stash state information.
1213+ if (req_wrap->continuation_data == nullptr ) {
1214+ req_wrap->continuation_data = std::unique_ptr<FSContinuationData>{
1215+ new FSContinuationData (req, mode, cb)};
1216+ req_wrap->continuation_data ->PushPath (std::move (path));
1217+ }
1218+
1219+ // on each iteration of algorithm, mkdir directory on top of stack.
1220+ std::string next_path = req_wrap->continuation_data ->PopPath ();
1221+ int err = uv_fs_mkdir (loop, req, next_path.c_str (), mode,
1222+ uv_fs_callback_t {[](uv_fs_t * req) {
1223+ FSReqBase* req_wrap = FSReqBase::from_req (req);
1224+ Environment* env = req_wrap->env ();
1225+ uv_loop_t * loop = env->event_loop ();
1226+ std::string path = req->path ;
1227+ int err = req->result ;
1228+
1229+ while (true ) {
1230+ switch (err) {
1231+ case 0 : {
1232+ if (req_wrap->continuation_data ->paths .size () == 0 ) {
1233+ req_wrap->continuation_data ->Done (0 );
1234+ } else {
1235+ uv_fs_req_cleanup (req);
1236+ MKDirpAsync (loop, req, path.c_str (),
1237+ req_wrap->continuation_data ->mode , nullptr );
1238+ }
1239+ break ;
1240+ }
1241+ case UV_ENOENT: {
1242+ std::string dirname = path.substr (0 ,
1243+ path.find_last_of (kPathSeparator ));
1244+ if (dirname != path) {
1245+ req_wrap->continuation_data ->PushPath (std::move (path));
1246+ req_wrap->continuation_data ->PushPath (std::move (dirname));
1247+ } else if (req_wrap->continuation_data ->paths .size () == 0 ) {
1248+ err = UV_EEXIST;
1249+ continue ;
1250+ }
1251+ uv_fs_req_cleanup (req);
1252+ MKDirpAsync (loop, req, path.c_str (),
1253+ req_wrap->continuation_data ->mode , nullptr );
1254+ break ;
1255+ }
1256+ case UV_EPERM: {
1257+ req_wrap->continuation_data ->Done (err);
1258+ break ;
1259+ }
1260+ default :
1261+ if (err == UV_EEXIST &&
1262+ req_wrap->continuation_data ->paths .size () > 0 ) {
1263+ uv_fs_req_cleanup (req);
1264+ MKDirpAsync (loop, req, path.c_str (),
1265+ req_wrap->continuation_data ->mode , nullptr );
1266+ } else {
1267+ // verify that the path pointed to is actually a directory.
1268+ uv_fs_req_cleanup (req);
1269+ int err = uv_fs_stat (loop, req, path.c_str (),
1270+ uv_fs_callback_t {[](uv_fs_t * req) {
1271+ FSReqBase* req_wrap = FSReqBase::from_req (req);
1272+ int err = req->result ;
1273+ if (err == 0 && !S_ISDIR (req->statbuf .st_mode )) err = UV_EEXIST;
1274+ req_wrap->continuation_data ->Done (err);
1275+ }});
1276+ if (err < 0 ) req_wrap->continuation_data ->Done (err);
1277+ }
1278+ break ;
1279+ }
1280+ break ;
1281+ }
1282+ }});
1283+
1284+ return err;
1285+ }
1286+
11511287static void MKDir (const FunctionCallbackInfo<Value>& args) {
11521288 Environment* env = Environment::GetCurrent (args);
11531289
11541290 const int argc = args.Length ();
1155- CHECK_GE (argc, 3 );
1291+ CHECK_GE (argc, 4 );
11561292
11571293 BufferValue path (env->isolate (), args[0 ]);
11581294 CHECK_NOT_NULL (*path);
11591295
11601296 CHECK (args[1 ]->IsInt32 ());
11611297 const int mode = args[1 ].As <Int32>()->Value ();
11621298
1163- FSReqBase* req_wrap_async = GetReqWrap (env, args[2 ]);
1299+ CHECK (args[2 ]->IsBoolean ());
1300+ bool mkdirp = args[2 ]->IsTrue ();
1301+
1302+ FSReqBase* req_wrap_async = GetReqWrap (env, args[3 ]);
11641303 if (req_wrap_async != nullptr ) { // mkdir(path, mode, req)
1165- AsyncCall (env, req_wrap_async, args, " mkdir" , UTF8, AfterNoArgs,
1166- uv_fs_mkdir, *path, mode);
1304+ AsyncCall (env, req_wrap_async, args, " mkdir" , UTF8,
1305+ AfterNoArgs, mkdirp ? MKDirpAsync : uv_fs_mkdir, *path, mode);
11671306 } else { // mkdir(path, mode, undefined, ctx)
1168- CHECK_EQ (argc, 4 );
1307+ CHECK_EQ (argc, 5 );
11691308 FSReqWrapSync req_wrap_sync;
11701309 FS_SYNC_TRACE_BEGIN (mkdir);
1171- SyncCall (env, args[3 ], &req_wrap_sync, " mkdir" ,
1172- uv_fs_mkdir, *path, mode);
1310+ if (mkdirp) {
1311+ SyncCall (env, args[4 ], &req_wrap_sync, " mkdir" ,
1312+ MKDirpSync, *path, mode);
1313+ } else {
1314+ SyncCall (env, args[4 ], &req_wrap_sync, " mkdir" ,
1315+ uv_fs_mkdir, *path, mode);
1316+ }
11731317 FS_SYNC_TRACE_END (mkdir);
11741318 }
11751319}
0 commit comments