Read Volume Descriptors and identify RR extensions, if any.
This commit is contained in:
329
src/fs_image.c
329
src/fs_image.c
@ -14,6 +14,8 @@
|
||||
#include "fs_image.h"
|
||||
#include "error.h"
|
||||
#include "ecma119.h"
|
||||
#include "messages.h"
|
||||
#include "rockridge.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -34,14 +36,14 @@ typedef struct
|
||||
{
|
||||
/** DataSource from where data will be read */
|
||||
IsoDataSource *src;
|
||||
|
||||
|
||||
/**
|
||||
* Counter of the times the filesystem has been openned still pending of
|
||||
* close. It is used to keep track of when we need to actually open or
|
||||
* close the IsoDataSource.
|
||||
*/
|
||||
unsigned int open_count;
|
||||
|
||||
|
||||
uid_t uid; /**< Default uid when no RR */
|
||||
gid_t gid; /**< Default uid when no RR */
|
||||
mode_t mode; /**< Default mode when no RR (only permissions) */
|
||||
@ -49,37 +51,37 @@ typedef struct
|
||||
struct libiso_msgs *messenger;
|
||||
|
||||
char *input_charset; /**< Input charset for RR names */
|
||||
|
||||
|
||||
/**
|
||||
* Will be filled with the block lba of the extend for the root directory,
|
||||
* as read from the PVM
|
||||
*/
|
||||
uint32_t iso_root_block;
|
||||
uint32_t iso_root_block;
|
||||
|
||||
/**
|
||||
* If we need to read RR extensions. i.e., if the image contains RR
|
||||
* extensions, and the user wants to read them.
|
||||
*/
|
||||
enum read_rr_ext rr;
|
||||
|
||||
enum read_rr_ext rr;
|
||||
|
||||
/**
|
||||
* The function used to read the name from a directoy record. For ISO,
|
||||
* the name is in US-ASCII. For Joliet, in UCS-2BE. Thus, we need
|
||||
* different functions for both.
|
||||
*/
|
||||
char *(*get_name)(const char *, size_t);
|
||||
|
||||
|
||||
/**
|
||||
* Joliet and RR version 1.10 does not have file serial numbers,
|
||||
* we need to generate it. TODO what is this for?!?!?!
|
||||
*/
|
||||
//ino_t ino;
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
uint8_t len_skp;
|
||||
uint8_t len_skp;
|
||||
|
||||
/* Volume attributes */
|
||||
char *volset_id;
|
||||
@ -98,28 +100,31 @@ typedef struct
|
||||
* RR version being used in image.
|
||||
* 0 no RR extension, 1 RRIP 1.10, 2 RRIP 1.12
|
||||
*/
|
||||
unsigned int rr_version : 2;
|
||||
enum read_rr_ext rr_version;
|
||||
|
||||
/** If Joliet extensions are available on image */
|
||||
unsigned int joliet : 1;
|
||||
|
||||
/**
|
||||
* Number of blocks of the volume, as reported in the PVM.
|
||||
*/
|
||||
uint32_t nblocks;
|
||||
|
||||
|
||||
//TODO el-torito information
|
||||
|
||||
|
||||
} _ImageFsData;
|
||||
|
||||
static
|
||||
int ifs_fs_open(IsoImageFilesystem *fs)
|
||||
{
|
||||
_ImageFsData *data;
|
||||
|
||||
|
||||
if (fs == NULL || fs->fs.data == NULL) {
|
||||
return ISO_NULL_POINTER;
|
||||
}
|
||||
|
||||
|
||||
data = (_ImageFsData*)fs->fs.data;
|
||||
|
||||
|
||||
if (data->open_count == 0) {
|
||||
/* we need to actually open the data source */
|
||||
int res = data->src->open(data->src);
|
||||
@ -135,13 +140,13 @@ static
|
||||
int ifs_fs_close(IsoImageFilesystem *fs)
|
||||
{
|
||||
_ImageFsData *data;
|
||||
|
||||
|
||||
if (fs == NULL || fs->fs.data == NULL) {
|
||||
return ISO_NULL_POINTER;
|
||||
}
|
||||
|
||||
|
||||
data = (_ImageFsData*)fs->fs.data;
|
||||
|
||||
|
||||
if (--data->open_count == 0) {
|
||||
/* we need to actually close the data source */
|
||||
return data->src->close(data->src);
|
||||
@ -154,18 +159,18 @@ void ifs_fs_free(IsoFilesystem *fs)
|
||||
{
|
||||
IsoImageFilesystem *ifs;
|
||||
_ImageFsData *data;
|
||||
|
||||
|
||||
ifs = (IsoImageFilesystem*)fs;
|
||||
data = (_ImageFsData*) fs->data;
|
||||
|
||||
|
||||
/* close data source if already openned */
|
||||
if (data->open_count > 0) {
|
||||
data->src->close(data->src);
|
||||
}
|
||||
|
||||
|
||||
/* free our ref to datasource */
|
||||
iso_data_source_unref(data->src);
|
||||
|
||||
|
||||
/* free volume atts */
|
||||
free(data->volset_id);
|
||||
free(data->volume_id);
|
||||
@ -176,37 +181,165 @@ void ifs_fs_free(IsoFilesystem *fs)
|
||||
free(data->copyright_file_id);
|
||||
free(data->abstract_file_id);
|
||||
free(data->biblio_file_id);
|
||||
|
||||
|
||||
free(data->input_charset);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(_ImageFsData *data, uint32_t block)
|
||||
{
|
||||
int ret;
|
||||
unsigned char buffer[2048];
|
||||
struct ecma119_dir_record *record;
|
||||
struct susp_sys_user_entry *sue;
|
||||
SuspIterator *iter;
|
||||
|
||||
ret = data->src->read_block(data->src, block, buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* record will be the "." directory entry for the root record */
|
||||
record = (struct ecma119_dir_record *)buffer;
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* 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(data->src, record, data->len_skp, data->messenger);
|
||||
if (iter == NULL) {
|
||||
return ISO_MEM_ERROR;
|
||||
}
|
||||
|
||||
/* first entry must be an SP system use entry */
|
||||
ret = susp_iter_next(iter, &sue);
|
||||
if (ret < 0) {
|
||||
/* error */
|
||||
susp_iter_free(iter);
|
||||
return ret;
|
||||
} else if (ret == 0 || !SUSP_SIG(sue, 'S', 'P') ) {
|
||||
iso_msg_debug(data->messenger, "SUSP/RR is not being used.");
|
||||
susp_iter_free(iter);
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
/* it is a SP system use entry */
|
||||
if (sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE
|
||||
|| sue->data.SP.ef[0] != 0xEF) {
|
||||
|
||||
iso_msg_sorry(data->messenger, LIBISO_SUSP_WRONG, "SUSP SP system use "
|
||||
"entry seems to be wrong. Ignoring Rock Ridge Extensions.");
|
||||
susp_iter_free(iter);
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
iso_msg_debug(data->messenger, "SUSP/RR is being used.");
|
||||
|
||||
/*
|
||||
* 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
|
||||
* feature is easy...
|
||||
*/
|
||||
data->len_skp = sue->data.SP.len_skp[0];
|
||||
|
||||
/*
|
||||
* Ok, now search for ER entry.
|
||||
* Just notice that the attributes for root dir are read elsewhere.
|
||||
*
|
||||
* 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 ((ret = susp_iter_next(iter, &sue)) > 0) {
|
||||
|
||||
/* ignore entries from different version */
|
||||
if (sue->version[0] != 1)
|
||||
continue;
|
||||
|
||||
if (SUSP_SIG(sue, 'E', 'R')) {
|
||||
|
||||
if (data->rr_version) {
|
||||
iso_msg_warn(data->messenger, LIBISO_SUSP_MULTIPLE_ER,
|
||||
"More than one ER has found. This is not supported. "
|
||||
"It will be ignored, but can cause problems. "
|
||||
"Please notify us about this.");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) ) {
|
||||
|
||||
iso_msg_debug(data->messenger,
|
||||
"Suitable Rock Ridge ER found. Version 1.10.");
|
||||
data->rr_version = 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)) ) {
|
||||
|
||||
iso_msg_debug(data->messenger,
|
||||
"Suitable Rock Ridge ER found. Version 1.12.");
|
||||
data->rr_version = RR_EXT_112;
|
||||
//TODO check also version?
|
||||
} else {
|
||||
iso_msg_warn(data->messenger, LIBISO_SUSP_MULTIPLE_ER,
|
||||
"Not Rock Ridge ER found.\n"
|
||||
"That will be ignored, but can cause problems in "
|
||||
"image reading. Please notify us about this");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
susp_iter_free(iter);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
int read_pvm(_ImageFsData *data, uint32_t block)
|
||||
{
|
||||
int ret;
|
||||
struct ecma119_pri_vol_desc *pvm;
|
||||
struct ecma119_dir_record *rootdr;
|
||||
unsigned char buffer[BLOCK_SIZE];
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
|
||||
/* read PVM */
|
||||
ret = data->src->read_block(data->src, block, buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pvm = (struct ecma119_pri_vol_desc *)buffer;
|
||||
|
||||
/* sanity checks */
|
||||
if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1
|
||||
|| strncmp((char*)pvm->std_identifier, "CD001", 5)
|
||||
|| pvm->file_structure_version[0] != 1 ) {
|
||||
|
||||
return ISO_WRONG_PVD;
|
||||
pvm = (struct ecma119_pri_vol_desc *)buffer;
|
||||
|
||||
/* sanity checks */
|
||||
if (pvm->vol_desc_type[0] != 1 || pvm->vol_desc_version[0] != 1
|
||||
|| strncmp((char*)pvm->std_identifier, "CD001", 5)
|
||||
|| pvm->file_structure_version[0] != 1) {
|
||||
|
||||
return ISO_WRONG_PVD;
|
||||
}
|
||||
|
||||
|
||||
/* ok, it is a valid PVD */
|
||||
|
||||
|
||||
/* fill volume attributes */
|
||||
data->volset_id = strcopy((char*)pvm->vol_set_id, 128);
|
||||
data->volume_id = strcopy((char*)pvm->volume_id, 32);
|
||||
@ -217,12 +350,12 @@ int read_pvm(_ImageFsData *data, uint32_t block)
|
||||
data->copyright_file_id = strcopy((char*)pvm->copyright_file_id, 37);
|
||||
data->abstract_file_id = strcopy((char*)pvm->abstract_file_id, 37);
|
||||
data->biblio_file_id = strcopy((char*)pvm->bibliographic_file_id, 37);
|
||||
|
||||
|
||||
data->nblocks = iso_read_bb(pvm->vol_space_size, 4, NULL);
|
||||
|
||||
|
||||
rootdr = (struct ecma119_dir_record*) pvm->root_dir_record;
|
||||
data->iso_root_block = iso_read_bb(rootdr->block, 4, NULL);
|
||||
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* PVM has other things that could be interesting, but that don't have a
|
||||
@ -230,7 +363,7 @@ int read_pvm(_ImageFsData *data, uint32_t block)
|
||||
* could keep the creation date and update the modification date, for
|
||||
* example.
|
||||
*/
|
||||
|
||||
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
@ -238,44 +371,46 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts,
|
||||
IsoImageFilesystem **fs)
|
||||
{
|
||||
int ret;
|
||||
uint32_t block;
|
||||
IsoImageFilesystem *ifs;
|
||||
_ImageFsData *data;
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
|
||||
if (src == NULL || opts == NULL || fs == NULL) {
|
||||
return ISO_NULL_POINTER;
|
||||
}
|
||||
|
||||
|
||||
data = calloc(1, sizeof(_ImageFsData));
|
||||
if (data == NULL) {
|
||||
return ISO_MEM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ifs = calloc(1, sizeof(IsoImageFilesystem));
|
||||
if (ifs == NULL) {
|
||||
free(data);
|
||||
return ISO_MEM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* get our ref to IsoDataSource */
|
||||
data->src = src;
|
||||
iso_data_source_ref(src);
|
||||
data->open_count = 0; //TODO
|
||||
|
||||
|
||||
/* fill data from opts */
|
||||
data->gid = opts->gid;
|
||||
data->uid = opts->uid;
|
||||
data->mode = opts->mode & ~S_IFMT;
|
||||
data->input_charset = strdup("UTF-8"); //TODO strdup(opts->input_charset);
|
||||
data->input_charset = strdup(opts->input_charset);
|
||||
data->messenger = opts->messenger;
|
||||
|
||||
|
||||
ifs->open = ifs_fs_open;
|
||||
ifs->close = ifs_fs_close;
|
||||
|
||||
|
||||
ifs->fs.data = data;
|
||||
ifs->fs.free = ifs_fs_free;
|
||||
|
||||
|
||||
/* read Volume Descriptors and ensure it is a valid image */
|
||||
|
||||
|
||||
/* 1. first, open the filesystem */
|
||||
ifs_fs_open(ifs);
|
||||
|
||||
@ -284,26 +419,102 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts,
|
||||
if (ret < 0) {
|
||||
goto fs_cleanup;
|
||||
}
|
||||
|
||||
|
||||
/* 3. read next volume descriptors */
|
||||
// TODO
|
||||
|
||||
block = opts->block + 17;
|
||||
do {
|
||||
ret = src->read_block(src, block, buffer);
|
||||
if (ret < 0) {
|
||||
/* cleanup and exit */
|
||||
goto fs_cleanup;
|
||||
}
|
||||
switch (buffer[0]) {
|
||||
case 0:
|
||||
/*
|
||||
* This is a boot record
|
||||
* Here we handle el-torito
|
||||
*/
|
||||
//TODO add support for El-Torito
|
||||
iso_msg_hint(data->messenger, LIBISO_UNSUPPORTED_VD,
|
||||
"El-Torito extensions not supported yet");
|
||||
break;
|
||||
case 2:
|
||||
/* suplementary volume descritor */
|
||||
// TODO support Joliet
|
||||
iso_msg_hint(data->messenger, LIBISO_UNSUPPORTED_VD,
|
||||
"Joliet extensions not supported yet");
|
||||
break;
|
||||
case 255:
|
||||
/*
|
||||
* volume set terminator
|
||||
* ignore, as it's checked in loop end condition
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
{
|
||||
iso_msg_hint(data->messenger, LIBISO_UNSUPPORTED_VD,
|
||||
"Ignoring Volume descriptor %x.", buffer[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
block++;
|
||||
} while (buffer[0] != 255);
|
||||
|
||||
/* 4. check if RR extensions are being used */
|
||||
//TODO
|
||||
//ret = read_root_susp_entries(info, volume->root, data->iso_root_block);
|
||||
ret = read_root_susp_entries(data, data->iso_root_block);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* user doesn't want to read RR extensions */
|
||||
if (opts->norock) {
|
||||
data->rr = RR_EXT_NO;
|
||||
} else {
|
||||
data->rr = data->rr_version;
|
||||
}
|
||||
|
||||
/* select what tree to read */
|
||||
//TODO
|
||||
|
||||
if (data->rr) {
|
||||
/* RR extensions are available */
|
||||
if (opts->preferjoliet && data->joliet) {
|
||||
/* if user prefers joliet, that is used */
|
||||
iso_msg_debug(data->messenger, "Reading Joliet extensions.");
|
||||
//data->get_name = ucs2str;
|
||||
data->rr = RR_EXT_NO;
|
||||
//data->root_dir_block = joliet root
|
||||
/* root_dir_block already contains root for joliet */
|
||||
//TODO add joliet support
|
||||
goto fs_cleanup;
|
||||
} else {
|
||||
/* RR will be used */
|
||||
iso_msg_debug(data->messenger, "Reading Rock Ridge extensions.");
|
||||
//data->root_dir_block = info.iso_root_block;
|
||||
data->get_name = strcopy;
|
||||
}
|
||||
} else {
|
||||
/* RR extensions are not available */
|
||||
if (opts->preferjoliet && data->joliet) {
|
||||
/* joliet will be used */
|
||||
iso_msg_debug(data->messenger, "Reading Joliet extensions.");
|
||||
//data->get_name = ucs2str;
|
||||
//data->root_dir_block = joliet root
|
||||
/* root_dir_block already contains root for joliet */
|
||||
//TODO add joliet support
|
||||
goto fs_cleanup;
|
||||
} else {
|
||||
/* default to plain iso */
|
||||
iso_msg_debug(data->messenger, "Reading plain ISO-9660 tree.");
|
||||
//data->root_dir_block = info.iso_root_block;
|
||||
data->get_name = strcopy;
|
||||
}
|
||||
}
|
||||
|
||||
/* and finally return. Note that we keep the DataSource opened */
|
||||
|
||||
|
||||
*fs = ifs;
|
||||
return ISO_SUCCESS;
|
||||
|
||||
fs_cleanup: ;
|
||||
|
||||
fs_cleanup: ;
|
||||
ifs_fs_free((IsoFilesystem*)ifs);
|
||||
free(ifs);
|
||||
return ret;
|
||||
|
Reference in New Issue
Block a user