From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 72D5F3857C4F for ; Thu, 23 Sep 2021 15:10:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 72D5F3857C4F Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-189-XG_E6bpMNoaSMuKy_r9OXg-1; Thu, 23 Sep 2021 11:09:59 -0400 X-MC-Unique: XG_E6bpMNoaSMuKy_r9OXg-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 367F49126B; Thu, 23 Sep 2021 15:09:58 +0000 (UTC) Received: from localhost (unknown [10.33.36.241]) by smtp.corp.redhat.com (Postfix) with ESMTP id D036519C87; Thu, 23 Sep 2021 15:09:57 +0000 (UTC) Date: Thu, 23 Sep 2021 16:09:57 +0100 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Make std::system_category() recognize Windows error codes Message-ID: MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: multipart/mixed; boundary="vTQxLo8c4W/A1zJS" Content-Disposition: inline X-Spam-Status: No, score=-14.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 23 Sep 2021 15:10:05 -0000 --vTQxLo8c4W/A1zJS Content-Type: text/plain; charset=us-ascii Content-Disposition: inline The std::system_category error category should be used for system-specific error codes, which means on Windows it should be used for Windows error codes. Currently that category assumes that the error numbers it deals with are errno numbers, which means that ERROR_ACCESS_DENIED (which has value 0x5) gets treated as whichever errno number happens to have that value (EIO on mingw32-w64). This adds a mapping from known Windows error codes to generic errno ones. This means we correctly treat ERROR_ACCESS_DENIED as corresponding to EACCES. Also make std::system_category().message(int) return the right message for Windows errors, by using FormatMessage instead of strerror. The output of FormatMessage includes ".\r\n" at the end, so we strip that off to allow the message to be used in contexts where that would be problematic. Signed-off-by: Jonathan Wakely libstdc++-v3/ChangeLog: * src/c++11/system_error.cc (system_error_category) [_WIN32]: Map Windows error codes to generic POSIX error numbers. Use FormatMessage instead of strerror. * testsuite/19_diagnostics/error_category/system_category.cc: Adjust for new behaviour on Windows. Tested x86_64-linux. Committed to trunk. --vTQxLo8c4W/A1zJS Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="patch.txt" commit 477897451e46d67acb46f3ac45585e6eb9e7dde5 Author: Jonathan Wakely Date: Wed Feb 10 16:10:46 2021 libstdc++: Make std::system_category() recognize Windows error codes The std::system_category error category should be used for system-specific error codes, which means on Windows it should be used for Windows error codes. Currently that category assumes that the error numbers it deals with are errno numbers, which means that ERROR_ACCESS_DENIED (which has value 0x5) gets treated as whichever errno number happens to have that value (EIO on mingw32-w64). This adds a mapping from known Windows error codes to generic errno ones. This means we correctly treat ERROR_ACCESS_DENIED as corresponding to EACCES. Also make std::system_category().message(int) return the right message for Windows errors, by using FormatMessage instead of strerror. The output of FormatMessage includes ".\r\n" at the end, so we strip that off to allow the message to be used in contexts where that would be problematic. Signed-off-by: Jonathan Wakely libstdc++-v3/ChangeLog: * src/c++11/system_error.cc (system_error_category) [_WIN32]: Map Windows error codes to generic POSIX error numbers. Use FormatMessage instead of strerror. * testsuite/19_diagnostics/error_category/system_category.cc: Adjust for new behaviour on Windows. diff --git a/libstdc++-v3/src/c++11/system_error.cc b/libstdc++-v3/src/c++11/system_error.cc index 7fc178a4deb..f1cfc03c3de 100644 --- a/libstdc++-v3/src/c++11/system_error.cc +++ b/libstdc++-v3/src/c++11/system_error.cc @@ -32,6 +32,11 @@ #include #undef __sso_string +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +#include +#endif + namespace { using std::string; @@ -81,9 +86,33 @@ namespace string message(int i) const final { +#if defined(_WIN32) && !defined(__CYGWIN__) + char* buf = nullptr; + auto len + = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + nullptr, + i, + LANG_USER_DEFAULT, + reinterpret_cast(&buf), + 0, + nullptr); + if (len > 0) + { + struct deleter { + void operator()(void* p) const { ::LocalFree(p); } + }; + std::unique_ptr guard(buf); + if (len > 3 && !__builtin_memcmp(buf + len - 3, ".\r\n", 3)) [[likely]] + len -= 3; + return string(buf, len); + } + return string("Unknown error code"); +#else // XXX locale issues: how does one get or set loc. // _GLIBCXX_HAVE_STRERROR_L, strerror_l(i, cloc) return string(strerror(i)); +#endif } std::error_condition @@ -93,6 +122,132 @@ namespace // and system category otherwise. switch (ev) { +#if defined(_WIN32) && !defined(__CYGWIN__) + case 0: + return {0, generic_category_instance.obj}; + // Convert Windows error code into a corresponding POSIX errno value. +#define X(w, e) case ERROR_##w: return {e, generic_category_instance.obj}; + // This list is based on Cygwin's winsup/cygwin/errno.cc + X (ACCESS_DENIED, EACCES); + X (ACTIVE_CONNECTIONS, EAGAIN); + X (ALREADY_EXISTS, EEXIST); + X (BAD_DEVICE, ENODEV); + X (BAD_EXE_FORMAT, ENOEXEC); + X (BAD_NETPATH, ENOENT); + X (BAD_NET_NAME, ENOENT); + X (BAD_NET_RESP, ENOSYS); + X (BAD_PATHNAME, ENOENT); + X (BAD_PIPE, EINVAL); + X (BAD_UNIT, ENODEV); + X (BAD_USERNAME, EINVAL); + X (BEGINNING_OF_MEDIA, EIO); + X (BROKEN_PIPE, EPIPE); + X (BUSY, EBUSY); + X (BUS_RESET, EIO); + X (CALL_NOT_IMPLEMENTED, ENOSYS); + X (CANCELLED, EINTR); + X (CANNOT_MAKE, EPERM); + X (CHILD_NOT_COMPLETE, EBUSY); + X (COMMITMENT_LIMIT, EAGAIN); + X (CONNECTION_REFUSED, ECONNREFUSED); + X (CRC, EIO); + X (DEVICE_DOOR_OPEN, EIO); + X (DEVICE_IN_USE, EAGAIN); + X (DEVICE_REQUIRES_CLEANING, EIO); + X (DEV_NOT_EXIST, ENOENT); + X (DIRECTORY, ENOTDIR); + X (DIR_NOT_EMPTY, ENOTEMPTY); + X (DISK_CORRUPT, EIO); +#ifdef ENOSPC + X (DISK_FULL, ENOSPC); +#endif + X (DS_GENERIC_ERROR, EIO); +#ifdef ENOSPC + X (END_OF_MEDIA, ENOSPC); +#endif + X (EOM_OVERFLOW, EIO); + X (EXE_MACHINE_TYPE_MISMATCH, ENOEXEC); + X (EXE_MARKED_INVALID, ENOEXEC); + X (FILEMARK_DETECTED, EIO); + X (FILENAME_EXCED_RANGE, ENAMETOOLONG); + X (FILE_CORRUPT, EEXIST); + X (FILE_EXISTS, EEXIST); + X (FILE_INVALID, ENXIO); + X (FILE_NOT_FOUND, ENOENT); +#ifdef ENOSPC + X (HANDLE_DISK_FULL, ENOSPC); +#endif + X (INVALID_ADDRESS, EINVAL); + X (INVALID_AT_INTERRUPT_TIME, EINTR); + X (INVALID_BLOCK_LENGTH, EIO); + X (INVALID_DATA, EINVAL); + X (INVALID_DRIVE, ENODEV); + X (INVALID_EA_NAME, EINVAL); + X (INVALID_EXE_SIGNATURE, ENOEXEC); + X (INVALID_HANDLE, EBADF); + X (INVALID_NAME, ENOENT); + X (INVALID_PARAMETER, EINVAL); + X (INVALID_SIGNAL_NUMBER, EINVAL); + X (IOPL_NOT_ENABLED, ENOEXEC); + X (IO_DEVICE, EIO); + X (IO_INCOMPLETE, EAGAIN); + X (IO_PENDING, EAGAIN); + X (LOCK_VIOLATION, EBUSY); + X (MAX_THRDS_REACHED, EAGAIN); + X (META_EXPANSION_TOO_LONG, EINVAL); + X (MOD_NOT_FOUND, ENOENT); + X (MORE_DATA, EMSGSIZE); + X (NEGATIVE_SEEK, EINVAL); + X (NETNAME_DELETED, ENOENT); + X (NOACCESS, EFAULT); + X (NONE_MAPPED, EINVAL); + X (NONPAGED_SYSTEM_RESOURCES, EAGAIN); + X (NOT_ENOUGH_MEMORY, ENOMEM); + X (NOT_ENOUGH_QUOTA, EIO); +#ifdef EPERM + X (NOT_OWNER, EPERM); +#else + X (NOT_OWNER, EACCES); +#endif + X (NOT_SAME_DEVICE, EXDEV); + X (NOT_SUPPORTED, ENOSYS); + X (NO_DATA, EPIPE); + X (NO_DATA_DETECTED, EIO); + X (NO_MORE_SEARCH_HANDLES, ENFILE); + X (NO_PROC_SLOTS, EAGAIN); + X (NO_SIGNAL_SENT, EIO); + X (NO_SYSTEM_RESOURCES, EFBIG); + X (NO_TOKEN, EINVAL); + X (OPEN_FAILED, EIO); + X (OPEN_FILES, EAGAIN); + X (OUTOFMEMORY, ENOMEM); + X (PAGED_SYSTEM_RESOURCES, EAGAIN); + X (PAGEFILE_QUOTA, EAGAIN); + X (PATH_NOT_FOUND, ENOENT); + X (PIPE_BUSY, EBUSY); + X (PIPE_CONNECTED, EBUSY); + X (POSSIBLE_DEADLOCK, EDEADLK); + X (PRIVILEGE_NOT_HELD, EPERM); + X (PROCESS_ABORTED, EFAULT); + X (PROC_NOT_FOUND, ESRCH); + X (SECTOR_NOT_FOUND, EINVAL); + X (SEEK, EINVAL); + X (SERVICE_REQUEST_TIMEOUT, EBUSY); + X (SETMARK_DETECTED, EIO); + X (SHARING_BUFFER_EXCEEDED, ENOLCK); + X (SHARING_VIOLATION, EBUSY); + X (SIGNAL_PENDING, EBUSY); + X (SIGNAL_REFUSED, EIO); + X (THREAD_1_INACTIVE, EINVAL); + X (TIMEOUT, EBUSY); + X (TOO_MANY_LINKS, EMLINK); + X (TOO_MANY_OPEN_FILES, EMFILE); + X (UNEXP_NET_ERR, EIO); + X (WORKING_SET_QUOTA, EAGAIN); + X (WRITE_PROTECT, EROFS); +#undef X + +#else // List of errno macros from [cerrno.syn]. // C11 only defines EDOM, EILSEQ and ERANGE, the rest are from POSIX. // They expand to integer constant expressions with type int, @@ -340,6 +495,7 @@ namespace return std::error_condition(EINVAL, std::generic_category()); */ +#endif default: return std::error_condition(ev, *this); } diff --git a/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc b/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc index 855f528a8aa..c289d532d4c 100644 --- a/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc +++ b/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc @@ -34,6 +34,19 @@ test02() const std::error_category& cat = std::system_category(); std::error_condition cond; +#if defined __MING32__ || defined __MINGW64__ + cond = cat.default_error_condition(8); // ERROR_NOT_ENOUGH_MEMORY + VERIFY( cond.value() == ENOMEM ); + VERIFY( cond.category() == std::generic_category() ); + VERIFY( cond == std::errc::not_enough_memory ); + + cond = cat.default_error_condition(5); // ERROR_ACCESS_DENIED + VERIFY( cond.value() == EACCES ); + VERIFY( cond.category() == std::generic_category() ); + VERIFY( cond == std::errc::permission_denied ); + return; +#endif + // As of 2011, ISO C only defines EDOM, EILSEQ and ERANGE: cond = cat.default_error_condition(EDOM); VERIFY( cond.value() == EDOM ); @@ -99,8 +112,13 @@ test03() // set "C" locale to get expected message auto loc = std::locale::global(std::locale::classic()); +#if defined __MING32__ || defined __MINGW64__ + std::string msg = std::system_category().message(5); // ERROR_ACCESS_DENIED + VERIFY(msg == "Access denied"); +#else std::string msg = std::system_category().message(EBADF); VERIFY( msg.find("file") != std::string::npos ); +#endif std::locale::global(loc); } --vTQxLo8c4W/A1zJS--