264 lines
6.4 KiB
C
264 lines
6.4 KiB
C
/*
|
|
data source for libisoburn.
|
|
|
|
Copyright 2007 - 2008 Vreixo Formoso Lopes <metalpain2002@yahoo.es>
|
|
and Thomas Schmitt <scdbackup@gmx.net>
|
|
*/
|
|
|
|
#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 */
|
|
/* Multi tile: 32 * 64 kB */
|
|
|
|
/* The size of a single tile.
|
|
Powers of 2 only ! Less than 16 makes not much sense.
|
|
*/
|
|
#define Libisoburn_tile_blockS 32
|
|
|
|
/* The number of tiles in the cache
|
|
*/
|
|
#define Libisoburn_cache_tileS 32
|
|
|
|
|
|
/* Debugging only: This reports cache loads on stderr.
|
|
#define Libisoburn_read_cache_reporT 1
|
|
*/
|
|
|
|
|
|
struct isoburn_cache_tile {
|
|
char cache_data[Libisoburn_tile_blockS * 2048];
|
|
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[Libisoburn_cache_tileS];
|
|
int current_age;
|
|
};
|
|
|
|
#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 implicitely 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 = (struct isoburn_cache_tile *) icd->tiles;
|
|
|
|
aligned_lba= lba & ~(Libisoburn_tile_blockS - 1);
|
|
|
|
for(i=0; i<Libisoburn_cache_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<Libisoburn_cache_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,
|
|
Libisoburn_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;
|
|
|
|
/* It is not required by the specs of libisofs but implicitely assumed
|
|
...
|
|
But it is not possible to ignore FAILURE.
|
|
libisofs insists in original error codes, i.e. libisoburn cannot
|
|
change severity FAILURE associated with ISO_FILE_READ_ERROR.
|
|
So ISO_FILE_READ_ERROR is not an option and libisoburn has to
|
|
misuse ISO_FILE_CANT_WRITE, which is actually for image generation
|
|
and not for image reading.
|
|
This is quite wrong, although the error message text is unclear
|
|
enough to make it appear plausible.
|
|
*/
|
|
ret= ISO_FILE_CANT_WRITE;
|
|
if(ret >= 0)
|
|
ret = -1;
|
|
sprintf(msg, "ds_read_block(%lu) returns %d", (unsigned long) lba, ret);
|
|
isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "DEBUG", 0);
|
|
return ret;
|
|
}
|
|
|
|
#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 void ds_free_data(IsoDataSource *src)
|
|
{
|
|
/* nothing to do */;
|
|
if(src->data != NULL)
|
|
free(src->data);
|
|
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)
|
|
{
|
|
IsoDataSource *ret;
|
|
struct isoburn_cached_drive *icd= NULL;
|
|
int i;
|
|
|
|
if (d==NULL)
|
|
return NULL;
|
|
ret = malloc(sizeof(IsoDataSource));
|
|
icd = calloc(1,sizeof(struct isoburn_cached_drive));
|
|
if (ret == NULL || icd == NULL)
|
|
return NULL;
|
|
ret->refcount = 1;
|
|
ret->read_block = ds_read_block;
|
|
ret->open = ds_open;
|
|
ret->close = ds_close;
|
|
ret->free_data = ds_free_data;
|
|
ret->data = icd;
|
|
icd->drive = d;
|
|
icd->current_age= 0;
|
|
for(i= 0; i<Libisoburn_cache_tileS; i++) {
|
|
icd->tiles[i].cache_lba = 0xffffffff;
|
|
icd->tiles[i].cache_hits = 0;
|
|
icd->tiles[i].last_error_lba = 0xffffffff;
|
|
icd->tiles[i].last_aligned_error_lba = 0xffffffff;
|
|
icd->tiles[i].age= 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
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<Libisoburn_cache_tileS; i++)
|
|
(icd->tiles)[i].age= 0;
|
|
icd->current_age= 1;
|
|
}
|
|
(icd->tiles)[idx].age= icd->current_age;
|
|
return(1);
|
|
}
|
|
|
|
|