Implement creating AaruFormat image and writing header.

This commit is contained in:
2025-08-07 15:43:35 +01:00
parent 676a87d25b
commit 3b012797cb
11 changed files with 354 additions and 2 deletions

View File

@@ -117,7 +117,11 @@ add_library(aaruformat SHARED include/aaruformat/consts.h include/aaruformat/enu
src/blocks/dump.c
src/blocks/checksum.c
src/index/index_v3.c
src/ddt/ddt_v2.c)
src/ddt/ddt_v2.c
include/aaruformat/structs/options.h
src/options.c
src/create.c
src/time.c)
include_directories(include include/aaruformat)

View File

@@ -115,7 +115,7 @@ typedef struct aaruformatContext
uint64_t cachedDdtOffset;
uint16_t *cachedSecondaryDdtSmall;
uint32_t *cachedSecondaryDdtBig;
bool isWriting;
} aaruformatContext;
typedef struct DumpHardwareEntriesWithData

View File

@@ -62,6 +62,11 @@ AARU_EXPORT int AARU_CALL aaruf_identify_stream(FILE *imageStream);
AARU_EXPORT void *AARU_CALL aaruf_open(const char *filepath);
AARU_EXPORT void *AARU_CALL 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 applicationMajorVersion, uint8_t applicationMinorVersion);
AARU_EXPORT int AARU_CALL aaruf_close(void *context);
AARU_EXPORT int32_t AARU_CALL aaruf_read_media_tag(void *context, uint8_t *data, int32_t tag, uint32_t *length);

View File

@@ -37,6 +37,9 @@
#define AARUF_ERROR_SECTOR_TAG_NOT_PRESENT -16
#define AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK -17
#define AARUF_ERROR_INVALID_BLOCK_CRC -18
#define AARUF_ERROR_CANNOT_CREATE_FILE -19
#define AARUF_ERROR_INVALID_APP_NAME_LENGTH -20
#define AARUF_ERROR_CANNOT_WRITE_HEADER -21
#define AARUF_STATUS_OK 0
#define AARUF_STATUS_SECTOR_NOT_DUMPED 1

View File

@@ -37,6 +37,7 @@
#include "structs/index.h"
#include "structs/metadata.h"
#include "structs/optical.h"
#include "structs/options.h"
#endif // LIBAARUFORMAT_STRUCTS_H

View File

@@ -0,0 +1,37 @@
/*
* 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/>.
*/
#ifndef LIBAARUFORMAT_OPTIONS_H
#define LIBAARUFORMAT_OPTIONS_H
typedef struct
{
bool compress;
bool deduplicate;
uint32_t dictionary;
uint8_t table_shift;
uint8_t data_shift;
uint8_t block_alignment;
bool md5;
bool sha1;
bool sha256;
bool blake3;
bool spamsum;
} aaru_options;
#endif // LIBAARUFORMAT_OPTIONS_H

View File

@@ -45,5 +45,7 @@ int32_t decode_ddt_single_level_v2(aaruformatContext *ctx, uint64_t sectorAddr
uint64_t *blockOffset, uint8_t *sectorStatus);
int32_t decode_ddt_multi_level_v2(aaruformatContext *ctx, uint64_t sectorAddress, uint64_t *offset,
uint64_t *blockOffset, uint8_t *sectorStatus);
aaru_options parse_options(const char *options);
uint64_t get_filetime_uint64();
#endif // LIBAARUFORMAT_INTERNAL_H

View File

