Added files - part of MS patch

master
Mario Danic 16 years ago
parent 0b1a9c5565
commit 1b7fec7751
  1. 107
      libisofs/data_source.c
  2. 646
      libisofs/ecma119_read.c
  3. 53
      libisofs/ecma119_read.h
  4. 313
      libisofs/ecma119_read_rr.c
  5. 144
      libisofs/ecma119_read_rr.h

@ -0,0 +1,107 @@
/*
* Contains a simple implementation of a data source that reads from a
* given file.
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "libisofs.h"
#define BLOCK_SIZE 2048
#define BLOCK_OUT_OF_FILE -1;
#define READ_ERROR -2;
#define SEEK_ERROR -3;
struct file_data_src {
int fd;
int nblocks;
};
static int
ds_read_block(struct data_source *src, int lba, unsigned char *buffer)
{
struct file_data_src *data;
assert(src && buffer);
data = (struct file_data_src*)src->data;
/* For block devices size is always 0, so this can't be used.
* if (lba >= data->nblocks)
* return BLOCK_OUT_OF_FILE;
*/
/* goes to requested block */
if ( lseek(data->fd, (off_t)lba * (off_t)BLOCK_SIZE, SEEK_SET) == (off_t) -1 )
return SEEK_ERROR;
if ( read(data->fd, buffer, BLOCK_SIZE) != BLOCK_SIZE )
return READ_ERROR;
return 0;
}
static int ds_get_size(struct data_source *src)
{
struct file_data_src *data;
assert(src);
data = (struct file_data_src*)src->data;
return data->nblocks;
}
static void ds_free_data(struct data_source *src)
{
struct file_data_src *data;
assert(src);
data = (struct file_data_src*)src->data;
/* close the file */
close(data->fd);
free(data);
}
struct data_source *data_source_from_file(const char *path)
{
int fd;
struct stat info;
struct file_data_src *data;
struct data_source *ret;
assert(path);
fd = open(path, O_RDONLY);
if (fd == -1)
return NULL;
fstat(fd, &info);
data = malloc(sizeof(struct file_data_src));
data->fd = fd;
data->nblocks = info.st_size / BLOCK_SIZE;
ret = malloc(sizeof(struct data_source));
ret->refcount = 1;
ret->read_block = ds_read_block;
ret->get_size = ds_get_size;
ret->free_data = ds_free_data;
ret->data = data;
return ret;
}
void data_source_free(struct data_source *src)
{
if (--src->refcount == 0) {
src->free_data(src);
free(src);
}
}

