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

This commit is contained in:
Vreixo Formoso 2008-08-20 02:14:47 +02:00
commit c1a7702f52
18 changed files with 1424 additions and 983 deletions

View File

@ -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);

View File

@ -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)

View File

@ -117,7 +117,11 @@ 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];
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);
@ -131,6 +135,7 @@ size_t calc_dir_size(Ecma119Image *t, Ecma119Node *dir, size_t *ce)
len += dirent_len;
}
}
}
/*
* The size of a dir is always a multiple of block size, as we must add
@ -235,11 +240,13 @@ int ecma119_writer_compute_data_blocks(IsoImageWriter *writer)
*/
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,8 +267,9 @@ 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
@ -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);
@ -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,14 +449,19 @@ 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);
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;
@ -471,9 +486,10 @@ int write_one_dir(Ecma119Image *t, Ecma119Node *dir)
buf = buffer;
}
/* write the directory entry in any case */
write_one_dir_record(t, child, -1, buf, fi_len, &info);
write_one_dir_record(t, child, -1, buf, fi_len, &info, section);
buf += len;
}
}
/* write the last block */
ret = iso_write(t, buffer, BLOCK_SIZE);
@ -1273,7 +1289,7 @@ int iso_write_opts_new(IsoWriteOpts **opts, int profile)
wopts->level = 1;
break;
case 1:
wopts->level = 2;
wopts->level = 3;
wopts->rockridge = 1;
break;
case 2:
@ -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;

View File

@ -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.
*/

View File

@ -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 "

View File

@ -525,7 +525,7 @@ write_section_entry(uint8_t *buf, Ecma119Image *t)
iso_lsb(se->load_seg, img->load_seg, 2);
se->system_type[0] = img->partition_type;
iso_lsb(se->sec_count, img->load_size, 2);
iso_lsb(se->block, t->bootimg->block, 4);
iso_lsb(se->block, t->bootimg->sections[0].block, 4);
}
static
@ -699,7 +699,8 @@ int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src)
/* fill fields */
file->prev_img = 0; /* TODO allow copy of old img catalog???? */
file->block = 0; /* to be filled later */
file->nsections = 0; /* to be filled later */
file->sections = NULL;
file->sort_weight = 1000; /* slightly high */
file->stream = stream;
@ -746,7 +747,7 @@ int eltorito_writer_write_vol_desc(IsoImageWriter *writer)
memcpy(vol.std_identifier, "CD001", 5);
vol.vol_desc_version[0] = 1;
memcpy(vol.boot_sys_id, "EL TORITO SPECIFICATION", 23);
iso_lsb(vol.boot_catalog, t->cat->block, 4);
iso_lsb(vol.boot_catalog, t->cat->sections[0].block, 4);
return iso_write(t, &vol, sizeof(struct ecma119_boot_rec_vol_desc));
}
@ -788,7 +789,7 @@ int patch_boot_image(uint8_t *buf, Ecma119Image *t, size_t imgsize)
info = (struct boot_info_table*)(buf + 8);
/*memset(info, 0, sizeof(struct boot_info_table));*/
iso_lsb(info->bi_pvd, t->ms_block + 16, 4);
iso_lsb(info->bi_file, t->bootimg->block, 4);
iso_lsb(info->bi_file, t->bootimg->sections[0].block, 4);
iso_lsb(info->bi_length, imgsize, 4);
iso_lsb(info->bi_csum, checksum, 4);
return ISO_SUCCESS;

View File

