summaryrefslogtreecommitdiffstats
path: root/sal/osl/unx/uunxapi.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sal/osl/unx/uunxapi.cxx896
1 files changed, 896 insertions, 0 deletions
diff --git a/sal/osl/unx/uunxapi.cxx b/sal/osl/unx/uunxapi.cxx
new file mode 100644
index 000000000..e8901784c
--- /dev/null
+++ b/sal/osl/unx/uunxapi.cxx
@@ -0,0 +1,896 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you 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 .
+ */
+
+#include <config_features.h>
+
+#include "uunxapi.hxx"
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <limits.h>
+#include <rtl/ustring.hxx>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+OString osl::OUStringToOString(const OUString& s)
+{
+ return OUStringToOString(s, osl_getThreadTextEncoding());
+}
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+
+#include <Foundation/Foundation.h>
+#include <Security/Security.h>
+#include <mach-o/dyld.h>
+
+static NSUserDefaults *userDefaults = NULL;
+static bool isSandboxed = false;
+
+static void do_once()
+{
+ SecCodeRef code;
+ OSStatus rc = SecCodeCopySelf(kSecCSDefaultFlags, &code);
+
+ SecStaticCodeRef staticCode;
+ if (rc == errSecSuccess)
+ rc = SecCodeCopyStaticCode(code, kSecCSDefaultFlags, &staticCode);
+
+ CFDictionaryRef signingInformation;
+ if (rc == errSecSuccess)
+ rc = SecCodeCopySigningInformation(staticCode, kSecCSRequirementInformation, &signingInformation);
+
+ CFDictionaryRef entitlements = NULL;
+ if (rc == errSecSuccess)
+ entitlements = (CFDictionaryRef) CFDictionaryGetValue(signingInformation, kSecCodeInfoEntitlementsDict);
+
+ if (entitlements != NULL)
+ if (CFDictionaryGetValue(entitlements, CFSTR("com.apple.security.app-sandbox")) != NULL)
+ isSandboxed = true;
+
+ if (isSandboxed)
+ userDefaults = [NSUserDefaults standardUserDefaults];
+}
+
+typedef struct {
+ NSURL *scopeURL;
+ NSAutoreleasePool *pool;
+} accessFilePathState;
+
+static accessFilePathState *
+prepare_to_access_file_path( const char *cpFilePath )
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, &do_once);
+ NSURL *fileURL = nil;
+ NSData *data = nil;
+ BOOL stale;
+ accessFilePathState *state;
+
+ if (!isSandboxed)
+ return NULL;
+
+ // If malloc() fails we are screwed anyway
+ state = (accessFilePathState*) malloc(sizeof(accessFilePathState));
+
+ state->pool = [[NSAutoreleasePool alloc] init];
+ state->scopeURL = nil;
+
+ if (userDefaults != nil)
+ fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:cpFilePath]];
+
+ if (fileURL != nil)
+ data = [userDefaults dataForKey:[@"bookmarkFor:" stringByAppendingString:[fileURL absoluteString]]];
+
+ if (data != nil)
+ state->scopeURL = [NSURL URLByResolvingBookmarkData:data
+ options:NSURLBookmarkResolutionWithSecurityScope
+ relativeToURL:nil
+ bookmarkDataIsStale:&stale
+ error:nil];
+ if (state->scopeURL != nil)
+ [state->scopeURL startAccessingSecurityScopedResource];
+
+ return state;
+}
+
+static void
+done_accessing_file_path( const char * /*cpFilePath*/, accessFilePathState *state )
+{
+ if (!isSandboxed)
+ return;
+
+ int saved_errno = errno;
+
+ if (state->scopeURL != nil)
+ [state->scopeURL stopAccessingSecurityScopedResource];
+ [state->pool release];
+ free(state);
+
+ errno = saved_errno;
+}
+
+#else
+
+typedef void accessFilePathState;
+
+#define prepare_to_access_file_path( cpFilePath ) nullptr
+
+#define done_accessing_file_path( cpFilePath, state ) ((void) cpFilePath, (void) state)
+
+#endif
+
+#ifdef MACOSX
+/*
+ * Helper function for resolving Mac native alias files (not the same as unix alias files)
+ * and to return the resolved alias as OString
+ */
+static OString macxp_resolveAliasAndConvert(OString const & p)
+{
+ char path[PATH_MAX];
+ if (p.getLength() < PATH_MAX)
+ {
+ strcpy(path, p.getStr());
+ macxp_resolveAlias(path, PATH_MAX);
+ return path;
+ }
+ return p;
+}
+#endif /* MACOSX */
+
+int osl::access(const OString& pstrPath, int mode)
+{
+ OString fn = pstrPath;
+#ifdef ANDROID
+ if (fn == "/assets" || fn.startsWith("/assets/"))
+ {
+ struct stat stat;
+ if (lo_apk_lstat(fn.getStr(), &stat) == -1)
+ return -1;
+ if (mode & W_OK)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+ }
+#endif
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ int result = ::access(fn.getStr(), mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "access(" << fn << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "access(" << fn << ",0" << std::oct << mode << std::dec << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+namespace {
+
+OString toOString(OString const & s) { return s; }
+
+OString toOString(OUString const & s) { return osl::OUStringToOString(s); }
+
+template<typename T> T fromOString(OString const &) = delete;
+
+template<> OString fromOString(OString const & s) { return s; }
+
+template<> OUString fromOString(OString const & s)
+{ return OStringToOUString(s, osl_getThreadTextEncoding()); }
+
+template<typename T> bool realpath_(const T& pstrFileName, T& ppstrResolvedName)
+{
+ OString fn = toOString(pstrFileName);
+#ifdef ANDROID
+ if (fn == "/assets" || fn.startsWith("/assets/"))
+ {
+ if (osl::access(fn, F_OK) == -1)
+ return false;
+
+ ppstrResolvedName = pstrFileName;
+
+ return true;
+ }
+#endif
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ char rp[PATH_MAX];
+ bool bRet = realpath(fn.getStr(), rp);
+ int saved_errno = errno;
+ if (!bRet)
+ SAL_INFO("sal.file", "realpath(" << fn << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "realpath(" << fn << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ if (bRet)
+ {
+ ppstrResolvedName = fromOString<T>(OString(rp));
+ }
+
+ errno = saved_errno;
+
+ return bRet;
+}
+
+}
+
+bool osl::realpath(const OUString& pustrFileName, OUString& ppustrResolvedName)
+{
+ return realpath_(pustrFileName, ppustrResolvedName);
+}
+
+bool osl::realpath(const OString& pstrFileName, OString& ppstrResolvedName)
+{
+ return realpath_(pstrFileName, ppstrResolvedName);
+}
+
+int stat_c(const char* cpPath, struct stat* buf)
+{
+#ifdef ANDROID
+ if (strncmp(cpPath, "/assets", sizeof("/assets")-1) == 0 &&
+ (cpPath[sizeof("/assets")-1] == '\0' ||
+ cpPath[sizeof("/assets")-1] == '/'))
+ return lo_apk_lstat(cpPath, buf);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = stat(cpPath, buf);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "stat(" << cpPath << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "stat(" << cpPath << "): OK");
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int lstat_c(const char* cpPath, struct stat* buf)
+{
+#ifdef ANDROID
+ if (strncmp(cpPath, "/assets", sizeof("/assets")-1) == 0 &&
+ (cpPath[sizeof("/assets")-1] == '\0' ||
+ cpPath[sizeof("/assets")-1] == '/'))
+ return lo_apk_lstat(cpPath, buf);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = lstat(cpPath, buf);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "lstat(" << cpPath << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "lstat(" << cpPath << "): OK");
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+namespace {
+
+template<typename T> int lstat_(const T& pstrPath, struct stat& buf)
+{
+ OString fn = toOString(pstrPath);
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ return lstat_c(fn.getStr(), &buf);
+}
+
+}
+
+int osl::lstat(const OUString& pustrPath, struct stat& buf)
+{
+ return lstat_(pustrPath, buf);
+}
+
+int osl::lstat(const OString& pstrPath, struct stat& buf)
+{
+ return lstat_(pstrPath, buf);
+}
+
+int osl::mkdir(const OString& path, mode_t mode)
+{
+ accessFilePathState *state = prepare_to_access_file_path(path.getStr());
+
+ int result = ::mkdir(path.getStr(), mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "mkdir(" << path << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "mkdir(" << path << ",0" << std::oct << mode << std::dec << "): OK");
+
+ done_accessing_file_path(path.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int open_c(const char *cpPath, int oflag, int mode)
+{
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = open(cpPath, oflag, mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "open(" << cpPath << ",0" << std::oct << oflag << ",0" << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "open(" << cpPath << ",0" << std::oct << oflag << ",0" << mode << std::dec << ") => " << result);
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ if (isSandboxed && result != -1 && (oflag & O_CREAT) && (oflag & O_EXCL))
+ {
+ // A new file was created. Check if it is outside the sandbox.
+ // (In that case it must be one the user selected as export or
+ // save destination in a file dialog, otherwise we wouldn't
+ // have been able to create it.) Create and store a security
+ // scoped bookmark for it so that we can access the file in
+ // the future, too. (For the "Recent Files" functionality.)
+ const char *sandbox = [NSHomeDirectory() UTF8String];
+ if (!(strncmp(sandbox, cpPath, strlen(sandbox)) == 0 &&
+ cpPath[strlen(sandbox)] == '/'))
+ {
+ NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:cpPath]];
+ NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:nil];
+ if (data != NULL)
+ {
+ [userDefaults setObject:data
+ forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
+ }
+ }
+ }
+#endif
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int utime_c(const char *cpPath, struct utimbuf *times)
+{
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = utime(cpPath, times);
+
+ done_accessing_file_path(cpPath, state);
+
+ return result;
+}
+
+int ftruncate_with_name(int fd, sal_uInt64 uSize, rtl_String* path)
+{
+ /* When sandboxed on macOS, ftruncate(), even if it takes an
+ * already open file descriptor which was returned from an open()
+ * call already checked by the sandbox, still requires a security
+ * scope bookmark for the file to be active in case the file is
+ * one that the sandbox doesn't otherwise allow access to. Luckily
+ * LibreOffice usually calls ftruncate() through the helpful C++
+ * abstraction layer that keeps the pathname around.
+ */
+
+ OString fn(path);
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ int result = ftruncate(fd, uSize);
+ int saved_errno = errno;
+ if (result < 0)
+ SAL_INFO("sal.file", "ftruncate(" << fd << "," << uSize << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "ftruncate(" << fd << "," << uSize << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+
+std::string UnixErrnoString(int nErrno)
+{
+ // Errnos from <asm-generic/errno-base.h> and <asm-generic/errno.h> on Linux and <sys/errno.h>
+ // on macOS.
+ switch (nErrno)
+ {
+ case EPERM:
+ return "EPERM";
+ case ENOENT:
+ return "ENOENT";
+ case ESRCH:
+ return "ESRCH";
+ case EINTR:
+ return "EINTR";
+ case EIO:
+ return "EIO";
+ case ENXIO:
+ return "ENXIO";
+ case E2BIG:
+ return "E2BIG";
+ case ENOEXEC:
+ return "ENOEXEC";
+ case EBADF:
+ return "EBADF";
+ case ECHILD:
+ return "ECHILD";
+ case EAGAIN:
+ return "EAGAIN";
+ case ENOMEM:
+ return "ENOMEM";
+ case EACCES:
+ return "EACCES";
+ case EFAULT:
+ return "EFAULT";
+#ifdef ENOTBLK
+ case ENOTBLK:
+ return "ENOTBLK";
+#endif
+ case EBUSY:
+ return "EBUSY";
+ case EEXIST:
+ return "EEXIST";
+ case EXDEV:
+ return "EXDEV";
+ case ENODEV:
+ return "ENODEV";
+ case ENOTDIR:
+ return "ENOTDIR";
+ case EISDIR:
+ return "EISDIR";
+ case EINVAL:
+ return "EINVAL";
+ case ENFILE:
+ return "ENFILE";
+ case EMFILE:
+ return "EMFILE";
+ case ENOTTY:
+ return "ENOTTY";
+ case ETXTBSY:
+ return "ETXTBSY";
+ case EFBIG:
+ return "EFBIG";
+ case ENOSPC:
+ return "ENOSPC";
+ case ESPIPE:
+ return "ESPIPE";
+ case EROFS:
+ return "EROFS";
+ case EMLINK:
+ return "EMLINK";
+ case EPIPE:
+ return "EPIPE";
+ case EDOM:
+ return "EDOM";
+ case ERANGE:
+ return "ERANGE";
+ case EDEADLK:
+ return "EDEADLK";
+ case ENAMETOOLONG:
+ return "ENAMETOOLONG";
+ case ENOLCK:
+ return "ENOLCK";
+ case ENOSYS:
+ return "ENOSYS";
+ case ENOTEMPTY:
+ return "ENOTEMPTY";
+ case ELOOP:
+ return "ELOOP";
+ case ENOMSG:
+ return "ENOMSG";
+ case EIDRM:
+ return "EIDRM";
+#ifdef ECHRNG
+ case ECHRNG:
+ return "ECHRNG";
+#endif
+#ifdef EL2NSYNC
+ case EL2NSYNC:
+ return "EL2NSYNC";
+#endif
+#ifdef EL3HLT
+ case EL3HLT:
+ return "EL3HLT";
+#endif
+#ifdef EL3RST
+ case EL3RST:
+ return "EL3RST";
+#endif
+#ifdef ELNRNG
+ case ELNRNG:
+ return "ELNRNG";
+#endif
+#ifdef EUNATCH
+ case EUNATCH:
+ return "EUNATCH";
+#endif
+#ifdef ENOCSI
+ case ENOCSI:
+ return "ENOCSI";
+#endif
+#ifdef EL2HLT
+ case EL2HLT:
+ return "EL2HLT";
+#endif
+#ifdef EBADE
+ case EBADE:
+ return "EBADE";
+#endif
+#ifdef EBADR
+ case EBADR:
+ return "EBADR";
+#endif
+#ifdef EXFULL
+ case EXFULL:
+ return "EXFULL";
+#endif
+#ifdef ENOANO
+ case ENOANO:
+ return "ENOANO";
+#endif
+#ifdef EBADRQC
+ case EBADRQC:
+ return "EBADRQC";
+#endif
+#ifdef EBADSLT
+ case EBADSLT:
+ return "EBADSLT";
+#endif
+#ifdef EBFONT
+ case EBFONT:
+ return "EBFONT";
+#endif
+ case ENOSTR:
+ return "ENOSTR";
+ case ENODATA:
+ return "ENODATA";
+ case ETIME:
+ return "ETIME";
+ case ENOSR:
+ return "ENOSR";
+#ifdef ENONET
+ case ENONET:
+ return "ENONET";
+#endif
+#ifdef ENOPKG
+ case ENOPKG:
+ return "ENOPKG";
+#endif
+#ifdef EREMOTE
+ case EREMOTE:
+ return "EREMOTE";
+#endif
+ case ENOLINK:
+ return "ENOLINK";
+#ifdef EADV
+ case EADV:
+ return "EADV";
+#endif
+#ifdef ESRMNT
+ case ESRMNT:
+ return "ESRMNT";
+#endif
+#ifdef ECOMM
+ case ECOMM:
+ return "ECOMM";
+#endif
+ case EPROTO:
+ return "EPROTO";
+ case EMULTIHOP:
+ return "EMULTIHOP";
+#ifdef EDOTDOT
+ case EDOTDOT:
+ return "EDOTDOT";
+#endif
+ case EBADMSG:
+ return "EBADMSG";
+ case EOVERFLOW:
+ return "EOVERFLOW";
+#ifdef ENOTUNIQ
+ case ENOTUNIQ:
+ return "ENOTUNIQ";
+#endif
+#ifdef EBADFD
+ case EBADFD:
+ return "EBADFD";
+#endif
+#ifdef EREMCHG
+ case EREMCHG:
+ return "EREMCHG";
+#endif
+#ifdef ELIBACC
+ case ELIBACC:
+ return "ELIBACC";
+#endif
+#ifdef ELIBBAD
+ case ELIBBAD:
+ return "ELIBBAD";
+#endif
+#ifdef ELIBSCN
+ case ELIBSCN:
+ return "ELIBSCN";
+#endif
+#ifdef ELIBMAX
+ case ELIBMAX:
+ return "ELIBMAX";
+#endif
+#ifdef ELIBEXEC
+ case ELIBEXEC:
+ return "ELIBEXEC";
+#endif
+ case EILSEQ:
+ return "EILSEQ";
+#ifdef ERESTART
+ case ERESTART:
+ return "ERESTART";
+#endif
+#ifdef ESTRPIPE
+ case ESTRPIPE:
+ return "ESTRPIPE";
+#endif
+#ifdef EUSERS
+ case EUSERS:
+ return "EUSERS";
+#endif
+ case ENOTSOCK:
+ return "ENOTSOCK";
+ case EDESTADDRREQ:
+ return "EDESTADDRREQ";
+ case EMSGSIZE:
+ return "EMSGSIZE";
+ case EPROTOTYPE:
+ return "EPROTOTYPE";
+ case ENOPROTOOPT:
+ return "ENOPROTOOPT";
+ case EPROTONOSUPPORT:
+ return "EPROTONOSUPPORT";
+#ifdef ESOCKTNOSUPPORT
+ case ESOCKTNOSUPPORT:
+ return "ESOCKTNOSUPPORT";
+#endif
+#ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+ return "EOPNOTSUPP";
+#endif
+ case EPFNOSUPPORT:
+ return "EPFNOSUPPORT";
+ case EAFNOSUPPORT:
+ return "EAFNOSUPPORT";
+ case EADDRINUSE:
+ return "EADDRINUSE";
+ case EADDRNOTAVAIL:
+ return "EADDRNOTAVAIL";
+ case ENETDOWN:
+ return "ENETDOWN";
+ case ENETUNREACH:
+ return "ENETUNREACH";
+ case ENETRESET:
+ return "ENETRESET";
+ case ECONNABORTED:
+ return "ECONNABORTED";
+ case ECONNRESET:
+ return "ECONNRESET";
+ case ENOBUFS:
+ return "ENOBUFS";
+ case EISCONN:
+ return "EISCONN";
+ case ENOTCONN:
+ return "ENOTCONN";
+#ifdef ESHUTDOWN
+ case ESHUTDOWN:
+ return "ESHUTDOWN";
+#endif
+#ifdef ETOOMANYREFS
+ case ETOOMANYREFS:
+ return "ETOOMANYREFS";
+#endif
+ case ETIMEDOUT:
+ return "ETIMEDOUT";
+ case ECONNREFUSED:
+ return "ECONNREFUSED";
+#ifdef EHOSTDOWN
+ case EHOSTDOWN:
+ return "EHOSTDOWN";
+#endif
+ case EHOSTUNREACH:
+ return "EHOSTUNREACH";
+ case EALREADY:
+ return "EALREADY";
+ case EINPROGRESS:
+ return "EINPROGRESS";
+ case ESTALE:
+ return "ESTALE";
+#ifdef EUCLEAN
+ case EUCLEAN:
+ return "EUCLEAN";
+#endif
+#ifdef ENOTNAM
+ case ENOTNAM:
+ return "ENOTNAM";
+#endif
+#ifdef ENAVAIL
+ case ENAVAIL:
+ return "ENAVAIL";
+#endif
+#ifdef EISNAM
+ case EISNAM:
+ return "EISNAM";
+#endif
+#ifdef EREMOTEIO
+ case EREMOTEIO:
+ return "EREMOTEIO";
+#endif
+ case EDQUOT:
+ return "EDQUOT";
+#ifdef ENOMEDIUM
+ case ENOMEDIUM:
+ return "ENOMEDIUM";
+#endif
+#ifdef EMEDIUMTYPE
+ case EMEDIUMTYPE:
+ return "EMEDIUMTYPE";
+#endif
+ case ECANCELED:
+ return "ECANCELED";
+#ifdef ENOKEY
+ case ENOKEY:
+ return "ENOKEY";
+#endif
+#ifdef EKEYEXPIRED
+ case EKEYEXPIRED:
+ return "EKEYEXPIRED";
+#endif
+#ifdef EKEYREVOKED
+ case EKEYREVOKED:
+ return "EKEYREVOKED";
+#endif
+#ifdef EKEYREJECTED
+ case EKEYREJECTED:
+ return "EKEYREJECTED";
+#endif
+#ifdef EOWNERDEAD
+ case EOWNERDEAD:
+ return "EOWNERDEAD";
+#endif
+#ifdef ENOTRECOVERABLE
+ case ENOTRECOVERABLE:
+ return "ENOTRECOVERABLE";
+#endif
+#ifdef ERFKILL
+ case ERFKILL:
+ return "ERFKILL";
+#endif
+#ifdef EHWPOISON
+ case EHWPOISON:
+ return "EHWPOISON";
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM:
+ return "EPROCLIM";
+#endif
+#ifdef EBADRPC
+ case EBADRPC:
+ return "EBADRPC";
+#endif
+#ifdef ERPCMISMATCH
+ case ERPCMISMATCH:
+ return "ERPCMISMATCH";
+#endif
+#ifdef EPROGUNAVAIL
+ case EPROGUNAVAIL:
+ return "EPROGUNAVAIL";
+#endif
+#ifdef EPROGMISMATCH
+ case EPROGMISMATCH:
+ return "EPROGMISMATCH";
+#endif
+#ifdef EPROCUNAVAIL
+ case EPROCUNAVAIL:
+ return "EPROCUNAVAIL";
+#endif
+#ifdef EFTYPE
+ case EFTYPE:
+ return "EFTYPE";
+#endif
+#ifdef EAUTH
+ case EAUTH:
+ return "EAUTH";
+#endif
+#ifdef ENEEDAUTH
+ case ENEEDAUTH:
+ return "ENEEDAUTH";
+#endif
+#ifdef EPWROFF
+ case EPWROFF:
+ return "EPWROFF";
+#endif
+#ifdef EDEVERR
+ case EDEVERR:
+ return "EDEVERR";
+#endif
+#ifdef EBADEXEC
+ case EBADEXEC:
+ return "EBADEXEC";
+#endif
+#ifdef EBADARCH
+ case EBADARCH:
+ return "EBADARCH";
+#endif
+#ifdef ESHLIBVERS
+ case ESHLIBVERS:
+ return "ESHLIBVERS";
+#endif
+#ifdef EBADMACHO
+ case EBADMACHO:
+ return "EBADMACHO";
+#endif
+#ifdef ENOATTR
+ case ENOATTR:
+ return "ENOATTR";
+#endif
+#ifdef EQFULL
+ case EQFULL:
+ return "EQFULL";
+#endif
+ default:
+ char* str = strerror(nErrno);
+ return std::to_string(nErrno) + " (" + std::string(str) + ")";
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */