Merge level3 branch, adding support for ISO-9660 Level 3.

release-1.5.4.branch
Vreixo Formoso 14 years ago
commit c1a7702f52
  1. 13
      demo/iso_read.c
  2. 58
      doc/devel/cookbook/Multi-Extent.txt
  3. 44
      libisofs/buffer.c
  4. 186
      libisofs/ecma119.c
  5. 108
      libisofs/ecma119.h
  6. 2
      libisofs/ecma119_tree.c
  7. 181
      libisofs/eltorito.c
  8. 80
      libisofs/filesrc.c
  9. 28
      libisofs/filesrc.h
  10. 835
      libisofs/fs_image.c
  11. 59
      libisofs/iso1999.c
  12. 61
      libisofs/joliet.c
  13. 40
      libisofs/libisofs.h
  14. 246
      libisofs/node.c
  15. 111
      libisofs/node.h
  16. 51
      libisofs/stream.c
  17. 28
      libisofs/stream.h
  18. 274
      test/test_rockridge.c

@ -39,7 +39,7 @@ print_type(mode_t mode)
case S_IFREG: printf("[R] "); break;
case S_IFBLK: printf("[B] "); break;
case S_IFDIR: printf("[D] "); break;
case S_IFIFO: printf("[F] "); break;
case S_IFIFO: printf("[F] "); break;
}
}
@ -51,6 +51,7 @@ print_file_src(IsoFileSource *file)
iso_file_source_lstat(file, &info);
print_type(info.st_mode);
print_permissions(info.st_mode);
printf(" %10llu ", info.st_size);
//printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino);
name = iso_file_source_get_name(file);
printf(" %s", name);
@ -116,7 +117,7 @@ int main(int argc, char **argv)
iso_init();
iso_set_msgs_severities("NEVER", "ALL", "");
result = iso_data_source_new_from_file(argv[1], &src);
if (result < 0) {
printf ("Error creating data source\n");
@ -134,10 +135,10 @@ int main(int argc, char **argv)
printf ("Error creating filesystem\n");
return 1;
}
printf("\nVOLUME INFORMATION\n");
printf("==================\n\n");
printf("Vol. id: %s\n", iso_image_fs_get_volume_id(fs));
printf("Publisher: %s\n", iso_image_fs_get_publisher_id(fs));
printf("Data preparer: %s\n", iso_image_fs_get_data_preparer_id(fs));
@ -149,7 +150,7 @@ int main(int argc, char **argv)
printf("\nDIRECTORY TREE\n");
printf("==============\n");
result = fs->get_root(fs, &root);
if (result < 0) {
printf ("Can't get root %d\n", result);
@ -158,7 +159,7 @@ int main(int argc, char **argv)
//print_file_src(root);
print_dir(root, 0);
iso_file_source_unref(root);
fs->close(fs);
iso_filesystem_unref((IsoFilesystem*)fs);
iso_data_source_unref(src);

@ -0,0 +1,58 @@
===============================================================================
ISO-9660 Level 3 Cookbook
===============================================================================
Creation date: 2008-Aug-17
Author: Vreixo Formoso
_______________________________________________________________________________
Contents:
---------
1. References
2. General
3. OS Support
4. Implementation
-------------------------------------------------------------------------------
1. References:
ECMA-119 "Volume and File Structure of CDROM for Information Interchange"
-------------------------------------------------------------------------------
2. General
In ECMA-119 standard, the size of a file section cannot be bigger than 4GB - 1,
because the Data Length field of the Directory Record is just 32 bits (9.1.4).
However, "each file shall consist of one or more File Sections" (6.5.1), and
that way we can store files greater than 4GB in a ECMA-119 image. Such image,
with multiple File Sections, is only supported at Level 3 (10.3), as Level 2
(10.2) states that "each file shall consist of only one File Section".
On disc, each file section is stored in a Extent (6.4.2), i.e. a set of
contiguous Logical Blocks.
-------------------------------------------------------------------------------
3. OS Support
Wikipedia states that "Microsoft Windows XP supports this, while Mac OS X
(as of 10.4.8) does not handle this case properly. In the case of Mac OS X,
the driver appears not to support file fragmentation at all (i.e. it only
supports ISO 9660 Level 2 but not Level 3). Linux supports multiple extents.
FreeBSD only shows and reads the last extent of a multi-extent file."
-------------------------------------------------------------------------------
4. Implementation
Each File Section will have its own Directory Record (6.5.1). So, for files
greater than 4 GB, we need to store several directory records, that will have
the same File Identifier, and stored in the order of the File Sections they
refer (9.3).
All but the last Directory Record must have the Multi-Extent flag set (9.1.6)

@ -1,19 +1,19 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/*
* Synchronized ring buffer, works with a writer thread and a read thread.
*
*
* TODO #00010 : optimize ring buffer
* - write/read at the end of buffer requires a second mutex_lock, even if
* there's enought space/data at the beginning
* - pre-buffer for writes < BLOCK_SIZE
*
*
*/
#include "buffer.h"
@ -30,13 +30,13 @@
struct iso_ring_buffer
{
uint8_t *buf;
/*
* Max number of bytes in buffer
*/
size_t cap;
/*
/*
* Number of bytes available.
*/
size_t size;
@ -45,11 +45,11 @@ struct iso_ring_buffer
size_t rpos;
size_t wpos;
/*
/*
* flags to report if read or writer threads ends execution
* 0 not finished, 1 finished ok, 2 finish with error
* 0 not finished, 1 finished ok, 2 finish with error
*/
unsigned int rend :2;
unsigned int rend :2;
unsigned int wend :2;
/* just for statistical purposes */
@ -63,9 +63,9 @@ struct iso_ring_buffer
/**
* Create a new buffer.
*
*
* The created buffer should be freed with iso_ring_buffer_free()
*
*
* @param size
* Number of blocks in buffer. You should supply a number >= 32, otherwise
* size will be ignored and 32 will be used by default, which leads to a
@ -85,14 +85,14 @@ int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf)
if (buffer == NULL) {
return ISO_OUT_OF_MEM;
}
buffer->cap = (size > 32 ? size : 32) * BLOCK_SIZE;
buffer->buf = malloc(buffer->cap);
if (buffer->buf == NULL) {
free(buffer);
return ISO_OUT_OF_MEM;
}
buffer->size = 0;
buffer->wpos = 0;
buffer->rpos = 0;
@ -126,7 +126,7 @@ void iso_ring_buffer_free(IsoRingBuffer *buf)
/**
* Write count bytes into buffer. It blocks until all bytes where written or
* reader close the buffer.
*
*
* @param buf
* the buffer
* @param data
@ -189,7 +189,7 @@ int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count)
* bytes has been read. If the writer finishes before outputting enought
* bytes, 0 (EOF) is returned, the number of bytes already read remains
* unknown.
*
*
* @return
* 1 success, 0 EOF, < 0 error
*/
@ -252,7 +252,7 @@ void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error)
void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error)
{
pthread_mutex_lock(&buf->mutex);
if (buf->rend) {
/* reader already closed */
pthread_mutex_unlock(&buf->mutex);
@ -285,9 +285,9 @@ unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf)
/**
* Get the status of the buffer used by a burn_source.
*
*
* @param b
* A burn_source previously obtained with
* A burn_source previously obtained with
* iso_image_create_burn_source().
* @param size
* Will be filled with the total size of the buffer, in bytes
@ -302,7 +302,7 @@ unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf)
* 6="ended" : consumption has ended without input error
* 7="aborted" : consumption has ended after input error
*/
int iso_ring_buffer_get_status(struct burn_source *b, size_t *size,
int iso_ring_buffer_get_status(struct burn_source *b, size_t *size,
size_t *free_bytes)
{
int ret;
@ -311,7 +311,7 @@ int iso_ring_buffer_get_status(struct burn_source *b, size_t *size,
return ISO_NULL_POINTER;
}
buf = ((Ecma119Image*)(b->data))->buffer;
/* get mutex */
pthread_mutex_lock(&buf->mutex);
if (size) {
@ -322,7 +322,7 @@ int iso_ring_buffer_get_status(struct burn_source *b, size_t *size,
}
ret = (buf->rend ? 4 : 0) + (buf->wend + 1);
pthread_mutex_unlock(&buf->mutex);
return ret;
}

@ -1,9 +1,9 @@
/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
@ -90,10 +90,10 @@ size_t calc_dirent_len(Ecma119Image *t, Ecma119Node *n)
/**
* Computes the total size of all directory entries of a single dir,
* acording to ECMA-119 6.8.1.1
*
*
* This also take into account the size needed for RR entries and
* SUSP continuation areas (SUSP, 5.1).
*
*
* @param ce
* Will be filled with the size needed for Continuation Areas
* @return
@ -117,24 +117,29 @@ size_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce)
for (i = 0; i < dir->info.dir->nchildren; ++i) {
size_t remaining;
int section, nsections;
Ecma119Node *child = dir->info.dir->children[i];
size_t dirent_len = calc_dirent_len(t, child);
if (t->rockridge) {
dirent_len += rrip_calc_len(t, child, 0, 255 - dirent_len, &ce_len);
*ce += ce_len;
}
remaining = BLOCK_SIZE - (len % BLOCK_SIZE);
if (dirent_len > remaining) {
/* child directory entry doesn't fit on block */
len += remaining + dirent_len;
} else {
len += dirent_len;
nsections = (child->type == ECMA119_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
size_t dirent_len = calc_dirent_len(t, child);
if (t->rockridge) {
dirent_len += rrip_calc_len(t, child, 0, 255 - dirent_len, &ce_len);
*ce += ce_len;
}
remaining = BLOCK_SIZE - (len % BLOCK_SIZE);
if (dirent_len > remaining) {
/* child directory entry doesn't fit on block */
len += remaining + dirent_len;
} else {
len += dirent_len;
}
}
}
/*
* The size of a dir is always a multiple of block size, as we must add
* the size of the unused space after the last directory record
* The size of a dir is always a multiple of block size, as we must add
* the size of the unused space after the last directory record
* (ECMA-119, 6.8.1.3)
*/
len = ROUND_UP(len, BLOCK_SIZE);
@ -222,24 +227,26 @@ int ecma119_writer_compute_data_blocks(IsoImageWriter *writer)
/**
* Write a single directory record (ECMA-119, 9.1)
*
*
* @param file_id
* if >= 0, we use it instead of the filename (for "." and ".." entries).
* @param len_fi
* Computed length of the file identifier. Total size of the directory
* entry will be len + 33 + padding if needed (ECMA-119, 9.1.12)
* @param info
* SUSP entries for the given directory record. It will be NULL for the
* root directory record in the PVD (ECMA-119, 8.4.18) (in order to
* SUSP entries for the given directory record. It will be NULL for the
* root directory record in the PVD (ECMA-119, 8.4.18) (in order to
* distinguish it from the "." entry in the root directory)
*/
static
void write_one_dir_record(Ecma119Image *t, Ecma119Node *node, int file_id,
uint8_t *buf, size_t len_fi, struct susp_info *info)
uint8_t *buf, size_t len_fi, struct susp_info *info,
int extent)
{
uint32_t len;
uint32_t block;
uint8_t len_dr; /*< size of dir entry without SUSP fields */
int multi_extend = 0;
uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id
: (uint8_t*)node->iso_name;
@ -260,12 +267,13 @@ void write_one_dir_record(Ecma119Image *t, Ecma119Node *node, int file_id,
len = node->info.dir->len;
block = node->info.dir->block;
} else if (node->type == ECMA119_FILE) {
len = iso_file_src_get_size(node->info.file);
block = node->info.file->block;
block = node->info.file->sections[extent].block;
len = node->info.file->sections[extent].size;
multi_extend = (node->info.file->nsections - 1 == extent) ? 0 : 1;
} else {
/*
* for nodes other than files and dirs, we set both
* len and block to 0
/*
* for nodes other than files and dirs, we set both
* len and block to 0
*/
len = 0;
block = 0;
@ -281,11 +289,13 @@ void write_one_dir_record(Ecma119Image *t, Ecma119Node *node, int file_id,
iso_bb(rec->block, block, 4);
iso_bb(rec->length, len, 4);
iso_datetime_7(rec->recording_time, t->now, t->always_gmt);
rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0;
rec->flags[0] = ((node->type == ECMA119_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0);
iso_bb(rec->vol_seq_number, 1, 2);
rec->len_fi[0] = len_fi;
/* and finally write the SUSP fields */
/*
* and finally write the SUSP fields.
*/
if (info != NULL) {
rrip_write_susp_fields(t, info, buf + len_dr);
}
@ -365,7 +375,7 @@ int ecma119_writer_write_vol_desc(IsoImageWriter *writer)
iso_lsb(vol.l_path_table_pos, t->l_path_table_pos, 4);
iso_msb(vol.m_path_table_pos, t->m_path_table_pos, 4);
write_one_dir_record(t, t->root, 0, vol.root_dir_record, 1, NULL);
write_one_dir_record(t, t->root, 0, vol.root_dir_record, 1, NULL, 0);
strncpy_pad((char*)vol.vol_set_id, volset_id, 128);
strncpy_pad((char*)vol.publisher_id, pub_id, 128);
@ -410,14 +420,14 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir)
/* initialize buffer with 0s */
memset(buffer, 0, BLOCK_SIZE);
/*
/*
* set susp_info to 0's, this way code for both plain ECMA-119 and
* RR is very similar
*/
memset(&info, 0, sizeof(struct susp_info));
if (t->rockridge) {
/* initialize the ce_block, it might be needed */
info.ce_block = dir->info.dir->block + DIV_UP(dir->info.dir->len,
info.ce_block = dir->info.dir->block + DIV_UP(dir->info.dir->len,
BLOCK_SIZE);
}
@ -429,7 +439,7 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir)
}
}
len = 34 + info.suf_len;
write_one_dir_record(t, dir, 0, buf, 1, &info);
write_one_dir_record(t, dir, 0, buf, 1, &info, 0);
buf += len;
if (t->rockridge) {
@ -439,40 +449,46 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir)
}
}
len = 34 + info.suf_len;
write_one_dir_record(t, dir, 1, buf, 1, &info);
write_one_dir_record(t, dir, 1, buf, 1, &info, 0);
buf += len;
for (i = 0; i < dir->info.dir->nchildren; i++) {
int section, nsections;
Ecma119Node *child = dir->info.dir->children[i];
/* compute len of directory entry */
fi_len = strlen(child->iso_name);
len = fi_len + 33 + (fi_len % 2 ? 0 : 1);
if (need_version_number(t, child)) {
len += 2;
}
/* get the SUSP fields if rockridge is enabled */
if (t->rockridge) {
ret = rrip_get_susp_fields(t, child, 0, 255 - len, &info);
if (ret < 0) {
return ret;
nsections = (child->type == ECMA119_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
/* compute len of directory entry */
len = fi_len + 33 + (fi_len % 2 ? 0 : 1);
if (need_version_number(t, child)) {
len += 2;
}
len += info.suf_len;
}
if ( (buf + len - buffer) > BLOCK_SIZE) {
/* dir doesn't fit in current block */
ret = iso_write(t, buffer, BLOCK_SIZE);
if (ret < 0) {
return ret;
/* get the SUSP fields if rockridge is enabled */
if (t->rockridge) {
ret = rrip_get_susp_fields(t, child, 0, 255 - len, &info);
if (ret < 0) {
return ret;
}
len += info.suf_len;
}
if ( (buf + len - buffer) > BLOCK_SIZE) {
/* dir doesn't fit in current block */
ret = iso_write(t, buffer, BLOCK_SIZE);
if (ret < 0) {
return ret;
}
memset(buffer, 0, BLOCK_SIZE);
buf = buffer;
}
memset(buffer, 0, BLOCK_SIZE);
buf = buffer;
/* write the directory entry in any case */
write_one_dir_record(t, child, -1, buf, fi_len, &info, section);
buf += len;
}
/* write the directory entry in any case */
write_one_dir_record(t, child, -1, buf, fi_len, &info);
buf += len;
}
/* write the last block */
@ -709,7 +725,7 @@ int pad_writer_write_data(IsoImageWriter *writer)
return ISO_ASSERT_FAILURE;
}
t = writer->target;
if (t->pad_blocks == 0) {
return ISO_SUCCESS;
}
@ -820,7 +836,7 @@ void *write_function(void *arg)
iso_msg_submit(target->image->id, ISO_IMAGE_WRITE_CANCELED, 0, NULL);
} else {
/* image write error */
iso_msg_submit(target->image->id, ISO_WRITE_ERROR, res,
iso_msg_submit(target->image->id, ISO_WRITE_ERROR, res,
"Image write error");
}
iso_ring_buffer_writer_close(target->buffer, 1);
@ -855,7 +871,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
target->iso1999 = opts->iso1999;
target->always_gmt = opts->always_gmt;
target->ino = 0;
target->omit_version_numbers = opts->omit_version_numbers
target->omit_version_numbers = opts->omit_version_numbers
| opts->max_37_char_filenames;
target->allow_deep_paths = opts->allow_deep_paths;
target->allow_longer_paths = opts->allow_longer_paths;
@ -882,7 +898,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
target->appendable = opts->appendable;
target->replace_timestamps = opts->replace_timestamps ? 1 : 0;
target->timestamp = opts->replace_timestamps == 2 ?
target->timestamp = opts->replace_timestamps == 2 ?
opts->timestamp : target->now;
/* el-torito? */
@ -909,10 +925,10 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
return ISO_OUT_OF_MEM;
}
/*
/*
* 2. Based on those options, create needed writers: iso, joliet...
* Each writer inits its structures and stores needed info into
* target.
* target.
* If the writer needs an volume descriptor, it increments image
* current block.
* Finally, create Writer for files.
@ -944,7 +960,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
if (ret < 0) {
goto target_cleanup;
}
/* create writer for El-Torito */
if (target->eltorito) {
ret = eltorito_writer_create(target);
@ -952,7 +968,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
goto target_cleanup;
}
}
/* create writer for Joliet structure */
if (target->joliet) {
ret = joliet_writer_create(target);
@ -960,7 +976,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
goto target_cleanup;
}
}
/* create writer for ISO 9660:1999 structure */
if (target->iso1999) {
ret = iso1999_writer_create(target);
@ -970,11 +986,11 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
}
voldesc_size = target->curblock - target->ms_block - 16;
/* Volume Descriptor Set Terminator */
target->curblock++;
/*
/*
* Create the writer for possible padding to ensure that in case of image
* growing we can safety overwrite the first 64 KiB of image.
*/
@ -990,7 +1006,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *opts, Ecma119Image **img)
}
/*
* 3.
* 3.
* Call compute_data_blocks() in each Writer.
* That function computes the size needed by its structures and
* increments image current block propertly.
@ -1130,14 +1146,14 @@ static void bs_free_data(struct burn_source *bs)
if (st < 4) {
/* forces writer to stop if it is still running */
iso_ring_buffer_reader_close(target->buffer, 0);
/* wait until writer thread finishes */
pthread_join(target->wthread, NULL);
iso_msg_debug(target->image->id, "Writer thread joined");
}
iso_msg_debug(target->image->id,
"Ring buffer was %d times full and %d times empty",
iso_msg_debug(target->image->id,
"Ring buffer was %d times full and %d times empty",
iso_ring_buffer_get_times_full(target->buffer),
iso_ring_buffer_get_times_empty(target->buffer));
@ -1153,9 +1169,9 @@ int bs_cancel(struct burn_source *bs)
Ecma119Image *target = (Ecma119Image*)bs->data;
st = iso_ring_buffer_get_status(bs, &cap, &free);
if (free == cap && (st == 2 || st == 3)) {
/* image was already consumed */
/* image was already consumed */
iso_ring_buffer_reader_close(target->buffer, 0);
} else {
iso_msg_debug(target->image->id, "Reader thread being cancelled");
@ -1176,7 +1192,7 @@ int bs_set_size(struct burn_source *bs, off_t size)
{
Ecma119Image *target = (Ecma119Image*)bs->data;
/*
/*
* just set the value to be returned by get_size. This is not used at
* all by libisofs, it is here just for helping libburn to correctly pad
* the image if needed.
@ -1235,12 +1251,12 @@ int iso_write(Ecma119Image *target, void *buf, size_t count)
if (ret > 0 && (target->total_size != (off_t) 0)){
unsigned int kbw, kbt;
int percent;
target->bytes_written += (off_t) count;
kbw = (unsigned int) (target->bytes_written >> 10);
kbt = (unsigned int) (target->total_size >> 10);
percent = (kbw * 100) / kbt;
/* only report in 5% chunks */
if (percent >= target->percent_written + 5) {
iso_msg_debug(target->image->id, "Processed %u of %u KB (%d %%)",
@ -1248,32 +1264,32 @@ int iso_write(Ecma119Image *target, void *buf, size_t count)
target->percent_written = percent;
}
}
return ret;
}
int iso_write_opts_new(IsoWriteOpts **opts, int profile)
{
IsoWriteOpts *wopts;
if (opts == NULL) {
return ISO_NULL_POINTER;
}
if (profile < 0 || profile > 2) {
return ISO_WRONG_ARG_VALUE;
}
wopts = calloc(1, sizeof(IsoWriteOpts));
if (wopts == NULL) {
return ISO_OUT_OF_MEM;
}
switch (profile) {
case 0:
wopts->level = 1;
break;
case 1:
wopts->level = 2;
wopts->level = 3;
wopts->rockridge = 1;
break;
case 2:
@ -1305,7 +1321,7 @@ void iso_write_opts_free(IsoWriteOpts *opts)
if (opts == NULL) {
return;
}
free(opts->output_charset);
free(opts);
}
@ -1315,7 +1331,7 @@ int iso_write_opts_set_iso_level(IsoWriteOpts *opts, int level)
if (opts == NULL) {
return ISO_NULL_POINTER;
}
if (level != 1 && level != 2) {
if (level != 1 && level != 2 && level != 3) {
return ISO_WRONG_ARG_VALUE;
}
opts->level = level;

@ -1,8 +1,8 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
@ -18,6 +18,18 @@
#define BLOCK_SIZE 2048
/*
* Maximum file section size. Set to 4GB - 1 = 0xffffffff
*/
#define MAX_ISO_FILE_SECTION_SIZE 0xffffffff
/*
* When a file need to be splitted in several sections, the maximum size
* of such sections, but the last one. Set to a multiple of BLOCK_SIZE.
* Default to 4GB - 2048 = 0xFFFFF800
*/
#define ISO_EXTENT_SIZE 0xFFFFF800
/**
* Holds the options for the image generation.
*/
@ -33,11 +45,11 @@ struct iso_write_opts {
/* allways write timestamps in GMT */
unsigned int always_gmt :1;
/*
/*
* Relaxed constraints. Setting any of these to 1 break the specifications,
* but it is supposed to work on most moderns systems. Use with caution.
* but it is supposed to work on most moderns systems. Use with caution.
*/
/**
* Omit the version number (";1") at the end of the ISO-9660 identifiers.
* Version numbers are usually not used.
@ -45,7 +57,7 @@ struct iso_write_opts {
unsigned int omit_version_numbers :1;
/**
* Allow ISO-9660 directory hierarchy to be deeper than 8 levels.
* Allow ISO-9660 directory hierarchy to be deeper than 8 levels.
*/
unsigned int allow_deep_paths :1;
@ -64,30 +76,30 @@ struct iso_write_opts {
/**
* ISO-9660 forces filenames to have a ".", that separates file name from
* extension. libisofs adds it if original filename doesn't has one. Set
* extension. libisofs adds it if original filename doesn't has one. Set
* this to 1 to prevent this behavior
*/
unsigned int no_force_dots :1;
/**
* Allow lowercase characters in ISO-9660 filenames. By default, only
* uppercase characters, numbers and a few other characters are allowed.
* Allow lowercase characters in ISO-9660 filenames. By default, only
* uppercase characters, numbers and a few other characters are allowed.
*/
unsigned int allow_lowercase :1;
/**
* Allow all ASCII characters to be appear on an ISO-9660 filename. Note
* that "/" and "\0" characters are never allowed, even in RR names.
*/
unsigned int allow_full_ascii :1;
/**
* Allow all characters to be part of Volume and Volset identifiers on
* the Primary Volume Descriptor. This breaks ISO-9660 contraints, but
* should work on modern systems.
*/
unsigned int relaxed_vol_atts :1;
/**
* Allow paths in the Joliet tree to have more than 240 characters.
*/
@ -102,7 +114,7 @@ struct iso_write_opts {
* If 0, the corresponding attribute will be kept as setted in the IsoNode.
* Unless you have changed it, it corresponds to the value on disc, so it
* is suitable for backup purposes. If set to 1, the corresponding attrib.
* will be changed by a default suitable value. Finally, if you set it to
* will be changed by a default suitable value. Finally, if you set it to
* 2, the attrib. will be changed with the value specified in the options
* below. Note that for mode attributes, only the permissions are set, the
* file type remains unchanged.
@ -133,62 +145,62 @@ struct iso_write_opts {
/**
* This flags control the type of the image to create. Libisofs support
* two kind of images: stand-alone and appendable.
*
* two kind of images: stand-alone and appendable.
*
* A stand-alone image is an image that is valid alone, and that can be
* mounted by its own. This is the kind of image you will want to create
* in most cases. A stand-alone image can be burned in an empty CD or DVD,
* or write to an .iso file for future burning or distribution.
*
*
* On the other side, an appendable image is not self contained, it refers
* to serveral files that are stored outside the image. Its usage is for
* multisession discs, where you add data in a new session, while the
* previous session data can still be accessed. In those cases, the old
* multisession discs, where you add data in a new session, while the
* previous session data can still be accessed. In those cases, the old
* data is not written again. Instead, the new image refers to it, and thus
* it's only valid when appended to the original. Note that in those cases
* the image will be written after the original, and thus you will want
* to use a ms_block greater than 0.
*
* Note that if you haven't import a previous image (by means of
* to use a ms_block greater than 0.
*
* Note that if you haven't import a previous image (by means of
* iso_image_import()), the image will always be a stand-alone image, as
* there is no previous data to refer to.
*/
unsigned int appendable : 1;
/**
* Start block of the image. It is supposed to be the lba where the first
* block of the image will be written on disc. All references inside the
* ISO image will take this into account, thus providing a mountable image.
*
* For appendable images, that are written to a new session, you should
*
* For appendable images, that are written to a new session, you should
* pass here the lba of the next writable address on disc.
*
* In stand alone images this is usually 0. However, you may want to
*
* In stand alone images this is usually 0. However, you may want to
* provide a different ms_block if you don't plan to burn the image in the
* first session on disc, such as in some CD-Extra disc whether the data
* image is written in a new session after some audio tracks.
* image is written in a new session after some audio tracks.
*/
uint32_t ms_block;
/**
* When not NULL, it should point to a buffer of at least 64KiB, where
* When not NULL, it should point to a buffer of at least 64KiB, where
* libisofs will write the contents that should be written at the beginning
* of a overwriteable media, to grow the image. The growing of an image is
* a way, used by first time in growisofs by Andy Polyakov, to allow the
* appending of new data to non-multisession media, such as DVD+RW, in the
* same way you append a new session to a multisession disc, i.e., without
* need to write again the contents of the previous image.
*
* need to write again the contents of the previous image.
*
* Note that if you want this kind of image growing, you will also need to
* set appendable to "1" and provide a valid ms_block after the previous
* image.
*
* You should initialize the buffer either with 0s, or with the contents of
* the first blocks of the image you're growing. In most cases, 0 is good
*
* You should initialize the buffer either with 0s, or with the contents of
* the first blocks of the image you're growing. In most cases, 0 is good
* enought.
*/
uint8_t *overwrite;
/**
* Size, in number of blocks, of the FIFO buffer used between the writer
* thread and the burn_source. You have to provide at least a 32 blocks
@ -228,13 +240,13 @@ struct ecma119_image
unsigned int no_force_dots :1;
unsigned int allow_lowercase :1;
unsigned int allow_full_ascii :1;
unsigned int relaxed_vol_atts : 1;
/** Allow paths on Joliet tree to be larger than 240 bytes */
unsigned int joliet_longer_paths :1;
/*
/*
* Mode replace. If one of these flags is set, the correspodent values are
* replaced with values below.
*/
@ -251,7 +263,7 @@ struct ecma119_image
time_t timestamp;
/**
* if sort files or not. Sorting is based of the weight of each file
* if sort files or not. Sorting is based of the weight of each file
*/
int sort_files;
@ -282,7 +294,7 @@ struct ecma119_image
*/
uint32_t curblock;
/*
/*
* number of dirs in ECMA-119 tree, computed together with dir position,
* and needed for path table computation in a efficient way
*/
@ -290,7 +302,7 @@ struct ecma119_image
uint32_t path_table_size;
uint32_t l_path_table_pos;
uint32_t m_path_table_pos;
/*
* Joliet related information
*/
@ -299,7 +311,7 @@ struct ecma119_image
uint32_t joliet_path_table_size;
uint32_t joliet_l_path_table_pos;
uint32_t joliet_m_path_table_pos;
/*
* ISO 9660:1999 related information
*/
@ -308,7 +320,7 @@ struct ecma119_image
uint32_t iso1999_path_table_size;
uint32_t iso1999_l_path_table_pos;
uint32_t iso1999_m_path_table_pos;
/*
* El-Torito related information
*/
@ -322,10 +334,10 @@ struct ecma119_image
* data. These padding blocks are added by libisofs to improve the handling
* of image growing. The idea is that the first blocks in the image are
* overwritten with the volume descriptors of the new image. These first
* blocks usually correspond to the volume descriptors and directory
* structure of the old image, and can be safety overwritten. However,
* with very small images they might correspond to valid data. To ensure
* this never happens, what we do is to add padding bytes, to ensure no
* blocks usually correspond to the volume descriptors and directory
* structure of the old image, and can be safety overwritten. However,
* with very small images they might correspond to valid data. To ensure
* this never happens, what we do is to add padding bytes, to ensure no
* file data is written in the first 64 KiB, that are the bytes we usually
* overwrite.
*/

@ -161,7 +161,7 @@ int create_file(Ecma119Image *img, IsoFile *iso, Ecma119Node **node)
off_t size;
size = iso_stream_get_size(iso->stream);
if (size > (off_t)0xffffffff) {
if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && img->iso_level != 3) {
char *ipath = iso_tree_get_node_path(ISO_NODE(iso));
ret = iso_msg_submit(img->image->id, ISO_FILE_TOO_BIG, 0,
"File \"%s\" can't be added to image because "

@ -1,8 +1,8 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
@ -26,7 +26,7 @@ struct boot_info_table {
uint8_t bi_file BP(5, 8); /* LBA of boot file */
uint8_t bi_length BP(9, 12); /* Length of boot file */
uint8_t bi_csum BP(13, 16); /* Checksum of boot file */
uint8_t bi_reserved BP(17, 56); /* Reserved */
uint8_t bi_reserved BP(17, 56); /* Reserved */
};
/**
@ -67,7 +67,7 @@ void el_torito_set_load_seg(ElToritoBootImage *bootimg, short segment)
/**
* Sets the number of sectors (512b) to be load at load segment during
* the initial boot procedure. This is only for no emulation boot images,
* the initial boot procedure. This is only for no emulation boot images,
* and is a NOP for other image types.
*/
void el_torito_set_load_size(ElToritoBootImage *bootimg, short sectors)
@ -109,7 +109,7 @@ int iso_tree_add_boot_node(IsoDir *parent, const char *name, IsoBoot **boot)
if (boot) {
*boot = NULL;
}
/* check if the name is valid */
if (!iso_node_is_valid_name(name)) {
return ISO_WRONG_ARG_VALUE;
@ -162,7 +162,7 @@ int iso_tree_add_boot_node(IsoDir *parent, const char *name, IsoBoot **boot)
}
static
static
int create_image(IsoImage *image, const char *image_path,
enum eltorito_boot_media_type type,
struct el_torito_boot_image **bootimg)
@ -182,18 +182,18 @@ int create_image(IsoImage *image, const char *image_path,
if (ret == 0) {
return ISO_NODE_DOESNT_EXIST;
}
if (imgfile->type != LIBISO_FILE) {
return ISO_BOOT_IMAGE_NOT_VALID;
}
stream = ((IsoFile*)imgfile)->stream;
/* we need to read the image at least two times */
if (!iso_stream_is_repeatable(stream)) {
return ISO_BOOT_IMAGE_NOT_VALID;
}
switch (type) {
case ELTORITO_FLOPPY_EMUL:
switch (iso_stream_get_size(stream)) {
@ -211,9 +211,9 @@ int create_image(IsoImage *image, const char *image_path,
"Invalid image size %d Kb. Must be one of 1.2, 1.44"
"or 2.88 Mb", iso_stream_get_size(stream) / 1024);
return ISO_BOOT_IMAGE_NOT_VALID;
break;
break;
}
/* it seems that for floppy emulation we need to load
/* it seems that for floppy emulation we need to load
* a single sector (512b) */
load_sectors = 1;
break;
@ -222,7 +222,7 @@ int create_image(IsoImage *image, const char *image_path,
size_t i;
struct hard_disc_mbr mbr;
int used_partition;
/* read the MBR on disc and get the type of the partition */
ret = iso_stream_open(stream);
if (ret < 0) {
@ -237,14 +237,14 @@ int create_image(IsoImage *image, const char *image_path,
"Can't read MBR from image file.");
return ret < 0 ? ret : ISO_FILE_READ_ERROR;
}
/* check valid MBR signature */
if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) {
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0,
"Invalid MBR. Wrong signature.");
return ISO_BOOT_IMAGE_NOT_VALID;
}
/* ensure single partition */
used_partition = -1;
for (i = 0; i < 4; ++i) {
@ -252,7 +252,7 @@ int create_image(IsoImage *image, const char *image_path,
/* it's an used partition */
if (used_partition != -1) {
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0,
"Invalid MBR. At least 2 partitions: %d and "
"Invalid MBR. At least 2 partitions: %d and "
"%d, are being used\n", used_partition, i);
return ISO_BOOT_IMAGE_NOT_VALID;
} else
@ -262,15 +262,15 @@ int create_image(IsoImage *image, const char *image_path,
partition_type = mbr.partition[used_partition].type;
}
boot_media_type = 4;
/* only load the MBR */
load_sectors = 1;
break;
case ELTORITO_NO_EMUL:
boot_media_type = 0;
break;
break;
}
boot = calloc(1, sizeof(struct el_torito_boot_image));
if (boot == NULL) {
return ISO_OUT_OF_MEM;
@ -281,11 +281,11 @@ int create_image(IsoImage *image, const char *image_path,
boot->type = boot_media_type;
boot->load_size = load_sectors;
boot->partition_type = partition_type;
if (bootimg) {
*bootimg = boot;
}
return ISO_SUCCESS;
}
@ -298,14 +298,14 @@ int iso_image_set_boot_image(IsoImage *image, const char *image_path,
struct el_torito_boot_catalog *catalog;
ElToritoBootImage *boot_image= NULL;
IsoBoot *cat_node= NULL;
if (image == NULL || image_path == NULL || catalog_path == NULL) {
return ISO_NULL_POINTER;
}
if (image->bootcat != NULL) {
return ISO_IMAGE_ALREADY_BOOTABLE;
}
/* create the node for the catalog */
{
IsoDir *parent;
@ -314,8 +314,8 @@ int iso_image_set_boot_image(IsoImage *image, const char *image_path,
if (catdir == NULL) {
return ISO_OUT_OF_MEM;
}
/* get both the dir and the name */
/* get both the dir and the name */
catname = strrchr(catdir, '/');
if (catname == NULL) {
free(catdir);
@ -345,13 +345,13 @@ int iso_image_set_boot_image(IsoImage *image, const char *image_path,
return ret;
}
}
/* create the boot image */
ret = create_image(image, image_path, type, &boot_image);
if (ret < 0) {
goto boot_image_cleanup;
}
/* creates the catalog with the given image */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
if (catalog == NULL) {
@ -362,13 +362,13 @@ int iso_image_set_boot_image(IsoImage *image, const char *image_path,
catalog->node = cat_node;
iso_node_ref((IsoNode*)cat_node);
image->bootcat = catalog;
if (boot) {
*boot = boot_image;
}
return ISO_SUCCESS;
boot_image_cleanup:;
if (cat_node) {
iso_node_take((IsoNode*)cat_node);
@ -383,32 +383,32 @@ boot_image_cleanup:;
/**
* Get El-Torito boot image of an ISO image, if any.
*
*
* This can be useful, for example, to check if a volume read from a previous
* session or an existing image is bootable.