2025-08-07 15:43:35 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Aaru Data Preservation Suite.
|
|
|
|
|
* Copyright (c) 2019-2025 Natalia Portillo.
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
* published by the Free Software Foundation; either version 2.1 of the
|
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful, but
|
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "aaruformat.h"
|
|
|
|
|
#include "internal.h"
|
2025-08-14 00:38:28 +01:00
|
|
|
#include "log.h"
|
2025-08-07 15:43:35 +01:00
|
|
|
|
|
|
|
|
void *aaruf_create(const char *filepath, uint32_t mediaType, uint32_t sectorSize, uint64_t userSectors,
|
|
|
|
|
uint64_t negativeSectors, uint64_t overflowSectors, const char *options,
|
|
|
|
|
const uint8_t *applicationName, uint8_t applicationNameLength, uint8_t applicationMajorVersion,
|
2025-08-13 16:17:45 +01:00
|
|
|
uint8_t applicationMinorVersion)
|
2025-08-07 15:43:35 +01:00
|
|
|
{
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Entering aaruf_create(%s, %u, %u, %llu, %llu, %llu, %s, %s, %u, %u, %u)", filepath, mediaType, sectorSize,
|
|
|
|
|
userSectors, negativeSectors, overflowSectors, options,
|
|
|
|
|
applicationName ? (const char *)applicationName : "NULL", applicationNameLength, applicationMajorVersion,
|
|
|
|
|
applicationMinorVersion);
|
|
|
|
|
|
2025-08-07 15:43:35 +01:00
|
|
|
// Parse the options
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Parsing options");
|
2025-08-07 15:43:35 +01:00
|
|
|
aaru_options parsedOptions = parse_options(options);
|
|
|
|
|
|
|
|
|
|
// Allocate context
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Allocating memory for context");
|
2025-08-13 16:17:45 +01:00
|
|
|
aaruformatContext *ctx = malloc(sizeof(aaruformatContext));
|
2025-08-07 15:43:35 +01:00
|
|
|
if(ctx == NULL)
|
|
|
|
|
{
|
2025-08-14 00:38:28 +01:00
|
|
|
FATAL("Not enough memory to create context");
|
2025-08-07 15:43:35 +01:00
|
|
|
errno = AARUF_ERROR_NOT_ENOUGH_MEMORY;
|
2025-08-14 00:38:28 +01:00
|
|
|
|
|
|
|
|
TRACE("Exiting aaruf_create() = NULL");
|
2025-08-07 15:43:35 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(ctx, 0, sizeof(aaruformatContext));
|
|
|
|
|
|
|
|
|
|
// Create the image file
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Creating image file %s", filepath);
|
2025-08-07 15:43:35 +01:00
|
|
|
ctx->imageStream = fopen(filepath, "wb+");
|
|
|
|
|
if(ctx->imageStream == NULL)
|
|
|
|
|
{
|
2025-08-14 00:38:28 +01:00
|
|
|
FATAL("Error %d opening file %s for writing", errno, filepath);
|
2025-08-07 15:43:35 +01:00
|
|
|
free(ctx);
|
|
|
|
|
errno = AARUF_ERROR_CANNOT_CREATE_FILE;
|
2025-08-14 00:38:28 +01:00
|
|
|
|
|
|
|
|
TRACE("Exiting aaruf_create() = NULL");
|
|
|
|
|
|
2025-08-07 15:43:35 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(applicationNameLength > AARU_HEADER_APP_NAME_LEN)
|
|
|
|
|
{
|
2025-08-14 00:38:28 +01:00
|
|
|
FATAL("Application name too long (%u bytes, maximum %u bytes)", applicationNameLength,
|
|
|
|
|
AARU_HEADER_APP_NAME_LEN);
|
2025-08-07 15:43:35 +01:00
|
|
|
free(ctx);
|
|
|
|
|
errno = AARUF_ERROR_INVALID_APP_NAME_LENGTH;
|
2025-08-14 00:38:28 +01:00
|
|
|
|
|
|
|
|
TRACE("Exiting aaruf_create() = NULL");
|
2025-08-07 15:43:35 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize header
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Initializing header");
|
2025-08-07 15:43:35 +01:00
|
|
|
ctx->header.identifier = AARU_MAGIC;
|
|
|
|
|
memcpy(ctx->header.application, applicationName, applicationNameLength);
|
|
|
|
|
ctx->header.imageMajorVersion = AARUF_VERSION_V2;
|
|
|
|
|
ctx->header.imageMinorVersion = 0;
|
|
|
|
|
ctx->header.applicationMajorVersion = applicationMajorVersion;
|
|
|
|
|
ctx->header.applicationMinorVersion = applicationMinorVersion;
|
|
|
|
|
ctx->header.mediaType = mediaType;
|
|
|
|
|
ctx->header.indexOffset = 0;
|
|
|
|
|
ctx->header.creationTime = get_filetime_uint64();
|
|
|
|
|
ctx->header.lastWrittenTime = get_filetime_uint64();
|
|
|
|
|
|
|
|
|
|
ctx->readableSectorTags = (bool *)malloc(sizeof(bool) * MaxSectorTag);
|
|
|
|
|
|
|
|
|
|
if(ctx->readableSectorTags == NULL)
|
|
|
|
|
{
|
|
|
|
|
free(ctx);
|
|
|
|
|
errno = AARUF_ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(ctx->readableSectorTags, 0, sizeof(bool) * MaxSectorTag);
|
|
|
|
|
|
2025-08-14 00:38:28 +01:00
|
|
|
// Initialize image info
|
|
|
|
|
TRACE("Initializing image info");
|
2025-08-07 15:43:35 +01:00
|
|
|
ctx->imageInfo.Application = ctx->header.application;
|
|
|
|
|
ctx->imageInfo.ApplicationVersion = (uint8_t *)malloc(32);
|
|
|
|
|
if(ctx->imageInfo.ApplicationVersion != NULL)
|
|
|
|
|
{
|
|
|
|
|
memset(ctx->imageInfo.ApplicationVersion, 0, 32);
|
|
|
|
|
sprintf((char *)ctx->imageInfo.ApplicationVersion, "%d.%d", ctx->header.applicationMajorVersion,
|
|
|
|
|
ctx->header.applicationMinorVersion);
|
|
|
|
|
}
|
|
|
|
|
ctx->imageInfo.Version = (uint8_t *)malloc(32);
|
|
|
|
|
if(ctx->imageInfo.Version != NULL)
|
|
|
|
|
{
|
|
|
|
|
memset(ctx->imageInfo.Version, 0, 32);
|
|
|
|
|
sprintf((char *)ctx->imageInfo.Version, "%d.%d", ctx->header.imageMajorVersion, ctx->header.imageMinorVersion);
|
|
|
|
|
}
|
|
|
|
|
ctx->imageInfo.MediaType = ctx->header.mediaType;
|
|
|
|
|
ctx->imageInfo.ImageSize = 0;
|
|
|
|
|
ctx->imageInfo.CreationTime = ctx->header.creationTime;
|
|
|
|
|
ctx->imageInfo.LastModificationTime = ctx->header.lastWrittenTime;
|
|
|
|
|
ctx->imageInfo.XmlMediaType = aaruf_get_xml_mediatype(ctx->header.mediaType);
|
|
|
|
|
ctx->imageInfo.SectorSize = sectorSize;
|
|
|
|
|
|
|
|
|
|
// Initialize caches
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Initializing caches");
|
2025-08-07 15:43:35 +01:00
|
|
|
ctx->blockHeaderCache.cache = NULL;
|
|
|
|
|
ctx->blockHeaderCache.max_items = MAX_CACHE_SIZE / (ctx->imageInfo.SectorSize * (1 << ctx->shift));
|
|
|
|
|
ctx->blockCache.cache = NULL;
|
|
|
|
|
ctx->blockCache.max_items = ctx->blockHeaderCache.max_items;
|
|
|
|
|
|
|
|
|
|
// TODO: Cache tracks and sessions?
|
|
|
|
|
|
|
|
|
|
// Initialize ECC for Compact Disc
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Initializing Compact Disc ECC");
|
2025-08-07 15:43:35 +01:00
|
|
|
ctx->eccCdContext = (CdEccContext *)aaruf_ecc_cd_init();
|
|
|
|
|
|
|
|
|
|
ctx->magic = AARU_MAGIC;
|
|
|
|
|
ctx->libraryMajorVersion = LIBAARUFORMAT_MAJOR_VERSION;
|
|
|
|
|
ctx->libraryMinorVersion = LIBAARUFORMAT_MINOR_VERSION;
|
|
|
|
|
|
|
|
|
|
// Initialize DDT2
|
2025-08-14 00:38:28 +01:00
|
|
|
TRACE("Initializing DDT2");
|
2025-08-07 15:43:35 +01:00
|
|
|
ctx->inMemoryDdt = true;
|
|
|
|
|
ctx->userDataDdtHeader.identifier = DeDuplicationTable2;
|
|
|
|
|
ctx->userDataDdtHeader.type = UserData;
|
|
|
|
|
ctx->userDataDdtHeader.compression = None;
|
|
|
|
|
ctx->userDataDdtHeader.levels = 2;
|
|
|
|
|
ctx->userDataDdtHeader.tableLevel = 0;
|
|
|
|
|
ctx->userDataDdtHeader.previousLevelOffset = 0;
|
|
|
|
|
ctx->userDataDdtHeader.negative = negativeSectors;
|
|
|
|
|
ctx->userDataDdtHeader.blocks = userSectors + overflowSectors + negativeSectors;
|
|
|
|
|
ctx->userDataDdtHeader.overflow = overflowSectors;
|
|
|
|
|
ctx->userDataDdtHeader.start = 0;
|
|
|
|
|
ctx->userDataDdtHeader.blockAlignmentShift = parsedOptions.block_alignment;
|
|
|
|
|
ctx->userDataDdtHeader.dataShift = parsedOptions.data_shift;
|
|
|
|
|
ctx->userDataDdtHeader.tableShift = parsedOptions.table_shift;
|
|
|
|
|
ctx->userDataDdtHeader.sizeType = 1;
|
|
|
|
|
ctx->userDataDdtHeader.entries = ctx->userDataDdtHeader.blocks / (1 << ctx->userDataDdtHeader.tableShift);
|
|
|
|
|
|
|
|
|
|
if(ctx->userDataDdtHeader.blocks % (1 << ctx->userDataDdtHeader.tableShift) != 0) ctx->userDataDdtHeader.entries++;
|
|
|
|
|
|
2025-09-28 17:08:33 +01:00
|
|
|
// Initialize index entries array
|
|
|
|
|
TRACE("Initializing index entries array");
|
|
|
|
|
UT_icd index_entry_icd = {sizeof(IndexEntry), NULL, NULL, NULL};
|
|
|
|
|
utarray_new(ctx->indexEntries, &index_entry_icd);
|
|
|
|
|
|
|
|
|
|
if(ctx->indexEntries == NULL)
|
|
|
|
|
{
|
|
|
|
|
FATAL("Not enough memory to create index entries array");
|
|
|
|
|
free(ctx->readableSectorTags);
|
|
|
|
|
free(ctx);
|
|
|
|
|
errno = AARUF_ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
|
|
|
|
|
|
TRACE("Exiting aaruf_create() = NULL");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-07 15:43:35 +01:00
|
|
|
// Is writing
|
|
|
|
|
ctx->isWriting = true;
|
2025-08-14 00:38:28 +01:00
|
|
|
|
|
|
|
|
TRACE("Exiting aaruf_create() = %p", ctx);
|
|
|
|
|
// Return context
|
|
|
|
|
return ctx;
|
2025-08-07 15:43:35 +01:00
|
|
|
}
|