#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_STORAGE_SOURCE 1 #include "PosixApi.h" #if !defined(NUCLEX_STORAGE_WIN32) #include "Utf8/checked.h" #include "Nuclex/Storage/Errors/FileAccessError.h" #include "Nuclex/Storage/Errors/PermissionError.h" #include "Nuclex/Storage/Errors/BadPathError.h" #include "Nuclex/Support/Text/LexicalAppend.h" #include // for std::vector #include // string.h for strerror() #include // for direct access to 'errno' #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" // defined but not used #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" // defined but not used #endif namespace { // ------------------------------------------------------------------------------------------- // /// Overload for the Posix version of strerror_r() /// Buffer into which the error message has been written /// A pointer to the start of the error message string const char *getStringFromStrErrorR(int, std::vector &buffer) { return &buffer[0]; } // ------------------------------------------------------------------------------------------- // /// Overload for the GNU/Linux version of strerror_r() /// Result returned from the strerror_r() function /// A pointer to the start of the error message string const char *getStringFromStrErrorR(const char *strErrorReturn, std::vector &) { return strErrorReturn; } // ------------------------------------------------------------------------------------------- // } // anonymous namespace #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif namespace Nuclex { namespace Storage { namespace Helpers { // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_STORAGE_STRERROR_IS_THREAD_LOCAL) std::string PosixApi::GetErrorMessage(int errorNumber) { errno = 0; const char *errorMessageString = ::strerror(errorNumber); int errorNumberFromStrError = errno; if(errorNumberFromStrError != 0) { std::string errorMessage(u8"Error "); Nuclex::Support::Text::lexical_append(errorMessage, errorNumber); errorMessage.append(u8" (and error message lookup failed)"); return errorMessage; } return std::string(errorMessageString); } #else // if we can't be sure strerror() is thread-local, let's do defensive programming std::string PosixApi::GetErrorMessage(int errorNumber) { std::vector buffer; buffer.resize(256); for(;;) { // Try to obtain the error number. The return value of strerror_r() is different // between GNU and Posix. There's no reliable error return, so we reset errno for // the current thread and check it again after calling strerror_r() errno = 0; const char *posixErrorMessage = getStringFromStrErrorR( ::strerror_r(errorNumber, &buffer[0], buffer.size()), buffer ); int errorNumberFromStrError = errno; if(errorNumberFromStrError == 0) { return std::string(posixErrorMessage); } // If the buffer was too small, try again with 1024 bytes, 4096 bytes and // 16384 bytes, then blow up. if(errorNumberFromStrError == ERANGE) { std::size_t bufferSize = buffer.size(); if(bufferSize < 16384) { buffer.resize(bufferSize * 4); continue; } } // We failed to look up the error message. At least output the original // error number and remark that we weren't able to look up the error message. std::string errorMessage(u8"Error "); Nuclex::Support::Text::lexical_append(errorMessage, errorNumber); errorMessage.append(u8" (and error message lookup failed)"); return errorMessage; } // for(;;) } #endif // ------------------------------------------------------------------------------------------- // bool PosixApi::IsPathError(int errorNumber) { return ( (errorNumber == ELOOP) || (errorNumber == EMLINK) || (errorNumber == ENAMETOOLONG) || (errorNumber == ENOENT) || (errorNumber == ENOTDIR) ); } // ------------------------------------------------------------------------------------------- // bool PosixApi::GetEnvironmentVariable(const std::string &name, std::string &value) { const char *valueAsChars = ::getenv(name.c_str()); if(valueAsChars == nullptr) { return false; } value.assign(valueAsChars); return true; } // ------------------------------------------------------------------------------------------- // void PosixApi::ThrowExceptionForSystemError(const std::string &errorMessage, int errorNumber) { using Nuclex::Storage::Helpers::PosixApi; std::string combinedErrorMessage(errorMessage); combinedErrorMessage.append(u8" - "); combinedErrorMessage.append(PosixApi::GetErrorMessage(errorNumber)); if(errorNumber == EACCES) { throw Nuclex::Storage::Errors::PermissionError( std::error_code(errorNumber, std::system_category()), combinedErrorMessage ); } else if(PosixApi::IsPathError(errorNumber)) { throw Nuclex::Storage::Errors::BadPathError( std::error_code(errorNumber, std::system_category()), combinedErrorMessage ); } else { throw Nuclex::Storage::Errors::FileAccessError( std::error_code(errorNumber, std::system_category()), combinedErrorMessage ); } } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Storage::Helpers #endif // !defined(NUCLEX_STORAGE_WIN32)