@ -0,0 +1,646 @@
/*
* Functions to read an ISO image.
*/
/*
* TODO
* we need some kind of force option, to continue reading image on
* minor errors, such as incorrect time stamps....
*
* TODO
* need to check the ZF linux-especific extension for transparent decompresion
* TODO
* what the RR entry is?
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "ecma119_read.h"
#include "ecma119_read_rr.h"
#include "ecma119.h"
#include "util.h"
#include "volume.h"
#include "tree.h"
#define BLOCK_SIZE 2048
static int
iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *parent,
uint32_t block);
/**
* This reads the "." directory entry, and set the properties of the
* given directory propertly.
*/
static int
iso_read_dot_record(struct iso_read_info *info,
struct iso_tree_node_dir *dir,
struct ecma119_dir_record *record)
{
struct susp_sys_user_entry *sue;
struct susp_iterator *iter;
assert( info && dir && record );
iter = susp_iter_new(info, record);
while ( (sue = susp_iter_next(iter)) ) {
/* ignore entries from different version */
if (sue->version[0] != 1)
continue;
/* we don't care about any RR entry but PX and TF */
if (SUSP_SIG(sue, 'P', 'X')) {
if (read_rr_PX(info, sue, &dir->node.attrib))
break;
} else if (SUSP_SIG(sue, 'T', 'F')) {
if (read_rr_TF(info, sue, &dir->node.attrib))
break;
}
}
susp_iter_free(iter);
if (info->error)
return -1;
return 0;
}
/**
* Creates a suitable iso_tree_node from a directory record, and adds
* it to parent dir. If the directory record refers to a dir, it calls
* recursively iso_read_dir.
* On success, return 0.
* If file is not supported, return 0 but a new tree node is not added
* to parent.
* On serious error, returns -1
*/
static int
iso_read_single_directory_record(struct iso_read_info *info,
struct iso_tree_node_dir *parent,
struct ecma119_dir_record *record)
{
struct iso_tree_node *node;
struct stat atts;
time_t recorded;
char *name = NULL;
char *linkdest = NULL;
uint32_t relocated_dir = 0;
assert(info && record && parent);
memset(&atts, 0, sizeof(atts));
/*
* 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.
* Then, we need some sanity checks.
* Finally, we select what kind of node it is, and set values properly.
*/
if (info->rr) {
struct susp_sys_user_entry *sue;
struct susp_iterator *iter;
iter = susp_iter_new(info, record);
while ( (sue = susp_iter_next(iter)) ) {
/* ignore entries from different version */
if (sue->version[0] != 1)
continue;
if (SUSP_SIG(sue, 'P', 'X')) {
if (read_rr_PX(info, sue, &atts))
break;
} else if (SUSP_SIG(sue, 'T', 'F')) {
if (read_rr_TF(info, sue, &atts))
break;
} else if (SUSP_SIG(sue, 'N', 'M')) {
name = read_rr_NM(sue, name);
if (!name) {
info->error = LIBISOFS_WRONG_RR;
break;
}
} else if (SUSP_SIG(sue, 'S', 'L')) {
linkdest = read_rr_SL(sue, linkdest);
if (!linkdest) {
info->error = LIBISOFS_WRONG_RR;
break;
}
} else if (SUSP_SIG(sue, 'R', 'E')) {
/*
* this directory entry refers to a relocated directory.
* We simply ignore it, as it will be correctly handled
* when found the CL
*/
susp_iter_free(iter);
free(name);
return 0; /* is not an error */
} else if (SUSP_SIG(sue, 'C', 'L')) {
/*
* This entry is a placeholder for a relocated dir.
* We need to ignore other entries, with the exception of NM.
* Then we create a directory node that represents the
* relocated dir, and iterate over its children.
*/
relocated_dir = iso_read_bb(sue->data.CL.child_loc, 4, NULL);
} else if (SUSP_SIG(sue, 'S', 'F')) {
printf("[ERROR] Sparse files not supported.\n");
info->error = LIBISOFS_UNSUPPORTED_IMAGE;
break;
} else if (SUSP_SIG(sue, 'R', 'R')) {
/* TODO I've seen this RR on mkisofs images. what's this? */
continue;
} else {
printf("[DEBUG] Unhandled SUSP entry %c%c\n", sue->sig[0], sue->sig[1]);
}
}
if ( !info->error && !relocated_dir && atts.st_mode == (mode_t) 0 ) {
printf("[ERROR] Mandatory Rock Ridge PX entry is not present "
"or it contains invalid values.\n");
info->error = LIBISOFS_WRONG_RR;
}
susp_iter_free(iter);
if (info->error)
return -1;
//TODO convert name to needed charset!!
} else {
/* RR extensions are not read / used */
//TODO allow to specify suitable values
atts.st_mode = 0555;
atts.st_gid = 0;
atts.st_uid = 0;
if (record->flags[0] & 0x02)
atts.st_mode |= S_IFDIR;
else
atts.st_mode |= S_IFREG;
}
/*
* if we haven't RR extensions, or no NM entry is present,
* we use the plain ISO name
*/
if (!name)
name = strcopy((char*)record->file_id, record->len_fi[0]);
/*
* if we haven't RR extensions, or a needed TF time stamp is not present,
* we use plain iso recording time
*/
recorded = iso_datetime_read_7(record->recording_time);
if ( atts.st_atime == (time_t) 0 ) {
atts.st_atime = recorded;
}
if ( atts.st_ctime == (time_t) 0 ) {
atts.st_ctime = recorded;
}
if ( atts.st_mtime == (time_t) 0 ) {
atts.st_mtime = recorded;
}
/* the size is read from iso directory record */
atts.st_size = iso_read_bb(record->length, 4, NULL);
if (relocated_dir) {
/*
* Ensure that a placeholder for a relocated dir appears as
* a directory (mode & S_IFDIR).
* This is need because the placeholder is really a file, and
* in theory PX entry must be ignored.
* However, to make code clearer, we don't ignore it, because
* anyway it will be replaced by "." entry when recursing.
*/
atts.st_mode = S_IFDIR | (atts.st_mode & ~S_IFMT);
}
//TODO sanity checks!!
switch(atts.st_mode & S_IFMT) {
case S_IFDIR:
{
node = calloc(1, sizeof(struct iso_tree_node_dir));
node->type = LIBISO_NODE_DIR;
}
break;
case S_IFREG:
{
node = calloc(1, sizeof(struct iso_tree_node_file));
node->type = LIBISO_NODE_FILE;
/* set block with extend */
((struct iso_tree_node_file*)node)->loc.block =
iso_read_bb(record->block, 4, NULL);
}
break;
case S_IFLNK:
{
node = calloc(1, sizeof(struct iso_tree_node_symlink));
node->type = LIBISO_NODE_SYMLINK;
/* set the link dest */
((struct iso_tree_node_symlink*)node)->dest = linkdest;
}
break;
default:
printf("[ERROR] File type not supported.\n");
return -1;
}
node->name = name;
node->attrib = atts;
node->refcount = 1;
node->procedence = LIBISO_PREVIMG;
iso_tree_add_child(parent, node);
if (node->type == LIBISO_NODE_DIR) {
uint32_t block;
if (relocated_dir)
block = relocated_dir;
else
block = iso_read_bb(record->block, 4, NULL);
/* add all children */
return iso_read_dir(info, (struct iso_tree_node_dir*)node, block);
} else
return 0;
}
/**
* Read all directory records in a directory, and creates a node for each
* of them, adding them to \p dir.
*/
static int
iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *dir,
uint32_t block)
{
unsigned char buffer[2048];
struct ecma119_dir_record *record;
uint32_t size;
uint32_t pos = 0;
uint32_t tlen = 0;
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return -1;
}
/* Attributes of dir are set in the "." entry */
record = (struct ecma119_dir_record *)(buffer + pos);
size = iso_read_bb(record->length, 4, NULL);
if (info->rr)
iso_read_dot_record(info, dir, record);
tlen += record->len_dr[0];
pos += record->len_dr[0];
/* skip ".." */
record = (struct ecma119_dir_record *)(buffer + pos);
tlen += record->len_dr[0];
pos += record->len_dr[0];
while( tlen < size ) {
record = (struct ecma119_dir_record *)(buffer + pos);
if (pos == 2048 || record->len_dr[0] == 0) {
/*
* The directory entries are splitted in several blocks
* read next block
*/
if ( info->src->read_block(info->src, ++block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return -1;
}
tlen += 2048 - pos;
pos = 0;
/* next block must begin with a non-0 directory record */
assert(buffer[0] != 0);
continue;
}
/*
* What about ignoring files with existence flag?
* if (record->flags[0] & 0x01)
* continue;
*/
/*
* TODO
* For a extrange reason, mkisofs relocates directories under
* a RR_MOVED dir. It seems that it is only used for that purposes,
* and thus it should be removed from the iso tree before
* generating a new image with libisofs, that don't uses it.
* We can do that here, but I think it's a better option doing it
* on an app. using the library, such as genisofs.
*
* if ( record->len_fi[0] == 8 &&
* !strncmp(record->file_id,"RR_MOVED", 8) ) {
* continue;
* }
*/
/* check for unsupported multiextend */
if (record->flags[0] & 0x80) {
printf("[ERROR] Unsupported image.\n"
"This image makes use of Multi-Extend features, that "
"are not supported at this time.\n"
"If you need support for that, please request us this feature.\n"
"Thank you in advance\n");
info->error = LIBISOFS_UNSUPPORTED_IMAGE;
return -1;
}
/* check for unsupported interleaved mode */
if ( record->file_unit_size[0] || record->interleave_gap_size[0] ) {
printf("[ERROR] Unsupported image.\n"
"This image has at least one file recorded in "
"interleaved mode.\n"
"We don't support this mode, as we think it's not used.\n"
"If you're reading this, then we're wrong :)\n"
"Please contact libisofs developers, so we can fix this.\n"
"Thank you in advance\n");
info->error = LIBISOFS_UNSUPPORTED_IMAGE;
return -1;
}
//TODO check for unsupported extended attribs?
//TODO check for other flags?
if ( iso_read_single_directory_record(info, dir, record) )
return -1;
tlen += record->len_dr[0];
pos += record->len_dr[0];
}
return 0;
}
/**
* Read the SUSP system user entries of the "." entry of the root directory,
* indentifying when Rock Ridge extensions are being used.
*/
static int
read_root_susp_entries(struct iso_read_info *info,
struct iso_tree_node_dir *root,
uint32_t block)
{
unsigned char buffer[2048];
struct ecma119_dir_record *record;
struct susp_sys_user_entry *sue;
struct susp_iterator *iter;
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return -1;
}
/* record will be the "." directory entry for the root record */
record = (struct ecma119_dir_record *)buffer;
/*
* FIXME
* SUSP specification claims that for CD-ROM XA the SP entry
* is not at position BP 1, but at BP 15. Is that used?
* In that case, we need to set info->len_skp to 15!!
*/
iter = susp_iter_new(info, record);
/* first entry must be an SP system use entry */
sue = susp_iter_next(iter);
if (!sue && info->error) {
susp_iter_free(iter);
return -1;
} else if (!sue || !SUSP_SIG(sue, 'S', 'P') ) {
printf("[DEBUG] SUSP/RR is not being used.\n");
susp_iter_free(iter);
return 0;
}
/* it is a SP system use entry */
if ( sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE
|| sue->data.SP.ef[0] != 0xEF) {
printf("[WARN] SUSP SP system use entry seems to be wrong.\n"
"Ignoring Rock Ridge Extensions.\n");
susp_iter_free(iter);
return 0;
}
printf("[DEBUG] SUSP is being used.\n");
/*
* The LEN_SKP field, defined in IEEE 1281, SUSP. 5.3, specifies the
* number of bytes to be skipped within each System Use field.
* I think this will be always 0, but given that support this standard
* features is easy...
*/
info->len_skp = sue->data.SP.len_skp[0];
/*
* Ok, now search for ER entry.
* Just notice that the attributes for root dir are read in
* iso_read_dir
*
* TODO if several ER are present, we need to identify the position of
* what refers to RR, and then look for corresponding ES entry in
* each directory record. I have not implemented this (it's not used,
* no?), but if we finally need it, it can be easily implemented in
* the iterator, transparently for the rest of the code.
*/
while ( (sue = susp_iter_next(iter)) ) {
/* ignore entries from different version */
if (sue->version[0] != 1)
continue;
if (SUSP_SIG(sue, 'E', 'R')) {
if (info->rr) {
printf("[WARN] More than one ER has found. "
"This is not supported.\n"
"It will be ignored, but can cause problems. "
"Please notify us about this.\n");
}
/*
* it seems that Rock Ridge can be identified with any
* of the following
*/
if ( sue->data.ER.len_id[0] == 10 &&
!strncmp((char*)sue->data.ER.ext_id, "RRIP_1991A", 10) ) {
printf("[DEBUG] suitable Rock Ridge ER found. Version 1.10.\n");
info->rr = RR_EXT_110;
} else if ( ( sue->data.ER.len_id[0] == 10 &&
!strncmp((char*)sue->data.ER.ext_id, "IEEE_P1282", 10) )
|| ( sue->data.ER.len_id[0] == 9 &&
!strncmp((char*)sue->data.ER.ext_id, "IEEE_1282", 9) ) ) {
printf("[DEBUG] suitable Rock Ridge ER found. Version 1.12.\n");
info->rr = RR_EXT_112;
//TODO check also version?
} else {
printf("[WARN] Not Rock Ridge ER (%s) found.\n"
"That will be ignored, but can cause problems in "
"image reading. Please notify us about this.\n",
sue->data.ER.ext_id);
}
} else {
//TODO look also for other RR entries???
//printf("[DEBUG] Unhandled SUSP entry %c%c\n", sue->sig[0], sue->sig[1]);
}
}
susp_iter_free(iter);
if (info->error)
return -1;
return 0;
}
static struct iso_volset *
read_pvm(struct iso_read_info *info, uint32_t block)
{
struct ecma119_pri_vol_desc *pvm;
struct iso_volume *volume;
struct iso_volset *volset;
struct ecma119_dir_record *rootdr;
char* volset_id;
unsigned char buffer[BLOCK_SIZE];
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return NULL;
}
pvm = (struct ecma119_pri_vol_desc *)buffer;
/* sanity checks */
if ( pvm->vol_desc_type[0] != 1
|| strncmp((char*)pvm->std_identifier, "CD001", 5)
|| pvm->vol_desc_version[0] != 1
|| pvm->file_structure_version[0] != 1 ) {
printf("Wrong file.\n"
"Maybe this is a damaged image, or it's not an ISO-9660 image.\n");
info->error = LIBISOFS_WRONG_PVM;
return NULL;
}
volume = iso_volume_new(NULL, NULL, NULL);
/* fill strings */
volume->volume_id = strcopy((char*)pvm->volume_id, 32);
volume->publisher_id = strcopy((char*)pvm->publisher_id, 128);
volume->data_preparer_id = strcopy((char*)pvm->data_prep_id, 128);
volume->system_id = strcopy((char*)pvm->system_id, 32);
volume->application_id = strcopy((char*)pvm->application_id, 128);
volume->copyright_file_id = strcopy((char*)pvm->copyright_file_id, 37);
volume->abstract_file_id = strcopy((char*)pvm->abstract_file_id, 37);
volume->biblio_file_id = strcopy((char*)pvm->bibliographic_file_id, 37);
volset_id = strcopy((char*)pvm->vol_set_id, 128);
volset = iso_volset_new(volume, volset_id);
free(volset_id);
/*
* FIXME
* I don't like the way the differences volset - volume are hanled now.
* While theorically right (a volset can contain several volumes), in
* practice it seems that this never happen. Current implementation, with
* the volume array in volset, make things innecessarily harder. I think
* we can refactor that in a single way.
*/
//volset->volset_size = pvm->vol_set_size[0];
rootdr = (struct ecma119_dir_record *)pvm->root_dir_record;
/*
* check if RR is being used. Note that this functions returns
* != 0 on error. Info about if RR is being used is stored in info
*/
if ( read_root_susp_entries(info, volume->root,
iso_read_bb(rootdr->block, 4, NULL)) ) {
/* error, cleanup and return */
iso_volset_free(volset);
return NULL;
}
/* we want to read RR? */
info->hasRR = info->rr ? 1 : 0;
if (info->norock)
info->rr = RR_EXT_NO;
/* Now, read the tree */
if ( iso_read_dir(info, volume->root,
iso_read_bb(rootdr->block, 4, NULL)) ) {
/* error, cleanup and return */
iso_volset_free(volset);
return NULL;
}
/*
* PVM has things that can be interested, but don't have a member in
* volume struct, such as creation date. In a multisession disc, we could
* keep the creation date and update the modification date, for example.
*/
return volset;
}
struct iso_volset *
iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts)
{
struct iso_read_info info;
struct iso_volset *volset;
assert(src && opts);
/* fill info with suitable values */
info.error = LIBISOFS_READ_OK;
info.src = src;
info.rr = RR_EXT_NO;
info.len_skp = 0;
info.ino = 0;
info.norock = opts->norock;
/* read primary volume description */
volset = read_pvm(&info, opts->block + 16);
if (volset == NULL) {
opts->error = info.error;
return NULL;
}
opts->hasRR = info.hasRR;
// TODO read other volume descriptors
// - supplementary: for joliet
// - boot: el-torito
// Read all volume descriptor till Volume Descriptor Set Terminator
// TODO merge tree info
// TODO free here? data_source_free(src);
return volset;
}

