@@ -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 \
@@ -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" , UTF8, AfterNoArgs,
1254- uv_fs_mkdir, *path, mode);
1392+ AsyncCall (env, req_wrap_async, args, " mkdir" , UTF8,
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