1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

Abstract Posix storage driver code into a separate module.

This commit is contained in:
David Steele 2018-04-05 14:48:16 -04:00
parent 348278bb68
commit bd25248df0
9 changed files with 382 additions and 292 deletions

View File

@ -36,7 +36,7 @@
</release-item>
<release-item>
<p>Storage object improvements. Convert all functions to variadic functions. Enforce read-only storage. Add <code>storageLocalWrite()</code> helper function. Add <code>storageExists()</code>, <code>storagePathCreate()</code>, <code>storageRemove()</code>, and <code>storageStat()</code>. Add <code>StorageFile</code> object and <code>storageOpenRead()</code>/<code>storageOpenWrite()</code>.</p>
<p>Storage object improvements. Convert all functions to variadic functions. Enforce read-only storage. Add <code>storageLocalWrite()</code> helper function. Add <code>storageExists()</code>, <code>storagePathCreate()</code>, and <code>storageRemove()</code>. Add <code>StorageFile</code> object and <code>storageOpenRead()</code>/<code>storageOpenWrite()</code>. Abstract Posix driver code into a separate module.</p>
</release-item>
<release-item>

View File

@ -100,6 +100,7 @@ my @stryCFile =
'config/parse.c',
'perl/config.c',
'postgres/pageChecksum.c',
'storage/driver/posix.c',
'storage/file.c',
'storage/helper.c',
'storage/storage.c',

View File

@ -79,6 +79,7 @@ SRCS = \
config/parse.c \
perl/config.c \
perl/exec.c \
storage/driver/posix.c \
storage/file.c \
storage/helper.c \
storage/storage.c \

287
src/storage/driver/posix.c Normal file
View File