@ -0,0 +1,53 @@
/*
* ecma119_read.h
*
* Defines structures for reading from a iso image.
*/
#ifndef ECMA119_READ_H_
#define ECMA119_READ_H_
#include <stdint.h>
#include "libisofs.h"
enum read_error {
LIBISOFS_READ_OK = 0,
LIBISOFS_READ_FAILURE, /**< Truncated image or read error */
LIBISOFS_WRONG_PVM, /**< Incorrect PVM */
LIBISOFS_UNSUPPORTED_IMAGE, /**< Format not supported (interleaved...) */
LIBISOFS_WRONG_RR /**< Wrong RR/SUSP extension format */
};
/**
* Should the RR extensions be read?
*/
enum read_rr_ext {
RR_EXT_NO = 0, /*< Do not use RR extensions */
RR_EXT_110, /*< RR extensions conforming version 1.10 */
RR_EXT_112 /*< RR extensions conforming version 1.12 */
};
/**
* Structure that keeps info needed in the read process.
*/
struct iso_read_info {
struct data_source *src;
enum read_error error;
enum read_rr_ext rr; /*< If we need to read RR extensions. i.e., if the image
* contains RR extensions, and the user wants to read them. */
ino_t ino; /*< RR version 1.10 does not have file serial numbers, we
* need to generate it */
uint8_t len_skp; /*< bytes skipped within the System Use field of a
directory record, before the beginning of the SUSP
system user entries. See IEEE 1281, SUSP. 5.3. */
unsigned int norock:1; /*< Do not read Rock Ridge extensions */
unsigned int hasRR:1; /*< It will be set to 1 if RR extensions are present,
to 0 if not. */
};
#endif /*ECMA119_READ_H_*/

