diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/file_access.cpp | 9 | ||||
-rw-r--r-- | zen/file_io.cpp | 20 | ||||
-rw-r--r-- | zen/file_path.cpp | 32 | ||||
-rw-r--r-- | zen/format_unit.cpp | 13 | ||||
-rw-r--r-- | zen/globals.h | 82 | ||||
-rw-r--r-- | zen/http.cpp | 1 | ||||
-rw-r--r-- | zen/json.h | 4 | ||||
-rw-r--r-- | zen/legacy_compiler.h | 2 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 80 | ||||
-rw-r--r-- | zen/socket.h | 9 | ||||
-rw-r--r-- | zen/stl_tools.h | 28 | ||||
-rw-r--r-- | zen/symlink_target.h | 5 | ||||
-rw-r--r-- | zen/sys_error.h | 3 | ||||
-rw-r--r-- | zen/sys_version.cpp | 4 | ||||
-rw-r--r-- | zen/zlib_wrap.cpp | 1 |
15 files changed, 158 insertions, 135 deletions
diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 6c47936c..4ce66acf 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -622,9 +622,8 @@ void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath) //th } //allow only consistent objects to be created -> don't place before ::symlink(); targetPath may already exist! - ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetPath); /*throw FileError*/ } - catch (FileError&) {}); - warn_static("log it!") + ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetPath); } + catch (const FileError& e) { logExtraError(e.toString()); }); //file times: essential for syncing a symlink: enforce this! (don't just try!) struct stat sourceInfo = {}; @@ -665,7 +664,7 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target } FileOutputPlain fileOut(fdTarget, targetFile); //pass ownership - //preallocate disk space + reduce fragmentation + //preallocate disk space + reduce fragmentation fileOut.reserveSpace(sourceInfo.st_size); //throw FileError unbufferedStreamCopy([&](void* buffer, size_t bytesToRead) @@ -684,7 +683,7 @@ FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& target }, fileOut.getBlockSize() /*throw FileError*/); //throw FileError, X - //possible improvement: copy_file_range() performs an in-kernel copy: https://github.com/coreutils/coreutils/blob/17479ef60c8edbd2fe8664e31a7f69704f0cd221/src/copy.c#L342 + //possible improvement: copy_file_range() performs an in-kernel copy: https://github.com/coreutils/coreutils/blob/17479ef60c8edbd2fe8664e31a7f69704f0cd221/src/copy.c#L342 #if 0 //clean file system cache: needed at all? no user complaints at all so far!!! diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 8c985f64..af80a69a 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -58,8 +58,7 @@ FileBase::~FileBase() { close(); //throw FileError } - catch (FileError&) { assert(false); } - warn_static("log it!") + catch (const FileError& e) { logExtraError(e.toString()); } } @@ -170,8 +169,7 @@ size_t FileInputPlain::tryRead(void* buffer, size_t bytesToRead) //throw FileErr if (bytesRead < 0) THROW_LAST_SYS_ERROR("read"); - if (makeUnsigned(bytesRead) > bytesToRead) //better safe than sorry - throw SysError(formatSystemError("read", L"", L"Buffer overflow.")); + ASSERT_SYSERROR(makeUnsigned(bytesRead) <= bytesToRead); //better safe than sorry return bytesRead; //"zero indicates end of file" } catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), e.toString()); } @@ -229,10 +227,9 @@ FileOutputPlain::~FileOutputPlain() if (::unlink(getFilePath().c_str()) != 0) THROW_LAST_SYS_ERROR("unlink"); } - catch (const SysError&) + catch (const SysError& e) { - assert(false); - warn_static("at least log on failure!") + logExtraError(replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(getFilePath())) + L"\n\n" + e.toString()); } } @@ -289,8 +286,8 @@ size_t FileOutputPlain::tryWrite(const void* buffer, size_t bytesToWrite) //thro THROW_LAST_SYS_ERROR("write"); } - if (makeUnsigned(bytesWritten) > bytesToWrite) //better safe than sorry - throw SysError(formatSystemError("write", L"", L"Buffer overflow.")); + + ASSERT_SYSERROR(makeUnsigned(bytesWritten) <= bytesToWrite); //better safe than sorry return bytesWritten; } catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), e.toString()); } @@ -330,9 +327,8 @@ void zen::setFileContent(const Zstring& filePath, const std::string_view byteStr tmpFile.close(); //throw FileError //take over ownership: - ZEN_ON_SCOPE_FAIL( try { removeFilePlain(tmpFilePath); /*throw FileError*/ } - catch (FileError&) {}); - warn_static("log it!") + ZEN_ON_SCOPE_FAIL( try { removeFilePlain(tmpFilePath); } + catch (const FileError& e) { logExtraError(e.toString()); }); //operation finished: move temp file transactionally moveAndRenameItem(tmpFilePath, filePath, true /*replaceExisting*/); //throw FileError, (ErrorMoveUnsupported), (ErrorTargetExisting) diff --git a/zen/file_path.cpp b/zen/file_path.cpp index 60ac4eb7..6503ba2f 100644 --- a/zen/file_path.cpp +++ b/zen/file_path.cpp @@ -173,13 +173,6 @@ std::unordered_map<Zstring, Zstring> getAllEnvVars() } constinit Global<std::unordered_map<Zstring, Zstring>> globalEnvVars; -GLOBAL_RUN_ONCE( - //*INDENT-OFF* - //mitigate static initialization order fiasco: (whatever comes first) - if (!globalEnvVars.get()) - globalEnvVars.set(std::make_unique<std::unordered_map<Zstring, Zstring>>(getAllEnvVars())) - //*INDENT-ON* -); } @@ -191,24 +184,17 @@ std::optional<Zstring> zen::getEnvironmentVar(const ZstringView name) getenv_s() to the rescue!? not implemented on GCC, apparently *still* not threadsafe!!! => *eff* this: make a global copy during start up! */ - std::shared_ptr<std::unordered_map<Zstring, Zstring>> envVars = globalEnvVars.get(); - if (!envVars) //access during static init or shutdown? + globalEnvVars.setOnce([] { return std::make_unique<std::unordered_map<Zstring, Zstring>>(getAllEnvVars()); }); + + if (std::shared_ptr<std::unordered_map<Zstring, Zstring>> envVars = globalEnvVars.get()) { - if (globalEnvVars.wasDestroyed()) - { - assert(false); - return {}; //SOL! - } - //mitigate static initialization order fiasco: (whatever comes first) - globalEnvVars.set(std::make_unique<std::unordered_map<Zstring, Zstring>>(getAllEnvVars())); - envVars = globalEnvVars.get(); + if (const auto it = envVars->find(name); + it != envVars->end()) + return it->second; } - - const auto it = envVars->find(name); - if (it == envVars->end()) - return {}; - - return it->second; + else + assert(false); //access during global shutdown => SOL! + return {}; } diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index a5dd5152..0ce68d9b 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -27,7 +27,8 @@ std::wstring zen::formatTwoDigitPrecision(double value) //print two digits: 0,1 | 1,1 | 11 if (std::abs(value) < 9.95) //9.99 must not be formatted as "10.0" return printNumber<std::wstring>(L"%.1f", value); - return numberTo<std::wstring>(std::llround(value)); + + return formatNumber(std::llround(value)); } @@ -38,7 +39,8 @@ std::wstring zen::formatThreeDigitPrecision(double value) return printNumber<std::wstring>(L"%.2f", value); if (std::abs(value) < 99.95) //99.99 must not be formatted as "100.0" return printNumber<std::wstring>(L"%.1f", value); - return numberTo<std::wstring>(std::llround(value)); + + return formatNumber(std::llround(value)); } @@ -151,10 +153,9 @@ std::wstring zen::formatRemainingTime(double timeInSec) std::wstring zen::formatProgressPercent(double fraction, int decPlaces) { -#if 0 //special case for perf!? - if (decPlaces == 0) - return numberTo<std::wstring>(static_cast<int>(fraction * 100)) + L'%'; -#endif + if (decPlaces == 0) //special case for perf + return numberTo<std::wstring>(static_cast<int>(std::floor(fraction * 100))) + L'%'; + //round down! don't show 100% when not actually done: https://freefilesync.org/forum/viewtopic.php?t=9781 const double blocks = std::pow(10, decPlaces); const double percent = std::floor(fraction * 100 * blocks) / blocks; diff --git a/zen/globals.h b/zen/globals.h index 5d4a7041..1a110373 100644 --- a/zen/globals.h +++ b/zen/globals.h @@ -87,16 +87,32 @@ public: std::swap(pod_.inst, tmpInst); else assert(false); + + pod_.initialized = true; } delete tmpInst; } - bool wasDestroyed() + //for initialization via a frequently-called function (which may be running on parallel threads) + template <class Function> + void setOnce(Function getInitialValue /*-> std::unique_ptr<T>*/) { pod_.spinLock.lock(); ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); - return pod_.destroyed; + if (!pod_.initialized) + { + assert(!pod_.inst); + if (!pod_.destroyed) + { + if (std::unique_ptr<T> newInst = getInitialValue()) //throw ? + pod_.inst = new std::shared_ptr<T>(std::move(newInst)); + } + else + assert(false); + + pod_.initialized = true; + } } private: @@ -105,6 +121,7 @@ private: PodSpinMutex spinLock; //rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction! //serialize access: can't use std::mutex: has non-trival destructor std::shared_ptr<T>* inst = nullptr; + bool initialized = false; bool destroyed = false; } pod_; }; @@ -128,29 +145,15 @@ class FunStatGlobal public: consteval FunStatGlobal() {}; //demand static zero-initialization! - //No ~FunStatGlobal()! + //No ~FunStatGlobal(): required to avoid generation of magic statics code for a function-scope static! - void initOnce(std::unique_ptr<T> (*getInitialValue)()) + std::shared_ptr<T> get() { static_assert(std::is_trivially_destructible_v<FunStatGlobal>, "this class must not generate code for magic statics!"); pod_.spinLock.lock(); ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); - if (!pod_.cleanUpEntry.cleanUpFun) - { - assert(!pod_.inst); - if (std::unique_ptr<T> newInst = (*getInitialValue)()) - pod_.inst = new std::shared_ptr<T>(std::move(newInst)); - registerDestruction(); - } - } - - std::shared_ptr<T> get() - { - pod_.spinLock.lock(); - ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); - if (pod_.inst) return *pod_.inst; return nullptr; @@ -165,13 +168,50 @@ public: pod_.spinLock.lock(); ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); - std::swap(pod_.inst, tmpInst); + if (!pod_.destroyed) + std::swap(pod_.inst, tmpInst); + else + assert(false); + registerDestruction(); } delete tmpInst; } + template <class Function> + void setOnce(Function getInitialValue /*-> std::unique_ptr<T>*/) + { + pod_.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); + + if (!pod_.cleanUpEntry.cleanUpFun) + { + assert(!pod_.inst); + if (!pod_.destroyed) + { + if (std::unique_ptr<T> newInst = getInitialValue()) //throw ? + pod_.inst = new std::shared_ptr<T>(std::move(newInst)); + } + else + assert(false); + + registerDestruction(); + } + } + private: + void destruct() + { + static_assert(std::is_trivially_destructible_v<Pod>, "this memory needs to live forever"); + + pod_.spinLock.lock(); + std::shared_ptr<T>* oldInst = std::exchange(pod_.inst, nullptr); + pod_.destroyed = true; + pod_.spinLock.unlock(); + + delete oldInst; + } + //call while holding pod_.spinLock void registerDestruction() { @@ -182,8 +222,7 @@ private: pod_.cleanUpEntry.callbackData = this; pod_.cleanUpEntry.cleanUpFun = [](void* callbackData) { - auto thisPtr = static_cast<FunStatGlobal*>(callbackData); - thisPtr->set(nullptr); + static_cast<FunStatGlobal*>(callbackData)->destruct(); }; registerGlobalForDestruction(pod_.cleanUpEntry); @@ -196,6 +235,7 @@ private: //serialize access; can't use std::mutex: has non-trival destructor std::shared_ptr<T>* inst = nullptr; CleanUpEntry cleanUpEntry; + bool destroyed = false; } pod_; }; diff --git a/zen/http.cpp b/zen/http.cpp index e1a828c1..8cdcd65c 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -207,7 +207,6 @@ private: void cleanup() { asyncStreamIn_->setReadError(std::make_exception_ptr(ThreadStopRequest())); - warn_static("log on error!") } std::shared_ptr<AsyncStreamBuffer> asyncStreamIn_ = std::make_shared<AsyncStreamBuffer>(HTTP_STREAM_BUFFER_SIZE); @@ -330,7 +330,7 @@ struct Token class Scanner { public: - Scanner(const std::string& stream) : stream_(stream), pos_(stream_.begin()) + explicit Scanner(const std::string& stream) : stream_(stream), pos_(stream_.begin()) { if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8)) pos_ += BYTE_ORDER_MARK_UTF8.size(); @@ -435,7 +435,7 @@ private: class JsonParser { public: - JsonParser(const std::string& stream) : + explicit JsonParser(const std::string& stream) : scn_(stream), tk_(scn_.getNextToken()) {} //throw JsonParsingError diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 2070d60f..c853b139 100644 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -41,7 +41,7 @@ basic_string<Char, Traits, Alloc> operator+(basic_string<Char, Traits, Alloc>&& //--------------------------------------------------------------------------------- //support for std::string::resize_and_overwrite() -#define ZEN_HAVE_RESIZE_AND_OVERWRITE 1 + #define ZEN_HAVE_RESIZE_AND_OVERWRITE 1 namespace zen { diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index 5cfd7f12..0d4a89ca 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -22,11 +22,40 @@ using namespace zen; +namespace +{ #ifndef OPENSSL_THREADS #error FFS, we are royally screwed! #endif -static_assert(OPENSSL_VERSION_NUMBER >= 0x30000000L, "OpenSSL version too old"); +static_assert(OPENSSL_VERSION_NUMBER >= 0x30000000L, "OpenSSL version is too old!"); + + +/* Sign a file using SHA-256: + openssl dgst -sha256 -sign private.pem -out file.sig file.txt + + verify the signature: (caveat: public key expected to be in pkix format!) + openssl dgst -sha256 -verify public.pem -signature file.sig file.txt */ + + +std::wstring formatOpenSSLError(const char* functionName, unsigned long ec) +{ + char errorBuf[256] = {}; //== buffer size used by ERR_error_string(); err.c: it seems the message uses at most ~200 bytes + ::ERR_error_string_n(ec, errorBuf, sizeof(errorBuf)); //includes null-termination + + return formatSystemError(functionName, replaceCpy(_("Error code %x"), L"%x", numberTo<std::wstring>(ec)), utfTo<std::wstring>(errorBuf)); +} + + +std::wstring formatLastOpenSSLError(const char* functionName) +{ + const auto ec = ::ERR_peek_last_error(); //"returns latest error code from the thread's error queue without modifying it" - unlike ERR_get_error() + //ERR_get_error: "returns the earliest error code from the thread's error queue and removes the entry. + // This function can be called repeatedly until there are no more error codes to return." + ::ERR_clear_error(); //clean up for next OpenSSL operation on this thread + return formatOpenSSLError(functionName, ec); +} +} void zen::openSslInit() @@ -36,19 +65,16 @@ void zen::openSslInit() //see Curl_ossl_cleanup(): https://github.com/curl/curl/blob/master/lib/vtls/openssl.c assert(runningOnMainThread()); - //excplicitly init OpenSSL on main thread: seems to initialize atomically! But it still might help to avoid issues: - [[maybe_unused]] const int rv = ::OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT | OPENSSL_INIT_NO_LOAD_CONFIG, nullptr); - assert(rv == 1); //https://www.openssl.org/docs/man1.1.0/ssl/OPENSSL_init_ssl.html - - warn_static("probably should log") + //explicitly init OpenSSL on main thread: seems to initialize atomically! But it still might help to avoid issues: + //https://www.openssl.org/docs/manmaster/man3/OPENSSL_init_ssl.html + if (::OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT | OPENSSL_INIT_NO_LOAD_CONFIG, nullptr) != 1) + logExtraError(_("Error during process initialization.") + L"\n\n" + formatLastOpenSSLError("OPENSSL_init_ssl")); } void zen::openSslTearDown() {} //OpenSSL 1.1.0+ deprecates all clean up functions //=> so much the theory, in practice it leaks, of course: https://github.com/openssl/openssl/issues/6283 -//=> OpenSslThreadCleanUp - namespace { struct OpenSslThreadCleanUp @@ -60,30 +86,6 @@ struct OpenSslThreadCleanUp }; thread_local OpenSslThreadCleanUp tearDownOpenSslThreadData; - -/* Sign a file using SHA-256: - openssl dgst -sha256 -sign private.pem -out file.sig file.txt - - verify the signature: (caveat: public key expected to be in pkix format!) - openssl dgst -sha256 -verify public.pem -signature file.sig file.txt */ - - -std::wstring formatOpenSSLError(const char* functionName, unsigned long ec) -{ - char errorBuf[256] = {}; //== buffer size used by ERR_error_string(); err.c: it seems the message uses at most ~200 bytes - ::ERR_error_string_n(ec, errorBuf, sizeof(errorBuf)); //includes null-termination - - return formatSystemError(functionName, replaceCpy(_("Error code %x"), L"%x", numberTo<std::wstring>(ec)), utfTo<std::wstring>(errorBuf)); -} - - -std::wstring formatLastOpenSSLError(const char* functionName) -{ - const auto ec = ::ERR_peek_last_error(); //"returns latest error code from the thread's error queue without modifying it" - unlike ERR_get_error() - ::ERR_clear_error(); //clean up for next OpenSSL operation on this thread - return formatOpenSSLError(functionName, ec); -} - //================================================================================ std::shared_ptr<EVP_PKEY> generateRsaKeyPair(int bits) //throw SysError @@ -217,7 +219,7 @@ std::string keyToStream(const EVP_PKEY* evp, RsaStreamType streamType, bool publ if (keyLen < 0) throw SysError(formatLastOpenSSLError("BIO_pending")); if (keyLen == 0) - throw SysError(formatSystemError("BIO_pending", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("BIO_pending", L"", L"No more error details.")); //no more error details std::string keyStream(keyLen, '\0'); @@ -281,7 +283,7 @@ std::string createHash(const std::string_view str, const EVP_MD* type) //throw S #else //streaming version EVP_MD_CTX* mdctx = ::EVP_MD_CTX_new(); if (!mdctx) - throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"No more error details.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_free(mdctx)); if (::EVP_DigestInit(mdctx, //EVP_MD_CTX* ctx @@ -308,7 +310,7 @@ std::string createSignature(const std::string_view message, EVP_PKEY* privateKey //https://www.openssl.org/docs/manmaster/man3/EVP_DigestSign.html EVP_MD_CTX* mdctx = ::EVP_MD_CTX_new(); if (!mdctx) - throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"No more error details.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_free(mdctx)); if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx @@ -347,7 +349,7 @@ void verifySignature(const std::string_view message, const std::string_view sign //https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerify.html EVP_MD_CTX* mdctx = ::EVP_MD_CTX_new(); if (!mdctx) - throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"No more error details.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_free(mdctx)); if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx @@ -553,7 +555,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string_view keyStream, const s EVP_CIPHER_CTX* cipCtx = ::EVP_CIPHER_CTX_new(); if (!cipCtx) - throw SysError(formatSystemError("EVP_CIPHER_CTX_new", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("EVP_CIPHER_CTX_new", L"", L"No more error details.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_CIPHER_CTX_free(cipCtx)); if (::EVP_DecryptInit(cipCtx, //EVP_CIPHER_CTX* ctx @@ -563,7 +565,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string_view keyStream, const s throw SysError(formatLastOpenSSLError("EVP_DecryptInit_ex")); if (::EVP_CIPHER_CTX_set_padding(cipCtx, 0 /*padding*/) != 1) - throw SysError(formatSystemError("EVP_CIPHER_CTX_set_padding", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("EVP_CIPHER_CTX_set_padding", L"", L"No more error details.")); //no more error details privateBlob.resize(privateBlobEnc.size() + ::EVP_CIPHER_block_size(EVP_aes_256_cbc())); //"EVP_DecryptUpdate() should have room for (inl + cipher_block_size) bytes" @@ -613,7 +615,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string_view keyStream, const s static_cast<int>(macData.size()), //int n reinterpret_cast<unsigned char*>(md), //unsigned char* md &mdLen)) //unsigned int* md_len - throw SysError(formatSystemError("HMAC", L"", L"Unexpected failure.")); //no more error details + throw SysError(formatSystemError("HMAC", L"", L"No more error details.")); //no more error details if (mac != std::string_view(md, mdLen)) throw SysError(keyEncrypted ? L"Wrong passphrase (or corrupted key)" : L"Validation failed: corrupted key"); diff --git a/zen/socket.h b/zen/socket.h index f706daab..c2fdb145 100644 --- a/zen/socket.h +++ b/zen/socket.h @@ -59,7 +59,6 @@ std::wstring formatGaiErrorCode(int ec) using SocketType = int; const SocketType invalidSocket = -1; inline void closeSocket(SocketType s) { ::close(s); } -warn_static("log on error!") void setNonBlocking(SocketType socket, bool value); //throw SysError @@ -194,8 +193,7 @@ size_t tryReadSocket(SocketType socket, void* buffer, size_t bytesToRead) //thro if (bytesReceived < 0) THROW_LAST_SYS_ERROR_WSA("recv"); - if (static_cast<size_t>(bytesReceived) > bytesToRead) //better safe than sorry - throw SysError(formatSystemError("recv", L"", L"Buffer overflow.")); + ASSERT_SYSERROR(makeUnsigned(bytesReceived) <= bytesToRead); //better safe than sorry return bytesReceived; //"zero indicates end of file" } @@ -218,11 +216,12 @@ size_t tryWriteSocket(SocketType socket, const void* buffer, size_t bytesToWrite } if (bytesWritten < 0) THROW_LAST_SYS_ERROR_WSA("send"); - if (bytesWritten > static_cast<int>(bytesToWrite)) - throw SysError(formatSystemError("send", L"", L"Buffer overflow.")); + if (bytesWritten == 0) throw SysError(formatSystemError("send", L"", L"Zero bytes processed.")); + ASSERT_SYSERROR(makeUnsigned(bytesWritten) <= bytesToWrite); //better safe than sorry + return bytesWritten; } } diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 03e7901a..ff574368 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -187,7 +187,7 @@ void removeDuplicates(std::vector<T, Alloc>& v, CompLess less) template <class T, class Alloc> inline void removeDuplicates(std::vector<T, Alloc>& v) { - removeDuplicates(v, std::less(), std::equal_to()); + removeDuplicates(v, std::less{}, std::equal_to{}); } @@ -196,14 +196,14 @@ void removeDuplicatesStable(std::vector<T, Alloc>& v, CompLess less) { std::set<T, CompLess> usedItems(less); v.erase(std::remove_if(v.begin(), v.end(), - [&usedItems](const T& e) { return !usedItems.insert(e).second; }), v.end()); + /**/[&usedItems](const T& e) { return !usedItems.insert(e).second; }), v.end()); } template <class T, class Alloc> inline void removeDuplicatesStable(std::vector<T, Alloc>& v) { - removeDuplicatesStable(v, std::less()); + removeDuplicatesStable(v, std::less{}); } @@ -234,30 +234,28 @@ BidirectionalIterator findLast(const BidirectionalIterator first, const Bidirect } -template <class RandomAccessIterator1, class RandomAccessIterator2> inline +template <class RandomAccessIterator1, class RandomAccessIterator2, class IsEq> inline RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last, - const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast) + const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast, IsEq isEqual) { if (needleLast - needleFirst == 1) //don't use expensive std::search unless required! - return std::find(first, last, *needleFirst); + return std::find_if(first, last, [needleFirst, isEqual](const auto c) { return isEqual(*needleFirst, c); }); + //"*needleFirst" could be improved with value rather than pointer access, at least for built-in types like "char" return std::search(first, last, - needleFirst, needleLast); + needleFirst, needleLast, isEqual); } -template <class RandomAccessIterator1, class RandomAccessIterator2, class IsEq> inline +template <class RandomAccessIterator1, class RandomAccessIterator2> inline RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last, - const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast, IsEq isEqual) + const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast) { - if (needleLast - needleFirst == 1) //don't use expensive std::search unless required! - return std::find_if(first, last, [needleFirst, isEqual](const auto c) { return isEqual(*needleFirst, c); }); - - return std::search(first, last, - needleFirst, needleLast, isEqual); + return searchFirst(first, last, needleFirst, needleLast, std::equal_to{}); } + template <class RandomAccessIterator1, class RandomAccessIterator2> inline RandomAccessIterator1 searchLast(const RandomAccessIterator1 first, RandomAccessIterator1 last, const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast) @@ -335,7 +333,7 @@ class FNV1aHash //FNV-1a: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%8 { public: FNV1aHash() {} - explicit FNV1aHash(Num startVal) : hashVal_(startVal) { assert(startVal != 0); /*(yes, might be a real hash, but) most likely bad init value*/} + explicit FNV1aHash(Num startVal) : hashVal_(startVal) { assert(startVal != 0); /*yes, might be a real hash, but most likely bad init value*/} void add(Num n) { diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 8580a9dd..24ce2313 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -50,7 +50,10 @@ SymlinkRawContent getSymlinkRawContent_impl(const Zstring& linkPath) //throw Sys const ssize_t bytesWritten = ::readlink(linkPath.c_str(), buf.data(), bufSize); if (bytesWritten < 0) THROW_LAST_SYS_ERROR("readlink"); - if (makeUnsigned(bytesWritten) >= bufSize) //detect truncation; not an error for readlink! + + ASSERT_SYSERROR(makeUnsigned(bytesWritten) <= bufSize); //better safe than sorry + + if (makeUnsigned(bytesWritten) == bufSize) //detect truncation; not an error for readlink! throw SysError(formatSystemError("readlink", L"", L"Buffer truncated.")); return {.targetPath = Zstring(buf.data(), bytesWritten)}; //readlink does not append 0-termination! diff --git a/zen/sys_error.h b/zen/sys_error.h index 73a92343..53cd2845 100644 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -10,6 +10,7 @@ #include "scope_guard.h" // #include "i18n.h" //not used by this header, but the "rest of the world" needs it! #include "zstring.h" // +#include "extra_log.h" // #include <glib.h> #include <cerrno> @@ -78,7 +79,7 @@ inline bool validateBool(void* b) { return b; } bool validateBool(int) = delete; //catch unintended bool conversions, e.g. HRESULT } #define ASSERT_SYSERROR_IMPL(expr, exprStr) \ - { if (!impl::validateBool(expr)) \ + { if (!zen::impl::validateBool(expr)) \ throw zen::SysError(L"Assertion failed: \"" L ## exprStr L"\""); } } diff --git a/zen/sys_version.cpp b/zen/sys_version.cpp index e57c9b69..705fbade 100644 --- a/zen/sys_version.cpp +++ b/zen/sys_version.cpp @@ -89,8 +89,8 @@ OsVersion zen::getOsVersion() } catch (const SysError& e) { - std::cerr << utfTo<std::string>(e.toString()) + '\n'; - return OsVersionDetail{}; //sigh, it's a jungle out there: https://freefilesync.org/forum/viewtopic.php?t=7276 + logExtraError(_("Cannot get process information.") + L"\n\n" + e.toString()); + return OsVersionDetail{}; //arrgh, it's a jungle out there: https://freefilesync.org/forum/viewtopic.php?t=7276 } }(); return verDetail.version; diff --git a/zen/zlib_wrap.cpp b/zen/zlib_wrap.cpp index 7e680131..5810ef56 100644 --- a/zen/zlib_wrap.cpp +++ b/zen/zlib_wrap.cpp @@ -172,7 +172,6 @@ public: { [[maybe_unused]] const int rv = ::deflateEnd(&gzipStream_); assert(rv == Z_OK); - warn_static("log on error") } size_t read(void* buffer, size_t bytesToRead) //throw SysError, X; return "bytesToRead" bytes unless end of stream! |