371 lines
8.6 KiB
C
371 lines
8.6 KiB
C
/*
|
|
data source for libisoburn.
|
|
|
|
Copyright 2007 - 2012 Vreixo Formoso Lopes <metalpain2002@yahoo.es>
|
|
and Thomas Schmitt <scdbackup@gmx.net>
|
|
Provided under GPL version 2 or later.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#ifndef Xorriso_standalonE
|
|
|
|
#include <libburn/libburn.h>
|
|
|
|
#include <libisofs/libisofs.h>
|
|
|
|
#else /* ! Xorriso_standalonE */
|
|
|
|
#include "../libisofs/libisofs.h"
|
|
#include "../libburn/libburn.h"
|
|
|
|
#endif /* Xorriso_standalonE */
|
|
|
|
|
|
#include "isoburn.h"
|
|
|
|
|
|
/* Cached reading of image tree data by multiple tiles */
|
|
|
|
|
|
/* Debugging only: This reports cache loads on stderr.
|
|
#define Libisoburn_read_cache_reporT 1
|
|
*/
|
|
|
|
|
|
struct isoburn_cache_tile {
|
|
char *cache_data;
|
|
uint32_t cache_lba;
|
|
uint32_t last_error_lba;
|
|
uint32_t last_aligned_error_lba;
|
|
int cache_hits;
|
|
int age;
|
|
};
|
|
|
|
struct isoburn_cached_drive {
|
|
struct burn_drive *drive;
|
|
struct isoburn_cache_tile **tiles;
|
|
int num_tiles;
|
|
int tile_blocks;
|
|
int current_age;
|
|
|
|
/**
|
|
Offset to be applied to all block addresses to compensate for an
|
|
eventual displacement of the block addresses relative to the image
|
|
start block address that was assumed when the image was created.
|
|
E.g. if track number 2 gets copied into a disk file and shall then
|
|
be loaded as ISO filesystem.
|
|
If displacement_sign is 1 then the displacement number will be
|
|
added to .read_block() addresses, if -1 it will be subtracted.
|
|
Else it will be ignored.
|
|
*/
|
|
uint32_t displacement;
|
|
int displacement_sign;
|
|
|
|
};
|
|
|
|
#define Libisoburn_max_agE 2000000000
|
|
|
|
static int ds_inc_age(struct isoburn_cached_drive *icd, int idx, int flag);
|
|
|
|
|
|
int ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer)
|
|
{
|
|
int ret, i, oldest, oldest_age;
|
|
struct burn_drive *d;
|
|
off_t count;
|
|
uint32_t aligned_lba;
|
|
char msg[80];
|
|
struct isoburn_cache_tile **tiles;
|
|
struct isoburn_cached_drive *icd;
|
|
|
|
if(src == NULL || buffer == NULL)
|
|
/* It is not required by the specs of libisofs but implicitly assumed
|
|
by its current implementation that a data source read result <0 is
|
|
a valid libisofs error code.
|
|
*/
|
|
return ISO_NULL_POINTER;
|
|
|
|
icd = (struct isoburn_cached_drive *) src->data;
|
|
d = (struct burn_drive*) icd->drive;
|
|
|
|
if(d == NULL) {
|
|
/* This would happen if libisoburn saw output data in the fifo and
|
|
performed early drive release and afterwards libisofs still tries
|
|
to read data.
|
|
That would constitute a bad conceptual problem in libisoburn.
|
|
*/
|
|
isoburn_msgs_submit(NULL, 0x00060000,
|
|
"Programming error: Drive released while libisofs still attempts to read",
|
|
0, "FATAL", 0);
|
|
return ISO_ASSERT_FAILURE;
|
|
}
|
|
|
|
tiles = icd->tiles;
|
|
|
|
if(icd->displacement_sign == 1) {
|
|
if(lba + icd->displacement < lba) {
|
|
address_rollover:;
|
|
return ISO_DISPLACE_ROLLOVER;
|
|
} else
|
|
lba += icd->displacement;
|
|
} else if(icd->displacement_sign == -1) {
|
|
if(lba < icd->displacement )
|
|
goto address_rollover;
|
|
else
|
|
lba -= icd->displacement;
|
|
}
|
|
|
|
aligned_lba= lba & ~(icd->tile_blocks - 1);
|
|
|
|
for (i = 0; i < icd->num_tiles; i++) {
|
|
if(aligned_lba == tiles[i]->cache_lba &&
|
|
tiles[i]->cache_lba != 0xffffffff) {
|
|
(tiles[i]->cache_hits)++;
|
|
memcpy(buffer, tiles[i]->cache_data + (lba - aligned_lba) * 2048, 2048);
|
|
count= 2048;
|
|
ds_inc_age(icd, i, 0);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* find oldest tile */
|
|
oldest_age= Libisoburn_max_agE;
|
|
oldest= 0;
|
|
for(i = 0; i < icd->num_tiles; i++) {
|
|
if(tiles[i]->cache_lba == 0xffffffff) {
|
|
oldest= i;
|
|
break;
|
|
}
|
|
if(tiles[i]->age < oldest_age) {
|
|
oldest_age= tiles[i]->age;
|
|
oldest= i;
|
|
}
|
|
}
|
|
|
|
tiles[oldest]->cache_lba= 0xffffffff; /* invalidate cache */
|
|
if(tiles[oldest]->last_aligned_error_lba == aligned_lba) {
|
|
ret = 0;
|
|
} else {
|
|
ret = burn_read_data(d, (off_t) aligned_lba * (off_t) 2048,
|
|
(char *) tiles[oldest]->cache_data,
|
|
icd->tile_blocks * 2048, &count, 2);
|
|
}
|
|
if (ret <= 0 ) {
|
|
tiles[oldest]->last_aligned_error_lba = aligned_lba;
|
|
|
|
/* Read-ahead failure ? Try to read 2048 directly. */
|
|
if(tiles[oldest]->last_error_lba == lba)
|
|
ret = 0;
|
|
else
|
|
ret = burn_read_data(d, (off_t) lba * (off_t) 2048, (char *) buffer,
|
|
2048, &count, 0);
|
|
if (ret > 0)
|
|
return 1;
|
|
tiles[oldest]->last_error_lba = lba;
|
|
sprintf(msg, "ds_read_block(%lu) returns %lX",
|
|
(unsigned long) lba, (unsigned long) ret);
|
|
isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "DEBUG", 0);
|
|
return ISO_DATA_SOURCE_MISHAP;
|
|
}
|
|
|
|
#ifdef Libisoburn_read_cache_reporT
|
|
fprintf(stderr, "Tile %2.2d : After %3d hits, new load from %8x , count= %d\n",
|
|
oldest, tiles[oldest]->cache_hits, aligned_lba, (int) count);
|
|
#endif
|
|
|
|
tiles[oldest]->cache_lba= aligned_lba;
|
|
tiles[oldest]->cache_hits= 1;
|
|
ds_inc_age(icd, oldest, 0);
|
|
|
|
memcpy(buffer, tiles[oldest]->cache_data + (lba - aligned_lba) * 2048, 2048);
|
|
count= 2048;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int ds_open(IsoDataSource *src)
|
|
{
|
|
/* nothing to do, device is always grabbed */
|
|
return 1;
|
|
}
|
|
|
|
static int ds_close(IsoDataSource *src)
|
|
{
|
|
/* nothing to do, device is always grabbed */
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int isoburn_cache_tile_destroy(struct isoburn_cache_tile **o,
|
|
int flag)
|
|
{
|
|
if (*o == NULL)
|
|
return(0);
|
|
if ((*o)->cache_data != NULL)
|
|
free((*o)->cache_data);
|
|
free(*o);
|
|
*o = NULL;
|
|
return(1);
|
|
}
|
|
|
|
|
|
static int isoburn_cache_tile_new(struct isoburn_cache_tile **o,
|
|
int tile_blocks, int flag)
|
|
{
|
|
struct isoburn_cache_tile *t;
|
|
|
|
*o = t = calloc(1, sizeof(struct isoburn_cache_tile));
|
|
if (t == NULL)
|
|
goto fail;
|
|
t->cache_data = NULL;
|
|
t->cache_lba = 0xffffffff;
|
|
t->cache_hits = 0;
|
|
t->last_error_lba = 0xffffffff;
|
|
t->last_aligned_error_lba = 0xffffffff;
|
|
t->age= 0;
|
|
|
|
t->cache_data = calloc(1, tile_blocks * 2048);
|
|
if (t->cache_data == NULL)
|
|
goto fail;
|
|
|
|
return(1);
|
|
fail:;
|
|
isoburn_cache_tile_destroy(o, 0);
|
|
return(-1);
|
|
}
|
|
|
|
|
|
static int isoburn_cached_drive_destroy(struct isoburn_cached_drive **o,
|
|
int flag)
|
|
{
|
|
struct isoburn_cached_drive *c;
|
|
int i;
|
|
|
|
if (*o == NULL)
|
|
return(0);
|
|
c= *o;
|
|
if (c->tiles != NULL) {
|
|
for (i = 0; i < c->num_tiles; i++)
|
|
isoburn_cache_tile_destroy(&(c->tiles[i]), 0);
|
|
free(c->tiles);
|
|
}
|
|
free(c);
|
|
*o= NULL;
|
|
return(1);
|
|
}
|
|
|
|
|
|
static int isoburn_cached_drive_new(struct isoburn_cached_drive **o,
|
|
struct burn_drive *d, int cache_tiles,
|
|
int tile_blocks, int flag)
|
|
{
|
|
struct isoburn_cached_drive *icd;
|
|
int i, ret;
|
|
|
|
*o = icd = calloc(1,sizeof(struct isoburn_cached_drive));
|
|
if (*o == NULL)
|
|
return(-1);
|
|
icd->drive = d;
|
|
icd->tiles = NULL;
|
|
icd->num_tiles = cache_tiles;
|
|
icd->tile_blocks = tile_blocks;
|
|
icd->current_age = 0;
|
|
icd->displacement = 0;
|
|
icd->displacement_sign = 0;
|
|
|
|
icd->tiles = calloc(1, sizeof(struct isoburn_cache_tile *) * icd->num_tiles);
|
|
if (icd->tiles == NULL)
|
|
goto fail;
|
|
for (i = 0; i < icd->num_tiles; i++) {
|
|
ret = isoburn_cache_tile_new(&(icd->tiles[i]), icd->tile_blocks, 0);
|
|
if (ret <= 0)
|
|
goto fail;
|
|
}
|
|
return(1);
|
|
fail:;
|
|
isoburn_cached_drive_destroy(o, 0);
|
|
return(-1);
|
|
}
|
|
|
|
|
|
static void ds_free_data(IsoDataSource *src)
|
|
{
|
|
struct isoburn_cached_drive *icd;
|
|
|
|
if(src->data != NULL) {
|
|
icd= (struct isoburn_cached_drive *) src->data;
|
|
isoburn_cached_drive_destroy(&icd, 0);
|
|
}
|
|
src->data= NULL;
|
|
}
|
|
|
|
|
|
int isoburn_data_source_shutdown(IsoDataSource *src, int flag)
|
|
{
|
|
struct isoburn_cached_drive *icd;
|
|
|
|
if(src==NULL)
|
|
return(0);
|
|
icd= (struct isoburn_cached_drive *) src->data;
|
|
icd->drive= NULL;
|
|
return(1);
|
|
}
|
|
|
|
|
|
IsoDataSource *isoburn_data_source_new(struct burn_drive *d,
|
|
uint32_t displacement, int displacement_sign,
|
|
int cache_tiles, int tile_blocks)
|
|
{
|
|
IsoDataSource *src;
|
|
struct isoburn_cached_drive *icd= NULL;
|
|
int ret;
|
|
|
|
if (d==NULL)
|
|
return NULL;
|
|
src = malloc(sizeof(IsoDataSource));
|
|
if (src == NULL)
|
|
return NULL;
|
|
ret = isoburn_cached_drive_new(&icd, d, cache_tiles, tile_blocks, 0);
|
|
if (ret <= 0) {
|
|
free(src);
|
|
return NULL;
|
|
}
|
|
src->version = 0;
|
|
src->refcount = 1;
|
|
src->read_block = ds_read_block;
|
|
src->open = ds_open;
|
|
src->close = ds_close;
|
|
src->free_data = ds_free_data;
|
|
src->data = icd;
|
|
icd->displacement = displacement;
|
|
icd->displacement_sign = displacement_sign;
|
|
return src;
|
|
}
|
|
|
|
|
|
static int ds_inc_age(struct isoburn_cached_drive *icd, int idx, int flag)
|
|
{
|
|
int i;
|
|
|
|
(icd->current_age)++;
|
|
if(icd->current_age>=Libisoburn_max_agE) { /* reset all ages (allow waste) */
|
|
for(i = 0; i < icd->num_tiles; i++)
|
|
(icd->tiles)[i]->age= 0;
|
|
icd->current_age= 1;
|
|
}
|
|
(icd->tiles)[idx]->age= icd->current_age;
|
|
return(1);
|
|
}
|
|
|
|
|