aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorkikht <kikht@yandex-team.ru>2022-02-10 16:45:14 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:45:14 +0300
commit194cae0e8855b11be2005e1eff12c660c3ee9774 (patch)
treeed29c437b616690880c017855ebe0be34fdf81a2 /util
parent49116032d905455a7b1c994e4a696afc885c1e71 (diff)
downloadydb-194cae0e8855b11be2005e1eff12c660c3ee9774.tar.gz
Restoring authorship annotation for <kikht@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'util')
-rw-r--r--util/folder/path.cpp56
-rw-r--r--util/folder/path_ut.cpp590
-rw-r--r--util/system/fstat.cpp6
3 files changed, 326 insertions, 326 deletions
diff --git a/util/folder/path.cpp b/util/folder/path.cpp
index bfe0c67d68..256940d96d 100644
--- a/util/folder/path.cpp
+++ b/util/folder/path.cpp
@@ -387,39 +387,39 @@ void TFsPath::MkDirs(const int mode) const {
}
void TFsPath::ForceDelete() const {
- if (!IsDefined()) {
- return;
- }
-
- TFileStat stat(GetPath().c_str(), true);
- if (stat.IsNull()) {
- const int err = LastSystemError();
-#ifdef _win_
- if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
-#else
- if (err == ENOENT) {
-#endif
- return;
- } else {
- ythrow TIoException() << "failed to stat " << Path_;
- }
- }
-
- ClearLastSystemError();
- if (stat.IsDir()) {
+ if (!IsDefined()) {
+ return;
+ }
+
+ TFileStat stat(GetPath().c_str(), true);
+ if (stat.IsNull()) {
+ const int err = LastSystemError();
+#ifdef _win_
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
+#else
+ if (err == ENOENT) {
+#endif
+ return;
+ } else {
+ ythrow TIoException() << "failed to stat " << Path_;
+ }
+ }
+
+ ClearLastSystemError();
+ if (stat.IsDir()) {
TVector<TFsPath> children;
List(children);
for (auto& i : children) {
i.ForceDelete();
}
- ::rmdir(this->c_str());
- } else {
- ::unlink(this->c_str());
- }
-
- if (LastSystemError()) {
- ythrow TIoException() << "failed to delete " << Path_;
- }
+ ::rmdir(this->c_str());
+ } else {
+ ::unlink(this->c_str());
+ }
+
+ if (LastSystemError()) {
+ ythrow TIoException() << "failed to delete " << Path_;
+ }
}
void TFsPath::CopyTo(const TString& newPath, bool force) const {
diff --git a/util/folder/path_ut.cpp b/util/folder/path_ut.cpp
index e6a3451016..8067cade1a 100644
--- a/util/folder/path_ut.cpp
+++ b/util/folder/path_ut.cpp
@@ -5,7 +5,7 @@
#include <library/cpp/testing/unittest/registar.h>
-#include <util/generic/scope.h>
+#include <util/generic/scope.h>
#include <util/system/platform.h>
#include <util/system/yassert.h>
#include <util/stream/output.h>
@@ -14,10 +14,10 @@
#include <algorithm>
-#ifdef _win_
- #include <aclapi.h>
-#endif
-
+#ifdef _win_
+ #include <aclapi.h>
+#endif
+
namespace {
/// empty directory for test that needs filesystem
/// recreates directory in constructor and removes directory in destructor
@@ -79,8 +79,8 @@ Y_UNIT_TEST_SUITE(TFsPathTests) {
Y_UNIT_TEST(MkDirFreak) {
TFsPath path;
- UNIT_ASSERT_EXCEPTION(path.MkDir(), TIoException);
- UNIT_ASSERT_EXCEPTION(path.MkDirs(), TIoException);
+ UNIT_ASSERT_EXCEPTION(path.MkDir(), TIoException);
+ UNIT_ASSERT_EXCEPTION(path.MkDirs(), TIoException);
path = ".";
path.MkDir();
path.MkDirs();
@@ -169,10 +169,10 @@ Y_UNIT_TEST_SUITE(TFsPathTests) {
}
Y_UNIT_TEST(TestRenameFail) {
- UNIT_ASSERT_EXCEPTION(TFsPath("sfsfsfsdfsfsdfdf").RenameTo("sdfsdf"), TIoException);
+ UNIT_ASSERT_EXCEPTION(TFsPath("sfsfsfsdfsfsdfdf").RenameTo("sdfsdf"), TIoException);
}
-#ifndef _win_
+#ifndef _win_
Y_UNIT_TEST(TestRealPath) {
UNIT_ASSERT(TFsPath(".").RealPath().IsDirectory());
@@ -188,7 +188,7 @@ Y_UNIT_TEST_SUITE(TFsPathTests) {
UNIT_ASSERT(NFs::SymLink(target2.RealPath(), link.GetPath()));
UNIT_ASSERT_VALUES_EQUAL(link.RealPath(), target2.RealPath()); // must not cache old value
}
-#endif
+#endif
Y_UNIT_TEST(TestSlashesAndBasename) {
TFsPath p("/db/BASE/primus121-025-1380131338//");
@@ -341,8 +341,8 @@ Y_UNIT_TEST_SUITE(TFsPathTests) {
UNIT_ASSERT_VALUES_EQUAL(TFsPath(".").RelativePath(TFsPath(".")), TFsPath());
UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/c").RelativePath(TFsPath("/a/b/../c")), TFsPath());
UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/.././b").RelativePath(TFsPath("b/c")), TFsPath(".."));
-
- UNIT_ASSERT_EXCEPTION(TFsPath("a/b/c").RelativePath(TFsPath("d/e")), TIoException);
+
+ UNIT_ASSERT_EXCEPTION(TFsPath("a/b/c").RelativePath(TFsPath("d/e")), TIoException);
}
Y_UNIT_TEST(TestUndefined) {
@@ -526,287 +526,287 @@ Y_UNIT_TEST_SUITE(TFsPathTests) {
UNIT_ASSERT(NFs::Exists(originDir));
}
#endif
-
- Y_UNIT_TEST(TestForceDeleteNonexisting) {
- TTempDir tempDir;
- TFsPath nonexisting = TFsPath(tempDir()).Child("nonexisting");
- nonexisting.ForceDelete();
- }
-
- // Here we want to test that all possible errors during TFsPath::ForceDelete
- // are properly handled. To do so we have to trigger fs operation errors in
- // three points:
- // 1. stat/GetFileInformationByHandle
- // 2. opendir
- // 3. unlink/rmdir
- //
- // On unix systems we can achieve this by simply setting access rights on
- // entry being deleted and its parent. But on windows it is more complicated.
- // Current Chmod implementation on windows is not enough as it sets only
- // FILE_ATTRIBUTE_READONLY throught SetFileAttributes call. But doing so does
- // not affect directory access rights on older versions of Windows and Wine
- // that we use to run autocheck tests.
- //
- // To get required access rights we use DACL in SetSecurityInfo. This is wrapped
- // in RAII class that drops requested permissions on file/dir and grantss them
- // back in destructor.
- //
- // Another obstacle is FILE_LIST_DIRECTORY permission when running on Wine.
- // Dropping this permission is necessary to provoke error
- // in GetFileInformationByHandle. Wine allows dropping this permission, but I
- // have not found a way to grant it back. So tests crash during cleanup sequence.
- // To make it possible to run this tests natively we detect Wine with special
- // registry key and skip these tests only there.
-
-#ifdef _win_
- struct TLocalFree {
- static void Destroy(void* ptr) {
- LocalFree((HLOCAL)ptr);
- }
- };
-
- bool IsWine() {
- HKEY subKey = nullptr;
- LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Wine", 0, KEY_READ, &subKey);
- if (result == ERROR_SUCCESS) {
- return true;
- }
- result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_READ, &subKey);
- if (result == ERROR_SUCCESS) {
- return true;
- }
-
- HMODULE hntdll = GetModuleHandle("ntdll.dll");
- if (!hntdll) {
- return false;
- }
-
- auto func = GetProcAddress(hntdll, "wine_get_version");
- return func != nullptr;
- }
-
- class TWinFileDenyAccessScope {
- public:
- TWinFileDenyAccessScope(const TFsPath& name, DWORD permissions)
- : Name_(name)
- , Perms_(permissions)
- {
- DWORD res = 0;
- PACL oldAcl = nullptr;
- PSECURITY_DESCRIPTOR sd = nullptr;
-
- res = GetNamedSecurityInfoA((LPSTR)name.c_str(),
- SE_FILE_OBJECT,
- DACL_SECURITY_INFORMATION,
- nullptr,
- nullptr,
- &oldAcl,
- nullptr,
- &sd);
- SdHolder_.Reset(sd);
- if (res != ERROR_SUCCESS) {
- ythrow TSystemError(res) << "error in GetNamedSecurityInfoA";
- }
-
- Acl_ = SetAcl(oldAcl, DENY_ACCESS);
- }
-
- ~TWinFileDenyAccessScope() {
- try {
- const TFsPath parent = Name_.Parent();
- Chmod(parent.c_str(), MODE0777);
- Chmod(Name_.c_str(), MODE0777);
- SetAcl((PACL)Acl_.Get(), GRANT_ACCESS);
- } catch (const yexception& ex) {
- Cerr << "~TWinFileDenyAccessScope failed: " << ex.AsStrBuf() << Endl;
- }
- }
-
- THolder<void, TLocalFree> SetAcl(PACL oldAcl, ACCESS_MODE accessMode) {
- DWORD res = 0;
- EXPLICIT_ACCESS ea;
- PACL newAcl = nullptr;
- THolder<void, TLocalFree> newAclHolder;
-
- memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
- ea.grfAccessPermissions = Perms_;
- ea.grfAccessMode = accessMode;
- ea.grfInheritance = NO_INHERITANCE;
- ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
- ea.Trustee.ptstrName = (LPSTR) "CURRENT_USER";
-
- res = SetEntriesInAcl(1, &ea, oldAcl, &newAcl);
- newAclHolder.Reset(newAcl);
- if (res != ERROR_SUCCESS) {
- ythrow TSystemError(res) << "error in SetEntriesInAcl";
- }
-
- res = SetNamedSecurityInfoA((LPSTR)Name_.c_str(),
- SE_FILE_OBJECT,
- DACL_SECURITY_INFORMATION,
- nullptr,
- nullptr,
- newAcl,
- nullptr);
- if (res != ERROR_SUCCESS) {
- ythrow TSystemError(res) << "error in SetNamedSecurityInfoA";
- }
-
- return std::move(newAclHolder);
- }
-
- private:
- const TFsPath Name_;
- const DWORD Perms_;
- THolder<void, TLocalFree> SdHolder_;
- THolder<void, TLocalFree> Acl_;
- };
-#endif
-
- Y_UNIT_TEST(TestForceDeleteErrorUnlink) {
- TTempDir tempDir;
-
- const TFsPath testDir = TFsPath(tempDir()).Child("dir");
- MakePathIfNotExist(testDir.c_str());
-
- const TFsPath testFile = testDir.Child("file");
- {
- TFixedBufferFileOutput out(testFile);
- out << "data111!!!";
- }
-
-#ifdef _win_
- Chmod(testFile.c_str(), S_IRUSR);
- Y_DEFER {
- Chmod(testFile.c_str(), MODE0777);
- };
-#else
- Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
- Y_DEFER {
- Chmod(testDir.c_str(), MODE0777);
- };
-#endif
-
- UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to delete");
- }
-
- Y_UNIT_TEST(TestForceDeleteErrorRmdir) {
- TTempDir tempDir;
-
- const TFsPath testDir = TFsPath(tempDir()).Child("dir");
- const TFsPath testSubdir = testDir.Child("file");
- MakePathIfNotExist(testSubdir.c_str());
-
-#ifdef _win_
- Chmod(testSubdir.c_str(), 0);
- Y_DEFER {
- Chmod(testSubdir.c_str(), MODE0777);
- };
- TWinFileDenyAccessScope dirAcl(testDir, FILE_WRITE_DATA);
-#else
- Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
- Y_DEFER {
- Chmod(testDir.c_str(), MODE0777);
- };
-#endif
-
- UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to delete");
- }
-
- Y_UNIT_TEST(TestForceDeleteErrorStatDir) {
- TTempDir tempDir;
-
- const TFsPath testDir = TFsPath(tempDir()).Child("dir");
- const TFsPath testSubdir = testDir.Child("file");
- MakePathIfNotExist(testSubdir.c_str());
-
-#ifdef _win_
- if (IsWine()) {
- // FILE_LIST_DIRECTORY seem to be irreversible on wine
- return;
- }
- TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_READ_ATTRIBUTES);
- TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY);
-#else
- Chmod(testDir.c_str(), 0);
- Y_DEFER {
- Chmod(testDir.c_str(), MODE0777);
- };
-#endif
-
- UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to stat");
- }
-
- Y_UNIT_TEST(TestForceDeleteErrorStatFile) {
- TTempDir tempDir;
-
- const TFsPath testDir = TFsPath(tempDir()).Child("dir");
- MakePathIfNotExist(testDir.c_str());
-
- const TFsPath testFile = testDir.Child("file");
- {
- TFixedBufferFileOutput out(testFile);
- out << "data111!!!";
- }
-
-#ifdef _win_
- if (IsWine()) {
- // FILE_LIST_DIRECTORY seem to be irreversible on wine
- return;
- }
- TWinFileDenyAccessScope fileAcl(testFile, FILE_READ_ATTRIBUTES);
- TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY);
-#else
- Chmod(testDir.c_str(), 0);
- Y_DEFER {
- Chmod(testDir.c_str(), MODE0777);
- };
-#endif
-
- UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to stat");
- }
-
- Y_UNIT_TEST(TestForceDeleteErrorListDir) {
- TTempDir tempDir;
-
- const TFsPath testDir = TFsPath(tempDir()).Child("dir");
- const TFsPath testSubdir = testDir.Child("file");
- MakePathIfNotExist(testSubdir.c_str());
-
-#ifdef _win_
- if (IsWine()) {
- // FILE_LIST_DIRECTORY seem to be irreversible on wine
- return;
- }
- TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_LIST_DIRECTORY);
-#else
- Chmod(testSubdir.c_str(), 0);
- Y_DEFER {
- Chmod(testSubdir.c_str(), MODE0777);
- };
-#endif
-
- UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to opendir");
- }
-
-#ifdef _unix_
- Y_UNIT_TEST(TestForceDeleteErrorSymlink) {
- TTempDir tempDir;
-
- const TFsPath testDir = TFsPath(tempDir()).Child("dir");
- MakePathIfNotExist(testDir.c_str());
-
- const TFsPath testSymlink = testDir.Child("symlink");
- NFs::SymLink("something", testSymlink);
-
- Chmod(testSymlink.c_str(), S_IRUSR);
- Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
- Y_DEFER {
- Chmod(testDir.c_str(), MODE0777);
- Chmod(testSymlink.c_str(), MODE0777);
- };
-
- UNIT_ASSERT_EXCEPTION_CONTAINS(testSymlink.ForceDelete(), TIoException, "failed to delete");
- }
-#endif
+
+ Y_UNIT_TEST(TestForceDeleteNonexisting) {
+ TTempDir tempDir;
+ TFsPath nonexisting = TFsPath(tempDir()).Child("nonexisting");
+ nonexisting.ForceDelete();
+ }
+
+ // Here we want to test that all possible errors during TFsPath::ForceDelete
+ // are properly handled. To do so we have to trigger fs operation errors in
+ // three points:
+ // 1. stat/GetFileInformationByHandle
+ // 2. opendir
+ // 3. unlink/rmdir
+ //
+ // On unix systems we can achieve this by simply setting access rights on
+ // entry being deleted and its parent. But on windows it is more complicated.
+ // Current Chmod implementation on windows is not enough as it sets only
+ // FILE_ATTRIBUTE_READONLY throught SetFileAttributes call. But doing so does
+ // not affect directory access rights on older versions of Windows and Wine
+ // that we use to run autocheck tests.
+ //
+ // To get required access rights we use DACL in SetSecurityInfo. This is wrapped
+ // in RAII class that drops requested permissions on file/dir and grantss them
+ // back in destructor.
+ //
+ // Another obstacle is FILE_LIST_DIRECTORY permission when running on Wine.
+ // Dropping this permission is necessary to provoke error
+ // in GetFileInformationByHandle. Wine allows dropping this permission, but I
+ // have not found a way to grant it back. So tests crash during cleanup sequence.
+ // To make it possible to run this tests natively we detect Wine with special
+ // registry key and skip these tests only there.
+
+#ifdef _win_
+ struct TLocalFree {
+ static void Destroy(void* ptr) {
+ LocalFree((HLOCAL)ptr);
+ }
+ };
+
+ bool IsWine() {
+ HKEY subKey = nullptr;
+ LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Wine", 0, KEY_READ, &subKey);
+ if (result == ERROR_SUCCESS) {
+ return true;
+ }
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_READ, &subKey);
+ if (result == ERROR_SUCCESS) {
+ return true;
+ }
+
+ HMODULE hntdll = GetModuleHandle("ntdll.dll");
+ if (!hntdll) {
+ return false;
+ }
+
+ auto func = GetProcAddress(hntdll, "wine_get_version");
+ return func != nullptr;
+ }
+
+ class TWinFileDenyAccessScope {
+ public:
+ TWinFileDenyAccessScope(const TFsPath& name, DWORD permissions)
+ : Name_(name)
+ , Perms_(permissions)
+ {
+ DWORD res = 0;
+ PACL oldAcl = nullptr;
+ PSECURITY_DESCRIPTOR sd = nullptr;
+
+ res = GetNamedSecurityInfoA((LPSTR)name.c_str(),
+ SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION,
+ nullptr,
+ nullptr,
+ &oldAcl,
+ nullptr,
+ &sd);
+ SdHolder_.Reset(sd);
+ if (res != ERROR_SUCCESS) {
+ ythrow TSystemError(res) << "error in GetNamedSecurityInfoA";
+ }
+
+ Acl_ = SetAcl(oldAcl, DENY_ACCESS);
+ }
+
+ ~TWinFileDenyAccessScope() {
+ try {
+ const TFsPath parent = Name_.Parent();
+ Chmod(parent.c_str(), MODE0777);
+ Chmod(Name_.c_str(), MODE0777);
+ SetAcl((PACL)Acl_.Get(), GRANT_ACCESS);
+ } catch (const yexception& ex) {
+ Cerr << "~TWinFileDenyAccessScope failed: " << ex.AsStrBuf() << Endl;
+ }
+ }
+
+ THolder<void, TLocalFree> SetAcl(PACL oldAcl, ACCESS_MODE accessMode) {
+ DWORD res = 0;
+ EXPLICIT_ACCESS ea;
+ PACL newAcl = nullptr;
+ THolder<void, TLocalFree> newAclHolder;
+
+ memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
+ ea.grfAccessPermissions = Perms_;
+ ea.grfAccessMode = accessMode;
+ ea.grfInheritance = NO_INHERITANCE;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
+ ea.Trustee.ptstrName = (LPSTR) "CURRENT_USER";
+
+ res = SetEntriesInAcl(1, &ea, oldAcl, &newAcl);
+ newAclHolder.Reset(newAcl);
+ if (res != ERROR_SUCCESS) {
+ ythrow TSystemError(res) << "error in SetEntriesInAcl";
+ }
+
+ res = SetNamedSecurityInfoA((LPSTR)Name_.c_str(),
+ SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION,
+ nullptr,
+ nullptr,
+ newAcl,
+ nullptr);
+ if (res != ERROR_SUCCESS) {
+ ythrow TSystemError(res) << "error in SetNamedSecurityInfoA";
+ }
+
+ return std::move(newAclHolder);
+ }
+
+ private:
+ const TFsPath Name_;
+ const DWORD Perms_;
+ THolder<void, TLocalFree> SdHolder_;
+ THolder<void, TLocalFree> Acl_;
+ };
+#endif
+
+ Y_UNIT_TEST(TestForceDeleteErrorUnlink) {
+ TTempDir tempDir;
+
+ const TFsPath testDir = TFsPath(tempDir()).Child("dir");
+ MakePathIfNotExist(testDir.c_str());
+
+ const TFsPath testFile = testDir.Child("file");
+ {
+ TFixedBufferFileOutput out(testFile);
+ out << "data111!!!";
+ }
+
+#ifdef _win_
+ Chmod(testFile.c_str(), S_IRUSR);
+ Y_DEFER {
+ Chmod(testFile.c_str(), MODE0777);
+ };
+#else
+ Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
+ Y_DEFER {
+ Chmod(testDir.c_str(), MODE0777);
+ };
+#endif
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to delete");
+ }
+
+ Y_UNIT_TEST(TestForceDeleteErrorRmdir) {
+ TTempDir tempDir;
+
+ const TFsPath testDir = TFsPath(tempDir()).Child("dir");
+ const TFsPath testSubdir = testDir.Child("file");
+ MakePathIfNotExist(testSubdir.c_str());
+
+#ifdef _win_
+ Chmod(testSubdir.c_str(), 0);
+ Y_DEFER {
+ Chmod(testSubdir.c_str(), MODE0777);
+ };
+ TWinFileDenyAccessScope dirAcl(testDir, FILE_WRITE_DATA);
+#else
+ Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
+ Y_DEFER {
+ Chmod(testDir.c_str(), MODE0777);
+ };
+#endif
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to delete");
+ }
+
+ Y_UNIT_TEST(TestForceDeleteErrorStatDir) {
+ TTempDir tempDir;
+
+ const TFsPath testDir = TFsPath(tempDir()).Child("dir");
+ const TFsPath testSubdir = testDir.Child("file");
+ MakePathIfNotExist(testSubdir.c_str());
+
+#ifdef _win_
+ if (IsWine()) {
+ // FILE_LIST_DIRECTORY seem to be irreversible on wine
+ return;
+ }
+ TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_READ_ATTRIBUTES);
+ TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY);
+#else
+ Chmod(testDir.c_str(), 0);
+ Y_DEFER {
+ Chmod(testDir.c_str(), MODE0777);
+ };
+#endif
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to stat");
+ }
+
+ Y_UNIT_TEST(TestForceDeleteErrorStatFile) {
+ TTempDir tempDir;
+
+ const TFsPath testDir = TFsPath(tempDir()).Child("dir");
+ MakePathIfNotExist(testDir.c_str());
+
+ const TFsPath testFile = testDir.Child("file");
+ {
+ TFixedBufferFileOutput out(testFile);
+ out << "data111!!!";
+ }
+
+#ifdef _win_
+ if (IsWine()) {
+ // FILE_LIST_DIRECTORY seem to be irreversible on wine
+ return;
+ }
+ TWinFileDenyAccessScope fileAcl(testFile, FILE_READ_ATTRIBUTES);
+ TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY);
+#else
+ Chmod(testDir.c_str(), 0);
+ Y_DEFER {
+ Chmod(testDir.c_str(), MODE0777);
+ };
+#endif
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to stat");
+ }
+
+ Y_UNIT_TEST(TestForceDeleteErrorListDir) {
+ TTempDir tempDir;
+
+ const TFsPath testDir = TFsPath(tempDir()).Child("dir");
+ const TFsPath testSubdir = testDir.Child("file");
+ MakePathIfNotExist(testSubdir.c_str());
+
+#ifdef _win_
+ if (IsWine()) {
+ // FILE_LIST_DIRECTORY seem to be irreversible on wine
+ return;
+ }
+ TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_LIST_DIRECTORY);
+#else
+ Chmod(testSubdir.c_str(), 0);
+ Y_DEFER {
+ Chmod(testSubdir.c_str(), MODE0777);
+ };
+#endif
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to opendir");
+ }
+
+#ifdef _unix_
+ Y_UNIT_TEST(TestForceDeleteErrorSymlink) {
+ TTempDir tempDir;
+
+ const TFsPath testDir = TFsPath(tempDir()).Child("dir");
+ MakePathIfNotExist(testDir.c_str());
+
+ const TFsPath testSymlink = testDir.Child("symlink");
+ NFs::SymLink("something", testSymlink);
+
+ Chmod(testSymlink.c_str(), S_IRUSR);
+ Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
+ Y_DEFER {
+ Chmod(testDir.c_str(), MODE0777);
+ Chmod(testSymlink.c_str(), MODE0777);
+ };
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(testSymlink.ForceDelete(), TIoException, "failed to delete");
+ }
+#endif
}
diff --git a/util/system/fstat.cpp b/util/system/fstat.cpp
index 81e98cbc6b..bd2af46fce 100644
--- a/util/system/fstat.cpp
+++ b/util/system/fstat.cpp
@@ -87,9 +87,9 @@ static bool GetStatByName(TSystemFStat& fs, const char* fileName, bool nofollow)
TFileHandle h = NFsPrivate::CreateFileWithUtf8Name(fileName, FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
(nofollow ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | FILE_FLAG_BACKUP_SEMANTICS, true);
- if (!h.IsOpen()) {
- return false;
- }
+ if (!h.IsOpen()) {
+ return false;
+ }
return GetStatByHandle(fs, h);
#else
return !(nofollow ? lstat : stat)(fileName, &fs);