diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 257e8d6..1b54fe8 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -1483,11 +1483,519 @@ static int finish_libjte(Ecma119Image *target) } +/* >>> need opportunity to just mark a partition in the older sessions +*/ + + +struct iso_interval_zeroizer { + int z_type; /* 0= $zero_start"-"$zero_end , + 1= "zero_mbrpt" , 2= "zero_gpt" , 3= "zero_apm" + */ + off_t zero_start; + off_t zero_end; +}; + +struct iso_interval_reader { + + /* Setup */ + + IsoImage *image; + + char *path; + + int flags; /* bit0= imported_iso, else local_fs + */ + + off_t start_byte; + off_t end_byte; + + struct iso_interval_zeroizer *zeroizers; + int num_zeroizers; + + char *source_pt; /* This is a parasite pointer of path. Do not free */ + + /* State information */ + + int initialized; + int is_block_aligned; + off_t cur_block; + int fd; + uint8_t read_buf[BLOCK_SIZE]; + uint8_t *pending_read_pt; + int pending_read_bytes; + off_t read_count; + int eof; + + int src_is_open; + + uint32_t apm_block_size; + +}; + +static +int iso_ivr_next_comp(char *read_pt, char **next_pt, int flag) +{ + *next_pt = NULL; + if (read_pt == NULL) + return 0; + *next_pt = strchr(read_pt, ':'); + if (*next_pt != NULL) + (*next_pt)++; + return 1; +} + +/* @param flag bit1= end number requested, forward to iso_scanf_io_size() +*/ +static +int iso_ivr_read_number(char *start_pt, char *end_pt, off_t *result, int flag) +{ + char txt[20]; + off_t num; + + if (end_pt - start_pt <= 0 || end_pt - start_pt > 16) { + iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, + "Number text too short or too long in interval reader description string"); + return ISO_MALFORMED_READ_INTVL; + } + if (end_pt - start_pt > 0) + strncpy(txt, start_pt, end_pt - start_pt); + txt[end_pt - start_pt] = 0; + + num = iso_scanf_io_size(start_pt, 1 | (flag & 2)); + if (num < 0.0 || num > 0xffffffffffff) { + iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, + "Negative or overly large number in interval reader description string"); + return ISO_MALFORMED_READ_INTVL; + } + *result = num; + return 1; +} + +static +int iso_ivr_parse_interval(char *start_pt, char *end_pt, off_t *start_byte, + off_t *end_byte, int flag) +{ + int ret; + char *m_pt; + + m_pt = strchr(start_pt, '-'); + if (m_pt == NULL) { + iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, + "Malformed byte interval in interval reader description string"); + return ISO_MALFORMED_READ_INTVL; + } + ret = iso_ivr_read_number(start_pt, m_pt, start_byte, 0); + if (ret < 0) + return ret; + ret = iso_ivr_read_number(m_pt + 1, end_pt - 1, end_byte, 2); + if (ret < 0) + return ret; + return ISO_SUCCESS; +} + +static +int iso_ivr_parse_zeroizers(struct iso_interval_reader *ivr, + char *pathpt, char *end_pt, int flag) +{ + int ret, num_zs = 1, idx, i; + char *rpt, *cpt; + + ivr->num_zeroizers = 0; + if (pathpt[0] == 0 || pathpt == end_pt) + return ISO_SUCCESS; + for(cpt = pathpt - 1; cpt != NULL && cpt < end_pt; num_zs++) + cpt = strchr(cpt + 1, ','); + LIBISO_ALLOC_MEM(ivr->zeroizers, struct iso_interval_zeroizer, num_zs); + for (i = 0; i < num_zs; i++) + ivr->zeroizers[i].zero_end = -1; + idx = 0; + for (rpt = pathpt; rpt != NULL && rpt < end_pt; idx++) { + cpt = strchr(rpt, ','); + if (cpt == NULL || cpt > end_pt) + cpt = end_pt; + + if (cpt == rpt) { + continue; + } else if (strncmp(rpt, "zero_mbrpt", cpt - rpt) == 0) { + ivr->zeroizers[idx].z_type = 1; + } else if (strncmp(rpt, "zero_gpt", cpt - rpt) == 0) { + ivr->zeroizers[idx].z_type = 2; + } else if (strncmp(rpt, "zero_apm", cpt - rpt) == 0) { + ivr->zeroizers[idx].z_type = 3; + } else { + ivr->zeroizers[idx].z_type = 0; + ret = iso_ivr_parse_interval(rpt, cpt, + &(ivr->zeroizers[idx].zero_start), + &(ivr->zeroizers[idx].zero_end), 0); + if (ret < 0) + goto ex; + } + rpt = cpt + 1; + ivr->num_zeroizers++; + } + ret = ISO_SUCCESS; +ex:; + return ret; +} + +static +int iso_ivr_parse(struct iso_interval_reader *ivr, char *path, int flag) +{ + int ret; + char *flags_pt, *interval_pt, *zeroize_pt; + + flags_pt = path; + iso_ivr_next_comp(flags_pt, &interval_pt, 0); + iso_ivr_next_comp(interval_pt, &zeroize_pt, 0); + iso_ivr_next_comp(zeroize_pt, &(ivr->source_pt), 0); + if (ivr->source_pt == NULL) { + iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, + "Not enough components in interval reader description string"); + return ISO_MALFORMED_READ_INTVL; + } + + ivr->flags = 0; + if (strncmp(flags_pt, "imported_iso", 12) == 0) { + ivr->flags |= 1; + } else if (strncmp(flags_pt, "local_fs", 8) == 0) { + ; + } else { + iso_msg_submit(-1, ISO_MALFORMED_READ_INTVL, 0, + "Unknown flag name in first component of interval reader description string"); + return ISO_MALFORMED_READ_INTVL; + } + ret = iso_ivr_parse_interval(interval_pt, zeroize_pt, &(ivr->start_byte), + &(ivr->end_byte), 0); + if (ret < 0) + goto ex; + ret = iso_ivr_parse_zeroizers(ivr, zeroize_pt, ivr->source_pt - 1, 0); + if (ret < 0) + goto ex; + + ret = ISO_SUCCESS; +ex:; + return ret; +} + +int iso_interval_reader_destroy(struct iso_interval_reader **ivr, int flag) +{ + struct iso_interval_reader *o; + + if (*ivr == NULL) + return 0; + o = *ivr; + + LIBISO_FREE_MEM(o->path); + LIBISO_FREE_MEM(o->zeroizers); + + if (o->fd != -1) + close(o->fd); + if (o->src_is_open) + (*o->image->import_src->close)(o->image->import_src); + + LIBISO_FREE_MEM(*ivr); + return ISO_SUCCESS; +} + +/* @param flag bit0= tolerate lack of import_src +*/ +int iso_interval_reader_new(IsoImage *img, char *path, + struct iso_interval_reader **ivr, + off_t *byte_count, int flag) +{ + int ret, no_img = 0; + struct iso_interval_reader *o = NULL; + + *ivr = NULL; + *byte_count = 0; + LIBISO_ALLOC_MEM(o, struct iso_interval_reader, 1); + o->image = img; + o->path = NULL; + o->zeroizers = NULL; + o->num_zeroizers = 0; + o->source_pt = NULL; + o->initialized = 0; + o->is_block_aligned = 0; + o->fd = -1; + o->pending_read_pt = NULL; + o->pending_read_bytes = 0; + o->eof = 0; + o->read_count = 0; + o->src_is_open = 0; + + o->apm_block_size = 0; + + LIBISO_ALLOC_MEM(o->path, char, strlen(path) + 1); + strcpy(o->path, path); + + ret = iso_ivr_parse(o, path, 0); + if (ret < 0) + goto ex; + + if (o->image == NULL) + no_img = 1; + else if (o->image->import_src == NULL) + no_img = 1; + if ((o->flags & 1) && no_img) { + iso_msg_submit(-1, ISO_NO_KEPT_DATA_SRC, 0, + "Interval reader lacks of data source object of imported ISO"); + if (!(flag & 1)) { + ret = ISO_BAD_PARTITION_FILE; + goto ex; + } + o->eof = 1; + } + *byte_count = o->end_byte - o->start_byte + 1; + *ivr = o; + ret = ISO_SUCCESS; +ex:; + if (ret < 0) + iso_interval_reader_destroy(&o, 0); + return ret; +} + +static +int iso_ivr_zeroize(struct iso_interval_reader *ivr, uint8_t *buf, + int buf_fill, int flag) +{ + int i; + off_t low, high, part_start, entry_count, apm_offset = -1, map_entries; + uint8_t *apm_buf; + struct iso_interval_zeroizer *zr; + + for (i = 0; i < ivr->num_zeroizers; i++) { + zr = ivr->zeroizers + i; + if (zr->z_type == 1) { /* zero_mbrpt */ + if (ivr->read_count > 0 || buf_fill < 512) + continue; + if (buf[510] != 0x55 || buf[511] != 0xaa) + continue; + memset(buf + 446, 0, 64); + + } else if (zr->z_type == 2) { /* zero_gpt */ + if (zr->zero_start <= zr->zero_end) + goto process_interval; + + if (ivr->read_count > 0 || buf_fill < 512 + 92) + continue; + if (strncmp((char *) buf + 512, "EFI PART", 8) != 0 || + buf[520] != 0 || buf[521] != 0 || buf[522] != 1 || + buf[523] != 0) + continue; + /* head_size , curr_lba , entry_size */ + if (iso_read_lsb(buf + 524, 4) != 92 || + iso_read_lsb(buf + 536, 4) != 1 || + iso_read_lsb(buf + 596, 4) != 128) + continue; + part_start = iso_read_lsb(buf + 584, 4); + entry_count = iso_read_lsb(buf + 592, 4); + if (part_start < 2 || part_start + (entry_count + 3) / 4 > 64) + continue; + zr->zero_start = part_start * 512; + zr->zero_end = (part_start + (entry_count + 3) / 4) * 512 - 1; + memset(buf + 512, 0, 92); + + } else if (zr->z_type == 3) { /* zero_apm */ + if (zr->zero_start <= zr->zero_end) + goto process_interval; + + if (ivr->read_count == 0) { + if (buf_fill < 512) + continue; + if (buf[0] != 'E' || buf[1] != 'R') + continue; + ivr->apm_block_size = iso_read_msb(buf + 2, 2); + if ((ivr->apm_block_size != 512 && + ivr->apm_block_size != 1024 && + ivr->apm_block_size != 2048) || + ((uint32_t) buf_fill) < ivr->apm_block_size) { + ivr->apm_block_size = 0; + continue; + } + if (ivr->read_count + buf_fill >= 2 * ivr->apm_block_size) + apm_offset = ivr->apm_block_size; + } else if (ivr->read_count == 2048 && + ivr->apm_block_size == 2048 && buf_fill == 2048) { + apm_offset = 0; + } + if (apm_offset < 0) + continue; + + /* Check for first APM entry */ + apm_buf = buf + apm_offset; + if(apm_buf[0] != 'P' || apm_buf[1] != 'M') + continue; + if (iso_read_msb(apm_buf + 8, 4) != 1) + continue; + map_entries = iso_read_msb(apm_buf + 4, 4); + if ((1 + map_entries) * ivr->apm_block_size > 16 * 2048) + continue; + zr->zero_start = ivr->apm_block_size; + zr->zero_end = (1 + map_entries) * ivr->apm_block_size; + } +process_interval:; + /* If an interval is defined by now: zeroize its intersection with buf + */ + if (zr->zero_start <= zr->zero_end) { + low = ivr->read_count >= zr->zero_start ? + ivr->read_count : zr->zero_start; + high = ivr->read_count + buf_fill - 1 <= zr->zero_end ? + ivr->read_count + buf_fill - 1 : zr->zero_end; + if (low <= high) + memset(buf + low - ivr->read_count, 0, high - low + 1); + } + } + + return ISO_SUCCESS; +} + +int iso_interval_reader_read(struct iso_interval_reader *ivr, uint8_t *buf, + int *buf_fill, int flag) +{ + int ret, read_done, to_copy, initializing = 0; + IsoDataSource *src; + uint8_t *read_buf; + off_t to_read; + + *buf_fill = 0; + src = ivr->image->import_src; + + if (ivr->eof) { +eof:; + memset(buf, 0, BLOCK_SIZE); + return 0; + } + + if (ivr->initialized) { + ivr->cur_block++; + } else { + initializing = 1; + ivr->cur_block = ivr->start_byte / BLOCK_SIZE; + ivr->is_block_aligned = !(ivr->start_byte % BLOCK_SIZE); + if (ivr->flags & 1) { + if (src == NULL) + goto eof; + ret = (*src->open)(src); + if (ret < 0) { + ivr->eof = 1; + return ret; + } + ivr->src_is_open = 1; + } else { + ivr->fd = open(ivr->source_pt, O_RDONLY); + if (ivr->fd == -1) { + iso_msg_submit(-1, ISO_BAD_PARTITION_FILE, 0, + "Cannot open local file for interval reading"); + ivr->eof = 1; + return ISO_BAD_PARTITION_FILE; + } + if (ivr->cur_block != 0) { + if (lseek(ivr->fd, ivr->cur_block * BLOCK_SIZE, SEEK_SET) == + -1) { + iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, + "Cannot address interval start in local file"); + ivr->eof = 1; + goto eof; + } + } + } + ivr->initialized = 1; + } + if (ivr->is_block_aligned) { + read_buf = buf; + } else { +process_pending:; + read_buf = ivr->read_buf; + /* Copy pending bytes from previous read */ + if (ivr->pending_read_bytes > 0) { + memcpy(buf, ivr->pending_read_pt, ivr->pending_read_bytes); + *buf_fill = ivr->pending_read_bytes; + ivr->pending_read_bytes = 0; + } + } + + /* Read next block */ + read_done = 0; + if (ivr->cur_block * BLOCK_SIZE <= ivr->end_byte) { + if (ivr->flags & 1) { + ret = (*src->read_block)(src, (uint32_t) ivr->cur_block, read_buf); + if (ret < 0) { + if (iso_error_get_severity(ret) > 0x68000000) /* > FAILURE */ + return ret; + iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, + "Premature EOF while interval reading from imported ISO"); + ivr->eof = 1; + } + read_done = BLOCK_SIZE; + } else { + read_done = 0; + to_read = ivr->end_byte - ivr->start_byte + 1 - ivr->read_count; + if (to_read > BLOCK_SIZE) + to_read = BLOCK_SIZE; + while (read_done < to_read) { + ret = read(ivr->fd, read_buf, to_read - read_done); + if (ret == -1) { + iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, + "Read error while interval reading from local file"); + ivr->eof = 1; + break; + } else if (ret == 0) { + iso_msg_submit(-1, ISO_INTVL_READ_PROBLEM, 0, + "Premature EOF while interval reading from local file"); + ivr->eof = 1; + break; + } else + read_done += ret; + } + } + } + if (ivr->is_block_aligned) { + *buf_fill = read_done; + } else if (initializing) { + ivr->pending_read_pt = ivr->read_buf + + (ivr->start_byte - ivr->cur_block * BLOCK_SIZE); + ivr->pending_read_bytes = (((off_t) ivr->cur_block) + 1) * BLOCK_SIZE - + ivr->start_byte; + initializing = 0; + goto process_pending; + + } else if (read_done > 0) { + /* Copy bytes from new read */ + to_copy = read_done > BLOCK_SIZE - *buf_fill ? + BLOCK_SIZE - *buf_fill : read_done; + memcpy(buf + *buf_fill, ivr->read_buf, to_copy); + *buf_fill += to_copy; + ivr->pending_read_pt = ivr->read_buf + to_copy; + ivr->pending_read_bytes = read_done - to_copy; + } + if (ivr->start_byte + ivr->read_count + *buf_fill - 1 > ivr->end_byte) { + *buf_fill = ivr->end_byte - ivr->start_byte + 1 - ivr->read_count; + ivr->eof = 1; + } + + if (*buf_fill < BLOCK_SIZE) + memset(buf + *buf_fill, 0, BLOCK_SIZE - *buf_fill); + + ret = iso_ivr_zeroize(ivr, buf, *buf_fill, 0); + if (ret < 0) + return ret; + + ivr->read_count += *buf_fill; + + return ISO_SUCCESS; +} + + int iso_write_partition_file(Ecma119Image *target, char *path, uint32_t prepad, uint32_t blocks, int flag) { + + struct iso_interval_reader *ivr = NULL; + int buf_fill; + off_t byte_count; FILE *fp = NULL; - uint32_t i; + + uint32_t i, intvl_blocks; uint8_t *buf = NULL; int ret; @@ -1498,33 +2006,46 @@ int iso_write_partition_file(Ecma119Image *target, char *path, goto ex; } -/* >>> need opportunity to read from input ISO image - resp. to just mark a partition in the older sessions -*/; + if (flag & 1) { + ret = iso_interval_reader_new(target->image, path, + &ivr, &byte_count, 0); + if (ret < 0) + goto ex; + intvl_blocks = (byte_count + BLOCK_SIZE - 1) / BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + ret = iso_interval_reader_read(ivr, buf, &buf_fill, 0); + if (ret < 0) + goto ex; + ret = iso_write(target, buf, BLOCK_SIZE); + if (ret < 0) + goto ex; + } + } else { + fp = fopen(path, "rb"); + if (fp == NULL) + {ret = ISO_BAD_PARTITION_FILE; goto ex;} - fp = fopen(path, "rb"); - if (fp == NULL) - {ret = ISO_BAD_PARTITION_FILE; goto ex;} - - for (i = 0; i < blocks; i++) { - memset(buf, 0, BLOCK_SIZE); - if (fp != NULL) { - ret = fread(buf, 1, BLOCK_SIZE, fp); - if (ret != BLOCK_SIZE) { - fclose(fp); - fp = NULL; + for (i = 0; i < blocks; i++) { + memset(buf, 0, BLOCK_SIZE); + if (fp != NULL) { + ret = fread(buf, 1, BLOCK_SIZE, fp); + if (ret != BLOCK_SIZE) { + fclose(fp); + fp = NULL; + } + } + ret = iso_write(target, buf, BLOCK_SIZE); + if (ret < 0) { + fclose(fp); + goto ex; } } - ret = iso_write(target, buf, BLOCK_SIZE); - if (ret < 0) { + if (fp != NULL) fclose(fp); - goto ex; - } } - if (fp != NULL) - fclose(fp); ret = ISO_SUCCESS; ex:; + iso_interval_reader_destroy(&ivr, 0); LIBISO_FREE_MEM(buf); return ret; } @@ -1592,9 +2113,10 @@ void *write_function(void *arg) if (target->opts->appended_partitions[i][0] == 0) continue; res = iso_write_partition_file(target, - target->opts->appended_partitions[i], - target->appended_part_prepad[i], - target->appended_part_size[i], 0); + target->opts->appended_partitions[i], + target->appended_part_prepad[i], + target->appended_part_size[i], + target->opts->appended_part_flags[i] & 1); if (res < 0) goto write_error; } @@ -2825,9 +3347,14 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile) wopts->tail_blocks = 0; wopts->prep_partition = NULL; + wopts->prep_part_flag = 0; wopts->efi_boot_partition = NULL; - for (i = 0; i < ISO_MAX_PARTITIONS; i++) + wopts->efi_boot_part_flag = 0; + for (i = 0; i < ISO_MAX_PARTITIONS; i++) { wopts->appended_partitions[i] = NULL; + wopts->appended_part_types[i] = 0; + wopts->appended_part_flags[i] = 0; + } wopts->appended_as_gpt = 0; wopts->ascii_disc_label[0] = 0; wopts->will_cancel = 0; @@ -3510,6 +4037,7 @@ int iso_write_opts_set_prep_img(IsoWriteOpts *opts, char *image_path, int flag) opts->prep_partition = strdup(image_path); if (opts->prep_partition == NULL) return ISO_OUT_OF_MEM; + opts->prep_part_flag = (flag & 1); return ISO_SUCCESS; } @@ -3523,6 +4051,7 @@ int iso_write_opts_set_efi_bootp(IsoWriteOpts *opts, char *image_path, opts->efi_boot_partition = strdup(image_path); if (opts->efi_boot_partition == NULL) return ISO_OUT_OF_MEM; + opts->efi_boot_part_flag = (flag & 1); return ISO_SUCCESS; } @@ -3539,6 +4068,7 @@ int iso_write_opts_set_partition_img(IsoWriteOpts *opts, int partition_number, if (opts->appended_partitions[partition_number - 1] == NULL) return ISO_OUT_OF_MEM; opts->appended_part_types[partition_number - 1] = partition_type; + opts->appended_part_flags[partition_number - 1] = (flag & 1); return ISO_SUCCESS; } diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index f151bce..23cc223 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -468,17 +468,20 @@ struct iso_write_opts { to HFS+/FAT and IsoFileSrc areas and marked by an MBR partition entry. */ char *prep_partition; + int prep_part_flag; /* Eventual disk file path of an EFI system partition image which shall be prepended to HFS+/FAT and IsoFileSrc areas and marked by a GPT entry. */ char *efi_boot_partition; + int efi_boot_part_flag; /* Eventual disk file paths of prepared images which shall be appended after the ISO image and described by partiton table entries in a MBR */ char *appended_partitions[ISO_MAX_PARTITIONS]; uint8_t appended_part_types[ISO_MAX_PARTITIONS]; + int appended_part_flags[ISO_MAX_PARTITIONS]; /* If 1: With appended partitions: create protective MBR and mark by GPT */ diff --git a/libisofs/fs_image.c b/libisofs/fs_image.c index e4eccef..714564a 100644 --- a/libisofs/fs_image.c +++ b/libisofs/fs_image.c @@ -124,6 +124,12 @@ struct iso_read_opts * submission by iso_write_opts_set_system_area(data, 0). */ int load_system_area; + + /** + * Keep data source of imported ISO filesystem in IsoImage.import_src + */ + int keep_import_src; + }; /** @@ -5620,6 +5626,10 @@ int iso_image_import(IsoImage *image, IsoDataSource *src, data = fs->data; + if (opts->keep_import_src) { + iso_data_source_ref(src); + image->import_src = src; + } if (opts->load_system_area) { if (image->system_area_data != NULL) free(image->system_area_data); @@ -6029,6 +6039,7 @@ int iso_read_opts_new(IsoReadOpts **opts, int profile) ropts->noaaip = 1; ropts->nomd5 = 1; ropts->load_system_area = 0; + ropts->keep_import_src = 0; *opts = ropts; return ISO_SUCCESS; @@ -6173,6 +6184,15 @@ int iso_read_opts_load_system_area(IsoReadOpts *opts, int mode) return ISO_SUCCESS; } +int iso_read_opts_keep_import_src(IsoReadOpts *opts, int mode) +{ + if (opts == NULL) { + return ISO_NULL_POINTER; + } + opts->keep_import_src = mode & 1; + return ISO_SUCCESS; +} + /** * Destroy an IsoReadImageFeatures object obtained with iso_image_import. */ diff --git a/libisofs/image.c b/libisofs/image.c index 30ec5b2..bf4f5a6 100644 --- a/libisofs/image.c +++ b/libisofs/image.c @@ -185,6 +185,7 @@ int iso_image_new(const char *name, IsoImage **image) img->hppa_kernel_64 = NULL; img->hppa_ramdisk = NULL; img->alpha_boot_image = NULL; + img->import_src = NULL; img->builder_ignore_acl = 1; img->builder_ignore_ea = 1; img->inode_counter = 0; @@ -245,6 +246,8 @@ void iso_image_unref(IsoImage *image) iso_image_set_hppa_palo(image, NULL, NULL, NULL, NULL, NULL, 1); if (image->alpha_boot_image != NULL) free(image->alpha_boot_image); + if (image->import_src != NULL) + iso_data_source_unref(image->import_src); free(image->volset_id); free(image->volume_id); free(image->publisher_id); diff --git a/libisofs/image.h b/libisofs/image.h index 96977e1..fcf5aca 100644 --- a/libisofs/image.h +++ b/libisofs/image.h @@ -64,6 +64,8 @@ struct Iso_Image /* Eventually loaded system area data, or NULL */ char *system_area_data; /* Prescribed/detected options, see iso_write_opts_set_system_area() */ + /* >>> Needs to be coordinated with .imported_sa_info->system_area_options + */ int system_area_options; /* @@ -100,6 +102,11 @@ struct Iso_Image IsoFilesystem *fs; /** + * Block storage of imported ISO if demanded by IsoReadOpts. + */ + IsoDataSource *import_src; + + /* * Default builder to use when adding files to the image tree. */ IsoNodeBuilder *builder; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 85e63fb..f6d0f54 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -2409,6 +2409,117 @@ int iso_write_opts_detach_jte(IsoWriteOpts *opts, void **libjte_handle); */ int iso_write_opts_set_tail_blocks(IsoWriteOpts *opts, uint32_t num_blocks); + +/** + * The libisofs interval reader is used internally and offered by libisofs API: + * @since 1.4.0 + * The functions iso_write_opts_set_prep_img(), iso_write_opts_set_efi_bootp(), + * and iso_write_opts_set_partition_img() accept with their flag bit0 an + * interval reader description string instead of a disk path. + * The API calls are iso_interval_reader_new(), iso_interval_reader_read(), + * and iso_interval_reader_destroy(). + * The data may be cut out and optionally partly zeroized. + * + * An interval reader description string has the form: + * $flags:$interval:$zeroizers:$source + * The component $flags modifies the further interpretation: + * "local_fs" ....... demands to read from a file depicted by the path in + * $source. + * "imported_iso" ... demands to read from the IsoDataSource object that was + * used with iso_image_import() when + * iso_read_opts_keep_import_src() was enabled. + * The text in $source is ignored. + * The application has to ensure that reading from the + * import source does not disturb production of the new + * ISO session. Especially this would be the case if the + * import source is the same libburn drive with a + * sequential optical medium to which the new session shall + * get burned. + * The component $interval consists of two byte address numbers separated + * by a "-" character. E.g. "0-429" means to read bytes 0 to 429. + * The component $zeroizers consists of zero or more comma separated strings. + * They define which part of the read data to zeroize. Byte number 0 means + * the byte read from the $interval start address. + * Each string may be either + * "zero_mbrpt" ..... demands to zeroize bytes 446 to 509 of the read data if + * bytes 510 and 511 bear the MBR signature 0x55 0xaa. + * "zero_gpt" ....... demands to check for a GPT header in bytes 512 to 1023, + * to zeroize it and its partition table blocks. + * "zero_apm" ....... demands to check for an APM block 0 and to zeroize + * its partition table blocks. But not the block 0 itself, + * because it could be actually MBR x86 machine code. + * $zero_start"-"$zero_end ... demands to zeroize the read-in bytes beginning + * with number $zero_start and ending after $zero_end. + * The component $source is the file path with "local_fs", and ignored with + * "imported_iso". + * Byte numbers may be scaled by a suffix out of {k,m,g,t,s,d} meaning + * multiplication by {1024, 1024k, 1024m, 1024g, 2048, 512}. A scaled value + * as end number depicts the last byte of the scaled range. + * E.g. "0d-0d" is "0-511". + * Examples: + * "local_fs:0-32767:zero_mbrpt,zero_gpt,440-443:/tmp/template.iso" + * "imported_iso:45056d-47103d::" + */ +struct iso_interval_reader; + +/** + * Create an interval reader object. + * + * @param img + * The IsoImage object which can provide the "imported_iso" data source. + * @param path + * The interval reader description string. See above. + * @param ivr + * Returns in case of success a pointer to the created object. + * Dispose it by iso_interval_reader_destroy() when no longer needed. + * @param byte_count + * Returns in case of success the number of bytes in the interval. + * @param flag + * bit0= tolerate (src == NULL) with "imported_iso". + * (Will immediately cause eof of interval input.) + * @return + * ISO_SUCCESS or error (which is < 0) + * + * @since 1.4.0 + */ +int iso_interval_reader_new(IsoImage *img, char *path, + struct iso_interval_reader **ivr, + off_t *byte_count, int flag); + +/** + * Dispose an interval reader object. + * + * @param ivr + * The reader object to be disposed. *ivr will be set to NULL. + * @return + * ISO_SUCCESS or error (which is < 0) + * + * @since 1.4.0 + */ +int iso_interval_reader_destroy(struct iso_interval_reader **ivr, int flag); + +/** + * Read the next block of 2048 bytes from an interval reader object. + * If end-of-input happens, the interval will get filled up with 0 bytes. + * + * @param ivr + * The object to read from. + * @param buf + * Pointer to memory for filling in at least 2048 bytes. + * @param buf_fill + * Will in case of success return the number of valid bytes. + * If this is smaller than 2048, then end-of-interval has occured. + * @param flag + * Unused yet. Submit 0. + * @return + * ISO_SUCCESS if data were read, 0 if not, < 0 if error + * + * @since 1.4.0 + */ +int iso_interval_reader_read(struct iso_interval_reader *ivr, uint8_t *buf, + int *buf_fill, int flag); + + /** * Copy a data file from the local filesystem into the emerging ISO image. * Mark it by an MBR partition entry as PreP partition and also cause @@ -2427,10 +2538,14 @@ int iso_write_opts_set_tail_blocks(IsoWriteOpts *opts, uint32_t num_blocks); * @param opts * The option set to be manipulated. * @param image_path - * File address in the local file system. + * File address in the local file system or instructions for interval + * reader. See flag bit0. * NULL revokes production of the PreP partition. * @param flag - * Reserved for future usage, set to 0. + * bit0= The path contains instructions for the interval reader. + * See above. + * @since 1.4.0 + * All other bits are reserved for future usage. Set them to 0. * @return * ISO_SUCCESS or error * @@ -2457,10 +2572,14 @@ int iso_write_opts_set_prep_img(IsoWriteOpts *opts, char *image_path, * @param opts * The option set to be manipulated. * @param image_path - * File address in the local file system. + * File address in the local file system or instructions for interval + * reader. See flag bit0. * NULL revokes production of the EFI boot partition. * @param flag - * Reserved for future usage, set to 0. + * bit0= The path contains instructions for the interval reader + * See above. + * @since 1.4.0 + * All other bits are reserved for future usage. Set them to 0. * @return * ISO_SUCCESS or error * @@ -2488,7 +2607,8 @@ int iso_write_opts_set_efi_bootp(IsoWriteOpts *opts, char *image_path, * unclaimable space before partition 1. * Range with SUN Disk Label: 2 to 8. * @param image_path - * File address in the local file system. + * File address in the local file system or instructions for interval + * reader. See flag bit0. * With SUN Disk Label: an empty name causes the partition to become * a copy of the next lower partition. * @param image_type @@ -2496,7 +2616,9 @@ int iso_write_opts_set_efi_bootp(IsoWriteOpts *opts, char *image_path, * Linux Native Partition = 0x83. See fdisk command L. * This parameter is ignored with SUN Disk Label. * @param flag - * bit0= The path may contain instructions for the interval reader + * bit0= The path contains instructions for the interval reader + * See above. + * @since 1.4.0 * All other bits are reserved for future usage. Set them to 0. * @return * ISO_SUCCESS or error @@ -2816,6 +2938,24 @@ int iso_read_opts_auto_input_charset(IsoReadOpts *opts, int mode); */ int iso_read_opts_load_system_area(IsoReadOpts *opts, int mode); +/** + * Control whether to keep a reference to the IsoDataSource object which + * allows access to the blocks of the imported ISO 9660 filesystem. + * This is needed if the interval reader shall read from "imported_iso". + * + * @param opts + * The option set to be manipulated + * @param mode + * Bitfield for control purposes: + * bit0= Keep a reference to the IsoDataSource until the IsoImage object + * gets disposed by its final iso_image_unref(). + * Submit any other bits with value 0. + * + * @since 1.4.0 + * + */ +int iso_read_opts_keep_import_src(IsoReadOpts *opts, int mode); + /** * Import a previous session or image, for growing or modify. * @@ -8161,7 +8301,17 @@ int iso_conv_name_chars(IsoWriteOpts *opts, char *name, size_t name_len, #define ISO_INQ_SYSAREA_PROP 0xE830FE6C /** DEC Alpha Boot Loader file is not a data file (FAILURE, HIGH, -405) */ -#define ISO_ALPHA_BOOT_NOTREG 0xE830FE6A +#define ISO_ALPHA_BOOT_NOTREG 0xE830FE6B + +/** No data source of imported ISO image available (WARNING, HIGH, -406) */ +#define ISO_NO_KEPT_DATA_SRC 0xD030FE6A + +/** Malformed description string for interval reader (FAILURE, HIGH, -407) */ +#define ISO_MALFORMED_READ_INTVL 0xE830FE69 + +/** Unreadable file, premature EOF, or failure to seek for interval reader + (WARNING, HIGH, -408) */ +#define ISO_INTVL_READ_PROBLEM 0xD030FE68 /* Internal developer note: diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index 0a97969..45dfd0f 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -141,6 +141,9 @@ iso_image_unref; iso_image_update_sizes; iso_init; iso_init_with_flag; +iso_interval_reader_destroy; +iso_interval_reader_new; +iso_interval_reader_read; iso_lib_is_compatible; iso_lib_version; iso_local_attr_support; @@ -216,6 +219,7 @@ iso_read_image_features_has_joliet; iso_read_image_features_has_rockridge; iso_read_opts_auto_input_charset; iso_read_opts_free; +iso_read_opts_keep_import_src; iso_read_opts_load_system_area; iso_read_opts_new; iso_read_opts_set_default_gid; diff --git a/libisofs/messages.c b/libisofs/messages.c index 3cb4e8f..3910623 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -521,6 +521,12 @@ const char *iso_error_to_msg(int errcode) return "Unrecognized inquiry for system area property"; case ISO_ALPHA_BOOT_NOTREG: return "DEC Alpha Boot Loader file is not a data file"; + case ISO_NO_KEPT_DATA_SRC: + return "No data source of imported ISO image available"; + case ISO_MALFORMED_READ_INTVL: + return "Malformed description string for interval reader"; + case ISO_INTVL_READ_PROBLEM: + return "Unreadable file, premature EOF, or failure to seek for interval reader"; default: return "Unknown error"; } diff --git a/libisofs/system_area.c b/libisofs/system_area.c index 886c2be..4fbad2c 100644 --- a/libisofs/system_area.c +++ b/libisofs/system_area.c @@ -107,12 +107,26 @@ void iso_compute_cyl_head_sec(uint64_t img_blocks, int hpc, int sph, } } -static uint32_t compute_partition_size(char *disk_path, uint32_t *size, - int flag) +/* @param flag bit0= The path contains instructions for the interval reader +*/ +static int compute_partition_size(Ecma119Image *t, char *disk_path, + uint32_t *size, int flag) { int ret; off_t num; struct stat stbuf; + struct iso_interval_reader *ivr; + off_t byte_count; + + if (flag & 1) { + ret = iso_interval_reader_new(t->image, disk_path, + &ivr, &byte_count, 0); + if (ret < 0) + return ret; + *size = (byte_count + BLOCK_SIZE - 1) / BLOCK_SIZE; + iso_interval_reader_destroy(&ivr, 0); + return ISO_SUCCESS; + } *size = 0; ret = stat(disk_path, &stbuf); @@ -150,8 +164,8 @@ int iso_compute_append_partitions(Ecma119Image *t, int flag) continue; if (t->opts->appended_partitions[i][0] == 0) continue; - ret = compute_partition_size(t->opts->appended_partitions[i], &size, - 0); + ret = compute_partition_size(t, t->opts->appended_partitions[i], &size, + t->opts->appended_part_flags[i]); if (ret < 0) return ret; add_pos = 0; @@ -255,7 +269,7 @@ static int write_mbr_partition_entry(int partition_number, int partition_type, */ static int make_grub_msdos_label(uint32_t img_blocks, int sph, int hpc, - uint8_t *buf, int flag) + uint8_t part_type, uint8_t *buf, int flag) { uint8_t *wpt; uint32_t end_lba, end_sec, end_head, end_cyl; @@ -286,7 +300,7 @@ int make_grub_msdos_label(uint32_t img_blocks, int sph, int hpc, *(wpt++) = 0; /* 0xcd (partition type) */ - *(wpt++) = 0xcd; + *(wpt++) = part_type; /* [3 bytes of C/H/S end], */ *(wpt++) = end_head; @@ -1660,7 +1674,7 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) int first_partition = 1, last_partition = 4, apm_flag, part_type = 0; int gpt_count = 0, gpt_idx[128], apm_count = 0, no_boot_mbr = 0; int offset_flag = 0; - uint32_t img_blocks, gpt_blocks, mbrp1_blocks; + uint32_t img_blocks, gpt_blocks, mbrp1_blocks, pml_blocks; uint64_t blk; uint8_t *wpt; @@ -1767,8 +1781,16 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) if (t->mbr_req_count == 0){ /* Write GRUB protective msdos label, i.e. a simple partition table */ - ret = make_grub_msdos_label(img_blocks, t->partition_secs_per_head, - t->partition_heads_per_cyl, buf, 0); + if (t->gpt_req_count > 0) { + part_type = 0xee; + pml_blocks = gpt_blocks; + } else { + part_type = 0xcd; + pml_blocks = img_blocks; + } + ret = make_grub_msdos_label(pml_blocks, t->partition_secs_per_head, + t->partition_heads_per_cyl, + (uint8_t) part_type, buf, 0); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; } @@ -1826,7 +1848,8 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) sa_type == 0 && t->mbr_req_count == 0) { /* Write a simple partition table. */ ret = make_grub_msdos_label(img_blocks, t->partition_secs_per_head, - t->partition_heads_per_cyl, buf, 2); + t->partition_heads_per_cyl, + (uint8_t) 0xcd, buf, 2); if (ret != ISO_SUCCESS) /* error should never happen */ return ISO_ASSERT_FAILURE; if (t->opts->appended_as_gpt && t->have_appended_partitions) { @@ -1961,7 +1984,8 @@ int iso_align_isohybrid(Ecma119Image *t, int flag) img_blocks = t->curblock + t->opts->tail_blocks + t->gpt_backup_size; imgsize = ((off_t) img_blocks) * (off_t) 2048; - if (((t->system_area_options & 3) || always_align) + if ((!(t->opts->appended_as_gpt && t->have_appended_partitions)) + && ((t->system_area_options & 3) || always_align) && (off_t) (t->partition_heads_per_cyl * t->partition_secs_per_head * 1024) * (off_t) 512 < imgsize) { /* Choose small values which can represent the image size */ @@ -2585,7 +2609,7 @@ static int partprepend_writer_compute_data_blocks(IsoImageWriter *writer) t->efi_boot_part_size += (src->sections[i].size + 2047) / 2048; } } else { - ret = compute_partition_size(t->opts->efi_boot_partition, + ret = compute_partition_size(t, t->opts->efi_boot_partition, &(t->efi_boot_part_size), 0); if (ret < 0) return ret; @@ -2615,7 +2639,7 @@ static int partprepend_writer_compute_data_blocks(IsoImageWriter *writer) } if (t->opts->prep_partition != NULL) { - ret = compute_partition_size(t->opts->prep_partition, + ret = compute_partition_size(t, t->opts->prep_partition, &(t->prep_part_size), 0); if (ret < 0) return ret; @@ -2672,14 +2696,16 @@ static int partprepend_writer_write_data(IsoImageWriter *writer) NULL, NULL, 0); } else { ret = iso_write_partition_file(t, t->opts->efi_boot_partition, - (uint32_t) 0, t->efi_boot_part_size, 0); + (uint32_t) 0, t->efi_boot_part_size, + t->opts->efi_boot_part_flag & 1); } if (ret < 0) return ret; } if (t->opts->prep_partition != NULL && t->prep_part_size) { ret = iso_write_partition_file(t, t->opts->prep_partition, - (uint32_t) 0, t->prep_part_size, 0); + (uint32_t) 0, t->prep_part_size, + t->opts->prep_part_flag & 1); if (ret < 0) return ret; } @@ -2762,7 +2788,8 @@ static int partappend_writer_write_data(IsoImageWriter *writer) res = iso_write_partition_file(target, target->opts->appended_partitions[i], target->appended_part_prepad[i], - target->appended_part_size[i], 0); + target->appended_part_size[i], + target->appended_part_flags[i] & 1); if (res < 0) return res; target->curblock += target->appended_part_size[i]; diff --git a/libisofs/util.c b/libisofs/util.c index cd62b12..873b86a 100644 --- a/libisofs/util.c +++ b/libisofs/util.c @@ -2305,3 +2305,41 @@ int iso_clone_mgtd_mem(char *in, char **out, size_t size) return iso_clone_mem(in, out, size); } + +/** Convert a text into a number of type double and multiply it by unit code + [kmgt] (2^10 to 2^40) or [s] (2048) or [d] (512). + (Also accepts capital letters.) + @param text Input like "42", "223062s", "3m" or "-1g" + @param flag Bitfield for control purposes: + bit0= return -1 rathern than 0 on failure + bit1= if scaled then compute the last byte of the last unit + @return The derived value +*/ +off_t iso_scanf_io_size(char *text, int flag) +{ + int c; + off_t ret = 0, fac = 1; + char *rpt; + + for (rpt = text; *rpt >= '0' && *rpt <= '9'; rpt++) + ret = ret * 10 + (*rpt - '0'); + if (rpt == text) + return (off_t) (flag & 1 ? -1 : 0); + c = *rpt; + if (c=='k' || c=='K') + fac = 1024; + else if (c=='m' || c=='M') + fac = 1024 * 1024; + else if (c=='g' || c=='G') + fac = 1024 * 1024 * 1024; + else if (c=='t' || c=='T') + fac = ((off_t) 1024) * 1024 * 1024 * 1024; + else if (c=='s' || c=='S') + fac = 2048; + else if (c=='d' || c=='D') + fac = 512; + ret *= fac; + if (flag & 2) + ret += fac - 1; + return ret; +} diff --git a/libisofs/util.h b/libisofs/util.h index cb73e41..a2cdb34 100644 --- a/libisofs/util.h +++ b/libisofs/util.h @@ -21,6 +21,10 @@ #include +#ifdef HAVE_STDLIB_H +#include +#endif + #ifndef MAX # define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif @@ -627,6 +631,16 @@ int iso_clone_mem(char *in, char **out, size_t size); */ int iso_clone_mgtd_mem(char *in, char **out, size_t size); +/** Convert a text into a number of type double and multiply it by unit code + [kmgt] (2^10 to 2^40) or [s] (2048) or [d] (512). + (Also accepts capital letters.) + @param text Input like "42", "223062s", "3m" or "-1g" + @param flag Bitfield for control purposes: + bit0= return -1 rathern than 0 on failure + bit1= if scaled then compute the last byte of the last unit + @return The derived value +*/ +off_t iso_scanf_io_size(char *text, int flag); /* ------------------------------------------------------------------------- */