@ -0,0 +1,287 @@
/***********************************************************************************************************************************
Storage Posix Driver
***********************************************************************************************************************************/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "common/memContext.h"
#include "common/regExp.h"
#include "storage/driver/posix.h"
#include "storage/storage.h"
/***********************************************************************************************************************************
Storage file data - holds the file handle.
***********************************************************************************************************************************/
typedef struct StorageFileDataPosix
{
MemContext *memContext;
int handle;
} StorageFileDataPosix;
#define STORAGE_DATA(file) \
((StorageFileDataPosix *)storageFileData(file))
/***********************************************************************************************************************************
Does a file/path exist?
***********************************************************************************************************************************/
bool
storageDriverPosixExists(const String *path)
{
bool result = false;
// Attempt to stat the file to determine if it exists
struct stat statFile;
// Any error other than entry not found should be reported
if (stat(strPtr(path), &statFile) == -1)
{
if (errno != ENOENT)
THROW_SYS_ERROR(FileOpenError, "unable to stat '%s'", strPtr(path));
}
// Else found
else
result = !S_ISDIR(statFile.st_mode);
return result;
}
/***********************************************************************************************************************************
Read from storage into a buffer
***********************************************************************************************************************************/
Buffer *
storageDriverPosixGet(const StorageFile *file)
{
Buffer volatile *result = NULL;
TRY_BEGIN()
{
// Create result buffer with buffer size
ssize_t actualBytes = 0;
size_t totalBytes = 0;
do
{
size_t bufferSize = storageBufferSize(storageFileStorage(file));
// Allocate the buffer before first read
if (result == NULL)
result = bufNew(bufferSize);
// Grow the buffer on subsequent reads
else
bufResize((Buffer *)result, bufSize((Buffer *)result) + bufferSize);
// Read and handle errors
actualBytes = read(
STORAGE_DATA(file)->handle, bufPtr((Buffer *)result) + totalBytes, bufferSize);
// Error occurred during write
if (actualBytes == -1)
THROW_SYS_ERROR(FileReadError, "unable to read '%s'", strPtr(storageFileName(file)));
// Track total bytes read
totalBytes += (size_t)actualBytes;
}
while (actualBytes != 0);
// Resize buffer to total bytes read
bufResize((Buffer *)result, totalBytes);
}
CATCH_ANY()
{
// Free buffer on error if it was allocated
bufFree((Buffer *)result);
RETHROW();
}
FINALLY()
{
close(STORAGE_DATA(file)->handle);
storageFileFree(file);
}
TRY_END();
return (Buffer *)result;
}
/***********************************************************************************************************************************
Get a list of files from a directory
***********************************************************************************************************************************/
StringList *
storageDriverPosixList(const String *path, bool errorOnMissing, const String *expression)
{
StringList *result = NULL;
DIR *dir = NULL;
RegExp *regExp = NULL;
TRY_BEGIN()
{
// Open the directory for read
dir = opendir(strPtr(path));
// If the directory could not be opened process errors but ignore missing directories when specified
if (!dir)
{
if (errorOnMissing || errno != ENOENT)
THROW_SYS_ERROR(PathOpenError, "unable to open directory '%s' for read", strPtr(path));
}
else
{
// Prepare regexp if an expression was passed
if (expression != NULL)
regExp = regExpNew(expression);
// Create the string list now that we know the directory is valid
result = strLstNew();
// Read the directory entries
struct dirent *dirEntry = readdir(dir);
while (dirEntry != NULL)
{
String *entry = strNew(dirEntry->d_name);
// Exclude current/parent directory and apply the expression if specified
if (!strEqZ(entry, ".") && !strEqZ(entry, "..") && (regExp == NULL || regExpMatch(regExp, entry)))
strLstAdd(result, entry);
else
strFree(entry);
dirEntry = readdir(dir);
}
}
}
CATCH_ANY()
{
// Free list on error
strLstFree(result);
RETHROW();
}
FINALLY()
{
if (dir != NULL)
closedir(dir);
if (regExp != NULL)
regExpFree(regExp);
}
TRY_END();
return result;
}
/***********************************************************************************************************************************
Open a file for reading
***********************************************************************************************************************************/
void *
storageDriverPosixOpenRead(const String *file, bool ignoreMissing)
{
StorageFileDataPosix *result = NULL;
// Open the file and handle errors
int fileHandle = open(strPtr(file), O_RDONLY, 0);
if (fileHandle == -1)
{
// Error unless ignore missing is specified
if (!ignoreMissing || errno != ENOENT)
THROW_SYS_ERROR(FileOpenError, "unable to open '%s' for read", strPtr(file));
}
// Else create the storage file and data
else
{
MEM_CONTEXT_NEW_BEGIN("StorageFileDataPosix")
{
result = memNew(sizeof(StorageFileDataPosix));
result->memContext = MEM_CONTEXT_NEW();
result->handle = fileHandle;
}
MEM_CONTEXT_NEW_END();
}
return result;
}
/***********************************************************************************************************************************
Open a file for writing
***********************************************************************************************************************************/
void *
storageDriverPosixOpenWrite(const String *file, mode_t mode)
{
// Open the file and handle errors
int fileHandle = open(strPtr(file), O_CREAT | O_TRUNC | O_WRONLY, mode);
if (fileHandle == -1)
THROW_SYS_ERROR(FileOpenError, "unable to open '%s' for write", strPtr(file));
// Create the storage file and data
StorageFileDataPosix *result = NULL;
MEM_CONTEXT_NEW_BEGIN("StorageFileDataPosix")
{
result = memNew(sizeof(StorageFileDataPosix));
result->memContext = MEM_CONTEXT_NEW();
result->handle = fileHandle;
}
MEM_CONTEXT_NEW_END();
return result;
}
/***********************************************************************************************************************************
Create a path
***********************************************************************************************************************************/
void
storageDriverPosixPathCreate(const String *path, bool errorOnExists, bool noParentCreate, mode_t mode)
{
// Attempt to create the directory
if (mkdir(strPtr(path), mode) == -1)
{
// If the parent path does not exist then create it if allowed
if (errno == ENOENT && !noParentCreate)
{
storageDriverPosixPathCreate(strPath(path), errorOnExists, noParentCreate, mode);
storageDriverPosixPathCreate(path, errorOnExists, noParentCreate, mode);
}
// Ignore path exists if allowed
else if (errno != EEXIST || errorOnExists)
THROW_SYS_ERROR(PathCreateError, "unable to create path '%s'", strPtr(path));
}
}
/***********************************************************************************************************************************
Write a buffer to storage
***********************************************************************************************************************************/
void
storageDriverPosixPut(const StorageFile *file, const Buffer *buffer)
{
TRY_BEGIN()
{
if (write(STORAGE_DATA(file)->handle, bufPtr(buffer), bufSize(buffer)) != (ssize_t)bufSize(buffer))
THROW_SYS_ERROR(FileWriteError, "unable to write '%s'", strPtr(storageFileName(file)));
}
FINALLY()
{
close(STORAGE_DATA(file)->handle);
storageFileFree(file);
}
TRY_END();
}
/***********************************************************************************************************************************
Remove a file
***********************************************************************************************************************************/
void
storageDriverPosixRemove(const String *file, bool errorOnMissing)
{
// Attempt to unlink the file
if (unlink(strPtr(file)) == -1)
{
if (errorOnMissing || errno != ENOENT)
THROW_SYS_ERROR(FileRemoveError, "unable to remove '%s'", strPtr(file));
}
}

