From 9e14b80e0716c2be71c6100cad7aa7c61ac46c6e Mon Sep 17 00:00:00 2001 From: Gil Date: Tue, 12 Jun 2018 09:36:08 -0700 Subject: Create Status objects from errno (#1374) * Add a portable interface to strerror * Add Status::FromErrno * Add strerror_test.cc to the Xcode project * Use glibc feature selection macros instead of return-type overloads * Fix tensorflow references --- .../src/firebase/firestore/util/CMakeLists.txt | 9 ++ .../core/src/firebase/firestore/util/status.cc | 151 +++++++++++++++++++++ .../core/src/firebase/firestore/util/status.h | 7 + .../src/firebase/firestore/util/status_apple.mm | 45 ++++++ .../core/src/firebase/firestore/util/statusor.h | 5 +- .../core/src/firebase/firestore/util/strerror.cc | 92 +++++++++++++ .../core/src/firebase/firestore/util/strerror.h | 36 +++++ 7 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/util/status_apple.mm create mode 100644 Firestore/core/src/firebase/firestore/util/strerror.cc create mode 100644 Firestore/core/src/firebase/firestore/util/strerror.h (limited to 'Firestore/core/src/firebase') diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index b2c4195..5b38b2e 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -193,6 +193,8 @@ cc_library( statusor.cc statusor.h statusor_internals.h + strerror.cc + strerror.h string_util.cc string_util.h type_traits.h @@ -203,3 +205,10 @@ cc_library( ${FIREBASE_FIRESTORE_UTIL_LOG} ${FIREBASE_FIRESTORE_UTIL_RANDOM} ) + +if(APPLE) + target_sources( + firebase_firestore_util PRIVATE + status_apple.mm + ) +endif() diff --git a/Firestore/core/src/firebase/firestore/util/status.cc b/Firestore/core/src/firebase/firestore/util/status.cc index 46f3ce6..3e8585a 100644 --- a/Firestore/core/src/firebase/firestore/util/status.cc +++ b/Firestore/core/src/firebase/firestore/util/status.cc @@ -16,6 +16,9 @@ #include "Firestore/core/src/firebase/firestore/util/status.h" +#include + +#include "Firestore/core/src/firebase/firestore/util/strerror.h" #include "Firestore/core/src/firebase/firestore/util/string_format.h" namespace firebase { @@ -29,6 +32,154 @@ Status::Status(FirestoreErrorCode code, absl::string_view msg) { state_->msg = static_cast(msg); } +/// Returns the Canonical error code for the given errno value. +static FirestoreErrorCode CodeForErrno(int errno_code) { + switch (errno_code) { + case 0: + return FirestoreErrorCode::Ok; + + // Internal canonical mappings call these failed preconditions, but for + // our purposes these must indicate an internal error in file handling. + case EBADF: // Invalid file descriptor +#if defined(EBADFD) + case EBADFD: // File descriptor in bad state +#endif + return FirestoreErrorCode::Internal; + + case EINVAL: // Invalid argument + case ENAMETOOLONG: // Filename too long + case E2BIG: // Argument list too long + case EDESTADDRREQ: // Destination address required + case EDOM: // Mathematics argument out of domain of function + case EFAULT: // Bad address + case EILSEQ: // Illegal byte sequence + case ENOPROTOOPT: // Protocol not available + case ENOSTR: // Not a STREAM + case ENOTSOCK: // Not a socket + case ENOTTY: // Inappropriate I/O control operation + case EPROTOTYPE: // Protocol wrong type for socket + case ESPIPE: // Invalid seek + return FirestoreErrorCode::InvalidArgument; + + case ETIMEDOUT: // Connection timed out + case ETIME: // Timer expired + return FirestoreErrorCode::DeadlineExceeded; + + case ENODEV: // No such device + case ENOENT: // No such file or directory +#if defined(ENOMEDIUM) + case ENOMEDIUM: // No medium found +#endif + case ENXIO: // No such device or address + case ESRCH: // No such process + return FirestoreErrorCode::NotFound; + + case EEXIST: // File exists + case EADDRNOTAVAIL: // Address not available + case EALREADY: // Connection already in progress +#if defined(ENOTUNIQ) + case ENOTUNIQ: // Name not unique on network +#endif + return FirestoreErrorCode::AlreadyExists; + + case EPERM: // Operation not permitted + case EACCES: // Permission denied +#if defined(ENOKEY) + case ENOKEY: // Required key not available +#endif + case EROFS: // Read only file system + return FirestoreErrorCode::PermissionDenied; + + case ENOTEMPTY: // Directory not empty + case EISDIR: // Is a directory + case ENOTDIR: // Not a directory + case EADDRINUSE: // Address already in use + case EBUSY: // Device or resource busy + case ECHILD: // No child processes + case EISCONN: // Socket is connected +#if defined(EISNAM) + case EISNAM: // Is a named type file +#endif + case ENOTBLK: // Block device required + case ENOTCONN: // The socket is not connected + case EPIPE: // Broken pipe + case ESHUTDOWN: // Cannot send after transport endpoint shutdown + case ETXTBSY: // Text file busy +#if defined(EUNATCH) + case EUNATCH: // Protocol driver not attached +#endif + return FirestoreErrorCode::FailedPrecondition; + + case ENOSPC: // No space left on device + case EDQUOT: // Disk quota exceeded + case EMFILE: // Too many open files + case EMLINK: // Too many links + case ENFILE: // Too many open files in system + case ENOBUFS: // No buffer space available + case ENODATA: // No message is available on the STREAM read queue + case ENOMEM: // Not enough space + case ENOSR: // No STREAM resources + case EUSERS: // Too many users + return FirestoreErrorCode::ResourceExhausted; + +#if defined(ECHRNG) + case ECHRNG: // Channel number out of range +#endif + case EFBIG: // File too large + case EOVERFLOW: // Value too large to be stored in data type + case ERANGE: // Result too large + return FirestoreErrorCode::OutOfRange; + +#if defined(ENOPKG) + case ENOPKG: // Package not installed +#endif + case ENOSYS: // Function not implemented + case ENOTSUP: // Operation not supported + case EAFNOSUPPORT: // Address family not supported + case EPFNOSUPPORT: // Protocol family not supported + case EPROTONOSUPPORT: // Protocol not supported + case ESOCKTNOSUPPORT: // Socket type not supported + case EXDEV: // Improper link + return FirestoreErrorCode::Unimplemented; + + case EAGAIN: // Resource temporarily unavailable +#if defined(ECOMM) + case ECOMM: // Communication error on send +#endif + case ECONNREFUSED: // Connection refused + case ECONNABORTED: // Connection aborted + case ECONNRESET: // Connection reset + case EINTR: // Interrupted function call + case EHOSTDOWN: // Host is down + case EHOSTUNREACH: // Host is unreachable + case ENETDOWN: // Network is down + case ENETRESET: // Connection aborted by network + case ENETUNREACH: // Network unreachable + case ENOLCK: // No locks available + case ENOLINK: // Link has been severed +#if defined(ENONET) + case ENONET: // Machine is not on the network +#endif + return FirestoreErrorCode::Unavailable; + + case EDEADLK: // Resource deadlock avoided + case ESTALE: // Stale file handle + return FirestoreErrorCode::Aborted; + + case ECANCELED: // Operation cancelled + return FirestoreErrorCode::Cancelled; + + default: { return FirestoreErrorCode::Unknown; } + } +} + +Status Status::FromErrno(int errno_code, absl::string_view msg) { + FirestoreErrorCode canonical_code = CodeForErrno(errno_code); + return Status{canonical_code, + util::StringFormat("%s (errno %s: %s)", msg, errno_code, + StrError(errno_code))}; +} + void Status::Update(const Status& new_status) { if (ok()) { *this = new_status; diff --git a/Firestore/core/src/firebase/firestore/util/status.h b/Firestore/core/src/firebase/firestore/util/status.h index 9121b36..5247c92 100644 --- a/Firestore/core/src/firebase/firestore/util/status.h +++ b/Firestore/core/src/firebase/firestore/util/status.h @@ -50,6 +50,13 @@ class ABSL_MUST_USE_RESULT Status { return Status(); } + /// Creates a status object from the given errno error code and message. + static Status FromErrno(int errno_code, absl::string_view msg); + +#if defined(__OBJC__) + static Status FromNSError(NSError* error); +#endif // defined(__OBJC__) + /// Returns true iff the status indicates success. bool ok() const { return (state_ == nullptr); diff --git a/Firestore/core/src/firebase/firestore/util/status_apple.mm b/Firestore/core/src/firebase/firestore/util/status_apple.mm new file mode 100644 index 0000000..2e2bb07 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/status_apple.mm @@ -0,0 +1,45 @@ +/* + * Copyright 2015, 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/firebase/firestore/util/status.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" + +namespace firebase { +namespace firestore { +namespace util { + +Status Status::FromNSError(NSError* error) { + NSError* original = error; + + while (error) { + if ([error.domain isEqualToString:NSPOSIXErrorDomain]) { + return FromErrno(static_cast(error.code), + MakeStringView(original.localizedDescription)); + } + + error = error.userInfo[NSUnderlyingErrorKey]; + } + + return Status{FirestoreErrorCode::Unknown, + StringFormat("Unknown error: %s", original)}; +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/statusor.h b/Firestore/core/src/firebase/firestore/util/statusor.h index dc5644a..9985956 100644 --- a/Firestore/core/src/firebase/firestore/util/statusor.h +++ b/Firestore/core/src/firebase/firestore/util/statusor.h @@ -59,7 +59,8 @@ // // StatusOr FooFactory::MakeNewFoo(int arg) { // if (arg <= 0) { -// return tensorflow::InvalidArgument("Arg must be positive"); +// return Status(FirestoreErrorCode::InvalidArgument, +// "Arg must be positive"); // } else { // return new Foo(arg); // } @@ -149,7 +150,7 @@ class ABSL_MUST_USE_RESULT StatusOr // // REQUIRES: !status.ok(). This requirement is DCHECKed. // In optimized builds, passing Status::OK() here will have the effect - // of passing tensorflow::error::INTERNAL as a fallback. + // of passing FirestoreErrorCode::Internal as a fallback. StatusOr(const Status& status); // NOLINT: allow non-explicit 1-param ctor StatusOr& operator=(const Status& status); diff --git a/Firestore/core/src/firebase/firestore/util/strerror.cc b/Firestore/core/src/firebase/firestore/util/strerror.cc new file mode 100644 index 0000000..ab1bbbb --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/strerror.cc @@ -0,0 +1,92 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Implementation note: +// +// This is ported from //base/strerror.cc, with several local modifications: +// +// * Removed non-portable optimization around to use sys_errlist where +// available without warnings. +// * Added __attribute__((unused)) to compile with -Wno-unused-functions. +// * Conformed to style/lint rules. + +#include "Firestore/core/src/firebase/firestore/util/strerror.h" + +#include +#include + +#if defined(_WIN32) +#define HAVE_STRERROR_S 1 + +#elif defined(__GLIBC__) +#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE +#define HAVE_POSIX_STRERROR_R 1 +#else +#define HAVE_GNU_STRERROR_R 1 +#endif + +#else +#define HAVE_POSIX_STRERROR_R 1 + +#endif + +namespace firebase { +namespace firestore { +namespace util { + +namespace { + +inline const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) { +#if HAVE_STRERROR_S + int rc = strerror_s(buf, buflen, errnum); + buf[buflen - 1] = '\0'; // guarantee NUL termination + + if (rc == 0 && strcmp(buf, "Unknown error") == 0) { + *buf = '\0'; + } + return buf; + +#elif HAVE_POSIX_STRERROR_R + if (strerror_r(errnum, buf, buflen)) { + *buf = '\0'; + } + return buf; + +#elif HAVE_GNU_STRERROR_R + return strerror_r(errnum, buf, buflen); + +#endif // HAVE_STRERROR_S +} + +} // namespace + +std::string StrError(int errnum) { + const int saved_errno = errno; + + char buf[100]; + const char* str = StrErrorAdaptor(errnum, buf, sizeof buf); + if (*str == '\0') { + snprintf(buf, sizeof buf, "Unknown error %d", errnum); + str = buf; + } + + errno = saved_errno; + return str; +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/strerror.h b/Firestore/core/src/firebase/firestore/util/strerror.h new file mode 100644 index 0000000..72c0300 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/strerror.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRERROR_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRERROR_H_ + +#include + +namespace firebase { +namespace firestore { +namespace util { + +// A portable and thread-safe alternative to strerror(). +// Returns a human-readable string describing the given POSIX error +// code. If the error code is not translatable, the string will be +// "Unknown error nnn". errno will not be modified by this call. +std::string StrError(int errnum); + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRERROR_H_ -- cgit v1.2.3