diff --git a/lib/blockstorestorage/longtail_blockstorestorage.c b/lib/blockstorestorage/longtail_blockstorestorage.c index b9881a6c..dfe6ddef 100644 --- a/lib/blockstorestorage/longtail_blockstorestorage.c +++ b/lib/blockstorestorage/longtail_blockstorestorage.c @@ -776,6 +776,25 @@ static int BlockStoreStorageAPI_OpenWriteFile( return ENOTSUP; } +static int BlockStoreStorageAPI_OpenAppendFile( + struct Longtail_StorageAPI* storage_api, + const char* path, + Longtail_StorageAPI_HOpenFile* out_open_file) +{ + MAKE_LOG_CONTEXT_FIELDS(ctx) + LONGTAIL_LOGFIELD(storage_api, "%p"), + LONGTAIL_LOGFIELD(path, "%s"), + LONGTAIL_LOGFIELD(out_open_file, "%p") + MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_OFF) + + LONGTAIL_VALIDATE_INPUT(ctx, storage_api != 0, return 0) + LONGTAIL_VALIDATE_INPUT(ctx, path != 0, return 0) + LONGTAIL_VALIDATE_INPUT(ctx, out_open_file != 0, return 0) + + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Unsupported, failed with %d", ENOTSUP) + return ENOTSUP; +} + static int BlockStoreStorageAPI_Write( struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, @@ -1407,7 +1426,8 @@ static int BlockStoreStorageAPI_Init( BlockStoreStorageAPI_UnlockFile, BlockStoreStorageAPI_GetParentPath, BlockStoreStorageAPI_MapFile, - BlockStoreStorageAPI_UnmapFile); + BlockStoreStorageAPI_UnmapFile, + BlockStoreStorageAPI_OpenAppendFile); struct BlockStoreStorageAPI* block_store_fs = (struct BlockStoreStorageAPI*)api; diff --git a/lib/concurrentchunkwrite/longtail_concurrentchunkwrite.c b/lib/concurrentchunkwrite/longtail_concurrentchunkwrite.c index b1a20396..8dc8a80f 100644 --- a/lib/concurrentchunkwrite/longtail_concurrentchunkwrite.c +++ b/lib/concurrentchunkwrite/longtail_concurrentchunkwrite.c @@ -139,9 +139,9 @@ static uint64_t ConcurrentChunkWriteAPI_GetPathHash(const char* path) struct OpenFileEntry { - uint32_t m_TotalWriteCount; - TLongtail_Atomic64 m_PendingWriteCount; Longtail_StorageAPI_HOpenFile m_FileHandle; + const char* m_FullPath; + TLongtail_Atomic32 m_OpenCount; }; struct PathLookup @@ -162,8 +162,7 @@ struct ConcurrentChunkWriteAPI struct Longtail_StorageAPI* m_StorageAPI; HLongtail_RWLock m_RWLock; struct PathLookup* m_PathHashToOpenFile; - struct HandleLookup* m_FileHandleToOpenFile; - struct OpenFileEntry* m_OpenFileEntries; + struct OpenFileEntry** m_OpenFileEntries; char* m_BasePath; }; @@ -199,79 +198,98 @@ static int ConcurrentChunkWriteAPI_Open( if (i != -1) { intptr_t open_file_index = api->m_PathHashToOpenFile[i].value; - const struct OpenFileEntry* open_file_entry = &api->m_OpenFileEntries[open_file_index]; - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_TotalWriteCount == chunk_write_count, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_PendingWriteCount > 0, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle != 0, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); - *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)open_file_entry->m_FileHandle; - Longtail_UnlockRWLockRead(api->m_RWLock); - return 0; + struct OpenFileEntry* open_file_entry = api->m_OpenFileEntries[open_file_index]; + if (open_file_entry->m_FileHandle != 0) + { + LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle != 0, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); + *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)open_file_entry; + Longtail_AtomicAdd32(&open_file_entry->m_OpenCount, 1); + Longtail_UnlockRWLockRead(api->m_RWLock); + return 0; + } } Longtail_UnlockRWLockRead(api->m_RWLock); } - char* full_asset_path = api->m_StorageAPI->ConcatPath(api->m_StorageAPI, api->m_BasePath, path); - if (full_asset_path == 0) - { - LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "ConcatPath() failed with %d", ENOMEM) - return ENOMEM; - } - - int err = EnsureParentPathExists(api->m_StorageAPI, full_asset_path); - if (err != 0) + Longtail_LockRWLockWrite(api->m_RWLock); + ptrdiff_t tmp; + intptr_t i = api->m_PathHashToOpenFile ? hmgeti_ts(api->m_PathHashToOpenFile, path_hash, tmp) : -1; + if (i == -1) { - Longtail_Free(full_asset_path); - LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "EnsureParentPathExists() failed with %d", err) - return err; - } + Longtail_UnlockRWLockWrite(api->m_RWLock); - { - Longtail_LockRWLockWrite(api->m_RWLock); - ptrdiff_t tmp; - intptr_t i = api->m_PathHashToOpenFile ? hmgeti_ts(api->m_PathHashToOpenFile, path_hash, tmp) : -1; - if (i != -1) + char* full_asset_path = api->m_StorageAPI->ConcatPath(api->m_StorageAPI, api->m_BasePath, path); + if (full_asset_path == 0) { - intptr_t open_file_index = api->m_PathHashToOpenFile[i].value; - const struct OpenFileEntry* open_file_entry = &api->m_OpenFileEntries[open_file_index]; - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_TotalWriteCount == chunk_write_count, Longtail_UnlockRWLockWrite(api->m_RWLock); Longtail_Free(full_asset_path); return EINVAL); - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_PendingWriteCount > 0, Longtail_UnlockRWLockWrite(api->m_RWLock); Longtail_Free(full_asset_path); return EINVAL); - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle != 0, Longtail_UnlockRWLockWrite(api->m_RWLock); return EINVAL); - *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)open_file_entry->m_FileHandle; - Longtail_UnlockRWLockWrite(api->m_RWLock); - Longtail_Free(full_asset_path); - return 0; + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "ConcatPath() failed with %d", ENOMEM) + return ENOMEM; } - Longtail_StorageAPI_HOpenFile r; - err = api->m_StorageAPI->OpenWriteFile(api->m_StorageAPI, full_asset_path, 0, &r); + int err = EnsureParentPathExists(api->m_StorageAPI, full_asset_path); if (err != 0) { - Longtail_UnlockRWLockWrite(api->m_RWLock); Longtail_Free(full_asset_path); - LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "OpenWriteFile() failed with %d", err) + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "EnsureParentPathExists() failed with %d", err) return err; } - if (chunk_write_count == 0) + + Longtail_LockRWLockWrite(api->m_RWLock); + i = api->m_PathHashToOpenFile ? hmgeti_ts(api->m_PathHashToOpenFile, path_hash, tmp) : -1; + if (i == -1) { + struct OpenFileEntry* open_file_entry = (struct OpenFileEntry*)Longtail_Alloc("ConcurrentChunkWriteAPI_Open", sizeof(struct OpenFileEntry)); + if (open_file_entry == 0) + { + err = ENOMEM; + Longtail_UnlockRWLockWrite(api->m_RWLock); + Longtail_Free(full_asset_path); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "OpenWriteFile() failed with %d", err) + return err; + } + open_file_entry->m_FileHandle = 0; + open_file_entry->m_FullPath = full_asset_path; + open_file_entry->m_OpenCount = 1; + full_asset_path = 0; + ptrdiff_t entry_index = arrlen(api->m_OpenFileEntries); + arrput(api->m_OpenFileEntries, open_file_entry); + hmput(api->m_PathHashToOpenFile, path_hash, entry_index); + + int err = api->m_StorageAPI->OpenWriteFile(api->m_StorageAPI, open_file_entry->m_FullPath, 0, &open_file_entry->m_FileHandle); + if (err != 0) + { + Longtail_UnlockRWLockWrite(api->m_RWLock); + Longtail_Free((void*)open_file_entry->m_FullPath); + Longtail_Free((void*)open_file_entry); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "OpenAppendFile() failed with %d", err) + return err; + } + *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)open_file_entry; Longtail_UnlockRWLockWrite(api->m_RWLock); - // Empty file, close immediately - api->m_StorageAPI->CloseFile(api->m_StorageAPI, r); - Longtail_Free(full_asset_path); - *out_open_file = 0; return 0; } - struct OpenFileEntry entry; - entry.m_FileHandle = r; - entry.m_TotalWriteCount = chunk_write_count; - entry.m_PendingWriteCount = (int64_t)chunk_write_count; - ptrdiff_t entry_index = arrlen(api->m_OpenFileEntries); - arrput(api->m_OpenFileEntries, entry); - hmput(api->m_PathHashToOpenFile, path_hash, entry_index); - hmput(api->m_FileHandleToOpenFile, r, entry_index); - *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)r; + Longtail_Free(full_asset_path); + } + + intptr_t open_file_index = api->m_PathHashToOpenFile[i].value; + struct OpenFileEntry* open_file_entry = api->m_OpenFileEntries[open_file_index]; + if (open_file_entry->m_FileHandle != 0) + { + LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle != 0, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); + *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)open_file_entry; + Longtail_AtomicAdd32(&open_file_entry->m_OpenCount, 1); Longtail_UnlockRWLockWrite(api->m_RWLock); + return 0; } - Longtail_Free(full_asset_path); + int err = api->m_StorageAPI->OpenAppendFile(api->m_StorageAPI, open_file_entry->m_FullPath, &open_file_entry->m_FileHandle); + if (err != 0) + { + Longtail_UnlockRWLockWrite(api->m_RWLock); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "OpenAppendFile() failed with %d", err) + return err; + } + Longtail_AtomicAdd32(&open_file_entry->m_OpenCount, 1); + *out_open_file = (Longtail_ConcurrentChunkWriteAPI_HOpenFile)open_file_entry; + Longtail_UnlockRWLockWrite(api->m_RWLock); return 0; } @@ -299,64 +317,66 @@ static int ConcurrentChunkWriteAPI_Write( #endif // defined(LONGTAIL_ASSERTS) LONGTAIL_VALIDATE_INPUT(ctx, concurrent_file_write_api != 0, return EINVAL); - LONGTAIL_VALIDATE_INPUT(ctx, (uintptr_t)in_open_file != 0, return EINVAL); + LONGTAIL_VALIDATE_INPUT(ctx, in_open_file != 0, return EINVAL); LONGTAIL_VALIDATE_INPUT(ctx, size != 0, return EINVAL); LONGTAIL_VALIDATE_INPUT(ctx, input != 0, return EINVAL); struct ConcurrentChunkWriteAPI* api = (struct ConcurrentChunkWriteAPI*)concurrent_file_write_api; - Longtail_StorageAPI_HOpenFile file_handle = (Longtail_StorageAPI_HOpenFile)in_open_file; + struct OpenFileEntry* open_file_entry = (struct OpenFileEntry*)in_open_file; + LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_OpenCount > 0, return EINVAL) + LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle != 0, return EINVAL) + LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FullPath != 0, return EINVAL) - int err = api->m_StorageAPI->Write(api->m_StorageAPI, file_handle, offset, size, input); + int err = api->m_StorageAPI->Write(api->m_StorageAPI, open_file_entry->m_FileHandle, offset, size, input); if (err) { - { - Longtail_LockRWLockWrite(api->m_RWLock); - ptrdiff_t open_file_index = 0; - struct OpenFileEntry* open_file_entry = &api->m_OpenFileEntries[open_file_index]; - open_file_entry->m_FileHandle = 0; - hmdel(api->m_FileHandleToOpenFile, file_handle); - Longtail_UnlockRWLockWrite(api->m_RWLock); - } - api->m_StorageAPI->CloseFile(api->m_StorageAPI, file_handle); return err; } + return 0; +} + +static int ConcurrentChunkWriteAPI_Close( + struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, + Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file) +{ +#if defined(LONGTAIL_ASSERTS) + MAKE_LOG_CONTEXT_FIELDS(ctx) + LONGTAIL_LOGFIELD(concurrent_file_write_api, "%p"), + MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) +#else + struct Longtail_LogContextFmt_Private* ctx = 0; +#endif // defined(LONGTAIL_ASSERTS) + + LONGTAIL_VALIDATE_INPUT(ctx, concurrent_file_write_api != 0, return EINVAL); + LONGTAIL_VALIDATE_INPUT(ctx, in_open_file != 0, return EINVAL); + + struct ConcurrentChunkWriteAPI* api = (struct ConcurrentChunkWriteAPI*)concurrent_file_write_api; + struct OpenFileEntry* open_file_entry = (struct OpenFileEntry*)in_open_file; - ptrdiff_t open_file_index = 0; - int close_on_write = 0; { Longtail_LockRWLockRead(api->m_RWLock); - ptrdiff_t tmp; - intptr_t i = api->m_PathHashToOpenFile ? hmgeti_ts(api->m_FileHandleToOpenFile, file_handle, tmp) : -1; - if (i == -1) + int32_t OpenCount = Longtail_AtomicAdd32(&open_file_entry->m_OpenCount, -1); + if (OpenCount > 0) { Longtail_UnlockRWLockRead(api->m_RWLock); - LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "ConcurrentChunkWriteAPI_Write() file not open, error %d", EINVAL) - return EINVAL; + return 0; } - open_file_index = api->m_FileHandleToOpenFile[i].value; - struct OpenFileEntry* open_file_entry = &api->m_OpenFileEntries[open_file_index]; - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_PendingWriteCount >= (int64_t)chunk_count, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle == file_handle, Longtail_UnlockRWLockRead(api->m_RWLock); return EINVAL); - int64_t pending_count = Longtail_AtomicAdd64(&open_file_entry->m_PendingWriteCount, -(int64_t)chunk_count); Longtail_UnlockRWLockRead(api->m_RWLock); - close_on_write = (pending_count == 0); - *out_chunks_remaining = (uint32_t)pending_count; } - if (close_on_write) { + Longtail_LockRWLockWrite(api->m_RWLock); + if (open_file_entry->m_OpenCount == 0) { - Longtail_LockRWLockWrite(api->m_RWLock); - struct OpenFileEntry* open_file_entry = &api->m_OpenFileEntries[open_file_index]; - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_PendingWriteCount == 0, Longtail_UnlockRWLockWrite(api->m_RWLock); return EINVAL); - LONGTAIL_FATAL_ASSERT(ctx, open_file_entry->m_FileHandle == file_handle, Longtail_UnlockRWLockWrite(api->m_RWLock); return EINVAL); - open_file_entry->m_FileHandle = 0; - hmdel(api->m_FileHandleToOpenFile, file_handle); - Longtail_UnlockRWLockWrite(api->m_RWLock); + if (open_file_entry->m_FileHandle) + { + api->m_StorageAPI->CloseFile(api->m_StorageAPI, open_file_entry->m_FileHandle); + open_file_entry->m_FileHandle = 0; + } } - api->m_StorageAPI->CloseFile(api->m_StorageAPI, file_handle); + Longtail_UnlockRWLockWrite(api->m_RWLock); } - return err; + return 0; } static int ConcurrentChunkWriteAPI_Flush( @@ -376,14 +396,16 @@ static int ConcurrentChunkWriteAPI_Flush( ptrdiff_t entry_count = arrlen(api->m_OpenFileEntries); for (ptrdiff_t i = 0; i < entry_count; ++i) { - Longtail_StorageAPI_HOpenFile file_handle = api->m_OpenFileEntries[i].m_FileHandle; + struct OpenFileEntry* open_file_entry = api->m_OpenFileEntries[i]; + Longtail_StorageAPI_HOpenFile file_handle = open_file_entry->m_FileHandle; if (file_handle) { api->m_StorageAPI->CloseFile(api->m_StorageAPI, file_handle); } + Longtail_Free((void*)open_file_entry->m_FullPath); + Longtail_Free((void*)open_file_entry); } hmfree(api->m_PathHashToOpenFile); - hmfree(api->m_FileHandleToOpenFile); arrfree(api->m_OpenFileEntries); Longtail_UnlockRWLockWrite(api->m_RWLock); return 0; @@ -441,7 +463,7 @@ static void ConcurrentChunkWriteAPI_Dispose(struct Longtail_API* concurrent_file LONGTAIL_LOGFIELD(concurrent_file_write_api, "%p") MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) - LONGTAIL_FATAL_ASSERT(ctx, concurrent_file_write_api != 0, return); + LONGTAIL_FATAL_ASSERT(ctx, concurrent_file_write_api != 0, return); struct ConcurrentChunkWriteAPI* api = (struct ConcurrentChunkWriteAPI*)concurrent_file_write_api; int err = ConcurrentChunkWriteAPI_Flush(&api->m_ConcurrentChunkWriteAPI); @@ -474,6 +496,7 @@ static int ConcurrentChunkWriteAPI_Init( ConcurrentChunkWriteAPI_Dispose, ConcurrentChunkWriteAPI_CreateDir, ConcurrentChunkWriteAPI_Open, + ConcurrentChunkWriteAPI_Close, ConcurrentChunkWriteAPI_Write, ConcurrentChunkWriteAPI_Flush); @@ -481,7 +504,6 @@ static int ConcurrentChunkWriteAPI_Init( storage_api->m_StorageAPI = storageAPI; Longtail_CreateRWLock(&storage_api[1], &storage_api->m_RWLock); storage_api->m_PathHashToOpenFile = 0; - storage_api->m_FileHandleToOpenFile = 0; storage_api->m_OpenFileEntries = 0; storage_api->m_BasePath = Longtail_Strdup(base_path); diff --git a/lib/filestorage/longtail_filestorage.c b/lib/filestorage/longtail_filestorage.c index bcd441c0..b747d5e1 100644 --- a/lib/filestorage/longtail_filestorage.c +++ b/lib/filestorage/longtail_filestorage.c @@ -154,6 +154,35 @@ static int FSStorageAPI_OpenWriteFile( return 0; } +static int FSStorageAPI_OpenAppendFile( + struct Longtail_StorageAPI* storage_api, + const char* path, + Longtail_StorageAPI_HOpenFile* out_open_file) +{ +#if defined(LONGTAIL_ASSERTS) + MAKE_LOG_CONTEXT_FIELDS(ctx) + LONGTAIL_LOGFIELD(storage_api, "%p"), + LONGTAIL_LOGFIELD(path, "%s"), + LONGTAIL_LOGFIELD(out_open_file, "%p") + MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) +#else + struct Longtail_LogContextFmt_Private* ctx = 0; +#endif // defined(LONGTAIL_ASSERTS) + + LONGTAIL_VALIDATE_INPUT(ctx, storage_api != 0, return EINVAL); + LONGTAIL_VALIDATE_INPUT(ctx, path != 0, return EINVAL); + LONGTAIL_VALIDATE_INPUT(ctx, out_open_file != 0, return EINVAL); + HLongtail_OpenFile r; + int err = Longtail_OpenAppendFile(path, &r); + if (err) + { + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "Longtail_OpenWriteFile() failed with %d", err) + return err; + } + *out_open_file = (Longtail_StorageAPI_HOpenFile)r; + return 0; +} + static int FSStorageAPI_Write( struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, @@ -667,7 +696,8 @@ static int FSStorageAPI_Init( FSStorageAPI_UnlockFile, FSStorageAPI_GetParentPath, FSStorageAPI_MapFile, - FSStorageAPI_UnmapFile); + FSStorageAPI_UnmapFile, + FSStorageAPI_OpenAppendFile); *out_storage_api = api; return 0; } diff --git a/lib/longtail_platform.c b/lib/longtail_platform.c index e2eb39e8..4241450d 100644 --- a/lib/longtail_platform.c +++ b/lib/longtail_platform.c @@ -905,12 +905,12 @@ int Longtail_OpenReadFile(const char* path, HLongtail_OpenFile* out_read_file) return 0; } -DWORD NativeOpenWriteFileWithRetry(wchar_t* long_path, DWORD create_disposition, HANDLE* out_handle) +DWORD NativeOpenWriteFileWithRetry(wchar_t* long_path, DWORD desired_access, DWORD create_disposition, HANDLE* out_handle) { int retry_count = 10; while (1) { - HANDLE handle = CreateFileW(long_path, GENERIC_READ | GENERIC_WRITE, 0, 0, create_disposition, 0, 0); + HANDLE handle = CreateFileW(long_path, desired_access, 0, 0, create_disposition, 0, 0); if (handle == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); @@ -937,7 +937,7 @@ int Longtail_OpenWriteFile(const char* path, uint64_t initial_size, HLongtail_Op wchar_t long_path_buffer[512]; wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); HANDLE handle; - DWORD error = NativeOpenWriteFileWithRetry(long_path, initial_size == 0 ? CREATE_ALWAYS : OPEN_ALWAYS, &handle); + DWORD error = NativeOpenWriteFileWithRetry(long_path, GENERIC_WRITE, initial_size == 0 ? CREATE_ALWAYS : OPEN_ALWAYS, &handle); if (long_path != long_path_buffer) { Longtail_Free(long_path); @@ -969,6 +969,25 @@ int Longtail_OpenWriteFile(const char* path, uint64_t initial_size, HLongtail_Op return 0; } +int Longtail_OpenAppendFile(const char* path, HLongtail_OpenFile* out_write_file) +{ + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); + HANDLE handle; + DWORD error = NativeOpenWriteFileWithRetry(long_path, GENERIC_WRITE, OPEN_ALWAYS, &handle); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } + if (error != ERROR_SUCCESS) + { + return Win32ErrorToErrno(error); + } + + *out_write_file = (HLongtail_OpenFile)handle; + return 0; +} + int Longtail_SetFileSize(HLongtail_OpenFile handle, uint64_t length) { HANDLE h = (HANDLE)(handle); @@ -2259,7 +2278,7 @@ int Longtail_OpenWriteFile(const char* path, uint64_t initial_size, HLongtail_Op int e = errno; return e; } - if (initial_size > 0) + if (initial_size > 0) { int err = ftruncate64(fileno(f), (off64_t)initial_size); if (err != 0) @@ -2273,6 +2292,18 @@ int Longtail_OpenWriteFile(const char* path, uint64_t initial_size, HLongtail_Op return 0; } +int Longtail_OpenAppendFile(const char* path, HLongtail_OpenFile* out_write_file) +{ + FILE* f = fopen(path, "ab"); + if (!f) + { + int e = errno; + return e; + } + *out_write_file = (HLongtail_OpenFile)f; + return 0; +} + int Longtail_SetFileSize(HLongtail_OpenFile handle, uint64_t length) { FILE* f = (FILE*)handle; diff --git a/lib/longtail_platform.h b/lib/longtail_platform.h index e9848bb4..493a3f8b 100644 --- a/lib/longtail_platform.h +++ b/lib/longtail_platform.h @@ -75,6 +75,7 @@ typedef struct Longtail_OpenFile_private* HLongtail_OpenFile; int Longtail_OpenReadFile(const char* path, HLongtail_OpenFile* out_read_file); int Longtail_OpenWriteFile(const char* path, uint64_t initial_size, HLongtail_OpenFile* out_write_file); +int Longtail_OpenAppendFile(const char* path, HLongtail_OpenFile* out_write_file); int Longtail_SetFileSize(HLongtail_OpenFile handle, uint64_t length); int Longtail_SetFilePermissions(const char* path, uint16_t permissions); int Longtail_GetFilePermissions(const char* path, uint16_t* out_permissions); diff --git a/lib/memstorage/longtail_memstorage.c b/lib/memstorage/longtail_memstorage.c index 07b7a4fe..ccbec20d 100644 --- a/lib/memstorage/longtail_memstorage.c +++ b/lib/memstorage/longtail_memstorage.c @@ -330,6 +330,76 @@ static int InMemStorageAPI_OpenWriteFile(struct Longtail_StorageAPI* storage_api return 0; } +static int InMemStorageAPI_OpenAppendFile(struct Longtail_StorageAPI* storage_api, const char* path, Longtail_StorageAPI_HOpenFile* out_open_file) +{ +#if defined(LONGTAIL_ASSERTS) + MAKE_LOG_CONTEXT_FIELDS(ctx) + LONGTAIL_LOGFIELD(storage_api, "%p"), + LONGTAIL_LOGFIELD(path, "%s"), + LONGTAIL_LOGFIELD(out_open_file, "%p") + MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) +#else + struct Longtail_LogContextFmt_Private* ctx = 0; +#endif // defined(LONGTAIL_ASSERTS) + + LONGTAIL_VALIDATE_INPUT(ctx, storage_api != 0, return EINVAL); + LONGTAIL_VALIDATE_INPUT(ctx, path != 0, return EINVAL); + LONGTAIL_VALIDATE_INPUT(ctx, out_open_file != 0, return EINVAL); + struct InMemStorageAPI* instance = (struct InMemStorageAPI*)storage_api; + uint32_t path_hash = InMemStorageAPI_GetPathHash(path); + uint32_t parent_path_hash = InMemStorageAPI_GetParentPathHash(path); + Longtail_LockSpinLock(instance->m_SpinLock); + if (parent_path_hash != 0 && hmgeti(instance->m_PathHashToContent, parent_path_hash) == -1) + { + Longtail_UnlockSpinLock(instance->m_SpinLock); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "File not found, failed with %d", ENOENT) + return ENOENT; + } + struct PathEntry* path_entry = 0; + intptr_t it = hmgeti(instance->m_PathHashToContent, path_hash); + if (it != -1) + { + path_entry = &instance->m_PathEntries[instance->m_PathHashToContent[it].value]; + if ((path_entry->m_Permissions & (Longtail_StorageAPI_OtherWriteAccess | Longtail_StorageAPI_GroupWriteAccess | Longtail_StorageAPI_UserWriteAccess)) == 0) + { + Longtail_UnlockSpinLock(instance->m_SpinLock); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "No permission to write, failed with %d", EACCES) + return EACCES; + } + if (path_entry->m_IsOpenWrite) + { + Longtail_UnlockSpinLock(instance->m_SpinLock); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "File already open for write, failed with %d", EPERM) + return EPERM; + } + + if (path_entry->m_IsOpenRead) + { + Longtail_UnlockSpinLock(instance->m_SpinLock); + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_INFO, "File already open for read, failed with %d", EPERM) + return EPERM; + } + path_entry->m_IsOpenWrite = 1; + } + else + { + ptrdiff_t entry_index = arrlen(instance->m_PathEntries); + arrsetlen(instance->m_PathEntries, (size_t)(entry_index + 1)); + path_entry = &instance->m_PathEntries[entry_index]; + path_entry->m_ParentHash = parent_path_hash; + path_entry->m_FileName = Longtail_Strdup(InMemStorageAPI_GetFileNamePart(path)); + path_entry->m_Content = 0; + path_entry->m_Permissions = 0644; + path_entry->m_IsOpenRead = 0; + path_entry->m_IsOpenWrite = 1; + arrsetcap(path_entry->m_Content, 16); + hmput(instance->m_PathHashToContent, path_hash, (uint32_t)entry_index); + } + Longtail_UnlockSpinLock(instance->m_SpinLock); + *out_open_file = (Longtail_StorageAPI_HOpenFile)(uintptr_t)path_hash; + return 0; +} + static int InMemStorageAPI_Write(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, uint64_t offset, uint64_t length, const void* input) { #if defined(LONGTAIL_ASSERTS) @@ -1204,7 +1274,8 @@ static int InMemStorageAPI_Init( InMemStorageAPI_UnlockFile, InMemStorageAPI_GetParentPath, InMemStorageAPI_MapFile, - InMemStorageAPI_UnmapFile); + InMemStorageAPI_UnmapFile, + InMemStorageAPI_OpenAppendFile); struct InMemStorageAPI* storage_api = (struct InMemStorageAPI*)api; diff --git a/src/longtail.c b/src/longtail.c index 4b900fb0..2aec2bfa 100644 --- a/src/longtail.c +++ b/src/longtail.c @@ -264,7 +264,8 @@ struct Longtail_StorageAPI* Longtail_MakeStorageAPI( Longtail_Storage_UnlockFileFunc unlock_file_func, Longtail_Storage_GetParentPathFunc get_parent_path_func, Longtail_Storage_MapFileFunc map_file_func, - Longtail_Storage_UnmapFileFunc unmap_file_func) + Longtail_Storage_UnmapFileFunc unmap_file_func, + Longtail_Storage_OpenAppendFileFunc open_append_file_func) { MAKE_LOG_CONTEXT_FIELDS(ctx) LONGTAIL_LOGFIELD(mem, "%p"), @@ -293,7 +294,8 @@ struct Longtail_StorageAPI* Longtail_MakeStorageAPI( LONGTAIL_LOGFIELD(unlock_file_func, "%p"), LONGTAIL_LOGFIELD(get_parent_path_func, "%p"), LONGTAIL_LOGFIELD(map_file_func, "%p"), - LONGTAIL_LOGFIELD(unmap_file_func, "%p") + LONGTAIL_LOGFIELD(unmap_file_func, "%p"), + LONGTAIL_LOGFIELD(open_append_file_func, "%p") MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) LONGTAIL_VALIDATE_INPUT(ctx, mem != 0, return 0) @@ -324,6 +326,7 @@ struct Longtail_StorageAPI* Longtail_MakeStorageAPI( api->GetParentPath = get_parent_path_func; api->MapFile = map_file_func; api->UnMapFile = unmap_file_func; + api->OpenAppendFile = open_append_file_func; return api; } @@ -352,6 +355,7 @@ int Longtail_Storage_UnlockFile(struct Longtail_StorageAPI* storage_api, Longtai char* Longtail_Storage_GetParentPath(struct Longtail_StorageAPI* storage_api, const char* path) { return storage_api->GetParentPath(storage_api, path); } int Longtail_Storage_MapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, uint64_t offset, uint64_t length, Longtail_StorageAPI_HFileMap* out_file_map, const void** out_data_ptr) { return storage_api->MapFile(storage_api, f, offset, length, out_file_map, out_data_ptr); } void Longtail_Storage_UnmapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HFileMap m) { storage_api->UnMapFile(storage_api, m); } +void Longtail_Storage_OpenAppendfile(struct Longtail_StorageAPI* storage_api, const char* path, Longtail_StorageAPI_HOpenFile* out_open_file) { storage_api->OpenAppendFile(storage_api, path, out_open_file); } ////////////// ConcurrentChunkWriteAPI @@ -365,6 +369,7 @@ LONGTAIL_EXPORT struct Longtail_ConcurrentChunkWriteAPI* Longtail_MakeConcurrent Longtail_DisposeFunc dispose_func, Longtail_ConcurrentChunkWrite_CreateDirFunc create_dir_func, Longtail_ConcurrentChunkWrite_OpenFunc open_func, + Longtail_ConcurrentChunkWrite_CloseFunc close_func, Longtail_ConcurrentChunkWrite_WriteFunc write_func, Longtail_ConcurrentChunkWrite_FlushFunc flush_func) { @@ -373,15 +378,17 @@ LONGTAIL_EXPORT struct Longtail_ConcurrentChunkWriteAPI* Longtail_MakeConcurrent LONGTAIL_LOGFIELD(dispose_func, "%p"), LONGTAIL_LOGFIELD(create_dir_func, "%p"), LONGTAIL_LOGFIELD(open_func, "%p"), + LONGTAIL_LOGFIELD(close_func, "%p"), LONGTAIL_LOGFIELD(write_func, "%p"), LONGTAIL_LOGFIELD(flush_func, "%p") - MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) + MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) LONGTAIL_VALIDATE_INPUT(ctx, mem != 0, return 0) struct Longtail_ConcurrentChunkWriteAPI* api = (struct Longtail_ConcurrentChunkWriteAPI*)mem; api->m_API.Dispose = dispose_func; api->CreateDir = create_dir_func; api->Open = open_func; + api->Close = close_func; api->Write = write_func; api->Flush = flush_func; return api; @@ -389,6 +396,7 @@ LONGTAIL_EXPORT struct Longtail_ConcurrentChunkWriteAPI* Longtail_MakeConcurrent int Longtail_ConcurrentChunkWrite_CreateDir(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, const char* path) { return concurrent_file_write_api->CreateDir(concurrent_file_write_api, path); } int Longtail_ConcurrentChunkWrite_Open(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, const char* path, uint32_t chunk_write_count, Longtail_ConcurrentChunkWriteAPI_HOpenFile* out_open_file) { return concurrent_file_write_api->Open(concurrent_file_write_api, path, chunk_write_count, out_open_file); } +void Longtail_ConcurrentChunkWrite_Close(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file) { concurrent_file_write_api->Close(concurrent_file_write_api, in_open_file); } int Longtail_ConcurrentChunkWrite_Write(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file, uint64_t offset, uint32_t size, uint32_t chunk_count, const void* input, uint32_t* out_chunks_remaining) { return concurrent_file_write_api->Write(concurrent_file_write_api, in_open_file, offset, size, chunk_count, input, out_chunks_remaining); } int Longtail_ConcurrentChunkWrite_Flush(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api) { return concurrent_file_write_api->Flush(concurrent_file_write_api); } @@ -8315,6 +8323,7 @@ static int WriteContentBlock2Job(void* context, uint32_t job_id, int detected_er { if (last_asset_index != asset_index) { + job->m_Context->m_ConcurrentChunkWriteApi->Close(job->m_Context->m_ConcurrentChunkWriteApi, asset_file_handle); asset_file_handle = 0; } } @@ -8341,6 +8350,7 @@ static int WriteContentBlock2Job(void* context, uint32_t job_id, int detected_er uint32_t* chunk_index_in_block_ptr = LongtailPrivate_LookupTable_Get(chunk_hash_to_chunk_index, chunk_hash); if (chunk_index_in_block_ptr == 0) { + job->m_Context->m_ConcurrentChunkWriteApi->Close(job->m_Context->m_ConcurrentChunkWriteApi, asset_file_handle); LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Failed to find chunk in block for `%s` with %d", asset_path, ENOENT) Longtail_Free(work_mem); SAFE_DISPOSE_STORED_BLOCK(job->m_StoredBlock); @@ -8372,6 +8382,7 @@ static int WriteContentBlock2Job(void* context, uint32_t job_id, int detected_er uint32_t* chunk_index_in_block_ptr_next = LongtailPrivate_LookupTable_Get(chunk_hash_to_chunk_index, chunk_hash_next); if (chunk_index_in_block_ptr_next == 0) { + job->m_Context->m_ConcurrentChunkWriteApi->Close(job->m_Context->m_ConcurrentChunkWriteApi, asset_file_handle); LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Failed to find chunk in block for `%s` with %d", asset_path, ENOENT) Longtail_Free(work_mem); SAFE_DISPOSE_STORED_BLOCK(job->m_StoredBlock); @@ -8396,6 +8407,7 @@ static int WriteContentBlock2Job(void* context, uint32_t job_id, int detected_er int err = job->m_Context->m_ConcurrentChunkWriteApi->Write(job->m_Context->m_ConcurrentChunkWriteApi, asset_file_handle, block_chunk_write_info->Offset, chunk_run_size, chunk_run_count, &block_data[chunk_offset_in_block], &pending_chunk_count); if (err) { + job->m_Context->m_ConcurrentChunkWriteApi->Close(job->m_Context->m_ConcurrentChunkWriteApi, asset_file_handle); LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Write() failed for `%s` with %d", asset_path, err) Longtail_Free(work_mem); SAFE_DISPOSE_STORED_BLOCK(job->m_StoredBlock); @@ -8409,6 +8421,12 @@ static int WriteContentBlock2Job(void* context, uint32_t job_id, int detected_er } block_write_chunk_info_index += chunk_run_count; } + if (asset_file_handle != 0) + { + job->m_Context->m_ConcurrentChunkWriteApi->Close(job->m_Context->m_ConcurrentChunkWriteApi, asset_file_handle); + asset_file_handle = 0; + } + Longtail_Free(work_mem); SAFE_DISPOSE_STORED_BLOCK(job->m_StoredBlock); LONGTAIL_MONTITOR_BLOCK_COMPLETE(job->m_Context->m_StoreIndex, job->m_BlockIndex, 0); diff --git a/src/longtail.h b/src/longtail.h index 745564b5..4c990ed1 100644 --- a/src/longtail.h +++ b/src/longtail.h @@ -359,6 +359,7 @@ typedef int (*Longtail_Storage_UnlockFileFunc)(struct Longtail_StorageAPI* stora typedef char* (*Longtail_Storage_GetParentPathFunc)(struct Longtail_StorageAPI* storage_api, const char* path); typedef int (*Longtail_Storage_MapFileFunc)(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, uint64_t offset, uint64_t length, Longtail_StorageAPI_HFileMap* out_file_map, const void** out_data_ptr); typedef void (*Longtail_Storage_UnmapFileFunc)(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HFileMap m); +typedef int (*Longtail_Storage_OpenAppendFileFunc)(struct Longtail_StorageAPI* storage_api, const char* path, Longtail_StorageAPI_HOpenFile* out_open_file); struct Longtail_StorageAPI { @@ -388,6 +389,7 @@ struct Longtail_StorageAPI Longtail_Storage_GetParentPathFunc GetParentPath; Longtail_Storage_MapFileFunc MapFile; Longtail_Storage_UnmapFileFunc UnMapFile; + Longtail_Storage_OpenAppendFileFunc OpenAppendFile; }; LONGTAIL_EXPORT uint64_t Longtail_GetStorageAPISize(); @@ -419,7 +421,8 @@ LONGTAIL_EXPORT struct Longtail_StorageAPI* Longtail_MakeStorageAPI( Longtail_Storage_UnlockFileFunc unlock_file_func, Longtail_Storage_GetParentPathFunc get_parent_path_func, Longtail_Storage_MapFileFunc map_file_func, - Longtail_Storage_UnmapFileFunc unmap_file_func); + Longtail_Storage_UnmapFileFunc unmap_file_func, + Longtail_Storage_OpenAppendFileFunc open_append_file_func); LONGTAIL_EXPORT int Longtail_Storage_OpenReadFile(struct Longtail_StorageAPI* storage_api, const char* path, Longtail_StorageAPI_HOpenFile* out_open_file); LONGTAIL_EXPORT int Longtail_Storage_GetSize(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, uint64_t* out_size); @@ -446,6 +449,7 @@ LONGTAIL_EXPORT int Longtail_Storage_UnlockFile(struct Longtail_StorageAPI* stor LONGTAIL_EXPORT char* Longtail_Storage_GetParentPath(struct Longtail_StorageAPI* storage_api, const char* path); LONGTAIL_EXPORT int Longtail_Storage_MapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, uint64_t offset, uint64_t length, Longtail_StorageAPI_HFileMap* out_file_map, const void** out_data_ptr); LONGTAIL_EXPORT void Longtail_Storage_UnmapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HFileMap m); +LONGTAIL_EXPORT void Longtail_Storage_OpenAppendfile(struct Longtail_StorageAPI* storage_api, const char* path, Longtail_StorageAPI_HOpenFile* out_open_file); ////////////// Longtail_ConcurrentChunkWriteAPI @@ -455,6 +459,7 @@ typedef struct Longtail_ConcurrentChunkWriteAPI_OpenFile* Longtail_ConcurrentChu typedef int (*Longtail_ConcurrentChunkWrite_CreateDirFunc)(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, const char* path); typedef int (*Longtail_ConcurrentChunkWrite_OpenFunc)(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, const char* path, uint32_t chunk_write_count, Longtail_ConcurrentChunkWriteAPI_HOpenFile* out_open_file); +typedef void (*Longtail_ConcurrentChunkWrite_CloseFunc)(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file); typedef int (*Longtail_ConcurrentChunkWrite_WriteFunc)(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file, uint64_t offset, uint32_t size, uint32_t chunk_count, const void* input, uint32_t* out_chunks_remaining); typedef int (*Longtail_ConcurrentChunkWrite_FlushFunc)(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api); @@ -463,6 +468,7 @@ struct Longtail_ConcurrentChunkWriteAPI struct Longtail_API m_API; Longtail_ConcurrentChunkWrite_CreateDirFunc CreateDir; Longtail_ConcurrentChunkWrite_OpenFunc Open; + Longtail_ConcurrentChunkWrite_CloseFunc Close; Longtail_ConcurrentChunkWrite_WriteFunc Write; Longtail_ConcurrentChunkWrite_FlushFunc Flush; }; @@ -474,11 +480,14 @@ LONGTAIL_EXPORT struct Longtail_ConcurrentChunkWriteAPI* Longtail_MakeConcurrent Longtail_DisposeFunc dispose_func, Longtail_ConcurrentChunkWrite_CreateDirFunc create_dir_func, Longtail_ConcurrentChunkWrite_OpenFunc open_func, + Longtail_ConcurrentChunkWrite_CloseFunc close_func, Longtail_ConcurrentChunkWrite_WriteFunc write_func, - Longtail_ConcurrentChunkWrite_FlushFunc flush_func); + Longtail_ConcurrentChunkWrite_FlushFunc flush_func +); LONGTAIL_EXPORT int Longtail_ConcurrentChunkWrite_CreateDir(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, const char* path); LONGTAIL_EXPORT int Longtail_ConcurrentChunkWrite_Open(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, const char* path, uint32_t chunk_write_count, Longtail_ConcurrentChunkWriteAPI_HOpenFile* out_open_file); +LONGTAIL_EXPORT void Longtail_ConcurrentChunkWrite_Close(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file); LONGTAIL_EXPORT int Longtail_ConcurrentChunkWrite_Write(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api, Longtail_ConcurrentChunkWriteAPI_HOpenFile in_open_file, uint64_t offset, uint32_t size, uint32_t chunk_count, const void* input, uint32_t* out_total_chunks_written); LONGTAIL_EXPORT int Longtail_ConcurrentChunkWrite_Flush(struct Longtail_ConcurrentChunkWriteAPI* concurrent_file_write_api); diff --git a/test/test.cpp b/test/test.cpp index ed732b7e..c9a49f17 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -5689,7 +5689,8 @@ struct FailableStorageAPI static int UnlockFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HLockFile lock_file) { struct FailableStorageAPI* api = (struct FailableStorageAPI*)storage_api; return api->m_BackingAPI->UnlockFile(api->m_BackingAPI, lock_file);} static char* GetParentPath(struct Longtail_StorageAPI* storage_api, const char* path) { struct FailableStorageAPI* api = (struct FailableStorageAPI*)storage_api; return api->m_BackingAPI->GetParentPath(api->m_BackingAPI, path);} static int MapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HOpenFile f, uint64_t offset, uint64_t length, Longtail_StorageAPI_HFileMap* out_file_map, const void** out_data_ptr) { struct FailableStorageAPI* api = (struct FailableStorageAPI*)storage_api; return api->m_BackingAPI->MapFile(api->m_BackingAPI, f, offset, length, out_file_map, out_data_ptr);} - static void UnmapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HFileMap m) { struct FailableStorageAPI* api = (struct FailableStorageAPI*)storage_api; return api->m_BackingAPI->UnMapFile(api->m_BackingAPI, m);} + static void UnmapFile(struct Longtail_StorageAPI* storage_api, Longtail_StorageAPI_HFileMap m) { struct FailableStorageAPI* api = (struct FailableStorageAPI*)storage_api; return api->m_BackingAPI->UnMapFile(api->m_BackingAPI, m); } + static int OpenAppendFile(struct Longtail_StorageAPI* storage_api, const char* path, Longtail_StorageAPI_HOpenFile* out_open_file) { struct FailableStorageAPI* api = (struct FailableStorageAPI*)storage_api; return api->m_BackingAPI->OpenAppendFile(api->m_BackingAPI, path, out_open_file); } }; struct FailableStorageAPI* CreateFailableStorageAPI(struct Longtail_StorageAPI* backing_api) @@ -5722,7 +5723,8 @@ struct FailableStorageAPI* CreateFailableStorageAPI(struct Longtail_StorageAPI* FailableStorageAPI::UnlockFile, FailableStorageAPI::GetParentPath, FailableStorageAPI::MapFile, - FailableStorageAPI::UnmapFile); + FailableStorageAPI::UnmapFile, + FailableStorageAPI::OpenAppendFile); struct FailableStorageAPI* failable_storage_api = (struct FailableStorageAPI*)api; failable_storage_api->m_BackingAPI = backing_api; failable_storage_api->m_PassCount = 0x7fffffff;