View File

@ -0,0 +1,26 @@
/***********************************************************************************************************************************
Storage Posix Driver
***********************************************************************************************************************************/
#ifndef STORAGE_DRIVER_POSIX_H
#define STORAGE_DRIVER_POSIX_H
#include <sys/types.h>
#include "common/type/buffer.h"
#include "common/type/string.h"
#include "storage/file.h"
#include "storage/storage.h"
/***********************************************************************************************************************************
Function
***********************************************************************************************************************************/
bool storageDriverPosixExists(const String *path);
Buffer *storageDriverPosixGet(const StorageFile *file);
StringList *storageDriverPosixList(const String *path, bool errorOnMissing, const String *expression);
void *storageDriverPosixOpenRead(const String *file, bool ignoreMissing);
void *storageDriverPosixOpenWrite(const String *file, mode_t mode);
void storageDriverPosixPathCreate(const String *path, bool errorOnExists, bool noParentCreate, mode_t mode);
void storageDriverPosixPut(const StorageFile *file, const Buffer *buffer);
void storageDriverPosixRemove(const String *file, bool errorOnMissing);
#endif

View File

@ -1,17 +1,12 @@
/***********************************************************************************************************************************
Storage Manager
***********************************************************************************************************************************/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "common/debug.h"
#include "common/memContext.h"
#include "common/regExp.h"
#include "common/wait.h"
#include "storage/driver/posix.h"
#include "storage/storage.h"
/***********************************************************************************************************************************
@ -28,18 +23,6 @@ struct Storage
StoragePathExpressionCallback pathExpressionFunction;
};
/***********************************************************************************************************************************
Storage file data - holds the file handle. This should eventually be moved to the Posix/CIFS driver.
***********************************************************************************************************************************/
typedef struct StorageFileDataPosix
{
MemContext *memContext;
int handle;
} StorageFileDataPosix;
#define STORAGE_DATA(file) \
((StorageFileDataPosix *)storageFileData(file))
/***********************************************************************************************************************************
Debug Asserts
***********************************************************************************************************************************/
@ -76,6 +59,15 @@ storageNew(const String *path, StorageNewParam param)
return this;
}
/***********************************************************************************************************************************
Get storage buffer size
***********************************************************************************************************************************/
size_t
storageBufferSize(const Storage *this)
{
return this->bufferSize;
}
/***********************************************************************************************************************************
Does a file/path exist?
***********************************************************************************************************************************/
@ -95,20 +87,11 @@ storageExists(const Storage *this, const String *pathExp, StorageExistsParam par
// Create Wait object of timeout > 0
Wait *wait = param.timeout != 0 ? waitNew(param.timeout) : NULL;
// Attempt to stat the file to determine if it exists
struct stat statFile;
// Loop until file exists or timeout
do
{
// Any error other than entry not found should be reported
if (stat(strPtr(path), &statFile) == -1)
{
if (errno != ENOENT)
THROW_SYS_ERROR(FileOpenError, "unable to stat '%s'", strPtr(path));
}
// Else found
else
result = true;
// Call driver function
result = storageDriverPosixExists(path);
}
while (!result && wait != NULL && waitMore(wait));
}
@ -123,58 +106,13 @@ Read from storage into a buffer
Buffer *
storageGet(const StorageFile *file)
{
Buffer volatile *result = NULL;
Buffer *result = NULL;
// Nothing to do unless a file was passed
// Call driver function if a file was passed
if (file != NULL)
{
TRY_BEGIN()
{
// Create result buffer with buffer size
ssize_t actualBytes = 0;
size_t totalBytes = 0;
result = storageDriverPosixGet(file);
do
{
// Allocate the buffer before first read
if (result == NULL)
result = bufNew(storageFileStorage(file)->bufferSize);
// Grow the buffer on subsequent reads
else
bufResize((Buffer *)result, bufSize((Buffer *)result) + (size_t)storageFileStorage(file)->bufferSize);
// Read and handle errors
actualBytes = read(
STORAGE_DATA(file)->handle, bufPtr((Buffer *)result) + totalBytes, storageFileStorage(file)->bufferSize);
// Error occurred during write
if (actualBytes == -1)
THROW_SYS_ERROR(FileReadError, "unable to read '%s'", strPtr(storageFileName(file)));
// Track total bytes read
totalBytes += (size_t)actualBytes;
}
while (actualBytes != 0);
// Resize buffer to total bytes read
bufResize((Buffer *)result, totalBytes);
}
CATCH_ANY()
{
// Free buffer on error if it was allocated
bufFree((Buffer *)result);
RETHROW();
}
FINALLY()
{
close(STORAGE_DATA(file)->handle);
storageFileFree(file);
}
TRY_END();
}
return (Buffer *)result;
return result;
}
/***********************************************************************************************************************************
@ -186,65 +124,18 @@ storageList(const Storage *this, const String *pathExp, StorageListParam param)
StringList *result = NULL;
String *path = NULL;
DIR *dir = NULL;
RegExp *regExp = NULL;
TRY_BEGIN()
{
// Build the path
path = storagePathNP(this, pathExp);
// Open the directory for read
dir = opendir(strPtr(path));
// If the directory could not be opened process errors but ignore missing directories when specified
if (!dir)
{
if (param.errorOnMissing || errno != ENOENT)
THROW_SYS_ERROR(PathOpenError, "unable to open directory '%s' for read", strPtr(path));
}
else
{
// Prepare regexp if an expression was passed
if (param.expression != NULL)
regExp = regExpNew(param.expression);
// Create the string list now that we know the directory is valid
result = strLstNew();
// Read the directory entries
struct dirent *dirEntry = readdir(dir);
while (dirEntry != NULL)
{
String *entry = strNew(dirEntry->d_name);
// Exclude current/parent directory and apply the expression if specified
if (!strEqZ(entry, ".") && !strEqZ(entry, "..") && (regExp == NULL || regExpMatch(regExp, entry)))
strLstAdd(result, entry);
else
strFree(entry);
dirEntry = readdir(dir);
}
}
}
CATCH_ANY()
{
// Free list on error
strLstFree(result);
RETHROW();
// Call driver function
result = storageDriverPosixList(path, param.errorOnMissing, param.expression);
}
FINALLY()
{
strFree(path);
if (dir != NULL)
closedir(dir);
if (regExp != NULL)
regExpFree(regExp);
}
TRY_END();
@ -263,36 +154,19 @@ storageOpenRead(const Storage *this, const String *fileExp, StorageOpenReadParam
MEM_CONTEXT_NEW_BEGIN("StorageFileRead")
{
String *fileName = storagePathNP(this, fileExp);
int fileHandle;
// Open the file and handle errors
fileHandle = open(strPtr(fileName), O_RDONLY, 0);
// Call driver function
void *data = storageDriverPosixOpenRead(fileName, param.ignoreMissing);
if (fileHandle == -1)
// Free mem contexts if missing files are ignored
if (data == NULL)
{
// Error unless ignore missing is specified
if (!param.ignoreMissing || errno != ENOENT)
THROW_SYS_ERROR(FileOpenError, "unable to open '%s' for read", strPtr(fileName));
// Free mem contexts if missing files are ignored
memContextSwitch(MEM_CONTEXT_OLD());
memContextFree(MEM_CONTEXT_NEW());
}
// Else create the storage file
else
{
// Create the storage file and data
StorageFileDataPosix *data = NULL;
MEM_CONTEXT_NEW_BEGIN("StorageFileReadDataPosix")
{
data = memNew(sizeof(StorageFileDataPosix));
data->memContext = MEM_CONTEXT_NEW();
data->handle = fileHandle;
}
MEM_CONTEXT_NEW_END();
result = storageFileNew(this, fileName, storageFileTypeRead, data);
}
}
MEM_CONTEXT_NEW_END();
@ -312,27 +186,11 @@ storageOpenWrite(const Storage *this, const String *fileExp, StorageOpenWritePar
MEM_CONTEXT_NEW_BEGIN("StorageFileWrite")
{
String *fileName = storagePathNP(this, fileExp);
int fileHandle;
// Open the file and handle errors
fileHandle = open(
strPtr(fileName), O_CREAT | O_TRUNC | O_WRONLY, param.mode == 0 ? this->modeFile : param.mode);
if (fileHandle == -1)
THROW_SYS_ERROR(FileOpenError, "unable to open '%s' for write", strPtr(fileName));
// Create the storage file and data
StorageFileDataPosix *data = NULL;
MEM_CONTEXT_NEW_BEGIN("StorageFileReadDataPosix")
{
data = memNew(sizeof(StorageFileDataPosix));
data->memContext = MEM_CONTEXT_NEW();
data->handle = fileHandle;
}
MEM_CONTEXT_NEW_END();
result = storageFileNew(this, fileName, storageFileTypeWrite, data);
// Call driver function
result = storageFileNew(
this, fileName, storageFileTypeWrite,
storageDriverPosixOpenWrite(fileName, param.mode != 0 ? param.mode : this->modeFile));
}
MEM_CONTEXT_NEW_END();
@ -451,19 +309,9 @@ storagePathCreate(const Storage *this, const String *pathExp, StoragePathCreateP
// Build the path
String *path = storagePathNP(this, pathExp);
// Attempt to create the directory
if (mkdir(strPtr(path), param.mode != 0 ? param.mode : STORAGE_PATH_MODE_DEFAULT) == -1)
{
// If the parent path does not exist then create it if allowed
if (errno == ENOENT && !param.noParentCreate)
{
storagePathCreate(this, strPath(path), param);
storagePathCreate(this, path, param);
}
// Ignore path exists if allowed
else if (errno != EEXIST || param.errorOnExists)
THROW_SYS_ERROR(PathCreateError, "unable to create path '%s'", strPtr(path));
}
// Call driver function
storageDriverPosixPathCreate(
path, param.errorOnExists, param.noParentCreate, param.mode != 0 ? param.mode : this->modePath);
}
MEM_CONTEXT_TEMP_END();
}
@ -476,81 +324,28 @@ storagePut(const StorageFile *file, const Buffer *buffer)
{
// Write data if buffer is not null. Otherwise, an empty file is expected.
if (buffer != NULL)
{
TRY_BEGIN()
{
if (write(STORAGE_DATA(file)->handle, bufPtr(buffer), bufSize(buffer)) != (ssize_t)bufSize(buffer))
THROW_SYS_ERROR(FileWriteError, "unable to write '%s'", strPtr(storageFileName(file)));
}
FINALLY()
{
close(STORAGE_DATA(file)->handle);
storageFileFree(file);
}
TRY_END();
}
storageDriverPosixPut(file, buffer);
}
/***********************************************************************************************************************************
Remove a file
***********************************************************************************************************************************/
void
storageRemove(const Storage *this, const String *pathExp, StorageRemoveParam param)
storageRemove(const Storage *this, const String *fileExp, StorageRemoveParam param)
{
ASSERT_STORAGE_ALLOWS_WRITE();
MEM_CONTEXT_TEMP_BEGIN()
{
// Build the path
String *file = storagePathNP(this, pathExp);
String *file = storagePathNP(this, fileExp);
// Attempt to unlink the file
if (unlink(strPtr(file)) == -1)
{
if (param.errorOnMissing || errno != ENOENT)
THROW_SYS_ERROR(FileRemoveError, "unable to remove '%s'", strPtr(file));
}
// Call driver function
storageDriverPosixRemove(file, param.errorOnMissing);
}
MEM_CONTEXT_TEMP_END();
}
/***********************************************************************************************************************************
Stat a file
***********************************************************************************************************************************/
StorageStat *
storageStat(const Storage *this, const String *pathExp, StorageStatParam param)
{
StorageStat *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Build the path
String *path = storagePathNP(this, pathExp);
// Attempt to stat the file
struct stat statFile;
if (stat(strPtr(path), &statFile) == -1)
{
if (errno != ENOENT || !param.ignoreMissing)
THROW_SYS_ERROR(FileOpenError, "unable to stat '%s'", strPtr(path));
}
// Else set stats
else
{
memContextSwitch(MEM_CONTEXT_OLD());
result = memNew(sizeof(StorageStat));
result->mode = statFile.st_mode & 0777;
memContextSwitch(MEM_CONTEXT_TEMP());
}
}
MEM_CONTEXT_TEMP_END();
return result;
}
/***********************************************************************************************************************************
Free storage
***********************************************************************************************************************************/

