diff options
author | setser <setser@yandex-team.ru> | 2022-03-16 14:01:41 +0300 |
---|---|---|
committer | setser <setser@yandex-team.ru> | 2022-03-16 14:01:41 +0300 |
commit | e53b11f9862a561704077616d62587223ffa9d70 (patch) | |
tree | 4f0147ef87635abfe52322bdf40807223a34f053 | |
parent | 3af0e2692ba281186cf54a397141a4b578793470 (diff) | |
download | ydb-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.cpp | 4 | ||||
-rw-r--r-- | util/system/fs_win_ut.cpp | 71 |
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)); + } +} |