@ -61,20 +61,45 @@ int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src)
iso_stream_get_id(file->stream, &fs_id, &dev_id, &ino_id);
fsrc = malloc(sizeof(IsoFileSrc));
fsrc = calloc(1, sizeof(IsoFileSrc));
if (fsrc == NULL) {
return ISO_OUT_OF_MEM;
}
/* fill key and other atts */
fsrc->prev_img = file->msblock ? 1 : 0;
fsrc->block = file->msblock;
fsrc->prev_img = file->from_old_session;
if (file->from_old_session && img->appendable) {
/*
* On multisession discs we keep file sections from old image.
*/
int ret = iso_file_get_old_image_sections(file, &(fsrc->nsections),
&(fsrc->sections), 0);
if (ret < 0) {
free(fsrc);
return ISO_OUT_OF_MEM;
}
} else {
/*
* For new files, or for image copy, we compute our own file sections.
* Block and size of each section will be filled later.
*/
off_t section_size = iso_stream_get_size(file->stream);
if (section_size > (off_t) MAX_ISO_FILE_SECTION_SIZE) {
fsrc->nsections = DIV_UP(section_size - (off_t) MAX_ISO_FILE_SECTION_SIZE,
(off_t)ISO_EXTENT_SIZE) + 1;
} else {
fsrc->nsections = 1;
}
fsrc->sections = calloc(fsrc->nsections, sizeof(struct iso_file_section));
}
fsrc->sort_weight = file->sort_weight;
fsrc->stream = file->stream;
/* insert the filesrc in the tree */
ret = iso_rbtree_insert(img->files, fsrc, (void**)src);
if (ret <= 0) {
free(fsrc->sections);
free(fsrc);
return ret;
}
@ -117,6 +142,7 @@ int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src)
void iso_file_src_free(void *node)
{
iso_stream_unref(((IsoFileSrc*)node)->stream);
free(((IsoFileSrc*)node)->sections);
free(node);
}
@ -174,8 +200,23 @@ int filesrc_writer_compute_data_blocks(IsoImageWriter *writer)
/* fill block value */
for (i = 0; i < size; ++i) {
int extent = 0;
IsoFileSrc *file = filelist[i];
file->block = t->curblock;
off_t section_size = iso_stream_get_size(file->stream);
for (extent = 0; extent < file->nsections - 1; ++extent) {
file->sections[extent].block = t->curblock + extent *
(ISO_EXTENT_SIZE / BLOCK_SIZE);
file->sections[extent].size = ISO_EXTENT_SIZE;
section_size -= (off_t) ISO_EXTENT_SIZE;
}
/*
* final section
*/
file->sections[extent].block = t->curblock + extent * (ISO_EXTENT_SIZE / BLOCK_SIZE);
file->sections[extent].size = (uint32_t)section_size;
t->curblock += DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE);
}
@ -259,11 +300,6 @@ int filesrc_writer_write_data(IsoImageWriter *writer)
i = 0;
while ((file = filelist[i++]) != NULL) {
/*
* TODO WARNING
* when we allow files greater than 4GB, current DIV_UP implementation
* can overflow!!
*/
uint32_t nblocks = DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE);
res = filesrc_open(file);

View File

@ -17,7 +17,11 @@
struct Iso_File_Src
{
unsigned int prev_img :1; /**< if the file comes from a previous image */
uint32_t block; /**< Block where this file will be written on image */
/** File Sections of the file in the image */
struct iso_file_section *sections;
int nsections;
int sort_weight;
IsoStream *stream;
};

View File

