libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
optical.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2026 Natalia Portillo.
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <inttypes.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "aaruformat.h"
26#include "erasure_internal.h"
27#include "internal.h"
28#include "log.h"
29
117{
118 int pos = 0;
119 size_t read_bytes = 0;
120 uint64_t crc64 = 0;
121 int j = 0, k = 0;
122
123 // Check if the context and image stream are valid
124 if(ctx == NULL || ctx->imageStream == NULL)
125 {
126 FATAL("Invalid context or image stream.");
127 return;
128 }
129
130 // Seek to block
131 pos = aaruf_fseek(ctx->imageStream, (aaru_off_t)entry->offset, SEEK_SET);
132 if(pos < 0 || aaruf_ftell(ctx->imageStream) != (aaru_off_t)entry->offset)
133 {
134 FATAL("Could not seek to %" PRIu64 " as indicated by index entry...\n", entry->offset);
135
136 return;
137 }
138
139 // Even if those two checks shall have been done before
140 read_bytes = fread(&ctx->tracks_header, 1, sizeof(TracksHeader), ctx->imageStream);
141
142 if(read_bytes != sizeof(TracksHeader))
143 {
144 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
145 TRACE("Could not read tracks header, continuing...\n");
146 return;
147 }
148
150 {
151 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
152 TRACE("Incorrect identifier for data block at position %" PRIu64 "\n", entry->offset);
153 }
154
155 ctx->image_info.ImageSize += sizeof(TrackEntry) * ctx->tracks_header.entries;
156
157 ctx->track_entries = (TrackEntry *)malloc(sizeof(TrackEntry) * ctx->tracks_header.entries);
158
159 if(ctx->track_entries == NULL)
160 {
161 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
162 FATAL("Could not allocate memory for metadata block, continuing...\n");
163 return;
164 }
165
166 read_bytes = fread(ctx->track_entries, sizeof(TrackEntry), ctx->tracks_header.entries, ctx->imageStream);
167
168 if(read_bytes != ctx->tracks_header.entries)
169 {
170 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
171 free(ctx->track_entries);
172 ctx->track_entries = NULL;
173 FATAL("Could not read metadata block, continuing...\n");
174
175 return;
176 }
177
178 crc64 = aaruf_crc64_data((const uint8_t *)ctx->track_entries, ctx->tracks_header.entries * sizeof(TrackEntry));
179
180 // Due to how C# wrote it, it is effectively reversed
181 if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64);
182
183 if(crc64 != ctx->tracks_header.crc64)
184 {
185 TRACE("Incorrect CRC found: 0x%" PRIx64 " found, expected 0x%" PRIx64, crc64,
186 ctx->tracks_header.crc64);
187
188 /* Attempt erasure coding recovery */
189 uint8_t *recovered = NULL;
190 uint32_t rec_size = 0;
191 if(ec_recover_meta_block(ctx, entry->offset, &recovered, &rec_size) == AARUF_STATUS_OK && recovered)
192 {
193 TRACE("EC recovery succeeded for tracks block at offset %" PRIu64, entry->offset);
194 /* Re-parse TracksHeader + TrackEntry array from recovered bytes */
195 if(rec_size >= sizeof(TracksHeader))
196 {
197 TracksHeader rec_hdr;
198 memcpy(&rec_hdr, recovered, sizeof(TracksHeader));
199 size_t entries_size = rec_hdr.entries * sizeof(TrackEntry);
200 if(rec_size >= sizeof(TracksHeader) + entries_size && rec_hdr.entries > 0)
201 {
202 free(ctx->track_entries);
203 ctx->track_entries = (TrackEntry *)malloc(entries_size);
204 if(ctx->track_entries)
205 {
206 memcpy(&ctx->tracks_header, &rec_hdr, sizeof(TracksHeader));
207 memcpy(ctx->track_entries, recovered + sizeof(TracksHeader), entries_size);
208 free(recovered);
209 goto tracks_parsed;
210 }
211 }
212 }
213 free(recovered);
214 }
215
216 return;
217 }
218
219 tracks_parsed:
220
221 TRACE("Found %d tracks at position %" PRIu64 ".\n", ctx->tracks_header.entries, entry->offset);
222
223 ctx->image_info.HasPartitions = true;
224 ctx->image_info.HasSessions = true;
225
226 ctx->number_of_data_tracks = 0;
227
228 for(j = 0; j < ctx->tracks_header.entries; j++)
229 {
230 if(ctx->track_entries[j].sequence <= 99) ctx->number_of_data_tracks++;
231 }
232
233 if(ctx->number_of_data_tracks > 0)
234 {
235 ctx->data_tracks = malloc(sizeof(TrackEntry) * ctx->number_of_data_tracks);
236 if(ctx->data_tracks == NULL)
237 {
238 FATAL("Could not allocate memory for data tracks, continuing without filtered list.\n");
239 ctx->number_of_data_tracks = 0;
240 }
241 }
242 else
243 ctx->data_tracks = NULL;
244
245 if(ctx->data_tracks != NULL)
246 {
247 k = 0;
248 for(j = 0; j < ctx->tracks_header.entries; j++)
249 {
250 if(ctx->track_entries[j].sequence <= 99)
251 memcpy(&ctx->data_tracks[k++], &ctx->track_entries[j], sizeof(TrackEntry));
252 }
253 }
254}
255
317AARU_EXPORT int32_t AARU_CALL aaruf_get_tracks(const void *context, uint8_t *buffer, size_t *length)
318{
319 TRACE("Entering aaruf_get_tracks(%p, %p, %zu)", context, buffer, (length ? *length : 0));
320
321 // Check context is correct AaruFormat context
322 if(context == NULL)
323 {
324 FATAL("Invalid context");
325
326 TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
328 }
329
330 const aaruformat_context *ctx = context;
331
332 // Not a libaaruformat context
333 if(ctx->magic != AARU_MAGIC)
334 {
335 FATAL("Invalid context");
336
337 TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
339 }
340
341 if(ctx->tracks_header.entries == 0 || ctx->track_entries == NULL)
342 {
343 FATAL("Image contains no tracks");
344
345 TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_TRACK_NOT_FOUND");
347 }
348
349 size_t required_length = ctx->tracks_header.entries * sizeof(TrackEntry);
350
351 if(buffer == NULL || length == NULL || *length < required_length)
352 {
353 if(length) *length = required_length;
354 TRACE("Buffer too small for tracks, required %zu bytes", required_length);
355
356 TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_BUFFER_TOO_SMALL");
358 }
359
360 memcpy(buffer, ctx->track_entries, required_length);
361 *length = required_length;
362
363 TRACE("Exiting aaruf_get_tracks(%p, %p, %zu) = AARUF_STATUS_OK", context, buffer, *length);
364 return AARUF_STATUS_OK;
365}
366
428AARU_EXPORT int32_t AARU_CALL aaruf_set_tracks(void *context, TrackEntry *tracks, const int count)
429{
430 TRACE("Entering aaruf_set_tracks(%p, %p, %d)", context, tracks, count);
431
432 // Check context is correct AaruFormat context
433 if(context == NULL)
434 {
435 FATAL("Invalid context");
436
437 TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
439 }
440
441 aaruformat_context *ctx = context;
442
443 // Not a libaaruformat context
444 if(ctx->magic != AARU_MAGIC)
445 {
446 FATAL("Invalid context");
447
448 TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
450 }
451
452 // Clearing existing tracks
453 if(count == 0)
454 {
455 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
456 free(ctx->track_entries);
457 ctx->track_entries = NULL;
458 free(ctx->data_tracks);
459 ctx->data_tracks = NULL;
460 ctx->number_of_data_tracks = 0;
461
462 TRACE("Exiting aaruf_set_tracks() = AARUF_STATUS_OK");
463 return AARUF_STATUS_OK;
464 }
465
466 if(tracks == NULL || count < 0)
467 {
468 FATAL("Invalid tracks data");
469
470 TRACE("Exiting aaruf_set_tracks() = AARUF_ERROR_INVALID_TRACK_FORMAT");
472 }
473
475 ctx->tracks_header.entries = (uint16_t)count;
476 free(ctx->track_entries);
477 ctx->track_entries = malloc(sizeof(TrackEntry) * count);
478 if(ctx->track_entries == NULL)
479 {
480 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
481 FATAL("Could not allocate memory for tracks");
482
483 TRACE("Exiting aaruf_set_tracks() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
485 }
486 memcpy(ctx->track_entries, tracks, sizeof(TrackEntry) * count);
487 ctx->tracks_header.crc64 = aaruf_crc64_data((const uint8_t *)ctx->track_entries, sizeof(TrackEntry) * count);
488
489 ctx->image_info.HasPartitions = true;
490 ctx->image_info.HasSessions = true;
491 ctx->dirty_tracks_block = true; // Mark tracks block as dirty
492
493 free(ctx->data_tracks);
494 ctx->data_tracks = NULL;
495
496 ctx->number_of_data_tracks = 0;
497
498 for(int j = 0; j < ctx->tracks_header.entries; j++)
499 if(ctx->track_entries[j].sequence <= 99) ctx->number_of_data_tracks++;
500
501 if(ctx->number_of_data_tracks > 0)
502 {
503 ctx->data_tracks = malloc(sizeof(TrackEntry) * ctx->number_of_data_tracks);
504 if(ctx->data_tracks == NULL)
505 {
506 free(ctx->track_entries);
507 ctx->track_entries = NULL;
508 memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
509 ctx->number_of_data_tracks = 0;
510 FATAL("Could not allocate memory for data tracks");
511
512 TRACE("Exiting aaruf_set_tracks() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
514 }
515 }
516
517 if(ctx->data_tracks != NULL)
518 {
519 int k = 0;
520 for(int j = 0; j < ctx->tracks_header.entries; j++)
521 if(ctx->track_entries[j].sequence <= 99)
522 memcpy(&ctx->data_tracks[k++], &ctx->track_entries[j], sizeof(TrackEntry));
523 }
524
525 TRACE("Exiting aaruf_set_tracks() = AARUF_STATUS_OK");
526 return AARUF_STATUS_OK;
527}
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
#define AARUF_VERSION_V1
First on‑disk version (C# implementation).
Definition consts.h:71
#define AARU_CALL
Definition decls.h:46
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
int32_t aaruf_get_tracks(const void *context, uint8_t *buffer, size_t *length)
Retrieve the array of track descriptors contained in an opened AaruFormat image.
Definition optical.c:317
int32_t aaruf_set_tracks(void *context, TrackEntry *tracks, const int count)
Replace (or clear) the in-memory track table for an AaruFormat image context.
Definition optical.c:428
#define AARU_EXPORT
Definition decls.h:55
#define bswap_64(x)
Definition endian.h:81
@ TracksBlock
Block containing optical disc tracks.
Definition enums.h:174
int32_t ec_recover_meta_block(aaruformat_context *ctx, uint64_t block_offset, uint8_t **recovered_data, uint32_t *recovered_size)
Attempt to recover a metadata/media-tag block at a given file offset.
Definition erasure.c:909
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:82
#define AARUF_ERROR_TRACK_NOT_FOUND
Referenced track number not present.
Definition errors.h:52
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
#define AARUF_ERROR_INVALID_TRACK_FORMAT
Track metadata internally inconsistent or malformed.
Definition errors.h:54
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
#define AARUF_ERROR_BUFFER_TOO_SMALL
Caller-supplied buffer insufficient for data.
Definition errors.h:49
void process_tracks_block(aaruformat_context *ctx, const IndexEntry *entry)
Parse and integrate a Tracks block from the image stream into the context.
Definition optical.c:116
static int aaruf_fseek(FILE *stream, aaru_off_t offset, int origin)
Definition internal.h:46
static aaru_off_t aaruf_ftell(FILE *stream)
Definition internal.h:52
int64_t aaru_off_t
Definition internal.h:42
#define FATAL(fmt,...)
Definition log.h:40
#define TRACE(fmt,...)
Definition log.h:25
uint8_t imageMajorVersion
Container format major version.
Definition header.h:110
uint8_t HasPartitions
Image contains partitions (or tracks for optical media); 0=no, non-zero=yes.
Definition aaru.h:935
uint8_t HasSessions
Image contains multiple sessions (optical media); 0=single/none, non-zero=multi.
Definition aaru.h:936
uint64_t ImageSize
Size of the image payload in bytes (excludes headers/metadata).
Definition aaru.h:937
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
uint8_t sequence
Track number (1..99 typical for CD audio/data).
Definition optical.h:73
Header for an optical tracks block listing track entries.
Definition optical.h:62
uint32_t identifier
Block identifier (must be BlockType::TracksBlock).
Definition optical.h:63
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
uint64_t crc64
CRC64-ECMA of the TrackEntry array (header excluded, legacy byte-swap for early versions).
Definition optical.h:65
Master context representing an open or in‑creation Aaru image.
Definition context.h:178
TrackEntry * data_tracks
Filtered list of data tracks (subset of trackEntries).
Definition context.h:253
bool dirty_tracks_block
True if tracks block should be written during close.
Definition context.h:333
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:181
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:180
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:182
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:270
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:252
uint8_t number_of_data_tracks
Count of tracks considered "data" (sequence 1..99 heuristics).
Definition context.h:255
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:254