@@ -47,6 +47,19 @@ int aaruf_close(void *context)
return -1;
}
if(ctx->isWriting)
{
// Write the header at the beginning of the file
fseek(ctx->imageStream, 0, SEEK_SET);
if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) != 1)
{
fclose(ctx->imageStream);
ctx->imageStream = NULL;
errno = AARUF_ERROR_CANNOT_WRITE_HEADER;
return -1;
}
}
// This may do nothing if imageStream is NULL, but as the behaviour is undefined, better sure than sorry
if(ctx->imageStream != NULL)
{

145
src/create.c Normal file
View File

@@ -0,0 +1,145 @@
/*
* 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"
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,
uint8_t applicationMinorVersion);
{
// Parse the options
aaru_options parsedOptions = parse_options(options);
// Allocate context
aaruformatContext *ctx = (aaruformatContext *)malloc(sizeof(aaruformatContext));
if(ctx == NULL)
{
errno = AARUF_ERROR_NOT_ENOUGH_MEMORY;
return NULL;
}
memset(ctx, 0, sizeof(aaruformatContext));
// Create the image file
ctx->imageStream = fopen(filepath, "wb+");
if(ctx->imageStream == NULL)
{
free(ctx);
errno = AARUF_ERROR_CANNOT_CREATE_FILE;
return NULL;
}
if(applicationNameLength > AARU_HEADER_APP_NAME_LEN)
{
free(ctx);
errno = AARUF_ERROR_INVALID_APP_NAME_LENGTH;
return NULL;
}
// Initialize header
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);
// Initilize image info
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
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
ctx->eccCdContext = (CdEccContext *)aaruf_ecc_cd_init();
ctx->magic = AARU_MAGIC;
ctx->libraryMajorVersion = LIBAARUFORMAT_MAJOR_VERSION;
ctx->libraryMinorVersion = LIBAARUFORMAT_MINOR_VERSION;
// Initialize DDT2
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++;
// Is writing
ctx->isWriting = true;
}

96
src/options.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "internal.h"
#include "structs/options.h"
aaru_options parse_options(const char *options)
{
aaru_options parsed = {.compress = true,
.deduplicate = true,
.dictionary = 33554432,
.table_shift = 9,
.data_shift = 12,
.block_alignment = 9,
.md5 = false,
.sha1 = false,
.sha256 = false,
.blake3 = false,
.spamsum = false};
char buffer[1024];
strncpy(buffer, options, sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0';
const char *token = strtok(buffer, ";");
while(token != NULL)
{
char *equal = strchr(token, '=');
if(equal)
{
*equal = '\0';
const char *key = token;
const char *value = equal + 1;
bool bval = strncmp(value, "true", 4) == 0;
if(strncmp(key, "compress", 8) == 0)
parsed.compress = bval;
else if(strncmp(key, "deduplicate", 11) == 0)
parsed.deduplicate = bval;
else if(strncmp(key, "dictionary", 10) == 0)
{
parsed.dictionary = (uint32_t)strtoul(value, NULL, 10);
if(parsed.dictionary == 0) parsed.dictionary = 33554432;
}
else if(strncmp(key, "table_shift", 11) == 0)
{
parsed.table_shift = (uint8_t)atoi(value);
if(parsed.table_shift == 0) parsed.table_shift = 9;
}
else if(strncmp(key, "data_shift", 10) == 0)
{
parsed.data_shift = (uint8_t)atoi(value);
if(parsed.data_shift == 0) parsed.data_shift = 12;
}
else if(strncmp(key, "block_alignment", 15) == 0)
{
parsed.block_alignment = (uint8_t)atoi(value);
if(parsed.block_alignment == 0) parsed.block_alignment = 9;
}
else if(strncmp(key, "md5", 3) == 0)
parsed.md5 = bval;
else if(strncmp(key, "sha1", 4) == 0)
parsed.sha1 = bval;
else if(strncmp(key, "sha256", 6) == 0)
parsed.sha256 = bval;
else if(strncmp(key, "blake3", 6) == 0)
parsed.blake3 = bval;
else if(strncmp(key, "spamsum", 7) == 0)
parsed.spamsum = bval;
}
token = strtok(NULL, ";");
}
return parsed;
}

46
src/time.c Normal file
View File

@@ -0,0 +1,46 @@
/*
* 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 <stddef.h>
#include <stdint.h>
#ifdef _WIN32
#include <windows.h>
uint64_t get_filetime_uint64()
{
FILETIME ft;
SYSTEMTIME st;
GetSystemTime(&st); // UTC time
SystemTimeToFileTime(&st, &ft);
return ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
}
#else
#include <sys/time.h>
uint64_t get_filetime_uint64()
{
struct timeval tv;
gettimeofday(&tv, NULL); // seconds + microseconds since 1970
const uint64_t EPOCH_DIFF = 11644473600ULL; // seconds between 1601 and 1970
uint64_t ft = (tv.tv_sec + EPOCH_DIFF) * 10000000ULL + tv.tv_usec * 10;
return ft;
}
#endif