diff options
author | kikht <kikht@yandex-team.ru> | 2022-02-10 16:45:14 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:14 +0300 |
commit | 194cae0e8855b11be2005e1eff12c660c3ee9774 (patch) | |
tree | ed29c437b616690880c017855ebe0be34fdf81a2 /util | |
parent | 49116032d905455a7b1c994e4a696afc885c1e71 (diff) | |
download | ydb-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.cpp | 56 | ||||
-rw-r--r-- | util/folder/path_ut.cpp | 590 | ||||
-rw-r--r-- | util/system/fstat.cpp | 6 |
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); |