Files
dvdisaster/udf.c
2009-11-21 16:29:02 +09:00

1259 lines
36 KiB
C

/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2009 Carsten Gnoerlich.
* Project home page: http://www.dvdisaster.com
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
* or direct your browser at http://www.gnu.org.
*/
#include "dvdisaster.h"
#include "udf.h"
#include "rs02-includes.h"
/***
*** Look for ecc headers in RS02 style media
***/
enum { HEADER_FOUND, TRY_NEXT_HEADER, TRY_NEXT_MODULO};
static int try_sector(DeviceHandle *dh, gint64 pos, EccHeader **ehptr, unsigned char *secbuf)
{ EccHeader *eh;
unsigned char fingerprint[16];
guint32 recorded_crc;
guint32 real_crc;
gint64 last_fp = -1;
/* Try reading the sector */
Verbose("udf/try_sector: trying sector %lld\n", pos);
if(ReadSectorsFast(dh, secbuf, pos, 2))
{ Verbose("udf/try_sector: read error, trying next header\n");
return TRY_NEXT_HEADER;
}
eh = (EccHeader*)secbuf;
/* See if the magic cookie is there. If not, searching within
this modulo makes no sense for write-once media.
However if the medium is rewriteable, there might be trash
data behind the image. So finding an invalid sector
does not imply there is not RS02 data present. */
if(strncmp((char*)eh->cookie, "*dvdisaster*", 12))
{ if(dh->rewriteable)
{ Verbose("udf/try_sector: no cookie but rewriteable medium: skipping header\n");
return TRY_NEXT_HEADER;
}
else
{ Verbose("udf/try_sector: no cookie, skipping current modulo\n");
return TRY_NEXT_MODULO;
}
}
else Verbose("udf/try_sector: header at %lld: magic cookie found\n", (long long int)pos);
/* Calculate CRC */
recorded_crc = eh->selfCRC;
#ifdef HAVE_BIG_ENDIAN
eh->selfCRC = 0x47504c00;
#else
eh->selfCRC = 0x4c5047;
#endif
real_crc = Crc32((unsigned char*)eh, sizeof(EccHeader));
if(real_crc != recorded_crc)
{ Verbose("udf/try_sector: CRC failed, skipping header\n");
return TRY_NEXT_HEADER;
}
eh = g_malloc(sizeof(EccHeader));
memcpy(eh, secbuf, sizeof(EccHeader));
#ifdef HAVE_BIG_ENDIAN
SwapEccHeaderBytes(eh);
#endif
eh->selfCRC = recorded_crc;
Verbose("udf/try_sector: CRC okay\n");
/* Compare medium fingerprint with that recorded in Ecc header */
if(last_fp != eh->fpSector) /* fingerprint in different sector as before? */
{ int fp_read;
/* read new fingerprint */
fp_read = GetMediumFingerprint(dh, fingerprint, eh->fpSector);
last_fp = eh->fpSector;
if(!fp_read) /* be optimistic if fingerprint sector is unreadable */
{ *ehptr = eh;
Verbose("udf/try_sector: read error in fingerprint sector\n");
return HEADER_FOUND;
}
if(!memcmp(fingerprint, eh->mediumFP, 16)) /* good fingerprint */
{ *ehptr = eh;
Verbose("udf/try_sector: fingerprint okay, header good\n");
return HEADER_FOUND;
}
/* This might be a header from a larger previous session.
Discard it and continue */
Verbose("udf/try_sector: fingerprint mismatch, skipping sector\n");
g_free(eh);
}
return TRY_NEXT_HEADER;
}
/*
* Dialog components for disabling RS02 search
*/
static void no_rs02_cb(GtkWidget *widget, gpointer data)
{ int state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
if(state) Closure->querySize = 1;
else Closure->querySize = 2;
UpdatePrefsQuerySize();
}
static void insert_buttons(GtkDialog *dialog)
{ GtkWidget *check,*align;
gtk_dialog_add_buttons(dialog,
_utf("Skip RS02 test"), 1,
_utf("Continue searching"), 0, NULL);
align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), align, FALSE, FALSE, 0);
check = gtk_check_button_new_with_label(_utf("Disable RS02 initialization in the preferences"));
gtk_container_add(GTK_CONTAINER(align), check);
gtk_container_set_border_width(GTK_CONTAINER(align), 10);
g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(no_rs02_cb), NULL);
gtk_widget_show(align);
gtk_widget_show(check);
}
/*
* RS02 header search
*/
EccHeader* FindHeaderInMedium(DeviceHandle *dh, gint64 max_sectors)
{ AlignedBuffer *ab = CreateAlignedBuffer(4096);
EccHeader *eh = NULL;
Bitmap *try_next_header, *try_next_modulo;
gint64 pos;
gint64 header_modulo;
int read_count = 0;
int answered_continue = FALSE;
int warning_shown = FALSE;
/*** Quick search at fixed offsets relative to ISO filesystem */
if(!max_sectors)
{ if(dh->isoInfo)
{ gint64 iso_size = dh->isoInfo->volumeSize;
/* Iso size is correct; look for root sector at +2 */
if(try_sector(dh, iso_size, &eh, ab->buf) == HEADER_FOUND)
{ Verbose("Root sector search at +0 successful\n");
FreeAlignedBuffer(ab);
return eh;
}
/* Strange stuff. Sometimes the iso size is increased by 150
sectors by the burning software. */
if(try_sector(dh, iso_size-150, &eh, ab->buf) == HEADER_FOUND)
{ Verbose("Root sector search at -150 successful\n");
FreeAlignedBuffer(ab);
return eh;
}
}
FreeAlignedBuffer(ab);
return NULL;
}
/*** Normal exhaustive search */
header_modulo = (gint64)1<<62;
try_next_header = CreateBitmap0(max_sectors);
try_next_modulo = CreateBitmap0(max_sectors);
Verbose("Medium rewriteable: %s\n", dh->rewriteable ? "TRUE" : "FALSE");
/*** Search for the headers */
while(header_modulo >= 32)
{ pos = max_sectors & ~(header_modulo - 1);
Verbose("FindHeaderInMedium: Trying modulo %lld\n", header_modulo);
while(pos > 0)
{ int result;
if(Closure->stopActions)
goto bail_out;
if(GetBit(try_next_header, pos))
{ Verbose("Sector %lld cached; skipping\n", pos);
goto check_next_header;
}
if(GetBit(try_next_modulo, pos))
{ Verbose("Sector %lld cached; skipping modulo\n", pos);
goto check_next_modulo;
}
result = try_sector(dh, pos, &eh, ab->buf);
switch(result)
{ case TRY_NEXT_HEADER:
SetBit(try_next_header, pos);
read_count++;
if(!answered_continue && read_count > 5)
{ if(Closure->guiMode)
{ int answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_buttons,
_("Faster medium initialization\n\n"
"Searching this medium for error correction data may take a long time.\n"
"Press \"Skip RS02 test\" if you are certain that this medium was\n"
"not augmented with RS02 error correction data."));
if(answer) goto bail_out;
answered_continue = TRUE;
}
if(!Closure->guiMode && !warning_shown)
{ PrintCLI(_("\nSearching this medium for error correction data may take a long time.\n"
"If you are certain that this medium was not augmented with RS02 error correction\n"
"data, you might wish to abort this command and re-run with the option\n"
"--query-size=udf\n"));
warning_shown = TRUE;
}
}
goto check_next_header;
case TRY_NEXT_MODULO:
SetBit(try_next_modulo, pos);
goto check_next_modulo;
case HEADER_FOUND:
FreeBitmap(try_next_header);
FreeBitmap(try_next_modulo);
FreeAlignedBuffer(ab);
return eh;
}
check_next_header:
pos -= header_modulo;
}
check_next_modulo:
header_modulo >>= 1;
}
bail_out:
FreeBitmap(try_next_header);
FreeBitmap(try_next_modulo);
FreeAlignedBuffer(ab);
return NULL;
}
gint64 MediumLengthFromRS02(DeviceHandle *dh, gint64 max_size)
{ EccHeader *eh;
RS02Layout *lay;
gint64 real_size;
eh = FindHeaderInMedium(dh, max_size);
if(eh)
{ dh->rs02Header = eh;
lay = CalcRS02Layout(uchar_to_gint64(eh->sectors), eh->eccBytes);
real_size = lay->eccSectors+lay->dataSectors;
g_free(lay);
return real_size;
}
return 0;
}
/***
*** Rudimentary UDF and ISO filesystem parsing.
***
* Information about UDF and ISO was gathered from ECMA-119,
* ECMA-167, ECMA-167 and the UDF 2.6 standard from OSTA.org.
* However, no claims are made to be actually conformant to any
* of those standards.
*/
/*
* ECMA sector indices start with 1;
* this definition make it easier to use the standard for reading strings
* from the buffer.
* begin and end are the bp postions of the string start and end position.
*/
/*
* 8bit values
*/
#define bp_get_byte(buf, idx) (buf[(idx)-1])
#define bp_set_byte(buf, idx, byte) (buf[(idx)-1] = (unsigned char)byte)
/*
* 16bit both byte order
*/
#ifdef HAVE_BIG_ENDIAN
#define bp_get_short_bbo(buf, idx) (*((guint16*)(buf+idx+1)))
#else
#define bp_get_short_bbo(buf, idx) (*((guint16*)(buf+idx-1)))
#endif
static void bp_set_short_bbo(unsigned char *buf, int idx, guint16 value)
{ unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
*buf++ = wx;
*buf++ = yz;
}
static void bp_set_short_lsb(unsigned char *buf, int idx, guint16 value)
{ unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
}
static void bp_set_short_msb(unsigned char *buf, int idx, guint16 value)
{ unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = wx;
*buf++ = yz;
}
/*
* 32bit single byte order
*/
#ifdef HAVE_BIG_ENDIAN
#define bp_get_long_lsb(buf, idx) (((guint32)buf[(idx)+2])<<24|((guint32)buf[(idx)+1])<<16|((guint32)buf[(idx)])<<8|((guint32)buf[(idx)-1]))
#else
#define bp_get_long_lsb(buf, idx) (*((guint32*)(buf+idx-1)))
#endif
#define bp_get_long_msb(buf, idx) (((guint32)buf[(idx)-1])<<24|((guint32)buf[idx])<<16|((guint32)buf[(idx)+1])<<8|((guint32)buf[(idx)+2]))
static void bp_set_long_lsb(unsigned char *buf, int idx, guint32 value)
{ unsigned char st = (value >> 24) & 0xff;
unsigned char uv = (value >> 16) & 0xff;
unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
*buf++ = uv;
*buf++ = st;
}
static void bp_set_long_msb(unsigned char *buf, int idx, guint32 value)
{ unsigned char st = (value >> 24) & 0xff;
unsigned char uv = (value >> 16) & 0xff;
unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = st;
*buf++ = uv;
*buf++ = wx;
*buf++ = yz;
}
/*
* 32bit both byte order
*/
#ifdef HAVE_BIG_ENDIAN
#define bp_get_long_bbo(buf, idx) (*((guint32*)(buf+idx+3)))
#else
#define bp_get_long_bbo(buf, idx) (*((guint32*)(buf+idx-1)))
#endif
static void bp_set_long_bbo(unsigned char *buf, int idx, guint32 value)
{ unsigned char st = (value >> 24) & 0xff;
unsigned char uv = (value >> 16) & 0xff;
unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
*buf++ = uv;
*buf++ = st;
*buf++ = st;
*buf++ = uv;
*buf++ = wx;
*buf++ = yz;
}
static void bp_get(unsigned char *dest, unsigned char *src, int begin, int end)
{ int length = end-begin+1;
strncpy((char*)dest, (char*)src+begin-1, length);
}
#if 0
static void bp_set(unsigned char *dest, unsigned char *src, int begin, int end)
{ int length = end-begin+1;
memcpy((char*)dest+begin-1, (char*)src, length);
}
#endif
/*
* String operations
*/
static void bp_get_string(unsigned char *dest, unsigned char *src, int begin, int end)
{ int length = end-begin+1;
strncpy((char*)dest, (char*)src+begin-1, length);
dest[length] = 0;
}
static void bp_set_string(unsigned char *dest, char *src, int begin, int end)
{ int length = end-begin+1;
strncpy((char*)(dest+begin-1), src, length);
}
/*
* D1 string conversion currently simply expands the string to 16bit characters.
*/
static void bp_set_d1_string(unsigned char *dest, char *src, int begin, int end)
{
dest = dest + begin - 1;
while(begin<=end-2 && *src)
{ *dest++ = 0;
*dest++ = (unsigned char)*src++;
begin += 2;
}
}
/*
* Date values
*/
static void get_date(unsigned char *date, unsigned char *buf, int idx)
{
idx--;
sprintf((char*)date, "dd-mm-yyyy hh:mm:ss.ss");
bp_get(date+ 6, buf, idx+ 1, idx+ 4);
bp_get(date+ 3, buf, idx+ 5, idx+ 6);
bp_get(date+ 0, buf, idx+ 7, idx+ 8);
bp_get(date+11, buf, idx+ 9, idx+10);
bp_get(date+14, buf, idx+11, idx+12);
bp_get(date+17, buf, idx+13, idx+14);
bp_get(date+20, buf, idx+15, idx+16);
}
static void set_date(unsigned char *date, int idx,
int year, int month, int day,
int hour, int minute, int second, int hsecond,
int gmt_offset)
{
sprintf((char*)(date+idx-1), "%04d%02d%02d%02d%02d%02d%02d",
year, month, day, hour, minute, second, hsecond);
*(date+idx+15) = (unsigned char)gmt_offset;
}
static void set_binary_date(unsigned char *dest, int idx,
int year, int month, int day,
int hour, int minute, int second,
int gmt_offset)
{
dest = dest + idx - 1;
*dest++ = (unsigned char)year;
*dest++ = (unsigned char)month;
*dest++ = (unsigned char)day;
*dest++ = (unsigned char)hour;
*dest++ = (unsigned char)minute;
*dest++ = (unsigned char)second;
*dest++ = (unsigned char)gmt_offset;
}
static void beautify_dchar(char *dchar)
{ int idx = strlen(dchar)-1;
while(idx>=0)
{ if(dchar[idx] != ' ') break;
dchar[idx--] = 0;
}
while(idx>0)
{ if(dchar[idx-1] != ' ')
dchar[idx] = tolower((int)dchar[idx]);
idx--;
}
if(!*dchar) strcpy(dchar, _("Unnamed"));
}
/***
*** Extract some useful information from the ISO file system
***/
void FreeIsoInfo(IsoInfo *ii)
{
g_free(ii);
}
static IsoInfo* examine_primary_vd(unsigned char *buf)
{ IsoInfo *ii = g_malloc(sizeof(IsoInfo));
unsigned char vlabel[33];
unsigned char str[80];
guint32 x,vss;
unsigned char date[32];
bp_get_string(str, buf, 9, 40);
Verbose(" System identifier : |%s|\n", str);
bp_get_string(vlabel, buf, 41, 72);
Verbose(" Volume identifier : |%s|\n", vlabel);
vss = bp_get_long_bbo(buf, 81);
Verbose(" Volume space size : %d sectors\n", vss);
x = bp_get_short_bbo(buf, 121);
Verbose(" Volume set size : %d\n", x);
x = bp_get_short_bbo(buf, 125);
Verbose(" Volume sequence size : %d\n", x);
x = bp_get_short_bbo(buf, 129);
Verbose(" Logical block size : %d\n", x);
x = bp_get_long_bbo(buf, 133);
Verbose(" Path table size : %d bytes\n", x);
x = bp_get_long_lsb(buf, 141);
Verbose(" L-Path table location : %d\n", x);
x = bp_get_long_lsb(buf, 145);
Verbose(" Opt L-Path table location : %d\n", x);
x = bp_get_long_msb(buf, 149);
Verbose(" M-Path table location : %d\n", x);
x = bp_get_long_msb(buf, 153);
Verbose(" Opt M-Path table location : %d\n", x);
/* 157 .. 190 directory record */
/* 191 .. 318 Volume set identifier */
/* 319 .. 446 Publisher Identifier */
/* 447 .. 574 Data Preparer Identifier */
/* 575 .. 702 Application Identfier */
/* 703 .. 739 Copyright File Identifier */
/* 740 .. 776 Abstract File Identifier */
/* 777 .. 813 Bibliographic File Identifier */
get_date(date, buf, 814);
Verbose(" Volume creation date/time : %s\n", date);
get_date(str, buf, 831);
Verbose(" Volume modification d/t : %s\n", str);
get_date(str, buf, 848);
Verbose(" Volume expiration d/t : %s\n", str);
get_date(str, buf, 865);
Verbose(" Volume effective d/t : %s\n", str);
x = bp_get_byte(buf,882);
Verbose(" File structure version : %d\n", x);
/* Extract information for IsoInfo */
ii->volumeSize = vss;
if(!Closure->screenShotMode)
{ strcpy(ii->volumeLabel, (char*)vlabel);
beautify_dchar(ii->volumeLabel);
}
else strcpy(ii->volumeLabel, _("Example disc"));
strcpy(ii->creationDate, (char*)date);
ii->creationDate[10] = 0;
return ii;
}
static IsoInfo* examine_iso(DeviceHandle *dh)
{ AlignedBuffer *ab = CreateAlignedBuffer(2048);
unsigned char *buf = ab->buf;
IsoInfo *ii = NULL;
int sector,status;
int vdt,vdt_ver;
unsigned char sid[6];
Verbose(" Examining the ISO file system...\n");
/*** Iterate over the volume decriptors */
for(sector=16; sector<32; sector++)
{ if(Closure->stopActions)
continue;
status = ReadSectorsFast(dh, buf, sector, 1);
if(status)
{ Verbose(" Sector %2d: unreadable\n", sector);
continue;
}
vdt = bp_get_byte(buf, 1); /* Volume descriptor type */
bp_get_string(sid, buf, 2, 6); /* Standard identifier */
vdt_ver = bp_get_byte(buf,7); /* Volume descriptor version */
Verbose(" Sector %2d:\n"
" Volume descriptor type = %d\n"
" Volume descriptor version = %d\n"
" Standard identifier = %s\n",
sector, vdt, vdt_ver, sid);
if(strncmp((char*)sid,"CD001",5))
{ Verbose(" * Wrong or missing standard identifier.\n");
continue;
}
switch(vdt)
{ case 0: Verbose(" -> boot record: *skipped*\n");
break;
case 1: Verbose(" -> primary volume descriptor:\n");
ii = examine_primary_vd(buf);
break;
case 2: Verbose(" -> supplementary volume descriptor: *skipped*\n");
break;
case 255: Verbose(" -> volume descriptor set terminator;\n"
" end of ISO file system parsing.\n");
goto finished;
break;
default : Verbose(" -> unknown volume descriptor: *skipped*\n");
break;
}
}
finished:
FreeAlignedBuffer(ab);
return ii;
}
/***
*** The main wrapper for visiting the ISO and UDF file system structures
***/
int ExamineUDF(DeviceHandle *dh)
{
Verbose("\nExamineUDF(%s)\n",dh->devinfo);
dh->isoInfo = examine_iso(dh);
Verbose(" Examining the UDF file system...\n");
Verbose(" not yet implemented.\n\n");
/* Try to find the root header at a fixed offset to the ISO filesystem end. */
dh->rs02Size = MediumLengthFromRS02(dh, 0);
return TRUE;
}
/***
*** Rudimentary support for creating .iso images.
*** Currently only used for creating the random image;
* can only create .iso images containing exactly one file.
*/
/*
* Directory record writing
*/
static IsoDir* create_iso_dir()
{ IsoDir *id = g_malloc0(sizeof(IsoDir));
id->dir = g_malloc0(2048);
id->nSectors = 1;
return id;
}
static void free_iso_dir(IsoDir *id)
{ g_free(id->dir);
g_free(id);
}
static void add_directory_record(IsoDir *iso_dir,
int extent, int file_flags, int file_identifier,
int year, int month, int day,
int hour, int minute, int second, int gmt_offset)
{ unsigned char *dir = iso_dir->dir + iso_dir->tail;
iso_dir->tail += 34;
bp_set_byte(dir, 1, 34); /* Length of directory record */
bp_set_byte(dir, 2, 0); /* Extended attribute length */
bp_set_long_bbo(dir, 3, extent); /* Location of Extent */
bp_set_long_bbo(dir, 11, 2048); /* Data length of file section */
set_binary_date(dir, 19, /* Binary date rep of creation time */
year,month,day,hour,minute,second,gmt_offset);
bp_set_byte(dir, 26, file_flags); /* File flags (we are a directory) */
bp_set_byte(dir, 27, 0); /* File unit size if interleaved */
bp_set_byte(dir, 28, 0); /* Interleave gap size */
bp_set_short_bbo(dir, 29, 1); /* Volume sequence number */
bp_set_byte(dir, 33, 1); /* Length of file identifier */
bp_set_byte(dir, 34, file_identifier); /* File identifier */
}
static void add_file_record(IsoDir *iso_dir, char *filename, guint64 size, int extent,
int year, int month, int day,
int hour, int minute, int second, int gmt_offset)
{ unsigned char *dir = iso_dir->dir + iso_dir->tail;
int sl = strlen(filename);
int entry_len = 34+sl;
iso_dir->tail += entry_len;
bp_set_byte(dir, 1, entry_len); /* Length of directory record */
bp_set_byte(dir, 2, 0); /* Extended attribute length */
bp_set_long_bbo(dir, 3, extent); /* Location of Extent */
bp_set_long_bbo(dir, 11, size); /* Data length of file section */
set_binary_date(dir, 19, /* Binary date rep of creation time */
year,month,day,hour,minute,second,gmt_offset);
bp_set_byte(dir, 26, 0); /* File flags (we are a file) */
bp_set_byte(dir, 27, 0); /* File unit size if interleaved */
bp_set_byte(dir, 28, 0); /* Interleave gap size */
bp_set_short_bbo(dir, 29, 1); /* Volume sequence number */
bp_set_byte(dir, 33, sl); /* Length of file identifier */
bp_set_string(dir, filename, 34, 34+sl); /* File name */
}
static void add_d1_file_record(IsoDir *iso_dir, char *filename, guint64 size, int extent,
int year, int month, int day,
int hour, int minute, int second, int gmt_offset)
{ unsigned char *dir = iso_dir->dir + iso_dir->tail;
int sl = 2*strlen(filename);
int entry_len = 34+sl;
iso_dir->tail += entry_len;
bp_set_byte(dir, 1, entry_len); /* Length of directory record */
bp_set_byte(dir, 2, 0); /* Extended attribute length */
bp_set_long_bbo(dir, 3, extent); /* Location of Extent */
bp_set_long_bbo(dir, 11, size); /* Data length of file section */
set_binary_date(dir, 19, /* Binary date rep of creation time */
year,month,day,hour,minute,second,gmt_offset);
bp_set_byte(dir, 26, 0); /* File flags (we are a file) */
bp_set_byte(dir, 27, 0); /* File unit size if interleaved */
bp_set_byte(dir, 28, 0); /* Interleave gap size */
bp_set_short_bbo(dir, 29, 1); /* Volume sequence number */
bp_set_byte(dir, 33, sl); /* Length of file identifier */
bp_set_d1_string(dir, filename, 34, 34+sl); /* File name */
}
/*
* Path tables
*/
static IsoPathTable* create_iso_path_table()
{ IsoPathTable *ipt = g_malloc0(sizeof(IsoPathTable));
ipt->lpath = g_malloc0(2048);
ipt->mpath = g_malloc0(2048);
ipt->nSectors = 1;
return ipt;
}
static void free_iso_path_table(IsoPathTable *ipt)
{ g_free(ipt->lpath);
g_free(ipt->mpath);
g_free(ipt);
}
static void add_path(IsoPathTable *ipt, int extent)
{ unsigned char *lpath = ipt->lpath + ipt->tail;
unsigned char *mpath = ipt->mpath + ipt->tail;
bp_set_byte(lpath, 1, 1); /* Length of directory identifier (root=1) */
bp_set_byte(mpath, 1, 1);
bp_set_byte(lpath, 2, 0); /* Extended attribute length */
bp_set_byte(mpath, 2, 0); /* Extended attribute length */
bp_set_long_lsb(lpath, 3, extent); /* Location of extent */
bp_set_long_msb(mpath, 3, extent);
bp_set_short_lsb(lpath, 7, 1); /* Parent directory number */
bp_set_short_msb(mpath, 7, 1);
bp_set_byte(lpath, 9, 0); /* Directory identifier (root=0) */
bp_set_byte(mpath, 9, 0);
}
/*
* Initialize the IsoHeader structure
*/
IsoHeader* InitIsoHeader(void)
{ IsoHeader *ih = g_malloc0(sizeof(IsoHeader));
unsigned char *pvd,*svd;
char blank[128], string[128];
memset(blank, ' ', 128);
/*** Initialize primary volume descriptor (sector 16). */
ih->pvd = pvd = g_malloc0(2048);
ih->dirSectors = 1;
/* 1..8: Standard identifier */
bp_set_byte(pvd, 1, 1); /* Volume descriptor type 1 */
bp_set_string(pvd, "CD001", 2, 6); /* Standard identifier */
bp_set_byte(pvd, 7, 1); /* Volume descriptor version 1 */
/* 9..40: System identifier */
bp_set_string(pvd, blank, 9, 40);
/* 41..72: Volume identifier */
memcpy(string, blank, 128);
memcpy(string, "RANDOM IMAGE", 12);
bp_set_string(pvd, string, 41, 72);
/* 73.. 80: Unused, zero bytes */
/* 81.. 88: Volume space size, filled in at WriteIsoHeader() */
/* 89..120: Unused, zero bytes */
/* 121..124: Volume set size */
bp_set_short_bbo(pvd, 121, 1);
/* 125..128: Volume sequence number */
bp_set_short_bbo(pvd, 125, 1);
/* 129..132: Logical block size */
bp_set_short_bbo(pvd, 129, 2048);
/* 133..140: Path table size, filled in at WriteIsoHeader() */
/* 141..144: L Path table location, filled in at WriteIsoHeader() */
/* 145..148: Optional L Path table location, filled in at WriteIsoHeader() */
/* 149..152: M Path table location, filled in at WriteIsoHeader() */
/* 153..156: Optional M Path table location, filled in at WriteIsoHeader() */
/* 157..190: Directory record for root dir, filled in at WriteIsoHeader() */
/* 191..318: Volume set identifier */
bp_set_string(pvd, blank, 191, 318);
/* 319..446: Publisher identifier */
bp_set_string(pvd, blank, 319, 446);
/* 447..574: Data preparer identifier */
bp_set_string(pvd, blank, 447, 574);
/* 575..702 Application Identfier */
bp_set_string(pvd, blank, 575, 702);
/* 703..739 Copyright File Identifier */
bp_set_string(pvd, blank, 703, 739);
/* 740..776 Abstract File Identifier */
bp_set_string(pvd, blank, 740, 776);
/* 777..813 Bibliographic File Identifier */
bp_set_string(pvd, blank, 777, 813);
/* 814..830 Volume creation time, filled in by WriteIsoHeader() */
/* 831..847 Volume modification time, filled in by WriteIsoHeader() */
/* 848..864 Volume expiration time, filled in by WriteIsoHeader() */
/* 865..881 Volume effective time, filled in by WriteIsoHeader() */
/* 882 File structure version */
bp_set_byte(pvd, 882, 1);
/* 883 Reserved, zero */
/* 884..1395 for application use */
/* 1396..2048 Reserved, zero */
/*** Initialize supplementary volume descriptor (sector 17). */
ih->svd = svd = g_malloc0(2048);
ih->dirSectors = 2;
/* 1..8: Standard identifier */
bp_set_byte(svd, 1, 2); /* Volume descriptor type 1 */
bp_set_string(svd, "CD001", 2, 6); /* Standard identifier */
bp_set_byte(svd, 7, 1); /* Volume descriptor version 1 */
bp_set_byte(svd, 8, 0); /* Volume flags */
/* 9..40: System identifier (zero filled) */
/* 41..72: Volume identifier */
bp_set_d1_string(svd, "random image", 41, 72);
/* 73.. 80: Unused, zero bytes */
/* 81.. 88: Volume space size, filled in at WriteIsoHeader() */
/* 89..120: Escape sequences */
bp_set_byte(svd, 89, 37);
bp_set_byte(svd, 90, 47);
bp_set_byte(svd, 91, 64);
/* 121..124: Volume set size */
bp_set_short_bbo(svd, 121, 1);
/* 125..128: Volume sequence number */
bp_set_short_bbo(svd, 125, 1);
/* 129..132: Logical block size */
bp_set_short_bbo(svd, 129, 2048);
/* 133..140: Path table size, filled in at WriteIsoHeader() */
/* 141..144: L Path table location, filled in at WriteIsoHeader() */
/* 145..148: Optional L Path table location, filled in at WriteIsoHeader() */
/* 149..152: M Path table location, filled in at WriteIsoHeader() */
/* 153..156: Optional M Path table location, filled in at WriteIsoHeader() */
/* 157..190: Directory record for root dir, filled in at WriteIsoHeader() */
/* The standard says to fill the identifier field with blanks,
but actual writing software seems to use zeroes. */
/* 191..318: Volume set identifier */
//bp_set_string(svd, blank, 191, 318);
/* 319..446: Publisher identifier */
//bp_set_string(svd, blank, 319, 446);
/* 447..574: Data preparer identifier */
//bp_set_string(svd, blank, 447, 574);
/* 575..702 Application Identfier */
//bp_set_string(svd, blank, 575, 702);
/* 703..739 Copyright File Identifier */
//bp_set_string(svd, blank, 703, 739);
/* 740..776 Abstract File Identifier */
//bp_set_string(svd, blank, 740, 776);
/* 777..813 Bibliographic File Identifier */
//bp_set_string(svd, blank, 777, 813);
/* 814..830 Volume creation time, filled in by WriteIsoHeader() */
/* 831..847 Volume modification time, filled in by WriteIsoHeader() */
/* 848..864 Volume expiration time, filled in by WriteIsoHeader() */
/* 865..881 Volume effective time, filled in by WriteIsoHeader() */
/* 882 File structure version */
bp_set_byte(svd, 882, 1);
/* 883 Reserved, zero */
/* 884..1395 for application use */
/* 1396..2048 Reserved, zero */
/*** The volume descriptor set terminator (sector 17)
is trivial and created during WriteIsoHeader() */
ih->dirSectors = 3;
/*** Allocate space for directories and path tables */
ih->proot = create_iso_dir();
add_directory_record(ih->proot, 21, 2, 0,
106, 7, 16, 12, 35, 46, 8);
add_directory_record(ih->proot, 21, 2, 1,
106, 7, 16, 12, 35, 46, 8);
ih->ppath = create_iso_path_table();
add_path(ih->ppath, 21);
ih->sroot = create_iso_dir();
add_directory_record(ih->sroot, 24, 2, 0,
106, 7, 16, 12, 35, 46, 8);
add_directory_record(ih->sroot, 24, 2, 1,
106, 7, 16, 12, 35, 46, 8);
ih->spath = create_iso_path_table();
add_path(ih->spath, 24);
ih->dirSectors += 6;
return ih;
}
void FreeIsoHeader(IsoHeader *ih)
{
free_iso_dir(ih->proot);
free_iso_dir(ih->sroot);
free_iso_path_table(ih->ppath);
free_iso_path_table(ih->spath);
g_free(ih->pvd);
g_free(ih->svd);
g_free(ih);
}
/*
* Add file to iso image (works currently only for one file!)
*/
void AddFile(IsoHeader *ih, char *name, guint64 size)
{ static int n;
char iso[20], joliet[strlen(name+3)];
n++;
sprintf(iso,"RAN_%04d.DAT;1", n);
add_file_record(ih->proot, iso, size, 25,
106, 7, 16, 12, 28, 10, 8);
sprintf(joliet,"%s;1", name);
add_d1_file_record(ih->sroot, joliet, size, 25,
106, 7, 16, 12, 28, 10, 8);
ih->volumeSpace += (size+2047)/2048;
}
/*
* Write out the IsoHeader, return number of sectors written
*/
void WriteIsoHeader(IsoHeader *ih, LargeFile *image)
{ unsigned char zero[2048], sector[2048];
unsigned char *pvd,*svd;
int i;
/* The first 16 sectors are zeroed out */
memset(zero, 0, 2048);
for(i=0; i<16; i++)
{ int n = LargeWrite(image, zero, 2048);
if(n != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)i, strerror(errno));
}
/* Complete the primary volume descriptor */
pvd = ih->pvd;
/* 81..88: Volume space size */
ih->volumeSpace += 16 + ih->dirSectors;
bp_set_long_bbo(pvd, 81, ih->volumeSpace);
/* 133..140: Path table size, Fixme: use real values */
/* 141..144: L Path table location */
/* 145..148: Optional L Path table location */
/* 149..152: M Path table location */
/* 153..156: Optional M Path table location */
bp_set_long_bbo(pvd, 133, 10);
bp_set_long_lsb(pvd, 141, 19);
bp_set_long_lsb(pvd, 145, 0);
bp_set_long_msb(pvd, 149, 20);
bp_set_long_msb(pvd, 153, 0);
/* 157..190: Directory record for root directory, Fixme: use real values */
bp_set_byte(pvd, 157, 34); /* Length of directory record */
bp_set_byte(pvd, 158, 0); /* Extended atribute length */
bp_set_long_bbo(pvd, 159, 21); /* Location of Extent */
bp_set_long_bbo(pvd, 167, 2048); /* Data length of file section */
set_binary_date(pvd, 175, /* Binary date rep of creation time */
106, 7, 16, 10, 35, 46, 8);
bp_set_byte(pvd, 182, 2); /* File flags (we are a directory) */
bp_set_byte(pvd, 183, 0); /* File unit size if interleaved */
bp_set_byte(pvd, 184, 0); /* Interleave gap size */
bp_set_short_bbo(pvd, 185, 1); /* Volume sequence number */
bp_set_byte(pvd, 189, 1); /* Length of file identifier */
bp_set_byte(pvd, 190, 0); /* File identifier */
/* 814..830 Volume creation time */
set_date(pvd, 814, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 831..847 Volume modification time */
set_date(pvd, 831, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 848..864 Volume expiration time */
set_date(pvd, 848, 2106, 7, 16, 10, 35, 46, 23, 8);
/* 865..881 Volume effective time */
set_date(pvd, 865, 2006, 7, 16, 10, 35, 46, 23, 8);
/* Write the pvd */
if(LargeWrite(image, pvd, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)16, strerror(errno));
/* Create the supplementary volume descriptor */
svd = ih->svd;
/* 81..88: Volume space size */
bp_set_long_bbo(svd, 81, ih->volumeSpace);
/* 133..140: Path table size, Fixme: use real values */
/* 141..144: L Path table location */
/* 145..148: Optional L Path table location */
/* 149..152: M Path table location */
/* 153..156: Optional M Path table location */
bp_set_long_bbo(svd, 133, 10);
bp_set_long_lsb(svd, 141, 22);
bp_set_long_lsb(svd, 145, 0);
bp_set_long_msb(svd, 149, 23);
bp_set_long_msb(svd, 153, 0);
/* 157..190: Directory record for root directory, Fixme: use real values */
bp_set_byte(svd, 157, 34); /* Length of directory record */
bp_set_byte(svd, 158, 0); /* Extended attribute length */
bp_set_long_bbo(svd, 159, 24); /* Location of Extent */
bp_set_long_bbo(svd, 167, 2048); /* Data length of file section */
set_binary_date(svd, 175, /* Binary date rep of creation time */
106, 7, 16, 10, 35, 46, 8);
bp_set_byte(svd, 182, 2); /* File flags (we are a directory) */
bp_set_byte(svd, 183, 0); /* File unit size if interleaved */
bp_set_byte(svd, 184, 0); /* Interleave gap size */
bp_set_short_bbo(svd, 185, 1); /* Volume sequence number */
bp_set_byte(svd, 189, 1); /* Length of file identifier */
bp_set_byte(svd, 190, 0); /* File identifier */
/* 814..830 Volume creation time */
set_date(svd, 814, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 831..847 Volume modification time */
set_date(svd, 831, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 848..864 Volume expiration time */
set_date(svd, 848, 2106, 7, 16, 10, 35, 46, 23, 8);
/* 865..881 Volume effective time */
set_date(svd, 865, 2006, 7, 16, 10, 35, 46, 23, 8);
/* Write the svd */
if(LargeWrite(image, svd, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)17, strerror(errno));
/*** Create and write the volume descriptor set terminator */
/* 1..8: Standard identifier */
memset(sector, 0, 2048);
bp_set_byte(sector, 1, 255); /* Volume descriptor set terminator */
bp_set_string(sector, "CD001", 2, 6); /* Standard identifier */
bp_set_byte(sector, 7, 1); /* Volume descriptor version 1 */
if(LargeWrite(image, sector, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)18, strerror(errno));
/*** Write the primary and supplementary path tables and root directories */
if(LargeWrite(image, ih->ppath->lpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)19, strerror(errno));
if(LargeWrite(image, ih->ppath->mpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)20, strerror(errno));
if(LargeWrite(image, ih->proot->dir, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)21, strerror(errno));
if(LargeWrite(image, ih->spath->lpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)22, strerror(errno));
if(LargeWrite(image, ih->spath->mpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)23, strerror(errno));
if(LargeWrite(image, ih->sroot->dir, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)24, strerror(errno));
}