From 62adfcd24d2b5b92fdb6627a7b5b0471b300f923 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 24 Jan 2018 18:32:05 +0300 Subject: Make try_rmfile() to delete read-only files on Windows --- libbutl/filesystem.cxx | 28 ++++++++++++++++++++++++++-- libbutl/filesystem.mxx | 4 ++++ libbutl/url.txx | 3 +++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/libbutl/filesystem.cxx b/libbutl/filesystem.cxx index fd9ba75..bd91c07 100644 --- a/libbutl/filesystem.cxx +++ b/libbutl/filesystem.cxx @@ -299,12 +299,36 @@ namespace butl try_rmfile (const path& p, bool ignore_error) { rmfile_status r (rmfile_status::success); + const char* f (p.string ().c_str ()); #ifndef _WIN32 - if (unlink (p.string ().c_str ()) != 0) + int ur (unlink (f)); #else - if (_unlink (p.string ().c_str ()) != 0) + int ur (_unlink (f)); + + // On Windows a file with the read-only attribute can not be deleted. This + // can be the reason if the deletion fails with the 'permission denied' + // error code. In such a case we just reset the attribute and repeat the + // attempt. If the attempt fails, then we try to restore the attribute. + // + if (ur != 0 && errno == EACCES) + { + DWORD a (GetFileAttributes (f)); + if (a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_READONLY) != 0 && + SetFileAttributes (f, a & ~FILE_ATTRIBUTE_READONLY)) + { + ur = _unlink (f); + + // Restoring the attribute is unlikely to fail as we managed to reset + // it earlier. + // + if (ur != 0) + SetFileAttributes (f, a); + } + } #endif + + if (ur != 0) { // Strangely on Linux unlink() removes a dangling symlink but returns // ENOENT. diff --git a/libbutl/filesystem.mxx b/libbutl/filesystem.mxx index 8ffbce9..a87e57b 100644 --- a/libbutl/filesystem.mxx +++ b/libbutl/filesystem.mxx @@ -196,6 +196,10 @@ LIBBUTL_MODEXPORT namespace butl // enum class rmfile_status {success, not_exist}; + // Note that on Windows the read-only attribute is reset prior to the file + // removal (as it can't otherwise be deleted). In such a case the operation + // is not atomic. + // LIBBUTL_SYMEXPORT rmfile_status try_rmfile (const path&, bool ignore_error = false); diff --git a/libbutl/url.txx b/libbutl/url.txx index addfe88..e511bed 100644 --- a/libbutl/url.txx +++ b/libbutl/url.txx @@ -413,8 +413,11 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (authority) { + // We can't append '//' string literal, so appending characters. + // r += '/'; r += '/'; + r += authority->string (); } -- cgit v1.1