You've already forked pgbackrest
							
							
				mirror of
				https://github.com/pgbackrest/pgbackrest.git
				synced 2025-10-30 23:37:45 +02:00 
			
		
		
		
	Abstract Posix storage driver code into a separate module.
This commit is contained in:
		| @@ -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> | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										287
									
								
								src/storage/driver/posix.c
									
									
									
									
									
										Normal 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)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								src/storage/driver/posix.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/storage/driver/posix.h
									
									
									
									
									
										Normal 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 | ||||
| @@ -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 | ||||
| ***********************************************************************************************************************************/ | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|                     }, | ||||
|                 }, | ||||
|   | ||||
| @@ -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"); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user