aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsetser <setser@yandex-team.ru>2022-03-16 14:01:41 +0300
committersetser <setser@yandex-team.ru>2022-03-16 14:01:41 +0300
commite53b11f9862a561704077616d62587223ffa9d70 (patch)
tree4f0147ef87635abfe52322bdf40807223a34f053
parent3af0e2692ba281186cf54a397141a4b578793470 (diff)
downloadydb-e53b11f9862a561704077616d62587223ffa9d70.tar.gz
Remove read-only files and directories on Windows (to match behavior on unix)
Current behavior of RemoveDirWithContents does not allow removing directories if any of files in this directory is read-only. This behavior, however, does not allow, for example, to remove temporary directory, in which git repository was created (on *nix systems, however, this problem was not noticed). This PR overrides this behavior. ref:1faf91e233008b4aece4f2b953884b52346ce2be
-rw-r--r--util/system/fs_win.cpp4
-rw-r--r--util/system/fs_win_ut.cpp71
2 files changed, 75 insertions, 0 deletions
diff --git a/util/system/fs_win.cpp b/util/system/fs_win.cpp
index e9f4e5ac33..45d7454b2f 100644
--- a/util/system/fs_win.cpp
+++ b/util/system/fs_win.cpp
@@ -60,6 +60,10 @@ namespace NFsPrivate {
}
WIN32_FILE_ATTRIBUTE_DATA fad;
if (::GetFileAttributesExW(wname, GetFileExInfoStandard, &fad)) {
+ if (fad.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
+ fad.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ ::SetFileAttributesW(wname, fad.dwFileAttributes);
+ }
if (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return ::RemoveDirectoryW(wname) != 0;
return ::DeleteFileW(wname) != 0;
diff --git a/util/system/fs_win_ut.cpp b/util/system/fs_win_ut.cpp
new file mode 100644
index 0000000000..46a8c5f2b0
--- /dev/null
+++ b/util/system/fs_win_ut.cpp
@@ -0,0 +1,71 @@
+#include "fs.h"
+#include "fs_win.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include "fileapi.h"
+
+#include "file.h"
+#include "fstat.h"
+#include "win_undef.h"
+#include <util/charset/wide.h>
+#include <util/folder/path.h>
+#include <util/generic/string.h>
+
+static void Touch(const TFsPath& path) {
+ TFile file(path, CreateAlways | WrOnly);
+ file.Write("1115", 4);
+}
+
+static LPCWSTR UTF8ToWCHAR(const TStringBuf str, TUtf16String& wstr) {
+ wstr.resize(str.size());
+ size_t written = 0;
+ if (!UTF8ToWide(str.data(), str.size(), wstr.begin(), written))
+ return nullptr;
+ wstr.erase(written);
+ static_assert(sizeof(WCHAR) == sizeof(wchar16), "expect sizeof(WCHAR) == sizeof(wchar16)");
+ return (const WCHAR*)wstr.data();
+}
+
+Y_UNIT_TEST_SUITE(TFsWinTest) {
+ Y_UNIT_TEST(TestRemoveDirWithROFiles) {
+ TFsPath dir1 = "dir1";
+ NFsPrivate::WinRemove(dir1);
+ UNIT_ASSERT(!NFsPrivate::WinExists(dir1));
+ UNIT_ASSERT(NFsPrivate::WinMakeDirectory(dir1));
+
+ UNIT_ASSERT(TFileStat(dir1).IsDir());
+ TFsPath file1 = dir1 / "file.txt";
+ Touch(file1);
+ UNIT_ASSERT(NFsPrivate::WinExists(file1));
+ {
+ TUtf16String wstr;
+ LPCWSTR wname = UTF8ToWCHAR(static_cast<const TString&>(file1), wstr);
+ UNIT_ASSERT(wname);
+ WIN32_FILE_ATTRIBUTE_DATA fad;
+ fad.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+ ::SetFileAttributesW(wname, fad.dwFileAttributes);
+ }
+ NFsPrivate::WinRemove(dir1);
+ UNIT_ASSERT(!NFsPrivate::WinExists(dir1));
+ }
+
+ Y_UNIT_TEST(TestRemoveReadOnlyDir) {
+ TFsPath dir1 = "dir1";
+ NFsPrivate::WinRemove(dir1);
+ UNIT_ASSERT(!NFsPrivate::WinExists(dir1));
+ UNIT_ASSERT(NFsPrivate::WinMakeDirectory(dir1));
+
+ UNIT_ASSERT(TFileStat(dir1).IsDir());
+ {
+ TUtf16String wstr;
+ LPCWSTR wname = UTF8ToWCHAR(static_cast<const TString&>(dir1), wstr);
+ UNIT_ASSERT(wname);
+ WIN32_FILE_ATTRIBUTE_DATA fad;
+ fad.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+ ::SetFileAttributesW(wname, fad.dwFileAttributes);
+ }
+ NFsPrivate::WinRemove(dir1);
+ UNIT_ASSERT(!NFsPrivate::WinExists(dir1));
+ }
+}