1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-05-31 22:49:46 +02:00
David Steele 256b727a3d Add S3 storage driver.
Only the storageNewRead() and storageList() functions are currently implemented, but this is enough to enable S3 for the archive-get command.
2018-11-21 19:32:49 -05:00

321 lines
13 KiB
C

/***********************************************************************************************************************************
Storage Helper
***********************************************************************************************************************************/
#include <string.h>
#include "common/debug.h"
#include "common/memContext.h"
#include "common/regExp.h"
#include "config/config.h"
#include "storage/driver/posix/storage.h"
#include "storage/driver/s3/storage.h"
#include "storage/helper.h"
/***********************************************************************************************************************************
Storage path constants
***********************************************************************************************************************************/
STRING_EXTERN(STORAGE_SPOOL_ARCHIVE_IN_STR, STORAGE_SPOOL_ARCHIVE_IN);
STRING_EXTERN(STORAGE_SPOOL_ARCHIVE_OUT_STR, STORAGE_SPOOL_ARCHIVE_OUT);
/***********************************************************************************************************************************
Local variables
***********************************************************************************************************************************/
static struct
{
MemContext *memContext; // Mem context for storage helper
Storage *storageLocal; // Local read-only storage
Storage *storageLocalWrite; // Local write storage
Storage *storageRepo; // Repository read-only storage
Storage *storageSpool; // Spool read-only storage
Storage *storageSpoolWrite; // Spool write storage
String *stanza; // Stanza for storage
RegExp *walRegExp; // Regular expression for identifying wal files
} storageHelper;
/***********************************************************************************************************************************
Create the storage helper memory context
***********************************************************************************************************************************/
static void
storageHelperInit(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.memContext == NULL)
{
MEM_CONTEXT_BEGIN(memContextTop())
{
storageHelper.memContext = memContextNew("storageHelper");
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RESULT_VOID();
}
/***********************************************************************************************************************************
Initialize the stanza and error if it changes
***********************************************************************************************************************************/
static void
storageHelperStanzaInit(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.stanza == NULL)
{
MEM_CONTEXT_BEGIN(storageHelper.memContext)
{
storageHelper.stanza = strDup(cfgOptionStr(cfgOptStanza));
}
MEM_CONTEXT_END();
}
else if (!strEq(storageHelper.stanza, cfgOptionStr(cfgOptStanza)))
{
THROW_FMT(
AssertError, "stanza has changed from '%s' to '%s'", strPtr(storageHelper.stanza), strPtr(cfgOptionStr(cfgOptStanza)));
}
FUNCTION_TEST_RESULT_VOID();
}
/***********************************************************************************************************************************
Get a local storage object
***********************************************************************************************************************************/
const Storage *
storageLocal(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.storageLocal == NULL)
{
storageHelperInit();
MEM_CONTEXT_BEGIN(storageHelper.memContext)
{
storageHelper.storageLocal = storageDriverPosixInterface(
storageDriverPosixNew(
FSLASH_STR, STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, false, NULL));
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RESULT(STORAGE, storageHelper.storageLocal);
}
/***********************************************************************************************************************************
Get a writable local storage object
This should be used very sparingly. If writes are not needed then always use storageLocal() or a specific storage object instead.
***********************************************************************************************************************************/
const Storage *
storageLocalWrite(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.storageLocalWrite == NULL)
{
storageHelperInit();
MEM_CONTEXT_BEGIN(storageHelper.memContext)
{
storageHelper.storageLocalWrite = storageDriverPosixInterface(
storageDriverPosixNew(
FSLASH_STR, STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL));
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RESULT(STORAGE, storageHelper.storageLocalWrite);
}
/***********************************************************************************************************************************
Get a spool storage object
***********************************************************************************************************************************/
static String *
storageRepoPathExpression(const String *expression, const String *path)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, expression);
FUNCTION_TEST_PARAM(STRING, path);
FUNCTION_TEST_ASSERT(expression != NULL);
FUNCTION_TEST_END();
String *result = NULL;
if (strEqZ(expression, STORAGE_REPO_ARCHIVE))
{
result = strNewFmt("archive/%s", strPtr(storageHelper.stanza));
if (path != NULL)
{
StringList *pathSplit = strLstNewSplitZ(path, "/");
String *file = strLstSize(pathSplit) == 2 ? strLstGet(pathSplit, 1) : NULL;
if (file != NULL && regExpMatch(storageHelper.walRegExp, file))
strCatFmt(result, "/%s/%s/%s", strPtr(strLstGet(pathSplit, 0)), strPtr(strSubN(file, 0, 16)), strPtr(file));
else
strCatFmt(result, "/%s", strPtr(path));
}
}
else
THROW_FMT(AssertError, "invalid expression '%s'", strPtr(expression));
FUNCTION_TEST_RESULT(STRING, result);
}
/***********************************************************************************************************************************
Get the repo storage
***********************************************************************************************************************************/
static Storage *
storageRepoGet(const String *type, bool write)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, type);
FUNCTION_TEST_PARAM(BOOL, write);
FUNCTION_TEST_ASSERT(type != NULL);
FUNCTION_TEST_ASSERT(write == false); // ??? Need to create cifs driver before storage can be writable
FUNCTION_TEST_END();
Storage *result = NULL;
// For now treat posix and cifs drivers as if they are the same. This won't be true once the repository storage becomes
// writable but for now it's OK. The assertion above should pop if we try to create writable repo storage.
if (strEqZ(type, STORAGE_TYPE_POSIX) || strEqZ(type, STORAGE_TYPE_CIFS))
{
result = storageDriverPosixInterface(
storageDriverPosixNew(
cfgOptionStr(cfgOptRepoPath), STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, write,
storageRepoPathExpression));
}
else if (strEqZ(type, STORAGE_TYPE_S3))
{
result = storageDriverS3Interface(
storageDriverS3New(
cfgOptionStr(cfgOptRepoPath), write, storageRepoPathExpression, cfgOptionStr(cfgOptRepoS3Bucket),
cfgOptionStr(cfgOptRepoS3Endpoint), cfgOptionStr(cfgOptRepoS3Region), cfgOptionStr(cfgOptRepoS3Key),
cfgOptionStr(cfgOptRepoS3KeySecret), cfgOptionTest(cfgOptRepoS3Token) ? cfgOptionStr(cfgOptRepoS3Token) : NULL,
cfgOptionTest(cfgOptRepoS3Host) ? cfgOptionStr(cfgOptRepoS3Host) : NULL,
STORAGE_DRIVER_S3_PORT_DEFAULT, STORAGE_DRIVER_S3_TIMEOUT_DEFAULT, cfgOptionBool(cfgOptRepoS3VerifySsl),
cfgOptionTest(cfgOptRepoS3CaFile) ? cfgOptionStr(cfgOptRepoS3CaFile) : NULL,
cfgOptionTest(cfgOptRepoS3CaPath) ? cfgOptionStr(cfgOptRepoS3CaPath) : NULL));
}
else
THROW_FMT(AssertError, "invalid storage type '%s'", strPtr(type));
FUNCTION_TEST_RESULT(STORAGE, result);
}
/***********************************************************************************************************************************
Get a read-only repository storage object
***********************************************************************************************************************************/
const Storage *
storageRepo(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.storageRepo == NULL)
{
storageHelperInit();
storageHelperStanzaInit();
MEM_CONTEXT_BEGIN(storageHelper.memContext)
{
storageHelper.walRegExp = regExpNew(STRING_CONST("^[0-F]{24}"));
storageHelper.storageRepo = storageRepoGet(cfgOptionStr(cfgOptRepoType), false);
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RESULT(STORAGE, storageHelper.storageRepo);
}
/***********************************************************************************************************************************
Get a spool storage object
***********************************************************************************************************************************/
static String *
storageSpoolPathExpression(const String *expression, const String *path)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, expression);
FUNCTION_TEST_PARAM(STRING, path);
FUNCTION_TEST_ASSERT(expression != NULL);
FUNCTION_TEST_END();
String *result = NULL;
if (strEqZ(expression, STORAGE_SPOOL_ARCHIVE_IN))
{
if (path == NULL)
result = strNewFmt("archive/%s/in", strPtr(storageHelper.stanza));
else
result = strNewFmt("archive/%s/in/%s", strPtr(storageHelper.stanza), strPtr(path));
}
else if (strEqZ(expression, STORAGE_SPOOL_ARCHIVE_OUT))
{
if (path == NULL)
result = strNewFmt("archive/%s/out", strPtr(storageHelper.stanza));
else
result = strNewFmt("archive/%s/out/%s", strPtr(storageHelper.stanza), strPtr(path));
}
else
THROW_FMT(AssertError, "invalid expression '%s'", strPtr(expression));
FUNCTION_TEST_RESULT(STRING, result);
}
/***********************************************************************************************************************************
Get a read-only spool storage object
***********************************************************************************************************************************/
const Storage *
storageSpool(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.storageSpool == NULL)
{
storageHelperInit();
storageHelperStanzaInit();
MEM_CONTEXT_BEGIN(storageHelper.memContext)
{
storageHelper.storageSpool = storageDriverPosixInterface(
storageDriverPosixNew(
cfgOptionStr(cfgOptSpoolPath), STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, false,
storageSpoolPathExpression));
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RESULT(STORAGE, storageHelper.storageSpool);
}
/***********************************************************************************************************************************
Get a writable spool storage object
***********************************************************************************************************************************/
const Storage *
storageSpoolWrite(void)
{
FUNCTION_TEST_VOID();
if (storageHelper.storageSpoolWrite == NULL)
{
storageHelperInit();
storageHelperStanzaInit();
MEM_CONTEXT_BEGIN(storageHelper.memContext)
{
storageHelper.storageSpoolWrite = storageDriverPosixInterface(
storageDriverPosixNew(
cfgOptionStr(cfgOptSpoolPath), STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true,
storageSpoolPathExpression));
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RESULT(STORAGE, storageHelper.storageSpoolWrite);
}