diff --git a/doc/xml/release/2024/2.50.xml b/doc/xml/release/2024/2.50.xml index 3ec8b96d6..79d40d130 100644 --- a/doc/xml/release/2024/2.50.xml +++ b/doc/xml/release/2024/2.50.xml @@ -68,6 +68,17 @@

Skip files truncated during backup when bundling.

+ + + + + + + + + +

Improve SFTP storage error messages.

+
diff --git a/src/storage/sftp/read.c b/src/storage/sftp/read.c index a3bd0225c..692e613b7 100644 --- a/src/storage/sftp/read.c +++ b/src/storage/sftp/read.c @@ -70,7 +70,11 @@ storageReadSftpOpen(THIS_VOID) THROW_FMT(FileMissingError, STORAGE_ERROR_READ_MISSING, strZ(this->interface.name)); } else - THROW_FMT(FileOpenError, STORAGE_ERROR_READ_OPEN, strZ(this->interface.name)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &FileOpenError, + strNewFmt(STORAGE_ERROR_READ_OPEN, strZ(this->interface.name)), NULL); + } } } // Else success @@ -152,7 +156,11 @@ storageReadSftp(THIS_VOID, Buffer *const buffer, const bool block) else if (rc == LIBSSH2_ERROR_EAGAIN) THROW_FMT(FileReadError, "timeout reading '%s'", strZ(this->interface.name)); else - THROW_FMT(FileReadError, "unable to read '%s'", strZ(this->interface.name)); + { + storageSftpEvalLibSsh2Error( + (int)rc, libssh2_sftp_last_error(this->sftpSession), &FileReadError, + strNewFmt("unable to read '%s'", strZ(this->interface.name)), NULL); + } } // Update amount of buffer used @@ -201,7 +209,11 @@ storageReadSftpClose(THIS_VOID) rc == LIBSSH2_ERROR_SFTP_PROTOCOL ? strZ(strNewFmt(": sftp errno [%lu]", libssh2_sftp_last_error(this->sftpSession))) : ""); else - THROW_FMT(FileCloseError, "timeout closing file '%s'", strZ(this->interface.name)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &FileCloseError, + strNewFmt("timeout closing file '%s'", strZ(this->interface.name)), NULL); + } } } diff --git a/src/storage/sftp/storage.c b/src/storage/sftp/storage.c index 790665dd9..c592c208e 100644 --- a/src/storage/sftp/storage.c +++ b/src/storage/sftp/storage.c @@ -126,6 +126,28 @@ storageSftpKnownHostCheckpFailureMsg(const int rc) FUNCTION_TEST_RETURN_CONST(STRINGZ, result); } +/**********************************************************************************************************************************/ +static String * +storageSftpLibSsh2SessionLastError(LIBSSH2_SESSION *const libSsh2Session) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM_P(VOID, libSsh2Session); + FUNCTION_TEST_END(); + + String *result; + char *libSsh2ErrMsg; + int libSsh2ErrMsgLen; + + const int rc = libssh2_session_last_error(libSsh2Session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0); + + if (libSsh2ErrMsgLen != 0) + result = strNewZN(libSsh2ErrMsg, (size_t)libSsh2ErrMsgLen); + else + result = strNewFmt("libssh2 no session error message provided [%d]", rc); + + FUNCTION_TEST_RETURN(STRING, result); +} + /*********************************************************************************************************************************** Rewrite the user's known_hosts file with a new entry ***********************************************************************************************************************************/ @@ -143,8 +165,6 @@ storageSftpUpdateKnownHostsFile( MEM_CONTEXT_TEMP_BEGIN() { - char *libSsh2ErrMsg; - int libSsh2ErrMsgLen; int rc; // Init a known host collection for the user's known_hosts file @@ -156,10 +176,10 @@ storageSftpUpdateKnownHostsFile( if (userKnownHostsList == NULL) { // Get the libssh2 error message and emit warning - rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0); - + const int rc = libssh2_session_last_errno(this->session); LOG_WARN_FMT( - "libssh2_knownhost_init failed for '%s' for update: libssh2 errno [%d] %s", userKnownHostsFile, rc, libSsh2ErrMsg); + "libssh2_knownhost_init failed for '%s' for update: libssh2 errno [%d] %s", userKnownHostsFile, rc, + strZ(storageSftpLibSsh2SessionLastError(this->session))); } else { @@ -214,11 +234,10 @@ storageSftpUpdateKnownHostsFile( else { // On readfile failure warn that we're unable to update the user's known_hosts file - rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0); - LOG_WARN_FMT( "libssh2 unable to read '%s' for update: libssh2 errno [%d] %s\n" - "HINT: does '%s' exist with proper permissions?", userKnownHostsFile, rc, libSsh2ErrMsg, userKnownHostsFile); + "HINT: does '%s' exist with proper permissions?", userKnownHostsFile, rc, + strZ(storageSftpLibSsh2SessionLastError(this->session)), userKnownHostsFile); } } @@ -342,7 +361,8 @@ storageSftpEvalLibSsh2Error( ASSERT(errorType != NULL); THROWP_FMT( - errorType, "%slibssh2 error [%d]%s%s", message != NULL ? zNewFmt("%s: ", strZ(message)) : "", ssh2Errno, + errorType, "%s%s%s%s", message != NULL ? zNewFmt("%s%s", strZ(message), ssh2Errno == 0 ? "" : ": ") : "", + ssh2Errno == 0 ? "" : zNewFmt("libssh2 error [%d]", ssh2Errno), ssh2Errno == LIBSSH2_ERROR_SFTP_PROTOCOL ? zNewFmt(": sftp error [%" PRIu64 "]", sftpErrno) : "", hint != NULL ? zNewFmt("\n%s", strZ(hint)) : ""); @@ -433,7 +453,10 @@ storageSftpInfo(THIS_VOID, const String *const file, const StorageInfoLevel leve // Throw libssh2 on errors other than no such file if (!storageSftpLibSsh2FxNoSuchFile(this, rc)) - THROW_FMT(FileOpenError, STORAGE_ERROR_INFO, strZ(file)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &FileOpenError, strNewFmt(STORAGE_ERROR_INFO, strZ(file)), NULL); + } } // Else the file exists else @@ -491,7 +514,11 @@ storageSftpInfo(THIS_VOID, const String *const file, const StorageInfoLevel leve THROW_FMT(FileReadError, "timeout getting destination for link '%s'", strZ(file)); if (linkDestinationSize < 0) - THROW_FMT(FileReadError, "unable to get destination for link '%s'", strZ(file)); + { + storageSftpEvalLibSsh2Error( + (int)linkDestinationSize, libssh2_sftp_last_error(this->sftpSession), &FileReadError, + strNewFmt("unable to get destination for link '%s'", strZ(file)), NULL); + } result.linkDestination = strNewZN(linkDestination, (size_t)linkDestinationSize); } @@ -706,7 +733,11 @@ storageSftpList(THIS_VOID, const String *const path, const StorageInfoLevel leve if (rc != 0) { if (rc != LIBSSH2_ERROR_EAGAIN) - THROW_FMT(PathCloseError, "unable to close path '%s' after listing", strZ(path)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &PathCloseError, + strNewFmt("unable to close path '%s' after listing", strZ(path)), NULL); + } else THROW_FMT(PathCloseError, "timeout closing path '%s' after listing", strZ(path)); } @@ -760,7 +791,11 @@ storageSftpRemove(THIS_VOID, const String *const file, const StorageInterfaceRem else { if (param.errorOnMissing) - THROW_FMT(FileRemoveError, "unable to remove '%s'", strZ(file)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &FileRemoveError, + strNewFmt("unable to remove '%s'", strZ(file)), NULL); + } } } @@ -879,11 +914,15 @@ storageSftpPathCreate( while (storageSftpWaitFd(this, rc)); if (rc == LIBSSH2_ERROR_EAGAIN) - THROW_FMT(PathCreateError, "timeout stating path '%s'", strZ(path)); + THROW_FMT(PathCreateError, "timeout stat'ing path '%s'", strZ(path)); // If rc = 0 then already exists if (rc == 0 && errorOnExists) - THROW_FMT(PathCreateError, "unable to create path '%s': path already exists", strZ(path)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &PathCreateError, + strNewFmt("unable to create path '%s': path already exists", strZ(path)), NULL); + } } // If the parent path does not exist then create it if allowed else if (sftpErrno == LIBSSH2_FX_NO_SUCH_FILE && !noParentCreate) @@ -896,10 +935,17 @@ storageSftpPathCreate( strFree(pathParent); } else if (sftpErrno != LIBSSH2_FX_FILE_ALREADY_EXISTS || errorOnExists) - THROW_FMT(PathCreateError, "sftp error unable to create path '%s'", strZ(path)); + { + storageSftpEvalLibSsh2Error( + rc, sftpErrno, &PathCreateError, strNewFmt("sftp error unable to create path '%s'", strZ(path)), NULL); + } } else - THROW_FMT(PathCreateError, "ssh2 error [%d] unable to create path '%s'", rc, strZ(path)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &PathCreateError, + strNewFmt("ssh2 error [%d] unable to create path '%s'", rc, strZ(path)), NULL); + } } FUNCTION_LOG_RETURN_VOID(); @@ -1007,7 +1053,9 @@ storageSftpPathRemove(THIS_VOID, const String *const path, const bool recurse, c // Path does not exist result = false; - THROW_FMT(PathRemoveError, STORAGE_ERROR_PATH_REMOVE, strZ(path)); + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &PathRemoveError, + strNewFmt(STORAGE_ERROR_PATH_REMOVE, strZ(path)), NULL); } } } @@ -1098,7 +1146,10 @@ storageSftpNew( THROW_FMT(ServiceError, "timeout during libssh2 handshake [%d]", rc); if (rc != 0) - THROW_FMT(ServiceError, "libssh2 handshake failed [%d]", rc); + { + THROW_FMT( + ServiceError, "libssh2 handshake failed [%d]: %s", rc, strZ(storageSftpLibSsh2SessionLastError(this->session))); + } int hashType = LIBSSH2_HOSTKEY_HASH_SHA1; size_t hashSize = 0; @@ -1158,7 +1209,14 @@ storageSftpNew( LIBSSH2_KNOWNHOSTS *const knownHostsList = libssh2_knownhost_init(this->session); if (knownHostsList == NULL) - THROW_FMT(ServiceError, "failure during libssh2_knownhost_init"); + { + const int rc = libssh2_session_last_errno(this->session); + + THROW_FMT( + ServiceError, + "failure during libssh2_knownhost_init: libssh2 errno [%d] %s", rc, + strZ(storageSftpLibSsh2SessionLastError(this->session))); + } // Get the list of known host files to search const StringList *const knownHostsPathList = storageSftpKnownHostsFilesList(param.knownHosts); @@ -1177,14 +1235,9 @@ storageSftpNew( LOG_DETAIL_FMT("libssh2 '%s' file is empty", currentKnownHostFile); else { - char *libSsh2ErrMsg; - int libSsh2ErrMsgLen; - - // Get the libssh2 error message - rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0); - LOG_DETAIL_FMT( - "libssh2 read '%s' failed: libssh2 errno [%d] %s", currentKnownHostFile, rc, libSsh2ErrMsg); + "libssh2 read '%s' failed: libssh2 errno [%d] %s", currentKnownHostFile, rc, + strZ(storageSftpLibSsh2SessionLastError(this->session))); } } else @@ -1305,7 +1358,11 @@ storageSftpNew( if (libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN) THROW_FMT(ServiceError, "timeout during init of libssh2_sftp session"); else - THROW_FMT(ServiceError, "unable to init libssh2_sftp session"); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &ServiceError, + strNewFmt("unable to init libssh2_sftp session"), NULL); + } } // Ensure libssh2/libssh2_sftp resources freed diff --git a/src/storage/sftp/write.c b/src/storage/sftp/write.c index 655697bba..a652466c6 100644 --- a/src/storage/sftp/write.c +++ b/src/storage/sftp/write.c @@ -98,7 +98,11 @@ storageWriteSftpOpen(THIS_VOID) } } else - THROW_FMT(FileOpenError, STORAGE_ERROR_WRITE_OPEN, strZ(this->interface.name)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &FileOpenError, + strNewFmt(STORAGE_ERROR_WRITE_OPEN, strZ(this->interface.name)), NULL); + } } FUNCTION_LOG_RETURN_VOID(); @@ -150,7 +154,11 @@ storageWriteSftp(THIS_VOID, const Buffer *const buffer) THROW_FMT(FileWriteError, "timeout writing '%s'", strZ(this->nameTmp)); if (rc < 0) - THROW_FMT(FileWriteError, "unable to write '%s'", strZ(this->nameTmp)); + { + storageSftpEvalLibSsh2Error( + (int)rc, libssh2_sftp_last_error(this->sftpSession), &FileWriteError, + strNewFmt("unable to write '%s'", strZ(this->nameTmp)), NULL); + } FUNCTION_LOG_RETURN_VOID(); } @@ -261,7 +269,11 @@ storageWriteSftpClose(THIS_VOID) if (rc == LIBSSH2_ERROR_EAGAIN) THROW_FMT(FileSyncError, "timeout syncing file '%s'", strZ(this->nameTmp)); else - THROW_FMT(FileSyncError, STORAGE_ERROR_WRITE_SYNC, strZ(this->nameTmp)); + { + storageSftpEvalLibSsh2Error( + rc, libssh2_sftp_last_error(this->sftpSession), &FileSyncError, + strNewFmt(STORAGE_ERROR_WRITE_SYNC, strZ(this->nameTmp)), NULL); + } } } diff --git a/test/src/common/harnessLibSsh2.c b/test/src/common/harnessLibSsh2.c index 1b57f941a..6436a0ecb 100644 --- a/test/src/common/harnessLibSsh2.c +++ b/test/src/common/harnessLibSsh2.c @@ -377,13 +377,20 @@ int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf) { // Avoid compiler complaining of unused params - (void)errmsg_len; (void)want_buf; HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_LAST_ERROR, NULL, (HrnLibSsh2 *)session); if (hrnLibSsh2->errMsg != NULL) + { *errmsg = hrnLibSsh2->errMsg; + *errmsg_len = (int)(strlen(hrnLibSsh2->errMsg) + 1); + } + else + { + *errmsg = NULL; + *errmsg_len = 0; + } return hrnLibSsh2->resultInt; } diff --git a/test/src/module/storage/sftpTest.c b/test/src/module/storage/sftpTest.c index de07b2c47..4846e417b 100644 --- a/test/src/module/storage/sftpTest.c +++ b/test/src/module/storage/sftpTest.c @@ -120,7 +120,9 @@ testRun(void) {.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]"}, {.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = LIBSSH2_ERROR_EAGAIN}, {.function = HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS, .resultInt = SSH2_BLOCK_WRITING}, - {.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = -1}, + {.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = LIBSSH2_ERROR_BAD_SOCKET}, + {.function = HRNLIBSSH2_SESSION_LAST_ERROR, .resultInt = LIBSSH2_ERROR_BAD_SOCKET, + .errMsg = (char *)"Bad socket provided"}, {.function = NULL} }); @@ -128,7 +130,7 @@ testRun(void) storageSftpNewP( TEST_PATH_STR, STRDEF("localhost"), 22, TEST_USER_STR, 1000, KEYPRIV, hashTypeSha1, .keyPub = KEYPUB, .hostKeyCheckType = SFTP_STRICT_HOSTKEY_CHECKING_STRICT), - ServiceError, "libssh2 handshake failed [-1]"); + ServiceError, "libssh2 handshake failed [-45]: Bad socket provided"); // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("handshake failure - timeout during libssh2 handshake"); @@ -291,6 +293,9 @@ testRun(void) {.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]"}, {.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = LIBSSH2_ERROR_NONE}, {.function = HRNLIBSSH2_KNOWNHOST_INIT, .resultNull = true}, + {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_ALLOC}, + {.function = HRNLIBSSH2_SESSION_LAST_ERROR, .resultInt = LIBSSH2_ERROR_ALLOC, + .errMsg = (char *)"Unable to allocate memory for known-hosts collection"}, {.function = NULL} }); @@ -321,7 +326,7 @@ testRun(void) .hostKeyCheckType = cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx), .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx))), ServiceError, - "failure during libssh2_knownhost_init"); + "failure during libssh2_knownhost_init: libssh2 errno [-6] Unable to allocate memory for known-hosts collection"); // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("libssh2_session_hostkey fail - return NULL - hostKeyCheckType = yes"); @@ -653,6 +658,7 @@ testRun(void) // storageSftpWaitFd returns false {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, {.function = NULL} }); @@ -731,6 +737,7 @@ testRun(void) {.function = HRNLIBSSH2_SFTP_INIT, .resultNull = true}, {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, {.function = NULL} }); @@ -840,6 +847,7 @@ testRun(void) {.function = HRNLIBSSH2_KNOWNHOST_CHECKP, .param = "[\"localhost\",22,\"" HOSTKEY "\",20,65537]", .resultInt = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND}, {.function = HRNLIBSSH2_KNOWNHOST_INIT, .resultNull = true}, + {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_ALLOC}, {.function = HRNLIBSSH2_SESSION_LAST_ERROR, .resultInt = LIBSSH2_ERROR_ALLOC, .errMsg = (char *)"Unable to allocate memory for known-hosts collection"}, {.function = HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX, @@ -1080,7 +1088,7 @@ testRun(void) {.function = HRNLIBSSH2_SESSION_LAST_ERROR, .errMsg = (char *)"Failed to open file", .resultInt = LIBSSH2_ERROR_FILE}, {.function = HRNLIBSSH2_KNOWNHOST_READFILE, .param = "[\"" ETC_KNOWNHOSTS2_FILE_CSTR "\",1]", .resultInt = LIBSSH2_ERROR_FILE}, - {.function = HRNLIBSSH2_SESSION_LAST_ERROR, .errMsg = (char *)"Failed to open file", .resultInt = LIBSSH2_ERROR_FILE}, + {.function = HRNLIBSSH2_SESSION_LAST_ERROR, .resultInt = LIBSSH2_ERROR_FILE}, {.function = HRNLIBSSH2_SESSION_HOSTKEY, .len = 20, .type = LIBSSH2_HOSTKEY_TYPE_RSA, .resultZ = HOSTKEY}, {.function = HRNLIBSSH2_KNOWNHOST_CHECKP, .param = "[\"localhost\",22,\"" HOSTKEY "\",20,65537]", .resultInt = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND}, @@ -1114,6 +1122,7 @@ testRun(void) storageTest = NULL; + harnessLogLevelSet(logLevelDetail); TEST_ASSIGN( storageTest, storageSftpNewP( @@ -1128,10 +1137,17 @@ testRun(void) .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx))), "new storage (defaults)"); TEST_RESULT_LOG( + "P00 DETAIL: libssh2 '/home/" TEST_USER "/.ssh/known_hosts' file is empty\n" + "P00 DETAIL: libssh2 read '/home/" TEST_USER "/.ssh/known_hosts2' failed: libssh2 errno [-16] Failed to open file\n" + "P00 DETAIL: libssh2 read '/etc/ssh/ssh_known_hosts' failed: libssh2 errno [-16] Failed to open file\n" + "P00 DETAIL: libssh2 read '/etc/ssh/ssh_known_hosts2' failed: libssh2 errno [-16] libssh2 no session error message " + "provided [-16]\n" "P00 WARN: host 'localhost' not found in known hosts files, attempting to add host to " "'/home/" TEST_USER "/.ssh/known_hosts'\n" "P00 WARN: pgBackRest added new host 'localhost' to '/home/" TEST_USER "/.ssh/known_hosts'"); + harnessLogLevelReset(); + memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); // ------------------------------------------------------------------------------------------------------------------------- @@ -1250,7 +1266,7 @@ testRun(void) #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 // ------------------------------------------------------------------------------------------------------------------------- - TEST_TITLE("sftp session init success - hostKeyCheckType = accept_new - add host to user's known_hosts ECDSA_384"); + TEST_TITLE("sftp session init success - hostKeyCheckType = accept-new - add host to user's known_hosts ECDSA_384"); hrnLibSsh2ScriptSet((HrnLibSsh2 []) { @@ -2064,9 +2080,11 @@ testRun(void) {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/noperm/noperm\",0]", .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/noperm/noperm\",0]", .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, // File and path {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/exists\",0]", .attrPerms = LIBSSH2_SFTP_S_IFREG, .resultInt = LIBSSH2_ERROR_NONE}, @@ -2135,8 +2153,12 @@ testRun(void) // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("permission denied"); - TEST_ERROR_FMT(storageExistsP(storageTest, fileNoPerm), FileOpenError, STORAGE_ERROR_INFO, strZ(fileNoPerm)); - TEST_ERROR_FMT(storagePathExistsP(storageTest, fileNoPerm), FileOpenError, STORAGE_ERROR_INFO, strZ(fileNoPerm)); + TEST_ERROR_FMT( + storageExistsP(storageTest, fileNoPerm), + FileOpenError, STORAGE_ERROR_INFO ": libssh2 error [-31]: sftp error [3]", strZ(fileNoPerm)); + TEST_ERROR_FMT( + storagePathExistsP(storageTest, fileNoPerm), + FileOpenError, STORAGE_ERROR_INFO ": libssh2 error [-31]: sftp error [3]", strZ(fileNoPerm)); // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("file and path"); @@ -2187,6 +2209,7 @@ testRun(void) {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/noperm/noperm\",1]", .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -2202,7 +2225,9 @@ testRun(void) .hostKeyCheckType = cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx), .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx)), .write = true); - TEST_ERROR_FMT(storageInfoP(storageTest, fileNoPerm), FileOpenError, STORAGE_ERROR_INFO, strZ(fileNoPerm)); + TEST_ERROR_FMT( + storageInfoP(storageTest, fileNoPerm), + FileOpenError, STORAGE_ERROR_INFO ": libssh2 error [-31]: sftp error [3]", strZ(fileNoPerm)); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); @@ -2211,6 +2236,7 @@ testRun(void) { HRNLIBSSH2_MACRO_STARTUP(), {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/noperm/noperm\",1]", .resultInt = LIBSSH2_ERROR_INVAL}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -2226,7 +2252,8 @@ testRun(void) .hostKeyCheckType = cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx), .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx)), .write = true); - TEST_ERROR_FMT(storageInfoP(storageTest, fileNoPerm), FileOpenError, STORAGE_ERROR_INFO, strZ(fileNoPerm)); + TEST_ERROR_FMT( + storageInfoP(storageTest, fileNoPerm), FileOpenError, STORAGE_ERROR_INFO ": libssh2 error [-34]", strZ(fileNoPerm)); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); @@ -2376,6 +2403,7 @@ testRun(void) {.function = HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS, .resultInt = SSH2_NO_BLOCK_READING_WRITING}, {.function = HRNLIBSSH2_SFTP_SYMLINK_EX, .param = "[\"" TEST_PATH "/testlink\",\"\",1]", .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_LINK_LOOP}, // Info - pipe {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/testpipe\",1]", .resultInt = LIBSSH2_ERROR_NONE, .attrPerms = LIBSSH2_SFTP_S_IFIFO | LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | @@ -2535,7 +2563,7 @@ testRun(void) // libssh2_sftp_symlink_ex fail link destination followLink false TEST_ERROR_FMT( storageInfoP(storageTest, linkName, .followLink = false), FileReadError, - "unable to get destination for link '" TEST_PATH "/testlink'"); + "unable to get destination for link '" TEST_PATH "/testlink': libssh2 error [-31]: sftp error [21]"); // -------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("info - pipe"); @@ -3809,6 +3837,7 @@ testRun(void) .flags = LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME | LIBSSH2_SFTP_ATTR_UIDGID, .mtime = 1555160000, .uid = TEST_USER_ID, .gid = TEST_GROUP_ID}, {.function = HRNLIBSSH2_SFTP_CLOSE_HANDLE, .resultInt = LIBSSH2_ERROR_SOCKET_RECV}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -3848,7 +3877,7 @@ testRun(void) TEST_ERROR_FMT( storageListP(storageTest, NULL, .errorOnMissing = true), PathCloseError, - "unable to close path '" TEST_PATH "' after listing"); + "unable to close path '" TEST_PATH "' after listing: libssh2 error [-43]"); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); #else @@ -4000,6 +4029,8 @@ testRun(void) // create /sub1 again fails mkdir_ex ssh error {.function = HRNLIBSSH2_SFTP_MKDIR_EX, .param = "[\"" TEST_PATH "/sub1\",488]", .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, + // create /sub1 again no error on file already exists, file already exists {.function = HRNLIBSSH2_SFTP_MKDIR_EX, .param = "[\"" TEST_PATH "/sub1\",488]", .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, @@ -4031,6 +4062,7 @@ testRun(void) .flags = LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME | LIBSSH2_SFTP_ATTR_UIDGID | LIBSSH2_SFTP_ATTR_SIZE, .mtime = 1656434296, .uid = TEST_USER_ID, .gid = TEST_GROUP_ID}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, // sub2 custom mode {.function = HRNLIBSSH2_SFTP_MKDIR_EX, .param = "[\"" TEST_PATH "/sub2\",511]", .resultInt = LIBSSH2_ERROR_NONE}, {.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" TEST_PATH "/sub2\",1]", .resultInt = LIBSSH2_ERROR_NONE, @@ -4088,11 +4120,11 @@ testRun(void) TEST_RESULT_INT(storageInfoP(storageTest, STRDEF("sub1")).mode, 0750, "check sub1 dir mode"); TEST_ERROR( storagePathCreateP(storageTest, STRDEF("sub1")), PathCreateError, - "ssh2 error [-7] unable to create path '" TEST_PATH "/sub1'"); + "ssh2 error [-7] unable to create path '" TEST_PATH "/sub1': libssh2 error [-7]"); TEST_RESULT_VOID(storagePathCreateP(storageTest, STRDEF("sub1")), "create sub1 again no .errorOnExists fail EAGAIN"); TEST_RESULT_VOID( storagePathCreateP(storageTest, STRDEF("sub1")), "create sub1 again no .errorOnExists fail other than EAGAIN"); - TEST_ERROR(storagePathCreateP(storageTest, STRDEF("sub1")), PathCreateError, "timeout stating path '" TEST_PATH "/sub1'"); + TEST_ERROR(storagePathCreateP(storageTest, STRDEF("sub1")), PathCreateError, "timeout stat'ing path '" TEST_PATH "/sub1'"); TEST_ERROR( storagePathCreateP(storageTest, STRDEF("sub1"), .errorOnExists = true), PathCreateError, "unable to create path '" TEST_PATH "/sub1': path already exists"); @@ -4104,7 +4136,7 @@ testRun(void) TEST_ERROR( storagePathCreateP(storageTest, STRDEF("sub3/sub4"), .noParentCreate = true), PathCreateError, - "sftp error unable to create path '" TEST_PATH "/sub3/sub4'"); + "sftp error unable to create path '" TEST_PATH "/sub3/sub4': libssh2 error [-31]: sftp error [2]"); TEST_RESULT_VOID(storagePathCreateP(storageTest, STRDEF("sub3/sub4")), "create sub3/sub4"); // LIBSSH2_ERROR_EAGAIN timeout fail @@ -4160,11 +4192,11 @@ testRun(void) TEST_RESULT_VOID(storagePathCreateP(storageTest, STRDEF("subfail")), "timeout success"); TEST_ERROR( storagePathCreateP(storageTest, STRDEF("subfail"), .noParentCreate = true), PathCreateError, - "sftp error unable to create path '" TEST_PATH "/subfail'"); + "sftp error unable to create path '" TEST_PATH "/subfail': libssh2 error [-31]: sftp error [3]"); TEST_RESULT_VOID(storagePathCreateP(storageTest, STRDEF("subfail")), "do not throw error on already exists"); TEST_ERROR( storagePathCreateP(storageTest, STRDEF("subfail"), .errorOnExists = true), PathCreateError, - "sftp error unable to create path '" TEST_PATH "/subfail'"); + "sftp error unable to create path '" TEST_PATH "/subfail': libssh2 error [-31]: sftp error [11]"); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); #else @@ -4521,6 +4553,7 @@ testRun(void) .resultInt = LIBSSH2_ERROR_NONE}, {.function = HRNLIBSSH2_SFTP_CLOSE_HANDLE, .resultInt = LIBSSH2_ERROR_NONE}, {.function = HRNLIBSSH2_SFTP_RMDIR_EX, .param = "[\"" TEST_PATH "/remove1\"]", .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -4538,8 +4571,8 @@ testRun(void) .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx)), .write = true), "new storage /"); TEST_ERROR_FMT( - storagePathRemoveP(storageTest, pathRemove1, .recurse = true), PathRemoveError, "unable to remove path '%s'", - strZ(pathRemove1)); + storagePathRemoveP(storageTest, pathRemove1, .recurse = true), PathRemoveError, + "unable to remove path '%s': libssh2 error [-7]", strZ(pathRemove1)); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); #else @@ -4572,6 +4605,7 @@ testRun(void) {.function = HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS, .resultInt = SSH2_BLOCK_READING_WRITING}, {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_EAGAIN}, {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_OK}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, // Error not sftp, not EAGAIN {.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" TEST_PATH "/readtest.txt\",1,0,0]", .resultNull = true}, {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_METHOD_NOT_SUPPORTED}, @@ -4605,7 +4639,7 @@ testRun(void) TEST_ERROR_FMT(ioReadOpen(storageReadIo(file)), FileMissingError, STORAGE_ERROR_READ_MISSING, strZ(fileName)); // Missing EAGAIN timeout - TEST_ERROR_FMT(ioReadOpen(storageReadIo(file)), FileOpenError, STORAGE_ERROR_READ_OPEN, strZ(fileName)); + TEST_ERROR_FMT(ioReadOpen(storageReadIo(file)), FileOpenError, STORAGE_ERROR_READ_OPEN ": libssh2 error [-37]", strZ(fileName)); // Missing not sftp, not EAGAIN TEST_RESULT_BOOL(ioReadOpen(storageReadIo(file)), false, "not sftp, not EAGAIN"); @@ -4731,6 +4765,7 @@ testRun(void) {.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" TEST_PATH "/readtest.txt\",1,0,0]"}, {.function = HRNLIBSSH2_SFTP_CLOSE_HANDLE, .resultInt = LIBSSH2_ERROR_EAGAIN}, {.function = HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS, .resultInt = SSH2_BLOCK_READING_WRITING}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, // close(ioSessionFd()...) HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -4750,7 +4785,7 @@ testRun(void) TEST_RESULT_BOOL(ioReadOpen(storageReadIo(file)), true, "open file"); TEST_ERROR( storageReadSftpClose((StorageReadSftp *)file->driver), FileCloseError, - "timeout closing file '" TEST_PATH "/readtest.txt'"); + "timeout closing file '" TEST_PATH "/readtest.txt': libssh2 error [-37]"); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); @@ -4862,6 +4897,7 @@ testRun(void) // storageReadSftpClose {.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" TEST_PATH "/readtest.txt\",1,0,0]"}, {.function = HRNLIBSSH2_SFTP_READ, .param = "[2]", .resultInt = LIBSSH2_ERROR_ZLIB}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -4880,7 +4916,7 @@ testRun(void) TEST_RESULT_BOOL(ioReadOpen(storageReadIo(file)), true, "open file"); TEST_ERROR( storageReadSftp(((StorageReadSftp *)file->driver), outBuffer, false), FileReadError, - "unable to read '" TEST_PATH "/readtest.txt'"); + "unable to read '" TEST_PATH "/readtest.txt': libssh2 error [-29]"); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); #else @@ -5097,6 +5133,7 @@ testRun(void) {.function = HRNLIBSSH2_SFTP_MKDIR_EX, .param = "[\"" TEST_PATH "/sub1\",488]"}, {.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" TEST_PATH "/sub1/testfile.pgbackrest.tmp\",26,416,0]"}, {.function = HRNLIBSSH2_SFTP_FSYNC, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -5117,7 +5154,8 @@ testRun(void) TEST_RESULT_VOID(ioWriteOpen(storageWriteIo(file)), "open file"); TEST_RESULT_INT(ioWriteFd(storageWriteIo(file)), -1, "check write fd"); TEST_ERROR_FMT( - ioWriteClose(storageWriteIo(file)), FileSyncError, "unable to sync file '%s' after write", strZ(fileNameTmp)); + ioWriteClose(storageWriteIo(file)), + FileSyncError, "unable to sync file '%s' after write: libssh2 error [-7]", strZ(fileNameTmp)); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); @@ -5799,6 +5837,7 @@ testRun(void) HRNLIBSSH2_MACRO_STARTUP(), {.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" TEST_PATH "/test.txt.pgbackrest.tmp\",26,416,0]"}, {.function = HRNLIBSSH2_SFTP_WRITE, .param = "[2]", .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -5815,7 +5854,7 @@ testRun(void) TEST_ERROR( storagePutP(storageNewWriteP(storageTest, STRDEF(TEST_PATH "/test.txt")), failBuffer), FileWriteError, - "unable to write '" TEST_PATH "/test.txt.pgbackrest.tmp'"); + "unable to write '" TEST_PATH "/test.txt.pgbackrest.tmp': libssh2 error [-7]"); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); @@ -6329,6 +6368,7 @@ testRun(void) {.function = HRNLIBSSH2_SFTP_UNLINK_EX, .param = "[\"/missing\"]", .resultInt = LIBSSH2_ERROR_EAGAIN}, {.function = HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS, .resultInt = SSH2_NO_BLOCK_READING_WRITING}, {.function = HRNLIBSSH2_SFTP_UNLINK_EX, .param = "[\"/missing\"]", .resultInt = LIBSSH2_ERROR_BAD_SOCKET}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -6344,7 +6384,7 @@ testRun(void) .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx)), .write = true); TEST_ERROR(storageRemoveP(storageTest, STRDEF("missing"), .errorOnMissing = true), FileRemoveError, - "unable to remove '/missing'"); + "unable to remove '/missing': libssh2 error [-45]"); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); @@ -6496,9 +6536,10 @@ testRun(void) HRNLIBSSH2_MACRO_STARTUP(), {.function = HRNLIBSSH2_SFTP_OPEN_EX, .resultNull = true, .param = "[\"" TEST_PATH "/sub1/testfile.pgbackrest.tmp\",26,416,0]"}, - {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_ZLIB}, - {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_ZLIB}, - {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_ZLIB}, + {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SOCKET_SEND}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_ERROR_NONE}, HRNLIBSSH2_MACRO_SHUTDOWN() }); @@ -6514,7 +6555,8 @@ testRun(void) .knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx)), .write = true); TEST_ASSIGN(file, storageNewWriteP(storageTest, fileName, .noSyncFile = true), "storageWriteSftpOpen ssh error"); - TEST_ERROR_FMT(ioWriteOpen(storageWriteIo(file)), FileOpenError, STORAGE_ERROR_WRITE_OPEN, strZ(fileName)); + TEST_ERROR_FMT( + ioWriteOpen(storageWriteIo(file)), FileOpenError, STORAGE_ERROR_WRITE_OPEN ": libssh2 error [-7]", strZ(fileName)); memContextFree(objMemContext((StorageSftp *)storageDriver(storageTest))); #else @@ -6540,6 +6582,7 @@ testRun(void) {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, {.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, + {.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = LIBSSH2_FX_PERMISSION_DENIED}, // File missing {.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" TEST_PATH "/test.file\",1,0,0]", .resultNull = true, .resultInt = LIBSSH2_ERROR_SFTP_PROTOCOL}, @@ -6578,7 +6621,10 @@ testRun(void) TEST_TITLE("permission denied"); TEST_ASSIGN(file, storageNewReadP(storageTest, fileNoPerm), "new no perm read file"); - TEST_ERROR_FMT(ioReadOpen(storageReadIo(file)), FileOpenError, STORAGE_ERROR_READ_OPEN, strZ(fileNoPerm)); + TEST_ERROR_FMT( + ioReadOpen(storageReadIo(file)), + FileOpenError, + STORAGE_ERROR_READ_OPEN ": libssh2 error [-31]: sftp error [3]", strZ(fileNoPerm)); // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("file missing");