|
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 {
|
@@ -1624,6 +1630,102 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) {
|
1624 | 1630 | }
|
1625 | 1631 | }
|
1626 | 1632 |
|
| 1633 | +static void RmSync(const FunctionCallbackInfo<Value>& args) { |
| 1634 | + Environment* env = Environment::GetCurrent(args); |
| 1635 | + Isolate* isolate = env->isolate(); |
| 1636 | + |
| 1637 | + CHECK_EQ(args.Length(), 4); // path, maxRetries, recursive, retryDelay |
| 1638 | + |
| 1639 | + BufferValue path(isolate, args[0]); |
| 1640 | + CHECK_NOT_NULL(*path); |
| 1641 | + ToNamespacedPath(env, &path); |
| 1642 | + THROW_IF_INSUFFICIENT_PERMISSIONS( |
| 1643 | + env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); |
| 1644 | + auto file_path = std::filesystem::path(path.ToStringView()); |
| 1645 | + std::error_code error; |
| 1646 | + auto file_status = std::filesystem::status(file_path, error); |
| 1647 | + |
| 1648 | + if (file_status.type() == std::filesystem::file_type::not_found) { |
| 1649 | + return; |
| 1650 | + } |
| 1651 | + |
| 1652 | + int maxRetries = args[1].As<Int32>()->Value(); |
| 1653 | + int recursive = args[2]->IsTrue(); |
| 1654 | + int retryDelay = args[3].As<Int32>()->Value(); |
| 1655 | + |
| 1656 | + // File is a directory and recursive is false |
| 1657 | + if (file_status.type() == std::filesystem::file_type::directory && |
| 1658 | + !recursive) { |
| 1659 | + return THROW_ERR_FS_EISDIR( |
| 1660 | + isolate, "Path is a directory: %s", file_path.c_str()); |
| 1661 | + } |
| 1662 | + |
| 1663 | + // Allowed errors are: |
| 1664 | + // - EBUSY: std::errc::device_or_resource_busy |
| 1665 | + // - EMFILE: std::errc::too_many_files_open |
| 1666 | + // - ENFILE: std::errc::too_many_files_open_in_system |
| 1667 | + // - ENOTEMPTY: std::errc::directory_not_empty |
| 1668 | + // - EPERM: std::errc::operation_not_permitted |
| 1669 | + auto can_omit_error = [](std::error_code error) -> bool { |
| 1670 | + return (error == std::errc::device_or_resource_busy || |
| 1671 | + error == std::errc::too_many_files_open || |
| 1672 | + error == std::errc::too_many_files_open_in_system || |
| 1673 | + error == std::errc::directory_not_empty || |
| 1674 | + error == std::errc::operation_not_permitted); |
| 1675 | + }; |
| 1676 | + |
| 1677 | + while (maxRetries >= 0) { |
| 1678 | + if (recursive) { |
| 1679 | + std::filesystem::remove_all(file_path, error); |
| 1680 | + } else { |
| 1681 | + std::filesystem::remove(file_path, error); |
| 1682 | + } |
| 1683 | + |
| 1684 | + if (!error || error == std::errc::no_such_file_or_directory) { |
| 1685 | + return; |
| 1686 | + } else if (!can_omit_error(error)) { |
| 1687 | + break; |
| 1688 | + } |
| 1689 | + |
| 1690 | + if (retryDelay != 0) { |
| 1691 | +#ifdef _WIN32 |
| 1692 | + Sleep(retryDelay / 1000); |
| 1693 | +#else |
| 1694 | + sleep(retryDelay / 1000); |
| 1695 | +#endif |
| 1696 | + } |
| 1697 | + maxRetries--; |
| 1698 | + } |
| 1699 | + |
| 1700 | + // This is required since std::filesystem::path::c_str() returns different |
| 1701 | + // values in Windows and Unix. |
| 1702 | +#ifdef _WIN32 |
| 1703 | + auto file_ = file_path.string().c_str(); |
| 1704 | + int permission_denied_error = EPERM; |
| 1705 | +#else |
| 1706 | + auto file_ = file_path.c_str(); |
| 1707 | + int permission_denied_error = EACCES; |
| 1708 | +#endif // !_WIN32 |
| 1709 | + |
| 1710 | + if (error == std::errc::operation_not_permitted) { |
| 1711 | + std::string message = "Operation not permitted: " + file_path.string(); |
| 1712 | + return env->ThrowErrnoException(EPERM, "rm", message.c_str(), file_); |
| 1713 | + } else if (error == std::errc::directory_not_empty) { |
| 1714 | + std::string message = "Directory not empty: " + file_path.string(); |
| 1715 | + return env->ThrowErrnoException(EACCES, "rm", message.c_str(), file_); |
| 1716 | + } else if (error == std::errc::not_a_directory) { |
| 1717 | + std::string message = "Not a directory: " + file_path.string(); |
| 1718 | + return env->ThrowErrnoException(ENOTDIR, "rm", message.c_str(), file_); |
| 1719 | + } else if (error == std::errc::permission_denied) { |
| 1720 | + std::string message = "Permission denied: " + file_path.string(); |
| 1721 | + return env->ThrowErrnoException( |
| 1722 | + permission_denied_error, "rm", message.c_str(), file_); |
| 1723 | + } |
| 1724 | + |
| 1725 | + std::string message = "Unknown error: " + error.message(); |
| 1726 | + return env->ThrowErrnoException(UV_UNKNOWN, "rm", message.c_str(), file_); |
| 1727 | +} |
| 1728 | + |
1627 | 1729 | int MKDirpSync(uv_loop_t* loop,
|
1628 | 1730 | uv_fs_t* req,
|
1629 | 1731 | const std::string& path,
|
@@ -3342,6 +3444,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
3342 | 3444 | SetMethod(isolate, target, "rename", Rename);
|
3343 | 3445 | SetMethod(isolate, target, "ftruncate", FTruncate);
|
3344 | 3446 | SetMethod(isolate, target, "rmdir", RMDir);
|
| 3447 | + SetMethod(isolate, target, "rmSync", RmSync); |
3345 | 3448 | SetMethod(isolate, target, "mkdir", MKDir);
|
3346 | 3449 | SetMethod(isolate, target, "readdir", ReadDir);
|
3347 | 3450 | SetFastMethod(isolate,
|
@@ -3468,6 +3571,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
3468 | 3571 | registry->Register(Rename);
|
3469 | 3572 | registry->Register(FTruncate);
|
3470 | 3573 | registry->Register(RMDir);
|
| 3574 | + registry->Register(RmSync); |
3471 | 3575 | registry->Register(MKDir);
|
3472 | 3576 | registry->Register(ReadDir);
|
3473 | 3577 | registry->Register(InternalModuleStat);
|
|
0 commit comments