You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Refactor storage modules with inline getters/setters.
Extended the pattern introduced in 79a2d02c
to the storage modules: Storage, StorageRead, StorageWrite.
This commit is contained in:
@ -4,7 +4,7 @@ Azure Storage
|
|||||||
#ifndef STORAGE_AZURE_STORAGE_H
|
#ifndef STORAGE_AZURE_STORAGE_H
|
||||||
#define STORAGE_AZURE_STORAGE_H
|
#define STORAGE_AZURE_STORAGE_H
|
||||||
|
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Storage type
|
Storage type
|
||||||
|
@ -4,7 +4,7 @@ CIFS Storage
|
|||||||
#ifndef STORAGE_CIFS_STORAGE_H
|
#ifndef STORAGE_CIFS_STORAGE_H
|
||||||
#define STORAGE_CIFS_STORAGE_H
|
#define STORAGE_CIFS_STORAGE_H
|
||||||
|
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Storage type
|
Storage type
|
||||||
|
@ -4,7 +4,7 @@ GCS Storage
|
|||||||
#ifndef STORAGE_GCS_STORAGE_H
|
#ifndef STORAGE_GCS_STORAGE_H
|
||||||
#define STORAGE_GCS_STORAGE_H
|
#define STORAGE_GCS_STORAGE_H
|
||||||
|
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Storage type
|
Storage type
|
||||||
|
@ -9,7 +9,7 @@ Object type
|
|||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
typedef struct StoragePosix StoragePosix;
|
typedef struct StoragePosix StoragePosix;
|
||||||
|
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Storage type
|
Storage type
|
||||||
|
@ -7,17 +7,16 @@ Storage Read Interface
|
|||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/memContext.h"
|
#include "common/memContext.h"
|
||||||
#include "common/type/object.h"
|
#include "common/type/object.h"
|
||||||
#include "storage/read.intern.h"
|
#include "storage/read.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Object type
|
Object type
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
struct StorageRead
|
struct StorageRead
|
||||||
{
|
{
|
||||||
|
StorageReadPub pub; // Publicly accessible variables
|
||||||
MemContext *memContext; // Object mem context
|
MemContext *memContext; // Object mem context
|
||||||
void *driver;
|
void *driver;
|
||||||
const StorageReadInterface *interface;
|
|
||||||
IoRead *io;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OBJECT_DEFINE_MOVE(STORAGE_READ);
|
OBJECT_DEFINE_MOVE(STORAGE_READ);
|
||||||
@ -49,85 +48,23 @@ storageReadNew(void *driver, const StorageReadInterface *interface)
|
|||||||
|
|
||||||
*this = (StorageRead)
|
*this = (StorageRead)
|
||||||
{
|
{
|
||||||
|
.pub =
|
||||||
|
{
|
||||||
|
.interface = interface,
|
||||||
|
.io = ioReadNew(driver, interface->ioInterface),
|
||||||
|
},
|
||||||
.memContext = memContextCurrent(),
|
.memContext = memContextCurrent(),
|
||||||
.driver = driver,
|
.driver = driver,
|
||||||
.interface = interface,
|
|
||||||
.io = ioReadNew(driver, interface->ioInterface),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FUNCTION_LOG_RETURN(STORAGE_READ, this);
|
FUNCTION_LOG_RETURN(STORAGE_READ, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
bool
|
|
||||||
storageReadIgnoreMissing(const StorageRead *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_READ, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->ignoreMissing);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
const Variant *
|
|
||||||
storageReadLimit(const StorageRead *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_READ, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
IoRead *
|
|
||||||
storageReadIo(const StorageRead *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_READ, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->io);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
const String *
|
|
||||||
storageReadName(const StorageRead *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_READ, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
const String *
|
|
||||||
storageReadType(const StorageRead *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_READ, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
String *
|
String *
|
||||||
storageReadToLog(const StorageRead *this)
|
storageReadToLog(const StorageRead *this)
|
||||||
{
|
{
|
||||||
return strNewFmt(
|
return strNewFmt(
|
||||||
"{type: %s, name: %s, ignoreMissing: %s}", strZ(this->interface->type), strZ(strToLog(this->interface->name)),
|
"{type: %s, name: %s, ignoreMissing: %s}", strZ(storageReadType(this)), strZ(strToLog(storageReadName(this))),
|
||||||
cvtBoolToConstZ(this->interface->ignoreMissing));
|
cvtBoolToConstZ(storageReadIgnoreMissing(this)));
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ Object type
|
|||||||
typedef struct StorageRead StorageRead;
|
typedef struct StorageRead StorageRead;
|
||||||
|
|
||||||
#include "common/io/read.h"
|
#include "common/io/read.h"
|
||||||
|
#include "storage/read.intern.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Functions
|
Functions
|
||||||
@ -22,20 +23,51 @@ StorageRead *storageReadMove(StorageRead *this, MemContext *parentNew);
|
|||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Getters/Setters
|
Getters/Setters
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
|
typedef struct StorageReadPub
|
||||||
|
{
|
||||||
|
const StorageReadInterface *interface; // File data (name, driver type, etc.)
|
||||||
|
IoRead *io; // Read interface
|
||||||
|
} StorageReadPub;
|
||||||
|
|
||||||
// Should a missing file be ignored?
|
// Should a missing file be ignored?
|
||||||
bool storageReadIgnoreMissing(const StorageRead *this);
|
__attribute__((always_inline)) static inline bool
|
||||||
|
storageReadIgnoreMissing(const StorageRead *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageReadPub *)this)->interface->ignoreMissing;
|
||||||
|
}
|
||||||
|
|
||||||
// Read interface
|
// Read interface
|
||||||
IoRead *storageReadIo(const StorageRead *this);
|
__attribute__((always_inline)) static inline IoRead *
|
||||||
|
storageReadIo(const StorageRead *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageReadPub *)this)->io;
|
||||||
|
}
|
||||||
|
|
||||||
// Is there a read limit? NULL for no limit.
|
// Is there a read limit? NULL for no limit.
|
||||||
const Variant *storageReadLimit(const StorageRead *this);
|
__attribute__((always_inline)) static inline const Variant *
|
||||||
|
storageReadLimit(const StorageRead *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageReadPub *)this)->interface->limit;
|
||||||
|
}
|
||||||
|
|
||||||
// File name
|
// File name
|
||||||
const String *storageReadName(const StorageRead *this);
|
__attribute__((always_inline)) static inline const String *
|
||||||
|
storageReadName(const StorageRead *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageReadPub *)this)->interface->name;
|
||||||
|
}
|
||||||
|
|
||||||
// Get file type
|
// Get file type
|
||||||
const String *storageReadType(const StorageRead *this);
|
__attribute__((always_inline)) static inline const String *
|
||||||
|
storageReadType(const StorageRead *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageReadPub *)this)->interface->type;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Destructor
|
Destructor
|
||||||
|
@ -5,7 +5,6 @@ Storage Read Interface Internal
|
|||||||
#define STORAGE_READ_INTERN_H
|
#define STORAGE_READ_INTERN_H
|
||||||
|
|
||||||
#include "common/io/read.intern.h"
|
#include "common/io/read.intern.h"
|
||||||
#include "storage/read.h"
|
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Constructors
|
Constructors
|
||||||
|
@ -5,7 +5,7 @@ Remote Storage
|
|||||||
#define STORAGE_REMOTE_STORAGE_H
|
#define STORAGE_REMOTE_STORAGE_H
|
||||||
|
|
||||||
#include "protocol/client.h"
|
#include "protocol/client.h"
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Storage type
|
Storage type
|
||||||
|
@ -4,7 +4,7 @@ S3 Storage
|
|||||||
#ifndef STORAGE_S3_STORAGE_H
|
#ifndef STORAGE_S3_STORAGE_H
|
||||||
#define STORAGE_S3_STORAGE_H
|
#define STORAGE_S3_STORAGE_H
|
||||||
|
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Storage type
|
Storage type
|
||||||
|
@ -14,18 +14,15 @@ Storage Interface
|
|||||||
#include "common/type/object.h"
|
#include "common/type/object.h"
|
||||||
#include "common/regExp.h"
|
#include "common/regExp.h"
|
||||||
#include "common/wait.h"
|
#include "common/wait.h"
|
||||||
#include "storage/storage.intern.h"
|
#include "storage/storage.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Object type
|
Object type
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
struct Storage
|
struct Storage
|
||||||
{
|
{
|
||||||
|
StoragePub pub; // Publicly accessible variables
|
||||||
MemContext *memContext;
|
MemContext *memContext;
|
||||||
void *driver;
|
|
||||||
StorageInterface interface;
|
|
||||||
const String *type;
|
|
||||||
|
|
||||||
const String *path;
|
const String *path;
|
||||||
mode_t modeFile;
|
mode_t modeFile;
|
||||||
mode_t modePath;
|
mode_t modePath;
|
||||||
@ -66,10 +63,13 @@ storageNew(
|
|||||||
|
|
||||||
*this = (Storage)
|
*this = (Storage)
|
||||||
{
|
{
|
||||||
|
.pub =
|
||||||
|
{
|
||||||
|
.type = type,
|
||||||
|
.driver = driver,
|
||||||
|
.interface = interface,
|
||||||
|
},
|
||||||
.memContext = memContextCurrent(),
|
.memContext = memContextCurrent(),
|
||||||
.driver = driver,
|
|
||||||
.interface = interface,
|
|
||||||
.type = type,
|
|
||||||
.path = strDup(path),
|
.path = strDup(path),
|
||||||
.modeFile = modeFile,
|
.modeFile = modeFile,
|
||||||
.modePath = modePath,
|
.modePath = modePath,
|
||||||
@ -242,7 +242,7 @@ storageInfo(const Storage *this, const String *fileExp, StorageInfoParam param)
|
|||||||
FUNCTION_LOG_END();
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
ASSERT(this != NULL);
|
||||||
ASSERT(this->interface.info != NULL);
|
ASSERT(this->pub.interface.info != NULL);
|
||||||
|
|
||||||
StorageInfo result = {0};
|
StorageInfo result = {0};
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ storageInfo(const Storage *this, const String *fileExp, StorageInfoParam param)
|
|||||||
}
|
}
|
||||||
// Else call the driver
|
// Else call the driver
|
||||||
else
|
else
|
||||||
result = storageInterfaceInfoP(this->driver, file, param.level, .followLink = param.followLink);
|
result = storageInterfaceInfoP(this->pub.driver, file, param.level, .followLink = param.followLink);
|
||||||
|
|
||||||
// Error if the file missing and not ignoring
|
// Error if the file missing and not ignoring
|
||||||
if (!result.exists && !param.ignoreMissing)
|
if (!result.exists && !param.ignoreMissing)
|
||||||
@ -342,7 +342,7 @@ storageInfoListSort(
|
|||||||
// If no sorting then use the callback directly
|
// If no sorting then use the callback directly
|
||||||
if (sortOrder == sortOrderNone)
|
if (sortOrder == sortOrderNone)
|
||||||
{
|
{
|
||||||
result = storageInterfaceInfoListP(this->driver, path, level, callback, callbackData, .expression = expression);
|
result = storageInterfaceInfoListP(this->pub.driver, path, level, callback, callbackData, .expression = expression);
|
||||||
}
|
}
|
||||||
// Else sort the info before sending it to the callback
|
// Else sort the info before sending it to the callback
|
||||||
else
|
else
|
||||||
@ -355,7 +355,7 @@ storageInfoListSort(
|
|||||||
};
|
};
|
||||||
|
|
||||||
result = storageInterfaceInfoListP(
|
result = storageInterfaceInfoListP(
|
||||||
this->driver, path, level, storageInfoListSortCallback, &data, .expression = expression);
|
this->pub.driver, path, level, storageInfoListSortCallback, &data, .expression = expression);
|
||||||
lstSort(data.infoList, sortOrder);
|
lstSort(data.infoList, sortOrder);
|
||||||
|
|
||||||
MEM_CONTEXT_TEMP_RESET_BEGIN()
|
MEM_CONTEXT_TEMP_RESET_BEGIN()
|
||||||
@ -459,7 +459,7 @@ storageInfoList(
|
|||||||
|
|
||||||
ASSERT(this != NULL);
|
ASSERT(this != NULL);
|
||||||
ASSERT(callback != NULL);
|
ASSERT(callback != NULL);
|
||||||
ASSERT(this->interface.infoList != NULL);
|
ASSERT(this->pub.interface.infoList != NULL);
|
||||||
ASSERT(!param.errorOnMissing || storageFeature(this, storageFeaturePath));
|
ASSERT(!param.errorOnMissing || storageFeature(this, storageFeaturePath));
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
@ -574,29 +574,29 @@ storageMove(const Storage *this, StorageRead *source, StorageWrite *destination)
|
|||||||
FUNCTION_LOG_END();
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
ASSERT(this != NULL);
|
||||||
ASSERT(this->interface.move != NULL);
|
ASSERT(this->pub.interface.move != NULL);
|
||||||
ASSERT(source != NULL);
|
ASSERT(source != NULL);
|
||||||
ASSERT(destination != NULL);
|
ASSERT(destination != NULL);
|
||||||
ASSERT(!storageReadIgnoreMissing(source));
|
ASSERT(!storageReadIgnoreMissing(source));
|
||||||
ASSERT(strEq(this->type, storageReadType(source)));
|
ASSERT(strEq(storageType(this), storageReadType(source)));
|
||||||
ASSERT(strEq(storageReadType(source), storageWriteType(destination)));
|
ASSERT(strEq(storageReadType(source), storageWriteType(destination)));
|
||||||
|
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
{
|
{
|
||||||
// If the file can't be moved it will need to be copied
|
// If the file can't be moved it will need to be copied
|
||||||
if (!storageInterfaceMoveP(this->driver, source, destination))
|
if (!storageInterfaceMoveP(this->pub.driver, source, destination))
|
||||||
{
|
{
|
||||||
// Perform the copy
|
// Perform the copy
|
||||||
storageCopyP(source, destination);
|
storageCopyP(source, destination);
|
||||||
|
|
||||||
// Remove the source file
|
// Remove the source file
|
||||||
storageInterfaceRemoveP(this->driver, storageReadName(source));
|
storageInterfaceRemoveP(this->pub.driver, storageReadName(source));
|
||||||
|
|
||||||
// Sync source path if the destination path was synced. We know the source and destination paths are different because
|
// Sync source path if the destination path was synced. We know the source and destination paths are different because
|
||||||
// the move did not succeed. This will need updating when drivers other than Posix/CIFS are implemented because there's
|
// the move did not succeed. This will need updating when drivers other than Posix/CIFS are implemented because there's
|
||||||
// no way to get coverage on it now.
|
// no way to get coverage on it now.
|
||||||
if (storageWriteSyncPath(destination))
|
if (storageWriteSyncPath(destination))
|
||||||
storageInterfacePathSyncP(this->driver, strPath(storageReadName(source)));
|
storageInterfacePathSyncP(this->pub.driver, strPath(storageReadName(source)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
@ -626,7 +626,7 @@ storageNewRead(const Storage *this, const String *fileExp, StorageNewReadParam p
|
|||||||
{
|
{
|
||||||
result = storageReadMove(
|
result = storageReadMove(
|
||||||
storageInterfaceNewReadP(
|
storageInterfaceNewReadP(
|
||||||
this->driver, storagePathP(this, fileExp), param.ignoreMissing, .compressible = param.compressible,
|
this->pub.driver, storagePathP(this, fileExp), param.ignoreMissing, .compressible = param.compressible,
|
||||||
.limit = param.limit),
|
.limit = param.limit),
|
||||||
memContextPrior());
|
memContextPrior());
|
||||||
}
|
}
|
||||||
@ -663,7 +663,7 @@ storageNewWrite(const Storage *this, const String *fileExp, StorageNewWriteParam
|
|||||||
{
|
{
|
||||||
result = storageWriteMove(
|
result = storageWriteMove(
|
||||||
storageInterfaceNewWriteP(
|
storageInterfaceNewWriteP(
|
||||||
this->driver, storagePathP(this, fileExp), .modeFile = param.modeFile != 0 ? param.modeFile : this->modeFile,
|
this->pub.driver, storagePathP(this, fileExp), .modeFile = param.modeFile != 0 ? param.modeFile : this->modeFile,
|
||||||
.modePath = param.modePath != 0 ? param.modePath : this->modePath, .user = param.user, .group = param.group,
|
.modePath = param.modePath != 0 ? param.modePath : this->modePath, .user = param.user, .group = param.group,
|
||||||
.timeModified = param.timeModified, .createPath = !param.noCreatePath, .syncFile = !param.noSyncFile,
|
.timeModified = param.timeModified, .createPath = !param.noCreatePath, .syncFile = !param.noSyncFile,
|
||||||
.syncPath = !param.noSyncPath, .atomic = !param.noAtomic, .compressible = param.compressible),
|
.syncPath = !param.noSyncPath, .atomic = !param.noAtomic, .compressible = param.compressible),
|
||||||
@ -788,7 +788,7 @@ storagePathCreate(const Storage *this, const String *pathExp, StoragePathCreateP
|
|||||||
FUNCTION_LOG_END();
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
ASSERT(this != NULL);
|
||||||
ASSERT(this->interface.pathCreate != NULL && storageFeature(this, storageFeaturePath));
|
ASSERT(this->pub.interface.pathCreate != NULL && storageFeature(this, storageFeaturePath));
|
||||||
ASSERT(this->write);
|
ASSERT(this->write);
|
||||||
|
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
@ -798,7 +798,7 @@ storagePathCreate(const Storage *this, const String *pathExp, StoragePathCreateP
|
|||||||
|
|
||||||
// Call driver function
|
// Call driver function
|
||||||
storageInterfacePathCreateP(
|
storageInterfacePathCreateP(
|
||||||
this->driver, path, param.errorOnExists, param.noParentCreate, param.mode != 0 ? param.mode : this->modePath);
|
this->pub.driver, path, param.errorOnExists, param.noParentCreate, param.mode != 0 ? param.mode : this->modePath);
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
|
|
||||||
@ -846,7 +846,7 @@ storagePathRemove(const Storage *this, const String *pathExp, StoragePathRemoveP
|
|||||||
String *path = storagePathP(this, pathExp);
|
String *path = storagePathP(this, pathExp);
|
||||||
|
|
||||||
// Call driver function
|
// Call driver function
|
||||||
if (!storageInterfacePathRemoveP(this->driver, path, param.recurse) && param.errorOnMissing)
|
if (!storageInterfacePathRemoveP(this->pub.driver, path, param.recurse) && param.errorOnMissing)
|
||||||
{
|
{
|
||||||
THROW_FMT(PathRemoveError, STORAGE_ERROR_PATH_REMOVE_MISSING, strZ(path));
|
THROW_FMT(PathRemoveError, STORAGE_ERROR_PATH_REMOVE_MISSING, strZ(path));
|
||||||
}
|
}
|
||||||
@ -868,11 +868,11 @@ void storagePathSync(const Storage *this, const String *pathExp)
|
|||||||
ASSERT(this->write);
|
ASSERT(this->write);
|
||||||
|
|
||||||
// Not all storage requires path sync so just do nothing if the function is not implemented
|
// Not all storage requires path sync so just do nothing if the function is not implemented
|
||||||
if (this->interface.pathSync != NULL)
|
if (this->pub.interface.pathSync != NULL)
|
||||||
{
|
{
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
{
|
{
|
||||||
storageInterfacePathSyncP(this->driver, storagePathP(this, pathExp));
|
storageInterfacePathSyncP(this->pub.driver, storagePathP(this, pathExp));
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
}
|
}
|
||||||
@ -917,70 +917,17 @@ storageRemove(const Storage *this, const String *fileExp, StorageRemoveParam par
|
|||||||
String *file = storagePathP(this, fileExp);
|
String *file = storagePathP(this, fileExp);
|
||||||
|
|
||||||
// Call driver function
|
// Call driver function
|
||||||
storageInterfaceRemoveP(this->driver, file, .errorOnMissing = param.errorOnMissing);
|
storageInterfaceRemoveP(this->pub.driver, file, .errorOnMissing = param.errorOnMissing);
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
|
|
||||||
FUNCTION_LOG_RETURN_VOID();
|
FUNCTION_LOG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
void *
|
|
||||||
storageDriver(const Storage *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
bool
|
|
||||||
storageFeature(const Storage *this, StorageFeature feature)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE, this);
|
|
||||||
FUNCTION_TEST_PARAM(ENUM, feature);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface.feature >> feature & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
StorageInterface
|
|
||||||
storageInterface(const Storage *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
const String *
|
|
||||||
storageType(const Storage *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
String *
|
String *
|
||||||
storageToLog(const Storage *this)
|
storageToLog(const Storage *this)
|
||||||
{
|
{
|
||||||
return strNewFmt(
|
return strNewFmt(
|
||||||
"{type: %s, path: %s, write: %s}", strZ(this->type), strZ(strToLog(this->path)), cvtBoolToConstZ(this->write));
|
"{type: %s, path: %s, write: %s}", strZ(storageType(this)), strZ(strToLog(this->path)), cvtBoolToConstZ(this->write));
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ typedef struct Storage Storage;
|
|||||||
#include "common/type/param.h"
|
#include "common/type/param.h"
|
||||||
#include "storage/info.h"
|
#include "storage/info.h"
|
||||||
#include "storage/read.h"
|
#include "storage/read.h"
|
||||||
|
#include "storage/storage.intern.h"
|
||||||
#include "storage/write.h"
|
#include "storage/write.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
@ -252,10 +253,20 @@ void storageRemove(const Storage *this, const String *fileExp, StorageRemovePara
|
|||||||
Getters/Setters
|
Getters/Setters
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
// Is the feature supported by this storage?
|
// Is the feature supported by this storage?
|
||||||
bool storageFeature(const Storage *this, StorageFeature feature);
|
__attribute__((always_inline)) static inline bool
|
||||||
|
storageFeature(const Storage *this, StorageFeature feature)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StoragePub *)this)->interface.feature >> feature & 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Storage type (posix, cifs, etc.)
|
// Storage type (posix, cifs, etc.)
|
||||||
const String *storageType(const Storage *this);
|
__attribute__((always_inline)) static inline const String *
|
||||||
|
storageType(const Storage *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StoragePub *)this)->type;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Macros for function logging
|
Macros for function logging
|
||||||
|
@ -14,9 +14,9 @@ in the description of each function.
|
|||||||
#define STORAGE_STORAGE_INTERN_H
|
#define STORAGE_STORAGE_INTERN_H
|
||||||
|
|
||||||
#include "common/type/param.h"
|
#include "common/type/param.h"
|
||||||
#include "storage/read.intern.h"
|
#include "storage/info.h"
|
||||||
#include "storage/storage.h"
|
#include "storage/read.h"
|
||||||
#include "storage/write.intern.h"
|
#include "storage/write.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Default file and path modes
|
Default file and path modes
|
||||||
@ -157,8 +157,8 @@ typedef struct StorageInterfaceInfoListParam
|
|||||||
} StorageInterfaceInfoListParam;
|
} StorageInterfaceInfoListParam;
|
||||||
|
|
||||||
typedef bool StorageInterfaceInfoList(
|
typedef bool StorageInterfaceInfoList(
|
||||||
void *thisVoid, const String *path, StorageInfoLevel level, StorageInfoListCallback callback, void *callbackData,
|
void *thisVoid, const String *path, StorageInfoLevel level, void (*callback)(void *callbackData, const StorageInfo *info),
|
||||||
StorageInterfaceInfoListParam param);
|
void *callbackData, StorageInterfaceInfoListParam param);
|
||||||
|
|
||||||
#define storageInterfaceInfoListP(thisVoid, path, level, callback, callbackData, ...) \
|
#define storageInterfaceInfoListP(thisVoid, path, level, callback, callbackData, ...) \
|
||||||
STORAGE_COMMON_INTERFACE(thisVoid).infoList( \
|
STORAGE_COMMON_INTERFACE(thisVoid).infoList( \
|
||||||
@ -282,11 +282,28 @@ typedef struct StorageCommon
|
|||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Getters/Setters
|
Getters/Setters
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
|
typedef struct StoragePub
|
||||||
|
{
|
||||||
|
const String *type; // Storage type
|
||||||
|
void *driver; // Storage driver
|
||||||
|
StorageInterface interface; // Storage interface
|
||||||
|
} StoragePub;
|
||||||
|
|
||||||
// Storage driver
|
// Storage driver
|
||||||
void *storageDriver(const Storage *this);
|
__attribute__((always_inline)) static inline void *
|
||||||
|
storageDriver(const Storage *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StoragePub *)this)->driver;
|
||||||
|
}
|
||||||
|
|
||||||
// Storage interface
|
// Storage interface
|
||||||
StorageInterface storageInterface(const Storage *this);
|
__attribute__((always_inline)) static inline StorageInterface
|
||||||
|
storageInterface(const Storage *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StoragePub *)this)->interface;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Macros for function logging
|
Macros for function logging
|
||||||
|
@ -7,17 +7,16 @@ Storage Write Interface
|
|||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/memContext.h"
|
#include "common/memContext.h"
|
||||||
#include "common/type/object.h"
|
#include "common/type/object.h"
|
||||||
#include "storage/write.intern.h"
|
#include "storage/write.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Object type
|
Object type
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
struct StorageWrite
|
struct StorageWrite
|
||||||
{
|
{
|
||||||
|
StorageWritePub pub; // Publicly accessible variables
|
||||||
MemContext *memContext; // Object mem context
|
MemContext *memContext; // Object mem context
|
||||||
void *driver;
|
void *driver;
|
||||||
const StorageWriteInterface *interface;
|
|
||||||
IoWrite *io;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OBJECT_DEFINE_MOVE(STORAGE_WRITE);
|
OBJECT_DEFINE_MOVE(STORAGE_WRITE);
|
||||||
@ -50,132 +49,18 @@ storageWriteNew(void *driver, const StorageWriteInterface *interface)
|
|||||||
|
|
||||||
*this = (StorageWrite)
|
*this = (StorageWrite)
|
||||||
{
|
{
|
||||||
|
.pub =
|
||||||
|
{
|
||||||
|
.interface = interface,
|
||||||
|
.io = ioWriteNew(driver, interface->ioInterface),
|
||||||
|
},
|
||||||
.memContext = memContextCurrent(),
|
.memContext = memContextCurrent(),
|
||||||
.driver = driver,
|
.driver = driver,
|
||||||
.interface = interface,
|
|
||||||
.io = ioWriteNew(driver, interface->ioInterface),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FUNCTION_LOG_RETURN(STORAGE_WRITE, this);
|
FUNCTION_LOG_RETURN(STORAGE_WRITE, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
bool
|
|
||||||
storageWriteAtomic(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->atomic);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
bool
|
|
||||||
storageWriteCreatePath(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->createPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
IoWrite *
|
|
||||||
storageWriteIo(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->io);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
mode_t
|
|
||||||
storageWriteModeFile(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->modeFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
mode_t
|
|
||||||
storageWriteModePath(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->modePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
const String *
|
|
||||||
storageWriteName(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
bool
|
|
||||||
storageWriteSyncFile(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->syncFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
bool
|
|
||||||
storageWriteSyncPath(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->syncPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
|
||||||
const String *
|
|
||||||
storageWriteType(const StorageWrite *this)
|
|
||||||
{
|
|
||||||
FUNCTION_TEST_BEGIN();
|
|
||||||
FUNCTION_TEST_PARAM(STORAGE_WRITE, this);
|
|
||||||
FUNCTION_TEST_END();
|
|
||||||
|
|
||||||
ASSERT(this != NULL);
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(this->interface->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
String *
|
String *
|
||||||
storageWriteToLog(const StorageWrite *this)
|
storageWriteToLog(const StorageWrite *this)
|
||||||
|
@ -15,6 +15,7 @@ typedef struct StorageWrite StorageWrite;
|
|||||||
#include "common/io/write.h"
|
#include "common/io/write.h"
|
||||||
#include "common/type/buffer.h"
|
#include "common/type/buffer.h"
|
||||||
#include "common/type/string.h"
|
#include "common/type/string.h"
|
||||||
|
#include "storage/write.intern.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Functions
|
Functions
|
||||||
@ -25,33 +26,84 @@ StorageWrite *storageWriteMove(StorageWrite *this, MemContext *parentNew);
|
|||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Getters/Setters
|
Getters/Setters
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
|
typedef struct StorageWritePub
|
||||||
|
{
|
||||||
|
const StorageWriteInterface *interface; // File data (name, driver type, etc.)
|
||||||
|
IoWrite *io; // Write interface
|
||||||
|
} StorageWritePub;
|
||||||
|
|
||||||
// Will the file be written atomically? Atomic writes means the file will be complete or be missing. Filesystems have different ways
|
// Will the file be written atomically? Atomic writes means the file will be complete or be missing. Filesystems have different ways
|
||||||
// to accomplish this.
|
// to accomplish this.
|
||||||
bool storageWriteAtomic(const StorageWrite *this);
|
__attribute__((always_inline)) static inline bool
|
||||||
|
storageWriteAtomic(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->atomic;
|
||||||
|
}
|
||||||
|
|
||||||
// Will the path be created if required?
|
// Will the path be created if required?
|
||||||
bool storageWriteCreatePath(const StorageWrite *this);
|
__attribute__((always_inline)) static inline bool
|
||||||
|
storageWriteCreatePath(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->createPath;
|
||||||
|
}
|
||||||
|
|
||||||
// Write interface
|
// Write interface
|
||||||
IoWrite *storageWriteIo(const StorageWrite *this);
|
__attribute__((always_inline)) static inline IoWrite *
|
||||||
|
storageWriteIo(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->io;
|
||||||
|
}
|
||||||
|
|
||||||
// File mode
|
// File mode
|
||||||
mode_t storageWriteModeFile(const StorageWrite *this);
|
__attribute__((always_inline)) static inline mode_t
|
||||||
|
storageWriteModeFile(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->modeFile;
|
||||||
|
}
|
||||||
|
|
||||||
// Path mode (if the destination path needs to be create)
|
// Path mode (if the destination path needs to be create)
|
||||||
mode_t storageWriteModePath(const StorageWrite *this);
|
__attribute__((always_inline)) static inline mode_t
|
||||||
|
storageWriteModePath(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->modePath;
|
||||||
|
}
|
||||||
|
|
||||||
// File name
|
// File name
|
||||||
const String *storageWriteName(const StorageWrite *this);
|
__attribute__((always_inline)) static inline const String *
|
||||||
|
storageWriteName(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->name;
|
||||||
|
}
|
||||||
|
|
||||||
// Will the file be synced before it is closed?
|
// Will the file be synced before it is closed?
|
||||||
bool storageWriteSyncFile(const StorageWrite *this);
|
__attribute__((always_inline)) static inline bool
|
||||||
|
storageWriteSyncFile(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->syncFile;
|
||||||
|
}
|
||||||
|
|
||||||
// Will the path be synced after the file is closed?
|
// Will the path be synced after the file is closed?
|
||||||
bool storageWriteSyncPath(const StorageWrite *this);
|
__attribute__((always_inline)) static inline bool
|
||||||
|
storageWriteSyncPath(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->syncPath;
|
||||||
|
}
|
||||||
|
|
||||||
// File type
|
// File type
|
||||||
const String *storageWriteType(const StorageWrite *this);
|
__attribute__((always_inline)) static inline const String *
|
||||||
|
storageWriteType(const StorageWrite *this)
|
||||||
|
{
|
||||||
|
ASSERT_INLINE(this != NULL);
|
||||||
|
return ((const StorageWritePub *)this)->interface->type;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Destructor
|
Destructor
|
||||||
|
@ -5,7 +5,6 @@ Storage Write Interface Internal
|
|||||||
#define STORAGE_WRITE_INTERN_H
|
#define STORAGE_WRITE_INTERN_H
|
||||||
|
|
||||||
#include "common/io/write.intern.h"
|
#include "common/io/write.intern.h"
|
||||||
#include "storage/write.h"
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
|
@ -532,8 +532,8 @@ testRun(void)
|
|||||||
// No prior checksum, no compression, no pageChecksum, no delta, no hasReference
|
// No prior checksum, no compression, no pageChecksum, no delta, no hasReference
|
||||||
|
|
||||||
// With the expected backupCopyResultCopy, unset the storageFeatureCompress bit for the storageRepo for code coverage
|
// With the expected backupCopyResultCopy, unset the storageFeatureCompress bit for the storageRepo for code coverage
|
||||||
uint64_t feature = storageRepo()->interface.feature;
|
uint64_t feature = storageRepo()->pub.interface.feature;
|
||||||
((Storage *)storageRepo())->interface.feature = feature & ((1 << storageFeatureCompress) ^ 0xFFFFFFFFFFFFFFFF);
|
((Storage *)storageRepo())->pub.interface.feature = feature & ((1 << storageFeatureCompress) ^ 0xFFFFFFFFFFFFFFFF);
|
||||||
|
|
||||||
// Create tmp file to make it look like a prior backup file failed partway through to ensure that retries work
|
// Create tmp file to make it look like a prior backup file failed partway through to ensure that retries work
|
||||||
TEST_RESULT_VOID(
|
TEST_RESULT_VOID(
|
||||||
@ -547,7 +547,7 @@ testRun(void)
|
|||||||
cipherTypeNone, NULL),
|
cipherTypeNone, NULL),
|
||||||
"pg file exists and shrunk, no repo file, no ignoreMissing, no pageChecksum, no delta, no hasReference");
|
"pg file exists and shrunk, no repo file, no ignoreMissing, no pageChecksum, no delta, no hasReference");
|
||||||
|
|
||||||
((Storage *)storageRepo())->interface.feature = feature;
|
((Storage *)storageRepo())->pub.interface.feature = feature;
|
||||||
|
|
||||||
TEST_RESULT_UINT(result.copySize + result.repoSize, 18, " copy=repo=pgFile size");
|
TEST_RESULT_UINT(result.copySize + result.repoSize, 18, " copy=repo=pgFile size");
|
||||||
TEST_RESULT_UINT(result.backupCopyResult, backupCopyResultCopy, " copy file");
|
TEST_RESULT_UINT(result.backupCopyResult, backupCopyResultCopy, " copy file");
|
||||||
@ -1898,18 +1898,18 @@ testRun(void)
|
|||||||
strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strZ(resumeLabel)))));
|
strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strZ(resumeLabel)))));
|
||||||
|
|
||||||
// Disable storageFeaturePath so paths will not be created before files are copied
|
// Disable storageFeaturePath so paths will not be created before files are copied
|
||||||
((Storage *)storageRepoWrite())->interface.feature ^= 1 << storageFeaturePath;
|
((Storage *)storageRepoWrite())->pub.interface.feature ^= 1 << storageFeaturePath;
|
||||||
|
|
||||||
// Disable storageFeaturePathSync so paths will not be synced
|
// Disable storageFeaturePathSync so paths will not be synced
|
||||||
((Storage *)storageRepoWrite())->interface.feature ^= 1 << storageFeaturePathSync;
|
((Storage *)storageRepoWrite())->pub.interface.feature ^= 1 << storageFeaturePathSync;
|
||||||
|
|
||||||
// Run backup
|
// Run backup
|
||||||
testBackupPqScriptP(PG_VERSION_95, backupTimeStart);
|
testBackupPqScriptP(PG_VERSION_95, backupTimeStart);
|
||||||
TEST_RESULT_VOID(cmdBackup(), "backup");
|
TEST_RESULT_VOID(cmdBackup(), "backup");
|
||||||
|
|
||||||
// Enable storage features
|
// Enable storage features
|
||||||
((Storage *)storageRepoWrite())->interface.feature |= 1 << storageFeaturePath;
|
((Storage *)storageRepoWrite())->pub.interface.feature |= 1 << storageFeaturePath;
|
||||||
((Storage *)storageRepoWrite())->interface.feature |= 1 << storageFeaturePathSync;
|
((Storage *)storageRepoWrite())->pub.interface.feature |= 1 << storageFeaturePathSync;
|
||||||
|
|
||||||
TEST_RESULT_LOG(
|
TEST_RESULT_LOG(
|
||||||
"P00 INFO: execute exclusive pg_start_backup(): backup begins after the next regular checkpoint completes\n"
|
"P00 INFO: execute exclusive pg_start_backup(): backup begins after the next regular checkpoint completes\n"
|
||||||
@ -2396,18 +2396,18 @@ testRun(void)
|
|||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
// Disable storageFeatureSymLink so tablespace (and latest) symlinks will not be created
|
// Disable storageFeatureSymLink so tablespace (and latest) symlinks will not be created
|
||||||
((Storage *)storageRepoWrite())->interface.feature ^= 1 << storageFeatureSymLink;
|
((Storage *)storageRepoWrite())->pub.interface.feature ^= 1 << storageFeatureSymLink;
|
||||||
|
|
||||||
// Disable storageFeatureHardLink so hardlinks will not be created
|
// Disable storageFeatureHardLink so hardlinks will not be created
|
||||||
((Storage *)storageRepoWrite())->interface.feature ^= 1 << storageFeatureHardLink;
|
((Storage *)storageRepoWrite())->pub.interface.feature ^= 1 << storageFeatureHardLink;
|
||||||
|
|
||||||
// Run backup
|
// Run backup
|
||||||
testBackupPqScriptP(PG_VERSION_11, backupTimeStart, .walCompressType = compressTypeGz, .walTotal = 3);
|
testBackupPqScriptP(PG_VERSION_11, backupTimeStart, .walCompressType = compressTypeGz, .walTotal = 3);
|
||||||
TEST_RESULT_VOID(cmdBackup(), "backup");
|
TEST_RESULT_VOID(cmdBackup(), "backup");
|
||||||
|
|
||||||
// Reset storage features
|
// Reset storage features
|
||||||
((Storage *)storageRepoWrite())->interface.feature |= 1 << storageFeatureSymLink;
|
((Storage *)storageRepoWrite())->pub.interface.feature |= 1 << storageFeatureSymLink;
|
||||||
((Storage *)storageRepoWrite())->interface.feature |= 1 << storageFeatureHardLink;
|
((Storage *)storageRepoWrite())->pub.interface.feature |= 1 << storageFeatureHardLink;
|
||||||
|
|
||||||
TEST_RESULT_LOG(
|
TEST_RESULT_LOG(
|
||||||
"P00 INFO: execute non-exclusive pg_start_backup(): backup begins after the next regular checkpoint completes\n"
|
"P00 INFO: execute non-exclusive pg_start_backup(): backup begins after the next regular checkpoint completes\n"
|
||||||
|
@ -188,13 +188,13 @@ testRun(void)
|
|||||||
Storage *storage = NULL;
|
Storage *storage = NULL;
|
||||||
TEST_ASSIGN(storage, storageRepoGet(0, false), "get repo storage");
|
TEST_ASSIGN(storage, storageRepoGet(0, false), "get repo storage");
|
||||||
TEST_RESULT_STR_Z(storage->path, "/repo", " check path");
|
TEST_RESULT_STR_Z(storage->path, "/repo", " check path");
|
||||||
TEST_RESULT_STR(((StorageAzure *)storage->driver)->account, TEST_ACCOUNT_STR, " check account");
|
TEST_RESULT_STR(((StorageAzure *)storageDriver(storage))->account, TEST_ACCOUNT_STR, " check account");
|
||||||
TEST_RESULT_STR(((StorageAzure *)storage->driver)->container, TEST_CONTAINER_STR, " check container");
|
TEST_RESULT_STR(((StorageAzure *)storageDriver(storage))->container, TEST_CONTAINER_STR, " check container");
|
||||||
TEST_RESULT_STR(
|
TEST_RESULT_STR(
|
||||||
strNewEncode(encodeBase64, ((StorageAzure *)storage->driver)->sharedKey), TEST_KEY_SHARED_STR, " check key");
|
strNewEncode(encodeBase64, ((StorageAzure *)storageDriver(storage))->sharedKey), TEST_KEY_SHARED_STR, " check key");
|
||||||
TEST_RESULT_STR_Z(((StorageAzure *)storage->driver)->host, TEST_ACCOUNT ".blob.core.windows.net", " check host");
|
TEST_RESULT_STR_Z(((StorageAzure *)storageDriver(storage))->host, TEST_ACCOUNT ".blob.core.windows.net", " check host");
|
||||||
TEST_RESULT_STR_Z(((StorageAzure *)storage->driver)->pathPrefix, "/" TEST_CONTAINER, " check path prefix");
|
TEST_RESULT_STR_Z(((StorageAzure *)storageDriver(storage))->pathPrefix, "/" TEST_CONTAINER, " check path prefix");
|
||||||
TEST_RESULT_UINT(((StorageAzure *)storage->driver)->blockSize, STORAGE_AZURE_BLOCKSIZE_MIN, " check block size");
|
TEST_RESULT_UINT(((StorageAzure *)storageDriver(storage))->blockSize, STORAGE_AZURE_BLOCKSIZE_MIN, " check block size");
|
||||||
TEST_RESULT_BOOL(storageFeature(storage, storageFeaturePath), false, " check path feature");
|
TEST_RESULT_BOOL(storageFeature(storage, storageFeaturePath), false, " check path feature");
|
||||||
TEST_RESULT_BOOL(storageFeature(storage, storageFeatureCompress), false, " check compress feature");
|
TEST_RESULT_BOOL(storageFeature(storage, storageFeatureCompress), false, " check compress feature");
|
||||||
}
|
}
|
||||||
@ -298,7 +298,7 @@ testRun(void)
|
|||||||
Storage *storage = NULL;
|
Storage *storage = NULL;
|
||||||
TEST_ASSIGN(storage, storageRepoGet(0, true), "get repo storage");
|
TEST_ASSIGN(storage, storageRepoGet(0, true), "get repo storage");
|
||||||
|
|
||||||
driver = (StorageAzure *)storage->driver;
|
driver = (StorageAzure *)storageDriver(storage);
|
||||||
TEST_RESULT_STR(driver->host, hrnServerHost(), " check host");
|
TEST_RESULT_STR(driver->host, hrnServerHost(), " check host");
|
||||||
TEST_RESULT_STR_Z(driver->pathPrefix, "/" TEST_ACCOUNT "/" TEST_CONTAINER, " check path prefix");
|
TEST_RESULT_STR_Z(driver->pathPrefix, "/" TEST_ACCOUNT "/" TEST_CONTAINER, " check path prefix");
|
||||||
TEST_RESULT_BOOL(driver->fileId == 0, false, " check file id");
|
TEST_RESULT_BOOL(driver->fileId == 0, false, " check file id");
|
||||||
@ -735,7 +735,7 @@ testRun(void)
|
|||||||
|
|
||||||
TEST_ASSIGN(storage, storageRepoGet(0, true), "get repo storage");
|
TEST_ASSIGN(storage, storageRepoGet(0, true), "get repo storage");
|
||||||
|
|
||||||
driver = (StorageAzure *)storage->driver;
|
driver = (StorageAzure *)storageDriver(storage);
|
||||||
TEST_RESULT_PTR_NE(driver->sasKey, NULL, "check sas key");
|
TEST_RESULT_PTR_NE(driver->sasKey, NULL, "check sas key");
|
||||||
|
|
||||||
hrnServerScriptAccept(service);
|
hrnServerScriptAccept(service);
|
||||||
|
@ -24,7 +24,7 @@ testRun(void)
|
|||||||
|
|
||||||
const Storage *storage = NULL;
|
const Storage *storage = NULL;
|
||||||
TEST_ASSIGN(storage, storageRepoGet(0, true), "get cifs repo storage");
|
TEST_ASSIGN(storage, storageRepoGet(0, true), "get cifs repo storage");
|
||||||
TEST_RESULT_STR_Z(storage->type, "cifs", "check storage type");
|
TEST_RESULT_STR_Z(storageType(storage), "cifs", "check storage type");
|
||||||
TEST_RESULT_BOOL(storageFeature(storage, storageFeaturePath), true, " check path feature");
|
TEST_RESULT_BOOL(storageFeature(storage, storageFeaturePath), true, " check path feature");
|
||||||
TEST_RESULT_BOOL(storageFeature(storage, storageFeatureCompress), true, " check compress feature");
|
TEST_RESULT_BOOL(storageFeature(storage, storageFeatureCompress), true, " check compress feature");
|
||||||
|
|
||||||
|
@ -209,10 +209,10 @@ testRun(void)
|
|||||||
Storage *storage = NULL;
|
Storage *storage = NULL;
|
||||||
TEST_ASSIGN(storage, storageRepoGet(0, false), "get repo storage");
|
TEST_ASSIGN(storage, storageRepoGet(0, false), "get repo storage");
|
||||||
TEST_RESULT_STR_Z(storage->path, "/repo", " check path");
|
TEST_RESULT_STR_Z(storage->path, "/repo", " check path");
|
||||||
TEST_RESULT_STR(((StorageGcs *)storage->driver)->bucket, TEST_BUCKET_STR, " check bucket");
|
TEST_RESULT_STR(((StorageGcs *)storageDriver(storage))->bucket, TEST_BUCKET_STR, " check bucket");
|
||||||
TEST_RESULT_STR_Z(((StorageGcs *)storage->driver)->endpoint, "storage.googleapis.com", " check endpoint");
|
TEST_RESULT_STR_Z(((StorageGcs *)storageDriver(storage))->endpoint, "storage.googleapis.com", " check endpoint");
|
||||||
TEST_RESULT_UINT(((StorageGcs *)storage->driver)->chunkSize, STORAGE_GCS_CHUNKSIZE_DEFAULT, " check chunk size");
|
TEST_RESULT_UINT(((StorageGcs *)storageDriver(storage))->chunkSize, STORAGE_GCS_CHUNKSIZE_DEFAULT, " check chunk size");
|
||||||
TEST_RESULT_STR(((StorageGcs *)storage->driver)->token, TEST_TOKEN_STR, " check token");
|
TEST_RESULT_STR(((StorageGcs *)storageDriver(storage))->token, TEST_TOKEN_STR, " check token");
|
||||||
TEST_RESULT_BOOL(storageFeature(storage, storageFeaturePath), false, " check path feature");
|
TEST_RESULT_BOOL(storageFeature(storage, storageFeaturePath), false, " check path feature");
|
||||||
TEST_RESULT_BOOL(storageFeature(storage, storageFeatureCompress), false, " check compress feature");
|
TEST_RESULT_BOOL(storageFeature(storage, storageFeatureCompress), false, " check compress feature");
|
||||||
}
|
}
|
||||||
@ -322,11 +322,11 @@ testRun(void)
|
|||||||
TEST_ASSIGN(storage, storageRepoGet(0, true), "get repo storage");
|
TEST_ASSIGN(storage, storageRepoGet(0, true), "get repo storage");
|
||||||
|
|
||||||
// Tests need the chunk size to be 16
|
// Tests need the chunk size to be 16
|
||||||
((StorageGcs *)storage->driver)->chunkSize = 16;
|
((StorageGcs *)storageDriver(storage))->chunkSize = 16;
|
||||||
|
|
||||||
// Generate the auth request. The JWT part will need to be ? since it can vary in content and size.
|
// Generate the auth request. The JWT part will need to be ? since it can vary in content and size.
|
||||||
const char *const preamble = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=";
|
const char *const preamble = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=";
|
||||||
const String *const jwt = storageGcsAuthJwt(((StorageGcs *)storage->driver), time(NULL));
|
const String *const jwt = storageGcsAuthJwt(((StorageGcs *)storageDriver(storage)), time(NULL));
|
||||||
|
|
||||||
String *const authRequest = strNewFmt(
|
String *const authRequest = strNewFmt(
|
||||||
"POST /token HTTP/1.1\r\n"
|
"POST /token HTTP/1.1\r\n"
|
||||||
@ -354,7 +354,7 @@ testRun(void)
|
|||||||
testResponseP(service);
|
testResponseP(service);
|
||||||
|
|
||||||
storageGcsRequestP(
|
storageGcsRequestP(
|
||||||
(StorageGcs *)storage->driver, HTTP_VERB_POST_STR, .noBucket = true,
|
(StorageGcs *)storageDriver(storage), HTTP_VERB_POST_STR, .noBucket = true,
|
||||||
.content = BUFSTR(jsonFromKv(kvPut(kvNew(), GCS_JSON_NAME_VAR, VARSTRDEF("bucket")))));
|
.content = BUFSTR(jsonFromKv(kvPut(kvNew(), GCS_JSON_NAME_VAR, VARSTRDEF("bucket")))));
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -102,9 +102,9 @@ testRun(void)
|
|||||||
TEST_RESULT_BOOL(storageTest->write, true, " check write");
|
TEST_RESULT_BOOL(storageTest->write, true, " check write");
|
||||||
TEST_RESULT_BOOL(storageTest->pathExpressionFunction != NULL, true, " check expression function is set");
|
TEST_RESULT_BOOL(storageTest->pathExpressionFunction != NULL, true, " check expression function is set");
|
||||||
|
|
||||||
TEST_RESULT_PTR(storageInterface(storageTest).info, storageTest->interface.info, " check interface");
|
TEST_RESULT_PTR(storageInterface(storageTest).info, storageTest->pub.interface.info, " check interface");
|
||||||
TEST_RESULT_PTR(storageDriver(storageTest), storageTest->driver, " check driver");
|
TEST_RESULT_PTR(storageDriver(storageTest), storageDriver(storageTest), " check driver");
|
||||||
TEST_RESULT_STR(storageType(storageTest), storageTest->type, " check type");
|
TEST_RESULT_STR(storageType(storageTest), storageType(storageTest), " check type");
|
||||||
TEST_RESULT_BOOL(storageFeature(storageTest, storageFeaturePath), true, " check path feature");
|
TEST_RESULT_BOOL(storageFeature(storageTest, storageFeaturePath), true, " check path feature");
|
||||||
TEST_RESULT_BOOL(storageFeature(storageTest, storageFeatureCompress), true, " check compress feature");
|
TEST_RESULT_BOOL(storageFeature(storageTest, storageFeatureCompress), true, " check compress feature");
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ testRun(void)
|
|||||||
storagePutP(storageNewWriteP(storageTest, strNew("repo/test.txt")), contentBuf);
|
storagePutP(storageNewWriteP(storageTest, strNew("repo/test.txt")), contentBuf);
|
||||||
|
|
||||||
// Disable protocol compression in the storage object to test no compression
|
// Disable protocol compression in the storage object to test no compression
|
||||||
((StorageRemote *)storageRemote->driver)->compressLevel = 0;
|
((StorageRemote *)storageDriver(storageRemote))->compressLevel = 0;
|
||||||
|
|
||||||
StorageRead *fileRead = NULL;
|
StorageRead *fileRead = NULL;
|
||||||
|
|
||||||
@ -370,7 +370,7 @@ testRun(void)
|
|||||||
TEST_RESULT_UINT(((StorageReadRemote *)fileRead->driver)->protocolReadBytes, 11, " check read size");
|
TEST_RESULT_UINT(((StorageReadRemote *)fileRead->driver)->protocolReadBytes, 11, " check read size");
|
||||||
|
|
||||||
// Enable protocol compression in the storage object
|
// Enable protocol compression in the storage object
|
||||||
((StorageRemote *)storageRemote->driver)->compressLevel = 3;
|
((StorageRemote *)storageDriver(storageRemote))->compressLevel = 3;
|
||||||
|
|
||||||
TEST_ASSIGN(
|
TEST_ASSIGN(
|
||||||
fileRead, storageNewReadP(storageRemote, strNew("test.txt"), .compressible = true), "get file (protocol compress)");
|
fileRead, storageNewReadP(storageRemote, strNew("test.txt"), .compressible = true), "get file (protocol compress)");
|
||||||
@ -490,7 +490,7 @@ testRun(void)
|
|||||||
ioBufferSizeSet(9999);
|
ioBufferSizeSet(9999);
|
||||||
|
|
||||||
// Disable protocol compression in the storage object to test no compression
|
// Disable protocol compression in the storage object to test no compression
|
||||||
((StorageRemote *)storageRemote->driver)->compressLevel = 0;
|
((StorageRemote *)storageDriver(storageRemote))->compressLevel = 0;
|
||||||
|
|
||||||
StorageWrite *write = NULL;
|
StorageWrite *write = NULL;
|
||||||
TEST_ASSIGN(write, storageNewWriteP(storageRemote, strNew("test.txt")), "new write file");
|
TEST_ASSIGN(write, storageNewWriteP(storageRemote, strNew("test.txt")), "new write file");
|
||||||
@ -513,7 +513,7 @@ testRun(void)
|
|||||||
bufEq(storageGetP(storageNewReadP(storageRemote, strNew("test.txt"))), contentBuf), true, "check file");
|
bufEq(storageGetP(storageNewReadP(storageRemote, strNew("test.txt"))), contentBuf), true, "check file");
|
||||||
|
|
||||||
// Enable protocol compression in the storage object
|
// Enable protocol compression in the storage object
|
||||||
((StorageRemote *)storageRemote->driver)->compressLevel = 3;
|
((StorageRemote *)storageDriver(storageRemote))->compressLevel = 3;
|
||||||
|
|
||||||
// Write the file again, but this time free it before close and make sure the .tmp file is left
|
// Write the file again, but this time free it before close and make sure the .tmp file is left
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -388,7 +388,7 @@ testRun(void)
|
|||||||
harnessCfgLoad(cfgCmdArchivePush, argList);
|
harnessCfgLoad(cfgCmdArchivePush, argList);
|
||||||
|
|
||||||
Storage *s3 = storageRepoGet(0, true);
|
Storage *s3 = storageRepoGet(0, true);
|
||||||
StorageS3 *driver = (StorageS3 *)s3->driver;
|
StorageS3 *driver = (StorageS3 *)storageDriver(s3);
|
||||||
|
|
||||||
TEST_RESULT_STR(s3->path, path, "check path");
|
TEST_RESULT_STR(s3->path, path, "check path");
|
||||||
TEST_RESULT_BOOL(storageFeature(s3, storageFeaturePath), false, "check path feature");
|
TEST_RESULT_BOOL(storageFeature(s3, storageFeaturePath), false, "check path feature");
|
||||||
@ -446,7 +446,7 @@ testRun(void)
|
|||||||
harnessCfgLoad(cfgCmdArchivePush, argList);
|
harnessCfgLoad(cfgCmdArchivePush, argList);
|
||||||
|
|
||||||
s3 = storageRepoGet(0, true);
|
s3 = storageRepoGet(0, true);
|
||||||
driver = (StorageS3 *)s3->driver;
|
driver = (StorageS3 *)storageDriver(s3);
|
||||||
|
|
||||||
TEST_RESULT_STR(s3->path, path, "check path");
|
TEST_RESULT_STR(s3->path, path, "check path");
|
||||||
TEST_RESULT_STR(driver->credRole, credRole, "check role");
|
TEST_RESULT_STR(driver->credRole, credRole, "check role");
|
||||||
@ -1020,7 +1020,7 @@ testRun(void)
|
|||||||
harnessCfgLoad(cfgCmdArchivePush, argList);
|
harnessCfgLoad(cfgCmdArchivePush, argList);
|
||||||
|
|
||||||
s3 = storageRepoGet(0, true);
|
s3 = storageRepoGet(0, true);
|
||||||
driver = (StorageS3 *)s3->driver;
|
driver = (StorageS3 *)storageDriver(s3);
|
||||||
|
|
||||||
// Set deleteMax to a small value for testing
|
// Set deleteMax to a small value for testing
|
||||||
driver->deleteMax = 2;
|
driver->deleteMax = 2;
|
||||||
|
Reference in New Issue
Block a user