@ -0,0 +1,313 @@
/*
* This file contains functions related to the reading of SUSP and
* Rock Ridge extensions on an ECMA-119 image.
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ecma119.h"
#include "ecma119_read.h"
#include "ecma119_read_rr.h"
#include "util.h"
#define BLOCK_SIZE 2048
/**
* Fills a struct stat with the values of a Rock Ridge PX entry
* On error, info->error is set propertly and the function returns != 0
*/
int
read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px,
struct stat *st)
{
assert( info && px && st);
assert( px->sig[0] == 'P' && px->sig[1] == 'X');
if ( info->rr == RR_EXT_112 && px->len_sue[0] != 44 ) {
printf("[ERROR] Invalid PX entry for RR version 1.12\n");
info->error = LIBISOFS_WRONG_RR;
return -1;
} else if ( info->rr == RR_EXT_110 && px->len_sue[0] != 36 ) {
printf("[ERROR] Invalid PX entry for RR version 1.10\n");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL);
st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL);
st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL);
st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL);
if (info->rr == RR_EXT_112) {
st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL);
} else {
st->st_ino = ++info->ino;
}
return 0;
}
/**
* Fills a struct stat with the values of a Rock Ridge TF entry
* On error, info->error is set propertly and the function returns != 0
*/
int
read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf,
struct stat *st)
{
time_t time;
int s;
int nts = 0;
assert( info && tf && st);
assert( tf->sig[0] == 'T' && tf->sig[1] == 'F');
if (tf->data.TF.flags[0] & (1 << 7)) {
/* long form */
s = 17;
} else {
s = 7;
}
/* 1. Creation time */
if (tf->data.TF.flags[0] & (1 << 0)) {
/* the creation is the recording time. we ignore this */
/* TODO maybe it would be good to manage it in ms discs, where
* the recording time could be different than now!! */
++nts;
}
/* 2. modify time */
if (tf->data.TF.flags[0] & (1 << 1)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
printf("[ERROR] RR TF entry too short.\n");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_mtime = time;
++nts;
}
/* 3. access time */
if (tf->data.TF.flags[0] & (1 << 2)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
printf("[ERROR] RR TF entry too short.\n");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_atime = time;
++nts;
}
/* 4. attributes time */
if (tf->data.TF.flags[0] & (1 << 3)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
printf("[ERROR] RR TF entry too short.\n");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_ctime = time;
++nts;
}
/* we ignore backup, expire and effect times */
return 0;
}
char *
read_rr_NM(struct susp_sys_user_entry *nm, char *name)
{
assert(nm);
assert( nm->sig[0] == 'N' && nm->sig[1] == 'M');
/* concatenate the results */
if (name) {
name = realloc(name, strlen(name) + nm->len_sue[0] - 5 + 1);
strncat(name, (char*)nm->data.NM.name, nm->len_sue[0] - 5);
} else {
name = strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5);
}
return name;
}
char *
read_rr_SL(struct susp_sys_user_entry *sl, char *dest)
{
int pos;
assert(sl);
assert( sl->sig[0] == 'S' && sl->sig[1] == 'L');
for (pos = 0; pos + 5 < sl->len_sue[0];
pos += 2 + sl->data.SL.comps[pos + 1]) {
char *comp;
uint8_t len;
uint8_t flags = sl->data.SL.comps[pos];
if (flags & 0x2) {
/* current directory */
len = 1;
comp = ".";
} else if (flags & 0x4) {
/* parent directory */
len = 2;
comp = "..";
} else if (flags & 0x8) {
/* root directory */
len = 1;
comp = "/";
} else if (flags & ~0x01) {
printf("[ERROR] SL component flag %x not supported.\n", flags);
return NULL;
} else {
len = sl->data.SL.comps[pos + 1];
comp = (char*)&sl->data.SL.comps[pos + 2];
}
if (dest) {
int size = strlen(dest);
dest = realloc(dest, strlen(dest) + len + 2);
if ( dest[size-1] != '/' ) {
dest[size] = '/';
dest[size+1] = '\0';
}
strncat(dest, comp, len);
} else {
dest = strcopy(comp, len);
}
}
return dest;
}
struct susp_iterator {
uint8_t* base;
int pos;
int size;
struct iso_read_info *info;
uint32_t ce_block;
uint32_t ce_off;
uint32_t ce_len; /*< Length of the next continuation area, 0 if
no more CA are specified */
uint8_t *buffer; /*< If there are continuation areas */
};
struct susp_iterator *
susp_iter_new(struct iso_read_info *info, struct ecma119_dir_record *record)
{
struct susp_iterator *iter = malloc(sizeof(struct susp_iterator));
int pad = (record->len_fi[0] + 1) % 2;
iter->base = record->file_id + record->len_fi[0] + pad;
iter->pos = info->len_skp; /* 0 in most cases */
iter->size = record->len_dr[0] - record->len_fi[0] - 33 -pad;
iter->info = info;
iter->ce_len = 0;
iter->buffer = NULL;
return iter;
}
struct susp_sys_user_entry *
susp_iter_next(struct susp_iterator* iter)
{
struct susp_sys_user_entry *entry;
entry = (struct susp_sys_user_entry*)(iter->base + iter->pos);
if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T')) ) {
/*
* End of the System Use Area or Continuation Area.
* Note that ST is not needed when the space left is less than 4.
* (IEEE 1281, SUSP. section 4)
*/
if (iter->ce_len) {
uint32_t block;
int nblocks;
/* A CE has found, there is another continuation area */
nblocks = div_up(iter->ce_off + iter->ce_len, BLOCK_SIZE);
iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE);
/* read all block needed to cache the full CE */
for (block = 0; block < nblocks; ++block) {
if (iter->info->src->read_block(iter->info->src,
iter->ce_block + block,
iter->buffer + block * BLOCK_SIZE)) {
iter->info->error = LIBISOFS_READ_FAILURE;
return NULL;
}
}
iter->base = iter->buffer + iter->ce_off;
iter->pos = 0;
iter->size = iter->ce_len;
iter->ce_len = 0;
entry = (struct susp_sys_user_entry*)iter->base;
} else {
return NULL;
}
}
if (entry->len_sue[0] == 0) {
/* a wrong image with this lead us to a infinity loop */
printf("[ERROR] Damaged RR/SUSP information.\n");
iter->info->error = LIBISOFS_WRONG_RR;
return NULL;
}
iter->pos += entry->len_sue[0];
if ( SUSP_SIG(entry, 'C', 'E') ) {
/* Continuation entry */
if (iter->ce_len) {
printf("[WARN] More than one CE System user entry has found "
"in a single System Use field or continuation area.\n"
"This breaks SUSP standard and it's not supported.\n"
"Ignoring last CE. Maybe the image is damaged.\n");
} else {
iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL);
iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL);
iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL);
}
/* we don't want to return CE entry to the user */
return susp_iter_next(iter);
} else if ( SUSP_SIG(entry, 'P', 'D') ) {
/* skip padding */
return susp_iter_next(iter);
}
return entry;
}
void
susp_iter_free(struct susp_iterator* iter)
{
free(iter->buffer);
free(iter);
}