View File

@ -164,33 +164,13 @@ typedef struct StorageRemoveParam
bool errorOnMissing;
} StorageRemoveParam;
#define storageRemoveP(this, pathExp, ...) \
storageRemove(this, pathExp, (StorageRemoveParam){__VA_ARGS__})
#define storageRemoveNP(this, pathExp) \
storageRemove(this, pathExp, (StorageRemoveParam){0})
#define storageRemoveP(this, fileExp, ...) \
storageRemove(this, fileExp, (StorageRemoveParam){__VA_ARGS__})
#define storageRemoveNP(this, fileExp) \
storageRemove(this, fileExp, (StorageRemoveParam){0})
void storageRemove(const Storage *this, const String *fileExp, StorageRemoveParam param);
/***********************************************************************************************************************************
storageStat
***********************************************************************************************************************************/
typedef struct StorageStat
{
mode_t mode;
} StorageStat;
typedef struct StorageStatParam
{
bool ignoreMissing;
} StorageStatParam;
#define storageStatP(this, pathExp, ...) \
storageStat(this, pathExp, (StorageStatParam){__VA_ARGS__})
#define storageStatNP(this, pathExp) \
storageStat(this, pathExp, (StorageStatParam){0})
StorageStat *storageStat(const Storage *this, const String *pathExp, StorageStatParam param);
/***********************************************************************************************************************************
storageFree
***********************************************************************************************************************************/
@ -199,4 +179,9 @@ storageFree
void storageFree(const Storage *this);
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
size_t storageBufferSize(const Storage *this);
#endif