@ -240,7 +240,12 @@ struct image_fs_data
struct stat info; /**< filled struct stat */
char *name; /**< name of this file */
uint32_t block; /**< block of the extend */
/**
* Location of file extents.
*/
struct iso_file_section *sections;
int nsections;
unsigned int opened : 2; /**< 0 not opened, 1 opened file, 2 opened dir */
/* info for content reading */
@ -372,7 +377,8 @@ int read_dir(ImageFileSourceData *data)
fs = data->fs;
fsdata = fs->data;
block = data->block;
/* a dir has always a single extent */
block = data->sections[0].block;
ret = fsdata->src->read_block(fsdata->src, block, buffer);
if (ret < 0) {
return ret;
@ -434,11 +440,21 @@ int read_dir(ImageFileSourceData *data)
*/
ret = iso_file_source_new_ifs(fs, NULL, record, &child);
if (ret < 0) {
if (child) {
/*
* This can only happen with multi-extent files.
*/
ImageFileSourceData *ifsdata = child->data;
free(ifsdata->sections);
free(ifsdata->name);
free(ifsdata);
free(child);
}
return ret;
}
/* add to the child list */
if (ret != 0) {
if (ret == 1) {
struct child_list *node;
node = malloc(sizeof(struct child_list));
if (node == NULL) {
@ -453,6 +469,7 @@ int read_dir(ImageFileSourceData *data)
node->next = data->data.content;
node->file = child;
data->data.content = node;
child = NULL;
}
tlen += record->len_dr[0];
@ -556,6 +573,76 @@ int ifs_close(IsoFileSource *src)
return ISO_SUCCESS;
}
/**
* Computes the block where the given offset should start.
*/
static
uint32_t block_from_offset(int nsections, struct iso_file_section *sections,
off_t offset)
{
int section = 0;
off_t bytes = 0;
do {
if ( (offset - bytes) < (off_t) sections[section].size ) {
return sections[section].block + (offset - bytes) / BLOCK_SIZE;
} else {
bytes += (off_t) sections[section].size;
section++;
}
} while(section < nsections);
return 0; /* should never happen */
}
/**
* Get the size available for reading on the corresponding block
*/
static
uint32_t size_available(int nsections, struct iso_file_section *sections,
off_t offset)
{
int section = 0;
off_t bytes = 0;
do {
if ( (offset - bytes) < (off_t) sections[section].size ) {
uint32_t curr_section_offset = (uint32_t)(offset - bytes);
uint32_t curr_section_left = sections[section].size - curr_section_offset;
uint32_t available = BLOCK_SIZE - curr_section_offset % BLOCK_SIZE;
return MIN(curr_section_left, available);
} else {
bytes += (off_t) sections[section].size;
section++;
}
} while(section < nsections);
return 0; /* should never happen */
}
/**
* Get the block offset for reading the given file offset
*/
static
uint32_t block_offset(int nsections, struct iso_file_section *sections,
off_t offset)
{
int section = 0;
off_t bytes = 0;
do {
if ( (offset - bytes) < (off_t) sections[section].size ) {
return (uint32_t)(offset - bytes) % BLOCK_SIZE;
} else {
bytes += (off_t) sections[section].size;
section++;
}
} while(section < nsections);
return 0; /* should never happen */
}
/**
* Attempts to read up to count bytes from the given source into
* the buffer starting at buf.
@ -599,7 +686,7 @@ int ifs_read(IsoFileSource *src, void *buf, size_t count)
size_t bytes;
uint8_t *orig;
if (data->data.offset % BLOCK_SIZE == 0) {
if (block_offset(data->nsections, data->sections, data->data.offset) == 0) {
/* we need to buffer next block */
uint32_t block;
_ImageFsData *fsdata;
@ -609,7 +696,8 @@ int ifs_read(IsoFileSource *src, void *buf, size_t count)
break;
}
fsdata = data->fs->data;
block = data->block + (data->data.offset / BLOCK_SIZE);
block = block_from_offset(data->nsections, data->sections,
data->data.offset);
ret = fsdata->src->read_block(fsdata->src, block,
data->data.content);
if (ret < 0) {
@ -618,13 +706,13 @@ int ifs_read(IsoFileSource *src, void *buf, size_t count)
}
/* how much can I read */
bytes = MIN(BLOCK_SIZE - (data->data.offset % BLOCK_SIZE),
bytes = MIN(size_available(data->nsections, data->sections, data->data.offset),
count - read);
if (data->data.offset + (off_t)bytes > data->info.st_size) {
bytes = data->info.st_size - data->data.offset;
}
orig = data->data.content;
orig += data->data.offset % BLOCK_SIZE;
orig += block_offset(data->nsections, data->sections, data->data.offset);
memcpy((uint8_t*)buf + read, orig, bytes);
read += bytes;
data->data.offset += (off_t)bytes;
@ -667,7 +755,11 @@ off_t ifs_lseek(IsoFileSource *src, off_t offset, int flag)
return (off_t)ISO_WRONG_ARG_VALUE;
}
if (data->data.offset % BLOCK_SIZE != 0) {
/*
* We check for block_offset != 0 because if it is already 0, the block
* will be read from image in the read function
*/
if (block_offset(data->nsections, data->sections, data->data.offset) != 0) {
/* we need to buffer the block */
uint32_t block;
_ImageFsData *fsdata;
@ -675,7 +767,8 @@ off_t ifs_lseek(IsoFileSource *src, off_t offset, int flag)
if (data->data.offset < data->info.st_size) {
int ret;
fsdata = data->fs->data;
block = data->block + (data->data.offset / BLOCK_SIZE);
block = block_from_offset(data->nsections, data->sections,
data->data.offset);
ret = fsdata->src->read_block(fsdata->src, block,
data->data.content);
if (ret < 0) {
@ -811,6 +904,8 @@ void ifs_free(IsoFileSource *src)
if (data->parent != NULL) {
iso_file_source_unref(data->parent);
}
free(data->sections);
free(data->name);
free(data);
}
@ -871,7 +966,11 @@ char *get_name(_ImageFsData *fsdata, const char *str, size_t len)
/**
*
* @param src
* if not-NULL, it points to a multi-extent file returned by a previous
* call to this function.
* @return
* 2 node is still incomplete (multi-extent)
* 1 success, 0 record ignored (not an error, can be a relocated dir),
* < 0 error
*/
@ -909,15 +1008,6 @@ int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
* First of all, check for unsupported ECMA-119 features
*/
/* check for unsupported multiextend */
if (record->flags[0] & 0x80) {
iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_ECMA119, 0,
"Unsupported image. This image makes use of Multi-Extend"
" features, that are not supported at this time. If you "
"need support for that, please request us this feature.");
return ISO_UNSUPPORTED_ECMA119;
}
/* check for unsupported interleaved mode */
if (record->file_unit_size[0] || record->interleave_gap_size[0]) {
iso_msg_submit(fsdata->msgid, ISO_UNSUPPORTED_ECMA119, 0,
@ -941,6 +1031,81 @@ int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
/* TODO #00013 : check for unsupported flags when reading a dir record */
/*
* If src is not-NULL, it refers to more extents of this file. We ensure
* name matches, otherwise it means we are dealing with wrong image
*/
if (*src != NULL) {
ImageFileSourceData* data = (*src)->data;
char* new_name = get_name(fsdata, (char*)record->file_id, record->len_fi[0]);
if (new_name == NULL) {
iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0,
"Can't retrieve file name");
return ISO_WRONG_ECMA119;
}
if (strcmp(new_name, data->name)) {
iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0,
"Multi-extent file lacks last entry.");
free(new_name);
return ISO_WRONG_ECMA119;
}
free(new_name);
}
/* check for multi-extent */
if (record->flags[0] & 0x80) {
iso_msg_debug(fsdata->msgid, "Found multi-extent file");
/*
* Directory entries can only have one section (ECMA-119, 6.8.1)
*/
if (record->flags[0] & 0x02) {
iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0,
"Directories with more than one section are not allowed.");
return ISO_WRONG_ECMA119;
}
if (*src == NULL) {
ifsdata = calloc(1, sizeof(ImageFileSourceData));
if (ifsdata == NULL) {
ret = ISO_OUT_OF_MEM;
goto ifs_cleanup;
}
ifsrc = calloc(1, sizeof(IsoFileSource));
if (ifsrc == NULL) {
ret = ISO_OUT_OF_MEM;
goto ifs_cleanup;
}
ifsrc->data = ifsdata;
ifsdata->name = get_name(fsdata, (char*)record->file_id, record->len_fi[0]);
if (ifsdata->name == NULL) {
iso_msg_submit(fsdata->msgid, ISO_WRONG_ECMA119, 0,
"Can't retrieve file name");
ret = ISO_WRONG_ECMA119;
goto ifs_cleanup;
}
*src = ifsrc;
} else {
ifsdata = (*src)->data;
}
/* store current extent */
ifsdata->sections = realloc(ifsdata->sections,
(1 + ifsdata->nsections) * sizeof(struct iso_file_section));
if (ifsdata->sections == NULL) {
free(ifsdata->name);
ret = ISO_OUT_OF_MEM;
goto ifs_cleanup;
}
ifsdata->sections[ifsdata->nsections].block = iso_read_bb(record->block, 4, NULL);
ifsdata->sections[ifsdata->nsections].size = iso_read_bb(record->length, 4, NULL);
ifsdata->info.st_size += (off_t) ifsdata->sections[ifsdata->nsections].size;
ifsdata->nsections++;
return 2;
}
/*
* The idea is to read all the RR entries (if we want to do that and RR
* extensions exist on image), storing the info we want from that.
@ -1264,6 +1429,7 @@ int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
}
/* ok, we can now create the file source */
if (*src == NULL) {
ifsdata = calloc(1, sizeof(ImageFileSourceData));
if (ifsdata == NULL) {
ret = ISO_OUT_OF_MEM;
@ -1274,6 +1440,12 @@ int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
ret = ISO_OUT_OF_MEM;
goto ifs_cleanup;
}
} else {
ifsdata = (*src)->data;
ifsrc = (*src);
free(ifsdata->name); /* we will assign a new one */
atts.st_size += (off_t)ifsdata->info.st_size;
}
/* fill data */
ifsdata->fs = fs;
@ -1284,7 +1456,18 @@ int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
}
ifsdata->info = atts;
ifsdata->name = name;
ifsdata->block = iso_read_bb(record->block, 4, NULL);
/* save extents */
ifsdata->sections = realloc(ifsdata->sections,
(1 + ifsdata->nsections) * sizeof(struct iso_file_section));
if (ifsdata->sections == NULL) {
free(ifsdata->name);
ret = ISO_OUT_OF_MEM;
goto ifs_cleanup;
}
ifsdata->sections[ifsdata->nsections].block = iso_read_bb(record->block, 4, NULL);
ifsdata->sections[ifsdata->nsections].size = iso_read_bb(record->length, 4, NULL);
ifsdata->nsections++;
if (S_ISLNK(atts.st_mode)) {
ifsdata->data.content = linkdest;
@ -1332,6 +1515,7 @@ int ifs_get_root(IsoFilesystem *fs, IsoFileSource **root)
}
/* get root attributes from "." entry */
*root = NULL;
ret = iso_file_source_new_ifs((IsoImageFilesystem*)fs, NULL,
(struct ecma119_dir_record*) buffer, root);
@ -2035,7 +2219,8 @@ int image_builder_create_node(IsoNodeBuilder *builder, IsoImage *image,
/* source is a regular file */
_ImageFsData *fsdata = data->fs->data;
if (fsdata->eltorito && data->block == fsdata->catblock) {
/* El-Torito images have only one section */
if (fsdata->eltorito && data->sections[0].block == fsdata->catblock) {
if (image->bootcat->node != NULL) {
ret = iso_msg_submit(image->id, ISO_EL_TORITO_WARN, 0,
@ -2080,21 +2265,21 @@ int image_builder_create_node(IsoNodeBuilder *builder, IsoImage *image,
return ISO_OUT_OF_MEM;
}
/* the msblock is taken from the image */
file->msblock = data->block;
/* mark file as from old session */
file->from_old_session = 1;
/*
* and we set the sort weight based on the block on image, to
* improve performance on image modifying.
*/
file->sort_weight = INT_MAX - data->block;
file->sort_weight = INT_MAX - data->sections[0].block;
file->stream = stream;
file->node.type = LIBISO_FILE;
new = (IsoNode*) file;
new->refcount = 0;
if (fsdata->eltorito && data->block == fsdata->imgblock) {
if (fsdata->eltorito && data->sections[0].block == fsdata->imgblock) {
/* it is boot image node */
if (image->bootcat->image->image != NULL) {
ret = iso_msg_submit(image->id, ISO_EL_TORITO_WARN, 0,
@ -2264,13 +2449,22 @@ int create_boot_img_filesrc(IsoImageFilesystem *fs, IsoFileSource **src)
goto boot_fs_cleanup;
}
ifsdata->sections = malloc(sizeof(struct iso_file_section));
if (ifsdata->sections == NULL) {
ret = ISO_OUT_OF_MEM;
goto boot_fs_cleanup;
}
/* fill data */
ifsdata->fs = fs;
iso_filesystem_ref(fs);
ifsdata->parent = NULL;
ifsdata->info = atts;
ifsdata->name = NULL;
ifsdata->block = fsdata->imgblock;
ifsdata->sections[0].block = fsdata->imgblock;
ifsdata->sections[0].size = BLOCK_SIZE;
ifsdata->nsections = 1;
ifsrc->class = &ifs_class;
ifsrc->data = ifsdata;
@ -2695,3 +2889,54 @@ int iso_read_image_features_has_eltorito(IsoReadImageFeatures *f)
{
return f->hasElTorito;
}
/**
* Get the start addresses and the sizes of the data extents of a file node
* if it was imported from an old image.
*
* @param file
* The file
* @param section_count
* Returns the number of extent entries in sections arrays
* @param sections
* Returns the array of file sections. Apply free() to dispose it.
* @param flag
* Reserved for future usage, submit 0
* @return
* 1 if there are valid extents (file comes from old image),
* 0 if file was newly added, i.e. it does not come from an old image,
* < 0 error
*/
int iso_file_get_old_image_sections(IsoFile *file, int *section_count,
struct iso_file_section **sections,
int flag)
{
if (file == NULL || section_count == NULL || sections == NULL) {
return ISO_NULL_POINTER;
}
if (flag != 0) {
return ISO_WRONG_ARG_VALUE;
}
if (file->from_old_session != 0) {
/*
* When file is from old session, we retrieve the original IsoFileSource
* to get the sections. This break encapsultation, but safes memory as
* we don't need to store the sections in the IsoFile node.
*/
FSrcStreamData *data = file->stream->data;
ImageFileSourceData *ifsdata = data->src->data;
*section_count = ifsdata->nsections;
*sections = malloc(ifsdata->nsections *
sizeof(struct iso_file_section));
if (*sections == NULL) {
return ISO_OUT_OF_MEM;
}
memcpy(*sections, ifsdata->sections,
ifsdata->nsections * sizeof(struct iso_file_section));
return 1;
}
return 0;
}

View File

@ -117,7 +117,7 @@ int create_node(Ecma119Image *t, IsoNode *iso, Iso1999Node **node)
IsoFile *file = (IsoFile*) iso;
size = iso_stream_get_size(file->stream);
if (size > (off_t)0xffffffff) {
if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && t->iso_level != 3) {
char *ipath = iso_tree_get_node_path(iso);
ret = iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0,
"File \"%s\" can't be added to image because is "
@ -557,8 +557,12 @@ size_t calc_dir_size(Ecma119Image *t, Iso1999Node *dir)
for (i = 0; i < dir->info.dir->nchildren; ++i) {
size_t remaining;
int section, nsections;
Iso1999Node *child = dir->info.dir->children[i];
size_t dirent_len = calc_dirent_len(t, child);
nsections = (child->type == ISO1999_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
remaining = BLOCK_SIZE - (len % BLOCK_SIZE);
if (dirent_len > remaining) {
/* child directory entry doesn't fit on block */
@ -567,6 +571,7 @@ size_t calc_dir_size(Ecma119Image *t, Iso1999Node *dir)
len += dirent_len;
}
}
}
/*
* The size of a dir is always a multiple of block size, as we must add
@ -663,11 +668,12 @@ int iso1999_writer_compute_data_blocks(IsoImageWriter *writer)
*/
static
void write_one_dir_record(Ecma119Image *t, Iso1999Node *node, int file_id,
uint8_t *buf, size_t len_fi)
uint8_t *buf, size_t len_fi, int extent)
{
uint32_t len;
uint32_t block;
uint8_t len_dr; /*< size of dir entry */
int multi_extend = 0;
uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id
: (uint8_t*)node->name;
@ -682,8 +688,9 @@ void write_one_dir_record(Ecma119Image *t, Iso1999Node *node, int file_id,
len = node->info.dir->len;
block = node->info.dir->block;
} else if (node->type == ISO1999_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
@ -703,7 +710,7 @@ void write_one_dir_record(Ecma119Image *t, Iso1999Node *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 == ISO1999_DIR) ? 2 : 0;
rec->flags[0] = ((node->type == ISO1999_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0);
iso_bb(rec->vol_seq_number, 1, 2);
rec->len_fi[0] = len_fi;
}
@ -762,7 +769,7 @@ int iso1999_writer_write_vol_desc(IsoImageWriter *writer)
iso_lsb(vol.l_path_table_pos, t->iso1999_l_path_table_pos, 4);
iso_msb(vol.m_path_table_pos, t->iso1999_m_path_table_pos, 4);
write_one_dir_record(t, t->iso1999_root, 0, vol.root_dir_record, 1);
write_one_dir_record(t, t->iso1999_root, 0, vol.root_dir_record, 1, 0);
strncpy_pad((char*)vol.vol_set_id, volset_id, 128);
strncpy_pad((char*)vol.publisher_id, pub_id, 128);
@ -809,18 +816,21 @@ int write_one_dir(Ecma119Image *t, Iso1999Node *dir)
memset(buffer, 0, BLOCK_SIZE);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf, 1);
write_one_dir_record(t, dir, 0, buf, 1, 0);
buf += 34;
write_one_dir_record(t, dir, 1, buf, 1);
write_one_dir_record(t, dir, 1, buf, 1, 0);
buf += 34;
for (i = 0; i < dir->info.dir->nchildren; i++) {
int section, nsections;
Iso1999Node *child = dir->info.dir->children[i];
/* compute len of directory entry */
fi_len = strlen(child->name);
len = fi_len + 33 + (fi_len % 2 ? 0 : 1);
nsections = (child->type == ISO1999_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
if ( (buf + len - buffer) > BLOCK_SIZE) {
/* dir doesn't fit in current block */
ret = iso_write(t, buffer, BLOCK_SIZE);
@ -831,9 +841,10 @@ int write_one_dir(Ecma119Image *t, Iso1999Node *dir)
buf = buffer;
}
/* write the directory entry in any case */
write_one_dir_record(t, child, -1, buf, fi_len);
write_one_dir_record(t, child, -1, buf, fi_len, section);
buf += len;
}
}
/* write the last block */
ret = iso_write(t, buffer, BLOCK_SIZE);

View File

@ -111,7 +111,7 @@ int create_node(Ecma119Image *t, IsoNode *iso, JolietNode **node)
IsoFile *file = (IsoFile*) iso;
size = iso_stream_get_size(file->stream);
if (size > (off_t)0xffffffff) {
if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && t->iso_level != 3) {
char *ipath = iso_tree_get_node_path(iso);
free(joliet);
ret = iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0,
@ -590,8 +590,12 @@ size_t calc_dir_size(Ecma119Image *t, JolietNode *dir)
for (i = 0; i < dir->info.dir->nchildren; ++i) {
size_t remaining;
int section, nsections;
JolietNode *child = dir->info.dir->children[i];
size_t dirent_len = calc_dirent_len(t, child);
nsections = (child->type == JOLIET_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
remaining = BLOCK_SIZE - (len % BLOCK_SIZE);
if (dirent_len > remaining) {
/* child directory entry doesn't fit on block */
@ -600,6 +604,7 @@ size_t calc_dir_size(Ecma119Image *t, JolietNode *dir)
len += dirent_len;
}
}
}
/*
* The size of a dir is always a multiple of block size, as we must add
@ -696,11 +701,12 @@ int joliet_writer_compute_data_blocks(IsoImageWriter *writer)
*/
static
void write_one_dir_record(Ecma119Image *t, JolietNode *node, int file_id,
uint8_t *buf, size_t len_fi)
uint8_t *buf, size_t len_fi, int extent)
{
uint32_t len;
uint32_t block;
uint8_t len_dr; /*< size of dir entry */
int multi_extend = 0;
uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id
: (uint8_t*)node->name;
@ -723,8 +729,9 @@ void write_one_dir_record(Ecma119Image *t, JolietNode *node, int file_id,
len = node->info.dir->len;
block = node->info.dir->block;
} else if (node->type == JOLIET_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
@ -744,7 +751,7 @@ void write_one_dir_record(Ecma119Image *t, JolietNode *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 == JOLIET_DIR) ? 2 : 0;
rec->flags[0] = ((node->type == JOLIET_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0);
iso_bb(rec->vol_seq_number, 1, 2);
rec->len_fi[0] = len_fi;
}
@ -827,7 +834,7 @@ int joliet_writer_write_vol_desc(IsoImageWriter *writer)
iso_lsb(vol.l_path_table_pos, t->joliet_l_path_table_pos, 4);
iso_msb(vol.m_path_table_pos, t->joliet_m_path_table_pos, 4);
write_one_dir_record(t, t->joliet_root, 0, vol.root_dir_record, 1);
write_one_dir_record(t, t->joliet_root, 0, vol.root_dir_record, 1, 0);
ucsncpy_pad((uint16_t*)vol.vol_set_id, volset_id, 128);
ucsncpy_pad((uint16_t*)vol.publisher_id, pub_id, 128);
@ -874,12 +881,13 @@ int write_one_dir(Ecma119Image *t, JolietNode *dir)
memset(buffer, 0, BLOCK_SIZE);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf, 1);
write_one_dir_record(t, dir, 0, buf, 1, 0);
buf += 34;
write_one_dir_record(t, dir, 1, buf, 1);
write_one_dir_record(t, dir, 1, buf, 1, 0);
buf += 34;
for (i = 0; i < dir->info.dir->nchildren; i++) {
int section, nsections;
JolietNode *child = dir->info.dir->children[i];
/* compute len of directory entry */
@ -889,6 +897,10 @@ int write_one_dir(Ecma119Image *t, JolietNode *dir)
len += 4;
}
nsections = (child->type == JOLIET_FILE) ? child->info.file->nsections : 1;
for (section = 0; section < nsections; ++section) {
if ( (buf + len - buffer) > BLOCK_SIZE) {
/* dir doesn't fit in current block */
ret = iso_write(t, buffer, BLOCK_SIZE);
@ -899,9 +911,10 @@ int write_one_dir(Ecma119Image *t, JolietNode *dir)
buf = buffer;
}
/* write the directory entry in any case */
write_one_dir_record(t, child, -1, buf, fi_len);
write_one_dir_record(t, child, -1, buf, fi_len, section);
buf += len;
}
}
/* write the last block */
ret = iso_write(t, buffer, BLOCK_SIZE);

View File

@ -107,6 +107,17 @@ enum IsoNodeType {
#define ISO_NODE(n) ((IsoNode*)n)
/**
* File section in an old image.
*
* @since 0.6.8
*/
struct iso_file_section
{
uint32_t block;
uint32_t size;
};
/**
* Context for iterate on directory children.
* @see iso_dir_get_children()
@ -917,7 +928,7 @@ int iso_lib_is_compatible(int major, int minor, int micro);
* start point from which to set your custom options.
* ---> 1 [BACKUP]
* POSIX compatibility for backup. Simple settings, ISO level is set to
* 2 and RR extensions are enabled. Useful for backup purposes.
* 3 and RR extensions are enabled. Useful for backup purposes.
* ---> 2 [DISTRIBUTION]
* Setting for information distribution. Both RR and Joliet are enabled
* to maximize compatibility with most systems. Permissions are set to
@ -943,6 +954,7 @@ void iso_write_opts_free(IsoWriteOpts *opts);
* -> 1 for higher compatibility with old systems. With this level
* filenames are restricted to 8.3 characters.
* -> 2 to allow up to 31 filename characters.
* -> 3 to allow files greater than 4GB
* @return
* 1 success, < 0 error
*
@ -2551,9 +2563,35 @@ IsoStream *iso_file_get_stream(IsoFile *file);
* added, i.e. it does not come from an old image, < 0 error
*
* @since 0.6.4
*
* @deprecated Use iso_file_get_old_image_sections(), as this function does
* not work with multi-extend files.
*/
int iso_file_get_old_image_lba(IsoFile *file, uint32_t *lba, int flag);
/**
* Get the start addresses and the sizes of the data extents of a file node
* if it was imported from an old image.
*
* @param file
* The file
* @param section_count
* Returns the number of extent entries in sections array.
* @param sections
* Returns the array of file sections. Apply free() to dispose it.
* @param flag
* Reserved for future usage, submit 0
* @return
* 1 if there are valid extents (file comes from old image),
* 0 if file was newly added, i.e. it does not come from an old image,
* < 0 error
*
* @since 0.6.8
*/
int iso_file_get_old_image_sections(IsoFile *file, int *section_count,
struct iso_file_section **sections,
int flag);
/*
* Like iso_file_get_old_image_lba(), but take an IsoNode.
*

View File

@ -936,19 +936,27 @@ dev_t iso_special_get_dev(IsoSpecial *special)
*/
int iso_file_get_old_image_lba(IsoFile *file, uint32_t *lba, int flag)
{
int ret;
int section_count;
struct iso_file_section *sections;
if (file == NULL || lba == NULL) {
return ISO_NULL_POINTER;
}
if (flag != 0) {
ret = iso_file_get_old_image_sections(file, &section_count, &sections, flag);
if (ret <= 0) {
return ret;
}
if (section_count != 1) {
free(sections);
return ISO_WRONG_ARG_VALUE;
}
if (file->msblock != 0) {
*lba = file->msblock;
return 1;
}
*lba = sections[0].block;
free(sections);
return 0;
}
/*
* Like iso_file_get_old_image_lba(), but take an IsoNode.
*

View File

@ -115,10 +115,7 @@ struct Iso_File
{
IsoNode node;
/**
* Location of a file extent in a ms disc, 0 for newly added file
*/
uint32_t msblock;
unsigned int from_old_session : 1;
/**
* It sorts the order in which the file data is written to the CD image.

View File

@ -19,16 +19,6 @@ ino_t serial_id = (ino_t)1;
ino_t mem_serial_id = (ino_t)1;
ino_t cut_out_serial_id = (ino_t)1;
typedef struct
{
IsoFileSource *src;
/* key for file identification inside filesystem */
dev_t dev_id;
ino_t ino_id;
off_t size; /**< size of this file */
} FSrcStreamData;
static
int fsrc_open(IsoStream *stream)
{
@ -612,6 +602,7 @@ void iso_stream_get_file_name(IsoStream *stream, char *name)
FSrcStreamData *data = stream->data;
char *path = iso_file_source_get_path(data->src);
strncpy(name, path, PATH_MAX);
free(path);
} else if (!strncmp(type, "boot", 4)) {
strcpy(name, "BOOT CATALOG");
} else if (!strncmp(type, "mem ", 4)) {

View File

@ -13,6 +13,16 @@
*/
#include "fsource.h"
typedef struct
{
IsoFileSource *src;
/* key for file identification inside filesystem */
dev_t dev_id;
ino_t ino_id;
off_t size; /**< size of this file */
} FSrcStreamData;
/**
* Get an identifier for the file of the source, for debug purposes
* @param name

View File

@ -23,7 +23,7 @@ static void test_rrip_calc_len_file()
file = malloc(sizeof(IsoFile));
CU_ASSERT_PTR_NOT_NULL_FATAL(file);
file->msblock = 0;
file->from_old_session = 0;
file->sort_weight = 0;
file->stream = NULL; /* it is not needed here */
file->node.type = LIBISO_FILE;
@ -303,7 +303,7 @@ void test_rrip_get_susp_fields_file()
file = malloc(sizeof(IsoFile));
CU_ASSERT_PTR_NOT_NULL_FATAL(file);
file->msblock = 0;
file->from_old_session = 0;
file->sort_weight = 0;
file->stream = NULL; /* it is not needed here */
file->node.type = LIBISO_FILE;