@ -0,0 +1,144 @@
/*
* This file contains functions related to the reading of SUSP and
* Rock Ridge extensions on an ECMA-119 image.
*/
#ifndef ECMA119_READ_RR_H_
#define ECMA119_READ_RR_H_
#include "libisofs.h"
#include "ecma119.h"
#include "ecma119_read.h"
#define SUSP_SIG(entry, a, b) ( (entry->sig[0] == a) && (entry->sig[1] == b) )
/**
* The SUSP iterator is used to iterate over the System User Entries
* of a ECMA-168 directory record.
* It takes care about Continuation Areas, handles the end of the different
* system user entries and skip padding areas. Thus, using an iteration
* we are accessing just to the meaning entries.
*/
struct susp_iterator;
struct susp_iterator *susp_iter_new(struct iso_read_info *info,
struct ecma119_dir_record *record);
/**
* Get the next SUSP System User Entry using given iterator.
* The returned pointer refers directly to an internal buffer and it's not
* guaranteed to be allocated after calling susp_iter_next() again. Thus,
* if you need to keep some entry you have to do a copy.
*
* It return NULL when no more entries are available. Also, it will return
* NULL on error. You must check info->error to distinguish between both
* situations.
*/
struct susp_sys_user_entry *susp_iter_next(struct susp_iterator* iter);
/**
* Free a given susp iterator.
*/
void susp_iter_free(struct susp_iterator* iter);
struct susp_CE {
uint8_t block[8];
uint8_t offset[8];
uint8_t len[8];
};
struct susp_SP {
uint8_t be[1];
uint8_t ef[1];
uint8_t len_skp[1];
};
struct susp_ER {
uint8_t len_id[1];
uint8_t len_des[1];
uint8_t len_src[1];
uint8_t ext_ver[1];
uint8_t ext_id[1]; /*< up to len_id bytes */
/* ext_des, ext_src */
};
/** POSIX file attributes. */
struct rr_PX {
uint8_t mode[8];
uint8_t links[8];
uint8_t uid[8];
uint8_t gid[8];
uint8_t serial[8];
};
/** Time stamps for a file. */
struct rr_TF {
uint8_t flags[1];
uint8_t t_stamps[1];
};
/** Alternate name. */
struct rr_NM {
uint8_t flags[1];
uint8_t name[1];
};
/** Link for a relocated directory. */
struct rr_CL {
uint8_t child_loc[8];
};
/** Sim link. */
struct rr_SL {
uint8_t flags[1];
uint8_t comps[1];
};
/**
* Struct for a SUSP System User Entry
*/
struct susp_sys_user_entry
{
uint8_t sig[2];
uint8_t len_sue[1];
uint8_t version[1];
union {
struct susp_CE CE;
struct susp_SP SP;
struct susp_ER ER;
struct rr_PX PX;
struct rr_TF TF;
struct rr_NM NM;
struct rr_CL CL;
struct rr_SL SL;
} data; /* 5 to 4+len_sue */
};
/**
* Fills a struct stat with the values of a Rock Ridge PX entry
* On error, info->error is set propertly and the function returns != 0
*/
int read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px,
struct stat *st);
/**
* Fills a struct stat with the values of a Rock Ridge TF entry
* On error, info->error is set propertly and the function returns != 0
*/
int read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf,
struct stat *st);
/**
* Apends the content of given Rock Ridge NM entry to \p name
* On error, returns NULL
*/
char *read_rr_NM(struct susp_sys_user_entry *nm, char *name);
/**
* Apends the components in specified SL entry to \p dest, adding
* needed '/'.
* On error, returns NULL
*/
char *read_rr_SL(struct susp_sys_user_entry *sl, char *dest);
#endif /*ECMA119_READ_RR_H_*/
Loading…
Cancel
Save