View File

@ -626,11 +626,12 @@ my $oTestDef =
},
{
&TESTDEF_NAME => 'storage',
&TESTDEF_TOTAL => 10,
&TESTDEF_TOTAL => 9,
&TESTDEF_C => true,
&TESTDEF_COVERAGE =>
{
'storage/driver/posix' => TESTDEF_COVERAGE_FULL,
'storage/storage' => TESTDEF_COVERAGE_FULL,
},
},

View File

@ -4,6 +4,21 @@ Test Storage Manager
#include "common/time.h"
#include "storage/file.h"
/***********************************************************************************************************************************
Get the mode of a file on local storage
***********************************************************************************************************************************/
mode_t
storageStatMode(const String *path)
{
// Attempt to stat the file
struct stat statFile;
if (stat(strPtr(path), &statFile) == -1) // {uncovered - error should not happen}
THROW_SYS_ERROR(FileOpenError, "unable to stat '%s'", strPtr(path)); // {uncovered+}
return statFile.st_mode & 0777;
}
/***********************************************************************************************************************************
Test function for path expression
***********************************************************************************************************************************/
@ -183,14 +198,14 @@ testRun()
if (testBegin("storagePathCreate()"))
{
TEST_RESULT_VOID(storagePathCreateNP(storageTest, strNew("sub1")), "create sub1");
TEST_RESULT_INT(storageStatNP(storageTest, strNew("sub1"))->mode, 0750, "check sub1 dir mode");
TEST_RESULT_INT(storageStatMode(storagePath(storageTest, strNew("sub1"))), 0750, "check sub1 dir mode");
TEST_RESULT_VOID(storagePathCreateNP(storageTest, strNew("sub1")), "create sub1 again");
TEST_ERROR(
storagePathCreateP(storageTest, strNew("sub1"), .errorOnExists = true), PathCreateError,
strPtr(strNewFmt("unable to create path '%s/sub1': [17] File exists", testPath())));
TEST_RESULT_VOID(storagePathCreateP(storageTest, strNew("sub2"), .mode = 0777), "create sub2 with custom mode");
TEST_RESULT_INT(storageStatNP(storageTest, strNew("sub2"))->mode, 0777, "check sub2 dir mode");
TEST_RESULT_INT(storageStatMode(storagePath(storageTest, strNew("sub2"))), 0777, "check sub2 dir mode");
TEST_ERROR(
storagePathCreateP(storageTest, strNew("sub3/sub4"), .noParentCreate = true), PathCreateError,
@ -230,14 +245,14 @@ testRun()
String *fileName = strNewFmt("%s/testfile", testPath());
TEST_ASSIGN(file, storageOpenWriteNP(storageTest, fileName), "open file for write (defaults)");
TEST_RESULT_INT(storageStatNP(storageTest, fileName)->mode, 0640, "check dir mode");
TEST_RESULT_INT(storageStatMode(storagePath(storageTest, fileName)), 0640, "check file mode");
close(STORAGE_DATA(file)->handle);
storageRemoveP(storageTest, fileName, .errorOnMissing = true);
// -------------------------------------------------------------------------------------------------------------------------
TEST_ASSIGN(file, storageOpenWriteP(storageTest, fileName, .mode = 0777), "open file for write (custom)");
TEST_RESULT_INT(storageStatNP(storageTest, fileName)->mode, 0777, "check file mode");
TEST_RESULT_INT(storageStatMode(storagePath(storageTest, fileName)), 0777, "check file mode");
close(STORAGE_DATA(file)->handle);
storageRemoveP(storageTest, fileName, .errorOnMissing = true);
@ -316,25 +331,4 @@ testRun()
storageRemoveNP(storageTest, fileNoPerm), FileRemoveError,
strPtr(strNewFmt("unable to remove '%s': [13] Permission denied", strPtr(fileNoPerm))));
}
// *****************************************************************************************************************************
if (testBegin("storageStat()"))
{
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_VOID(storagePathCreateP(storageTest, strNew("dir"), .mode = 0777), "create dir with custom mode");
TEST_RESULT_INT(storageStatNP(storageTest, strNew("dir"))->mode, 0777, "check dir mode");
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(
storageStatNP(storageTest, strNew("missing")), FileOpenError,
strPtr(strNewFmt("unable to stat '%s/missing': [2] No such file or directory", testPath())));
TEST_RESULT_PTR(storageStatP(storageTest, strNew("missing"), .ignoreMissing = true), NULL, "ignore missing file");
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(
storageStatNP(storageTest, fileNoPerm), FileOpenError,
strPtr(strNewFmt("unable to stat '%s': [13] Permission denied", strPtr(fileNoPerm))));
TEST_RESULT_INT(system(strPtr(strNewFmt("sudo rm -rf %s", strPtr(strPath(fileNoPerm))))), 0, "remove no perm dir");
}
}