|
48 | 48 | # include <io.h> |
49 | 49 | #endif |
50 | 50 |
|
| 51 | +#ifdef _WIN32 |
| 52 | +#include <windows.h> |
| 53 | +#else |
| 54 | +#include <unistd.h> |
| 55 | +#endif |
| 56 | + |
51 | 57 | namespace node { |
52 | 58 |
|
53 | 59 | namespace fs { |
@@ -1599,6 +1605,102 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) { |
1599 | 1605 | } |
1600 | 1606 | } |
1601 | 1607 |
|
| 1608 | +static void RmSync(const FunctionCallbackInfo<Value>& args) { |
| 1609 | + Environment* env = Environment::GetCurrent(args); |
| 1610 | + Isolate* isolate = env->isolate(); |
| 1611 | + |
| 1612 | + CHECK_EQ(args.Length(), 4); // path, maxRetries, recursive, retryDelay |
| 1613 | + |
| 1614 | + BufferValue path(isolate, args[0]); |
| 1615 | + CHECK_NOT_NULL(*path); |
| 1616 | + ToNamespacedPath(env, &path); |
| 1617 | + THROW_IF_INSUFFICIENT_PERMISSIONS( |
| 1618 | + env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); |
| 1619 | + auto file_path = std::filesystem::path(path.ToStringView()); |
| 1620 | + std::error_code error; |
| 1621 | + auto file_status = std::filesystem::status(file_path, error); |
| 1622 | + |
| 1623 | + if (file_status.type() == std::filesystem::file_type::not_found) { |
| 1624 | + return; |
| 1625 | + } |
| 1626 | + |
| 1627 | + int maxRetries = args[1].As<Int32>()->Value(); |
| 1628 | + int recursive = args[2]->IsTrue(); |
| 1629 | + int retryDelay = args[3].As<Int32>()->Value(); |
| 1630 | + |
| 1631 | + // File is a directory and recursive is false |
| 1632 | + if (file_status.type() == std::filesystem::file_type::directory && |
| 1633 | + !recursive) { |
| 1634 | + return THROW_ERR_FS_EISDIR( |
| 1635 | + isolate, "Path is a directory: %s", file_path.c_str()); |
| 1636 | + } |
| 1637 | + |
| 1638 | + // Allowed errors are: |
| 1639 | + // - EBUSY: std::errc::device_or_resource_busy |
| 1640 | + // - EMFILE: std::errc::too_many_files_open |
| 1641 | + // - ENFILE: std::errc::too_many_files_open_in_system |
| 1642 | + // - ENOTEMPTY: std::errc::directory_not_empty |
| 1643 | + // - EPERM: std::errc::operation_not_permitted |
| 1644 | + auto can_omit_error = [](std::error_code error) -> bool { |
| 1645 | + return (error == std::errc::device_or_resource_busy || |
| 1646 | + error == std::errc::too_many_files_open || |
| 1647 | + error == std::errc::too_many_files_open_in_system || |
| 1648 | + error == std::errc::directory_not_empty || |
| 1649 | + error == std::errc::operation_not_permitted); |
| 1650 | + }; |
| 1651 | + |
| 1652 | + while (maxRetries >= 0) { |
| 1653 | + if (recursive) { |
| 1654 | + std::filesystem::remove_all(file_path, error); |
| 1655 | + } else { |
| 1656 | + std::filesystem::remove(file_path, error); |
| 1657 | + } |
| 1658 | + |
| 1659 | + if (!error || error == std::errc::no_such_file_or_directory) { |
| 1660 | + return; |
| 1661 | + } else if (!can_omit_error(error)) { |
| 1662 | + break; |
| 1663 | + } |
| 1664 | + |
| 1665 | + if (retryDelay != 0) { |
| 1666 | +#ifdef _WIN32 |
| 1667 | + Sleep(retryDelay / 1000); |
| 1668 | +#else |
| 1669 | + sleep(retryDelay / 1000); |
| 1670 | +#endif |
| 1671 | + } |
| 1672 | + maxRetries--; |
| 1673 | + } |
| 1674 | + |
| 1675 | + // This is required since std::filesystem::path::c_str() returns different |
| 1676 | + // values in Windows and Unix. |
| 1677 | +#ifdef _WIN32 |
| 1678 | + auto file_ = file_path.string().c_str(); |
| 1679 | + int permission_denied_error = EPERM; |
| 1680 | +#else |
| 1681 | + auto file_ = file_path.c_str(); |
| 1682 | + int permission_denied_error = EACCES; |
| 1683 | +#endif // !_WIN32 |
| 1684 | + |
| 1685 | + if (error == std::errc::operation_not_permitted) { |
| 1686 | + std::string message = "Operation not permitted: " + file_path.string(); |
| 1687 | + return env->ThrowErrnoException(EPERM, "rm", message.c_str(), file_); |
| 1688 | + } else if (error == std::errc::directory_not_empty) { |
| 1689 | + std::string message = "Directory not empty: " + file_path.string(); |
| 1690 | + return env->ThrowErrnoException(EACCES, "rm", message.c_str(), file_); |
| 1691 | + } else if (error == std::errc::not_a_directory) { |
| 1692 | + std::string message = "Not a directory: " + file_path.string(); |
| 1693 | + return env->ThrowErrnoException(ENOTDIR, "rm", message.c_str(), file_); |
| 1694 | + } else if (error == std::errc::permission_denied) { |
| 1695 | + std::string message = "Permission denied: " + file_path.string(); |
| 1696 | + return env->ThrowErrnoException( |
| 1697 | + permission_denied_error, "rm", message.c_str(), file_); |
| 1698 | + } |
| 1699 | + |
| 1700 | + std::string message = "Unknown error: " + error.message(); |
| 1701 | + return env->ThrowErrnoException(UV_UNKNOWN, "rm", message.c_str(), file_); |
| 1702 | +} |
| 1703 | + |
1602 | 1704 | int MKDirpSync(uv_loop_t* loop, |
1603 | 1705 | uv_fs_t* req, |
1604 | 1706 | const std::string& path, |
@@ -3317,6 +3419,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, |
3317 | 3419 | SetMethod(isolate, target, "rename", Rename); |
3318 | 3420 | SetMethod(isolate, target, "ftruncate", FTruncate); |
3319 | 3421 | SetMethod(isolate, target, "rmdir", RMDir); |
| 3422 | + SetMethod(isolate, target, "rmSync", RmSync); |
3320 | 3423 | SetMethod(isolate, target, "mkdir", MKDir); |
3321 | 3424 | SetMethod(isolate, target, "readdir", ReadDir); |
3322 | 3425 | SetFastMethod(isolate, |
@@ -3441,6 +3544,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
3441 | 3544 | registry->Register(Rename); |
3442 | 3545 | registry->Register(FTruncate); |
3443 | 3546 | registry->Register(RMDir); |
| 3547 | + registry->Register(RmSync); |
3444 | 3548 | registry->Register(MKDir); |
3445 | 3549 | registry->Register(ReadDir); |
3446 | 3550 | registry->Register(InternalModuleStat); |
|
0 commit comments