From d54efd4bc23dd767c7d8907b330b92b23667743c Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Fri, 10 Sep 2010 17:10:03 +0000 Subject: [PATCH] Enabled multi-session with partition offset --- libisoburn/burn_wrap.c | 4 +- libisoburn/isoburn.c | 97 ++++++++++++++++++------------- libisoburn/isoburn.h | 41 ++++++++----- libisoburn/isofs_wrap.c | 113 ++++++++++++++++++++++++++++++++++-- libisoburn/libisoburn.h | 22 +++++++ xorriso/xorriso_timestamp.h | 2 +- 6 files changed, 215 insertions(+), 64 deletions(-) diff --git a/libisoburn/burn_wrap.c b/libisoburn/burn_wrap.c index 2eca3b10..fc1eab37 100644 --- a/libisoburn/burn_wrap.c +++ b/libisoburn/burn_wrap.c @@ -1192,7 +1192,7 @@ int isoburn_emulate_toc(struct burn_drive *d, int flag) ret= isoburn_read_iso_head(d, lba, &image_size, NULL, 0); if(ret<=0) {ret= 0; goto failure;} - lba= Libisoburn_overwriteable_starT; + lba= o->target_iso_head_size / 2048; with_enclosure= 1; if((flag & 16) && o->emulation_mode == 1) { ret= 1; @@ -1219,7 +1219,7 @@ int isoburn_emulate_toc(struct burn_drive *d, int flag) /* growisofs aligns to 16 rather than 32. Overwriteable TOC emulation relies on not accidentially seeing inter-session trash data. But one can safely access 16 blocks earlier because a xorriso header - would have overwritten with the unused 16 blocks at its start. + would have been overwritten with the unused 16 blocks at its start. If libisoburn alignment would increase, then this would not be possible any more. */ diff --git a/libisoburn/isoburn.c b/libisoburn/isoburn.c index 9ba11e26..2907cc91 100644 --- a/libisoburn/isoburn.c +++ b/libisoburn/isoburn.c @@ -115,7 +115,7 @@ struct isoburn *isoburn_list_start= NULL; int isoburn_new(struct isoburn **objpt, int flag) { struct isoburn *o; - int i, ret; + int ret; *objpt= o= (struct isoburn *) malloc(sizeof(struct isoburn)); if(o==NULL) { @@ -137,8 +137,9 @@ int isoburn_new(struct isoburn **objpt, int flag) o->fabricated_disc_status= BURN_DISC_UNREADY; o->toc= NULL; o->wrote_well= -1; - for(i=0;itarget_iso_head[i]= 0; + o->loaded_partition_offset= 0; + o->target_iso_head_size= Libisoburn_target_head_sizE; + o->target_iso_head= NULL; o->image= NULL; o->iso_data_source= NULL; o->read_pacifier= NULL; @@ -148,9 +149,15 @@ int isoburn_new(struct isoburn **objpt, int flag) o->msgs_submit_flag= 0; o->prev= NULL; o->next= NULL; + o->target_iso_head= calloc(1, o->target_iso_head_size); + if(o->target_iso_head == NULL) { + isoburn_report_iso_error(ISO_OUT_OF_MEM, "Cannot allocate overwrite buffer", + 0, "FATAL", 0); + goto failed; + } ret= iso_image_new("ISOIMAGE", &o->image); if(ret<0) { - isoburn_report_iso_error(ret, "Cannot create image", 0, "FATAL", 0); + isoburn_report_iso_error(ret, "Cannot create image object", 0, "FATAL", 0); goto failed; } isoburn_link(o, isoburn_list_start, 1); @@ -188,6 +195,8 @@ int isoburn_destroy(struct isoburn **objpt, int flag) burn_source_free(o->iso_source); if(o->iso_data_source!=NULL) iso_data_source_unref(o->iso_data_source); + if(o->target_iso_head != NULL) + free(o->target_iso_head); free((char *) o); *objpt= NULL; return(1); @@ -346,6 +355,37 @@ int isoburn_msgs_submit(struct isoburn *o, int error_code, char msg_text[], } +/* TWINTREE: + Check whether size of target_iso_head matches partition offset. + Eventually adjust size. +*/ +int isoburn_adjust_target_iso_head(struct isoburn *o, + uint32_t offst, int flag) +{ + uint8_t *new_buf; + uint32_t new_size; + + if(o->target_iso_head_size == Libisoburn_target_head_sizE + 2048 * offst) + return(1); + new_size= Libisoburn_target_head_sizE + 2048 * offst; + new_buf= calloc(1, new_size); + if(new_buf == NULL) { + isoburn_msgs_submit(o, 0x00060000, + "Cannot re-allocate overwrite buffer", 0, "FATAL", 0); + return(-1); + } + memcpy(new_buf, o->target_iso_head, + o->target_iso_head_size < new_size ? o->target_iso_head_size : new_size); + free(o->target_iso_head); + o->target_iso_head= new_buf; + o->target_iso_head_size= new_size; + if(o->nwa == o->zero_nwa) + o->nwa= Libisoburn_overwriteable_starT + offst; + o->zero_nwa= Libisoburn_overwriteable_starT + offst; + return(1); +} + + /* @param flag bit0= modifying rather than growing bit1= prepare for early release of input drive: wait until input and then disable image data source @@ -458,10 +498,17 @@ int isoburn_prepare_disc_aux(struct burn_drive *in_d, struct burn_drive *out_d, opts->vol_expiration_time, opts->vol_effective_time, opts->vol_uuid); + ret= isoburn_adjust_target_iso_head(out_o, opts->partition_offset, 0); + if(ret <= 0) + {ret= -1; goto ex;} + iso_write_opts_set_overwrite_buf(wopts, + nwa>0 ? out_o->target_iso_head : NULL); + if(opts->no_emul_toc) { if(out_o->nwa == out_o->zero_nwa && - out_o->zero_nwa == Libisoburn_overwriteable_starT && - out_o->emulation_mode == 1) { + out_o->zero_nwa == Libisoburn_overwriteable_starT + + opts->partition_offset + && out_o->emulation_mode == 1) { out_o->nwa= 0; out_o->zero_nwa= 0; out_o->min_start_byte= 0; @@ -477,41 +524,9 @@ int isoburn_prepare_disc_aux(struct burn_drive *in_d, struct burn_drive *out_d, } iso_write_opts_set_ms_block(wopts, nwa); iso_write_opts_set_appendable(wopts, !new_img); - iso_write_opts_set_overwrite_buf(wopts, - nwa>0 ? out_o->target_iso_head : NULL); - - if (opts->partition_offset > 0) { - - if (nwa > 0) { - /* >>> refuse because for now this works only for single session with - no_emul_toc - <<< to be removed when multi session stuff is fully ready - */ - isoburn_msgs_submit(in_o, 0x00060000, - "Programming error: non-zero NWA combined with partition offset", - 0, "FATAL", 0); - {ret= -4; goto ex;} - } - - iso_write_opts_set_part_offset(wopts, opts->partition_offset, - opts->partition_secs_per_head, - opts->partition_heads_per_cyl); - } -#ifdef Libisoburn_partition_offseT -#if Libisoburn_partition_offseT >= 16 - - else { - /* <<< For preliminary testing of emulated TOC and partition offset. - Problem is that only this macro can prepare the overwrite buffer - for partition offset yet. Emulated TOC does not work yet. - */ - iso_write_opts_set_part_offset(wopts, - (uint32_t) Libisoburn_partition_offseT, - 0, 0); - } - -#endif -#endif + iso_write_opts_set_part_offset(wopts, opts->partition_offset, + opts->partition_secs_per_head, + opts->partition_heads_per_cyl); ret = iso_image_create_burn_source(in_o->image, wopts, &wsrc); if (ret < 0) { diff --git a/libisoburn/isoburn.h b/libisoburn/isoburn.h index 53aa91cd..2ca76e07 100644 --- a/libisoburn/isoburn.h +++ b/libisoburn/isoburn.h @@ -36,20 +36,15 @@ int isoburn_toc_entry_new(struct isoburn_toc_entry **objpt, int isoburn_toc_entry_destroy(struct isoburn_toc_entry **o, int flag); -/* >>> TWINTREE : provisory test of partition offset with emulated TOC. - .target_iso_head must become dynamically allocated -#define Libisoburn_partition_offseT 16 +/* Minimal size of target_iso_head which is to be written during + isoburn_activate_session(). + TWINTREE: + Within this size there is everything that is needed for image access with + no partition offset. The actual target_iso_head buffer must be larger by + the evential partition offset. */ - - -/* Size of target_iso_head which is to be written during - isoburn_activate_session() -*/ -#ifdef Libisoburn_partition_offseT -#define Libisoburn_target_head_sizE (32*2048 +Libisoburn_partition_offseT*2048) -#else #define Libisoburn_target_head_sizE (32*2048) -#endif + struct isoburn { @@ -114,9 +109,14 @@ struct isoburn { int wrote_well; - /* Buffered ISO head from media (should that become part of - ecma119_read_opts ?) */ - uint8_t target_iso_head[Libisoburn_target_head_sizE]; + /* ISO head buffer to be filled by write run */ + int target_iso_head_size; + uint8_t *target_iso_head; + + /* The 2k offset which was read from a loaded image. + */ + uint32_t loaded_partition_offset; + /* Libisofs image context */ IsoImage *image; @@ -253,6 +253,13 @@ isoburn_data_source_new(struct burn_drive *d); int isoburn_data_source_shutdown(IsoDataSource *src, int flag); +/** TWINTREE: + Check whether size of target_iso_head matches offset. + Eventually adjust size. +*/ +int isoburn_adjust_target_iso_head(struct isoburn *o, + uint32_t offst, int flag); + /** * Options for image reading. (Comments here may be outdated. API getter/setter function descriptions @@ -576,6 +583,10 @@ struct isoburn_imgen_opts { written by random access before they were written sequentially. In this case, no copy of the session 1 header is maintained and no TOC will be possible. Thus writing begins sequentially at LBA 0. + + TWINTREE: + This macro gives the minimal size of an image header. It has to be + enlarged by the eventual partition offset. */ #define Libisoburn_overwriteable_starT \ ((off_t) (Libisoburn_target_head_sizE/2048)) diff --git a/libisoburn/isofs_wrap.c b/libisoburn/isofs_wrap.c index d8f8ec51..fd9a990f 100644 --- a/libisoburn/isofs_wrap.c +++ b/libisoburn/isofs_wrap.c @@ -32,8 +32,8 @@ #endif /* Xorriso_standalonE */ -#include "isoburn.h" #include "libisoburn.h" +#include "isoburn.h" #define BP(a,b) [(b) - (a) + 1] @@ -299,12 +299,111 @@ int isoburn_activate_session(struct burn_drive *drive) return 1; ret = burn_random_access_write(drive, (off_t) 0, (char*)o->target_iso_head, - Libisoburn_target_head_sizE, 1); + o->target_iso_head_size, 1); return ret; } +/** TWINTREE: + API @since 0.6.2 +*/ +int isoburn_get_img_partition_offset(struct burn_drive *drive, + uint32_t *block_offset_2k) +{ + int ret; + struct isoburn *o; + + ret = isoburn_find_emulator(&o, drive, 0); + if(ret < 0 || o == NULL) + return -1; + *block_offset_2k= o->loaded_partition_offset; + if(o->loaded_partition_offset == 0) + return(0); + if(o->target_iso_head_size == (off_t) Libisoburn_target_head_sizE + + (off_t) 2048 * (off_t) o->loaded_partition_offset) + return(1); + return(2); +} + + +/* TWINTREE: + Check for MBR signature and a first partition that starts at a 2k block + and ends where the image ends. + If not too large or too small, accept its start as partition offset. +*/ +static int isoburn_inspect_partition(struct isoburn *o, uint32_t img_size, + int flag) +{ + uint8_t *mbr, *part, buf[2048]; + uint32_t offst, numsec; + struct ecma119_pri_vol_desc *pvm; + off_t data_count; + int ret; + char msg[160]; + static int max_offst= 512 - 32; + + mbr= o->target_iso_head; + part= mbr + 446; + if(mbr[510] != 0x55 || mbr[511] != 0xAA) + return(2); /* not an MBR */ + + /* Does the first partition entry look credible ? */ + if(part[0] != 0x80 && part[0] != 0x00) + return(2); /* Invalid partition status */ + if(part[1] == 0 && part[2] == 0 && part[3] == 0) + return(2); /* Zero C/H/S start address */ + + /* Does it match the normal ISO image ? */ + offst= iso_read_lsb(part + 8, 4); + numsec= iso_read_lsb(part + 12, 4); + if(offst < 64) + return(2); /* Zero or unusably small partition start */ + if((offst % 4) || (numsec % 4)) + return(2); /* Not aligned to 2k */ + if(numsec < 72) + return(2); /* No room for volume descriptors */ + offst/= 4; + numsec/= 4; + if(offst + numsec != img_size) + return(2); /* Partition end does not match image end */ + + /* Is there a PVD at the partition start ? */ + ret = burn_read_data(o->drive, (off_t) (offst + 16) * (off_t) 2048, + (char*) buf, 2048, &data_count, 2); + if(ret <= 0) + return(2); + pvm = (struct ecma119_pri_vol_desc *) buf; + if (strncmp((char*) pvm->std_identifier, "CD001", 5) != 0) + return(2); /* not a PVD */ + if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1 + || pvm->file_structure_version[0] != 1 ) + return(2); /* failed sanity check */ + + if(iso_read_lsb(pvm->vol_space_size, 4) + offst != img_size) + return(2); /* Image ends do not match */ + + /* Now it is credible. Not yet clear is whether it is acceptable. */ + o->loaded_partition_offset= offst; + + /* If the partition start is too large: Report but do not accept. */ + if(offst > max_offst) { /* Not more than 1 MB of .target_iso_head */ + sprintf(msg, + "Detected partition offset of %.f blocks. Maximum for load buffer is %d", + (double) offst, max_offst); + isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "WARNING", 0); + return(3); + } + + /* Accept partition start and adjust buffer size */ + ret= isoburn_adjust_target_iso_head(o, offst, 0); + if(ret <= 0) + return(ret); + + return(1); +} + + /** Initialize the emulation of multi-session on random access media. The need for emulation is confirmed already. @param o A freshly created isoburn object. isoburn_create_data_source() was @@ -337,8 +436,8 @@ int isoburn_start_emulation(struct isoburn *o, int flag) if (capacity > 0 || role == 2) { /* Might be a block device on a system where libburn cannot determine its size. Try to read anyway. */ - memset(o->target_iso_head, 0, Libisoburn_target_head_sizE); - to_read = Libisoburn_target_head_sizE; + to_read = o->target_iso_head_size; + memset(o->target_iso_head, 0, to_read); if(capacity > 0 && (off_t) capacity * (off_t) 2048 < to_read) to_read = (off_t) capacity * (off_t) 2048; ret = burn_read_data(drive, (off_t) 0, (char*)o->target_iso_head, @@ -370,7 +469,7 @@ int isoburn_start_emulation(struct isoburn *o, int flag) pvm = (struct ecma119_pri_vol_desc *)(o->target_iso_head + 16 * 2048); - if (!strncmp((char*)pvm->std_identifier, "CD001", 5)) { + if (strncmp((char*)pvm->std_identifier, "CD001", 5) == 0) { off_t size; /* sanity check */ @@ -383,6 +482,9 @@ int isoburn_start_emulation(struct isoburn *o, int flag) /* ok, PVM found, set size */ size = (off_t) iso_read_lsb(pvm->vol_space_size, 4); + ret= isoburn_inspect_partition(o, (uint32_t) size, 0); + if (ret <= 0) + return ret; size *= (off_t) 2048; /* block size in bytes */ isoburn_set_start_byte(o, size, 0); o->fabricated_disc_status= BURN_DISC_APPENDABLE; @@ -395,6 +497,7 @@ int isoburn_start_emulation(struct isoburn *o, int flag) /* treat any disc in an unknown format as full */ o->fabricated_disc_status= BURN_DISC_FULL; } + return 1; } diff --git a/libisoburn/libisoburn.h b/libisoburn/libisoburn.h index 531dc874..7ab8aab2 100644 --- a/libisoburn/libisoburn.h +++ b/libisoburn/libisoburn.h @@ -1361,6 +1361,24 @@ int isoburn_set_read_pacifier(struct burn_drive *drive, int (*read_pacifier)(IsoImage*, IsoFileSource*), void *app_handle); +/** TWINTREE: + Inquire the partition offset of the loaded image. The first 512 bytes of + the image get examined whether they bear an MBR signature and a first + partition table entry which matches the size of the image. In this case + the start address is recorded as partition offset and internal buffers + get adjusted. + See also isoburn_igopt_set_part_offset(). + @since 0.6.2 + @param drive The drive with the loaded image + @param block_offset_2k returns the recognized partition offset + @return <0 = error + 0 = no partition offset recognized + 1 = acceptable non-zero offset, buffers are adjusted + 2 = offset is credible but not acceptable for buffer size +*/ +int isoburn_get_img_partition_offset(struct burn_drive *drive, + uint32_t *block_offset_2k); + /** Set the IsoImage to be used with a drive. This eventually releases the reference to the old IsoImage attached to the drive. @@ -1659,3 +1677,7 @@ void isoburn_finish(void); int isoburn_needs_emulation(struct burn_drive *d); +/* ---------------------------- Test area ----------------------------- */ + +/* no tests active, currently */ + diff --git a/xorriso/xorriso_timestamp.h b/xorriso/xorriso_timestamp.h index 2c2ae4a0..76cf9897 100644 --- a/xorriso/xorriso_timestamp.h +++ b/xorriso/xorriso_timestamp.h @@ -1 +1 @@ -#define Xorriso_timestamP "2010.09.06.103347" +#define Xorriso_timestamP "2010.09.10.170925"