@@ -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." 
8191#define  GET_TRACE_ENABLED                                                   \
@@ -1236,28 +1246,162 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) {
12361246  }
12371247}
12381248
1249+ int  MKDirpSync (uv_loop_t * loop, uv_fs_t * req, const  std::string& path, int  mode,
1250+                uv_fs_cb cb = nullptr ) {
1251+   FSContinuationData continuation_data (req, mode, cb);
1252+   continuation_data.PushPath (std::move (path));
1253+ 
1254+   while  (continuation_data.paths .size () > 0 ) {
1255+     std::string next_path = continuation_data.PopPath ();
1256+     int  err = uv_fs_mkdir (loop, req, next_path.c_str (), mode, nullptr );
1257+     while  (true ) {
1258+       switch  (err) {
1259+         case  0 :
1260+           if  (continuation_data.paths .size () == 0 ) {
1261+             return  0 ;
1262+           }
1263+           break ;
1264+         case  UV_ENOENT: {
1265+           std::string dirname = next_path.substr (0 ,
1266+                                         next_path.find_last_of (kPathSeparator ));
1267+           if  (dirname != next_path) {
1268+             continuation_data.PushPath (std::move (next_path));
1269+             continuation_data.PushPath (std::move (dirname));
1270+           } else  if  (continuation_data.paths .size () == 0 ) {
1271+             err = UV_EEXIST;
1272+             continue ;
1273+           }
1274+           break ;
1275+         }
1276+         case  UV_EPERM: {
1277+           return  err;
1278+         }
1279+         default :
1280+           uv_fs_req_cleanup (req);
1281+           err = uv_fs_stat (loop, req, next_path.c_str (), nullptr );
1282+           if  (err == 0  && !S_ISDIR (req->statbuf .st_mode )) return  UV_EEXIST;
1283+           if  (err < 0 ) return  err;
1284+           break ;
1285+       }
1286+       break ;
1287+     }
1288+     uv_fs_req_cleanup (req);
1289+   }
1290+ 
1291+   return  0 ;
1292+ }
1293+ 
1294+ int  MKDirpAsync (uv_loop_t * loop,
1295+                 uv_fs_t * req,
1296+                 const  char * path,
1297+                 int  mode,
1298+                 uv_fs_cb cb) {
1299+   FSReqBase* req_wrap = FSReqBase::from_req (req);
1300+   //  on the first iteration of algorithm, stash state information.
1301+   if  (req_wrap->continuation_data  == nullptr ) {
1302+     req_wrap->continuation_data  = std::unique_ptr<FSContinuationData>{
1303+       new  FSContinuationData (req, mode, cb)};
1304+     req_wrap->continuation_data ->PushPath (std::move (path));
1305+   }
1306+ 
1307+   //  on each iteration of algorithm, mkdir directory on top of stack.
1308+   std::string next_path = req_wrap->continuation_data ->PopPath ();
1309+   int  err = uv_fs_mkdir (loop, req, next_path.c_str (), mode,
1310+                         uv_fs_callback_t {[](uv_fs_t * req) {
1311+     FSReqBase* req_wrap = FSReqBase::from_req (req);
1312+     Environment* env = req_wrap->env ();
1313+     uv_loop_t * loop = env->event_loop ();
1314+     std::string path = req->path ;
1315+     int  err = req->result ;
1316+ 
1317+     while  (true ) {
1318+       switch  (err) {
1319+         case  0 : {
1320+           if  (req_wrap->continuation_data ->paths .size () == 0 ) {
1321+             req_wrap->continuation_data ->Done (0 );
1322+           } else  {
1323+             uv_fs_req_cleanup (req);
1324+             MKDirpAsync (loop, req, path.c_str (),
1325+                         req_wrap->continuation_data ->mode , nullptr );
1326+           }
1327+           break ;
1328+         }
1329+         case  UV_ENOENT: {
1330+           std::string dirname = path.substr (0 ,
1331+                                             path.find_last_of (kPathSeparator ));
1332+           if  (dirname != path) {
1333+             req_wrap->continuation_data ->PushPath (std::move (path));
1334+             req_wrap->continuation_data ->PushPath (std::move (dirname));
1335+           } else  if  (req_wrap->continuation_data ->paths .size () == 0 ) {
1336+             err = UV_EEXIST;
1337+             continue ;
1338+           }
1339+           uv_fs_req_cleanup (req);
1340+           MKDirpAsync (loop, req, path.c_str (),
1341+                       req_wrap->continuation_data ->mode , nullptr );
1342+           break ;
1343+         }
1344+         case  UV_EPERM: {
1345+           req_wrap->continuation_data ->Done (err);
1346+           break ;
1347+         }
1348+         default :
1349+           if  (err == UV_EEXIST &&
1350+               req_wrap->continuation_data ->paths .size () > 0 ) {
1351+             uv_fs_req_cleanup (req);
1352+             MKDirpAsync (loop, req, path.c_str (),
1353+                         req_wrap->continuation_data ->mode , nullptr );
1354+           } else  {
1355+             //  verify that the path pointed to is actually a directory.
1356+             uv_fs_req_cleanup (req);
1357+             int  err = uv_fs_stat (loop, req, path.c_str (),
1358+                                  uv_fs_callback_t {[](uv_fs_t * req) {
1359+               FSReqBase* req_wrap = FSReqBase::from_req (req);
1360+               int  err = req->result ;
1361+               if  (err == 0  && !S_ISDIR (req->statbuf .st_mode )) err = UV_EEXIST;
1362+               req_wrap->continuation_data ->Done (err);
1363+             }});
1364+             if  (err < 0 ) req_wrap->continuation_data ->Done (err);
1365+           }
1366+           break ;
1367+       }
1368+       break ;
1369+     }
1370+   }});
1371+ 
1372+   return  err;
1373+ }
1374+ 
12391375static  void  MKDir (const  FunctionCallbackInfo<Value>& args) {
12401376  Environment* env = Environment::GetCurrent (args);
12411377
12421378  const  int  argc = args.Length ();
1243-   CHECK_GE (argc, 3 );
1379+   CHECK_GE (argc, 4 );
12441380
12451381  BufferValue path (env->isolate (), args[0 ]);
12461382  CHECK_NOT_NULL (*path);
12471383
12481384  CHECK (args[1 ]->IsInt32 ());
12491385  const  int  mode = args[1 ].As <Int32>()->Value ();
12501386
1251-   FSReqBase* req_wrap_async = GetReqWrap (env, args[2 ]);
1387+   CHECK (args[2 ]->IsBoolean ());
1388+   bool  mkdirp = args[2 ]->IsTrue ();
1389+ 
1390+   FSReqBase* req_wrap_async = GetReqWrap (env, args[3 ]);
12521391  if  (req_wrap_async != nullptr ) {  //  mkdir(path, mode, req)
1253-     AsyncCall (env, req_wrap_async, args, " mkdir"  AfterNoArgs, 
1254-               uv_fs_mkdir, *path, mode);
1392+     AsyncCall (env, req_wrap_async, args, " mkdir" 
1393+               AfterNoArgs, mkdirp ? MKDirpAsync :  uv_fs_mkdir, *path, mode);
12551394  } else  {  //  mkdir(path, mode, undefined, ctx)
1256-     CHECK_EQ (argc, 4 );
1395+     CHECK_EQ (argc, 5 );
12571396    FSReqWrapSync req_wrap_sync;
12581397    FS_SYNC_TRACE_BEGIN (mkdir);
1259-     SyncCall (env, args[3 ], &req_wrap_sync, " mkdir" 
1260-              uv_fs_mkdir, *path, mode);
1398+     if  (mkdirp) {
1399+       SyncCall (env, args[4 ], &req_wrap_sync, " mkdir" 
1400+                MKDirpSync, *path, mode);
1401+     } else  {
1402+       SyncCall (env, args[4 ], &req_wrap_sync, " mkdir" 
1403+                uv_fs_mkdir, *path, mode);
1404+     }
12611405    FS_SYNC_TRACE_END (mkdir);
12621406  }
12631407}
0 commit comments