Add function to create an IsoFileSource from an ISO image.
This commit is contained in:
parent
dc6cd09877
commit
be37936534
536
src/fs_image.c
536
src/fs_image.c
@ -20,6 +20,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ISO_IMAGE_FS_ID 2
|
||||
|
||||
/** unique identifier for each image */
|
||||
unsigned int fs_dev_id = 0;
|
||||
|
||||
/**
|
||||
* Should the RR extensions be read?
|
||||
*/
|
||||
@ -37,6 +42,9 @@ typedef struct
|
||||
/** DataSource from where data will be read */
|
||||
IsoDataSource *src;
|
||||
|
||||
/** unique id for the each image (filesystem instance) */
|
||||
unsigned int id;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -71,12 +79,6 @@ typedef struct
|
||||
*/
|
||||
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.
|
||||
@ -114,6 +116,522 @@ typedef struct
|
||||
|
||||
} _ImageFsData;
|
||||
|
||||
typedef struct image_fs_data ImageFileSourceData;
|
||||
|
||||
struct image_fs_data
|
||||
{
|
||||
IsoImageFilesystem *fs; /**< reference to the image it belongs to */
|
||||
IsoFileSource *parent; /**< reference to the parent (NULL if root) */
|
||||
|
||||
struct stat info; /**< filled struct stat */
|
||||
char *name; /**< name of this file */
|
||||
|
||||
uint32_t block; /**< block of the extend */
|
||||
unsigned int opened : 2; /**< 0 not opened, 1 opened file, 2 opened dir */
|
||||
|
||||
/* info for content reading */
|
||||
union
|
||||
{
|
||||
/**
|
||||
* - For regular files, once opened it points to a temporary data
|
||||
* buffer of 2048 bytes.
|
||||
* - For dirs, once opened it points to a IsoFileSource* array with
|
||||
* its children
|
||||
* - For symlinks, it points to link destination
|
||||
*/
|
||||
void *content;
|
||||
|
||||
/**
|
||||
* - For regular files, number of bytes already read.
|
||||
* - For dirs, position of child iterator
|
||||
*/
|
||||
uint32_t offset;
|
||||
} data;
|
||||
};
|
||||
|
||||
static
|
||||
char* ifs_get_path(IsoFileSource *src)
|
||||
{
|
||||
ImageFileSourceData *data;
|
||||
data = src->data;
|
||||
|
||||
if (data->parent == NULL) {
|
||||
return strdup("");
|
||||
} else {
|
||||
char *path = ifs_get_path(data->parent);
|
||||
int pathlen = strlen(path);
|
||||
path = realloc(path, pathlen + strlen(data->name) + 2);
|
||||
path[pathlen] = '/';
|
||||
path[pathlen + 1] = '\0';
|
||||
return strcat(path, data->name);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
char* ifs_get_name(IsoFileSource *src)
|
||||
{
|
||||
ImageFileSourceData *data;
|
||||
data = src->data;
|
||||
return strdup(data->name);
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_lstat(IsoFileSource *src, struct stat *info)
|
||||
{
|
||||
ImageFileSourceData *data;
|
||||
|
||||
if (src == NULL || info == NULL) {
|
||||
return ISO_NULL_POINTER;
|
||||
}
|
||||
|
||||
data = src->data;
|
||||
*info = data->info;
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_stat(IsoFileSource *src, struct stat *info)
|
||||
{
|
||||
//TODO to implement
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_access(IsoFileSource *src)
|
||||
{
|
||||
/* we always have access, it is controlled by DataSource */
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_open(IsoFileSource *src)
|
||||
{
|
||||
//TODO implement
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_close(IsoFileSource *src)
|
||||
{
|
||||
//TODO implement
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_read(IsoFileSource *src, void *buf, size_t count)
|
||||
{
|
||||
//TODO implement
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_readdir(IsoFileSource *src, IsoFileSource **child)
|
||||
{
|
||||
//TODO implement
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_readlink(IsoFileSource *src, char *buf, size_t bufsiz)
|
||||
{
|
||||
//TODO implement
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
IsoFilesystem* ifs_get_filesystem(IsoFileSource *src)
|
||||
{
|
||||
ImageFileSourceData *data;
|
||||
|
||||
if (src == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = src->data;
|
||||
return (IsoFilesystem*) data->fs;
|
||||
}
|
||||
|
||||
static
|
||||
void ifs_free(IsoFileSource *src)
|
||||
{
|
||||
ImageFileSourceData *data;
|
||||
|
||||
data = src->data;
|
||||
|
||||
/* close the file if it is already openned */
|
||||
if (data->opened) {
|
||||
src->class->close(src);
|
||||
}
|
||||
|
||||
//TODO free destination on symlinks
|
||||
iso_filesystem_unref((IsoFilesystem*)data->fs);
|
||||
iso_file_source_unref(data->parent);
|
||||
free(data->name);
|
||||
free(data);
|
||||
}
|
||||
|
||||
IsoFileSourceIface ifs_class = {
|
||||
ifs_get_path,
|
||||
ifs_get_name,
|
||||
ifs_lstat,
|
||||
ifs_stat,
|
||||
ifs_access,
|
||||
ifs_open,
|
||||
ifs_close,
|
||||
ifs_read,
|
||||
ifs_readdir,
|
||||
ifs_readlink,
|
||||
ifs_get_filesystem,
|
||||
ifs_free
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* 1 success, 0 record ignored (not an error, can be a relocated dir),
|
||||
* < 0 error
|
||||
*/
|
||||
static
|
||||
int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
|
||||
struct ecma119_dir_record *record,
|
||||
IsoFileSource **src)
|
||||
{
|
||||
int ret;
|
||||
struct stat atts;
|
||||
time_t recorded;
|
||||
_ImageFsData *fsdata;
|
||||
IsoFileSource *ifsrc = NULL;
|
||||
ImageFileSourceData *ifsdata = NULL;
|
||||
|
||||
int namecont = 0; /* 1 if found a NM with CONTINUE flag */
|
||||
char *name = NULL;
|
||||
|
||||
/* 1 if found a SL with CONTINUE flag,
|
||||
* 2 if found a component with continue flag */
|
||||
int linkdestcont = 0;
|
||||
char *linkdest = NULL;
|
||||
|
||||
uint32_t relocated_dir = 0;
|
||||
|
||||
if (fs == NULL || fs->fs.data == NULL || parent == NULL || record == NULL
|
||||
|| src == NULL) {
|
||||
return ISO_NULL_POINTER;
|
||||
}
|
||||
|
||||
fsdata = (_ImageFsData*)fs->fs.data;
|
||||
|
||||
memset(&atts, 0, sizeof(struct stat));
|
||||
|
||||
/*
|
||||
* 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 (fsdata->rr) {
|
||||
struct susp_sys_user_entry *sue;
|
||||
SuspIterator *iter;
|
||||
|
||||
|
||||
iter = susp_iter_new(fsdata->src, record, fsdata->len_skp,
|
||||
fsdata->messenger);
|
||||
if (iter == NULL) {
|
||||
return ISO_MEM_ERROR;
|
||||
}
|
||||
|
||||
while ((ret = susp_iter_next(iter, &sue)) > 0) {
|
||||
|
||||
/* ignore entries from different version */
|
||||
if (sue->version[0] != 1)
|
||||
continue;
|
||||
|
||||
if (SUSP_SIG(sue, 'P', 'X')) {
|
||||
ret = read_rr_PX(sue, &atts);
|
||||
if (ret < 0) {
|
||||
/* notify and continue */
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Invalid PX entry");
|
||||
}
|
||||
} else if (SUSP_SIG(sue, 'T', 'F')) {
|
||||
ret = read_rr_TF(sue, &atts);
|
||||
if (ret < 0) {
|
||||
/* notify and continue */
|
||||
iso_msg_warn(fsdata->messenger, LIBISO_RR_WARNING,
|
||||
"Invalid TF entry");
|
||||
}
|
||||
} else if (SUSP_SIG(sue, 'N', 'M')) {
|
||||
if (name != NULL && namecont == 0) {
|
||||
/* ups, RR standard violation */
|
||||
iso_msg_warn(fsdata->messenger, LIBISO_RR_WARNING,
|
||||
"New NM entry found without previous"
|
||||
"CONTINUE flag. Ignored");
|
||||
continue;
|
||||
}
|
||||
ret = read_rr_NM(sue, &name, &namecont);
|
||||
if (ret < 0) {
|
||||
/* notify and continue */
|
||||
iso_msg_warn(fsdata->messenger, LIBISO_RR_WARNING,
|
||||
"Invalid NM entry");
|
||||
}
|
||||
} else if (SUSP_SIG(sue, 'S', 'L')) {
|
||||
if (linkdest != NULL && linkdestcont == 0) {
|
||||
/* ups, RR standard violation */
|
||||
iso_msg_warn(fsdata->messenger, LIBISO_RR_WARNING,
|
||||
"New SL entry found without previous"
|
||||
"CONTINUE flag. Ignored");
|
||||
continue;
|
||||
}
|
||||
ret = read_rr_SL(sue, &linkdest, &linkdestcont);
|
||||
if (ret < 0) {
|
||||
/* notify and continue */
|
||||
iso_msg_warn(fsdata->messenger, LIBISO_RR_WARNING,
|
||||
"Invalid SL entry");
|
||||
}
|
||||
} 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; /* it's 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);
|
||||
if (relocated_dir == 0) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Invalid SL entry, no child location");
|
||||
ret = ISO_WRONG_RR;
|
||||
break;
|
||||
}
|
||||
} else if (SUSP_SIG(sue, 'P', 'N')) {
|
||||
ret = read_rr_PN(sue, &atts);
|
||||
if (ret < 0) {
|
||||
/* notify and continue */
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Invalid PN entry");
|
||||
}
|
||||
} else if (SUSP_SIG(sue, 'S', 'F')) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_UNSUPPORTED,
|
||||
"Sparse files not supported.");
|
||||
ret = ISO_UNSUPPORTED_RR;
|
||||
break;
|
||||
} else if (SUSP_SIG(sue, 'R', 'R')) {
|
||||
/* TODO I've seen this RR on mkisofs images. what's this? */
|
||||
continue;
|
||||
} else {
|
||||
iso_msg_hint(fsdata->messenger, LIBISO_SUSP_UNHANLED,
|
||||
"Unhandled SUSP entry %c%c.", sue->sig[0], sue->sig[1]);
|
||||
}
|
||||
}
|
||||
|
||||
susp_iter_free(iter);
|
||||
|
||||
/* check for RR problems */
|
||||
|
||||
if (ret < 0) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Error parsing RR entries");
|
||||
} else if (!relocated_dir && atts.st_mode == (mode_t) 0 ) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR, "Mandatory Rock "
|
||||
"Ridge PX entry is not present or it contains invalid values.");
|
||||
ret = ISO_WRONG_RR;
|
||||
} else {
|
||||
/* ensure both name and link dest are finished */
|
||||
if (namecont != 0) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Incomplete RR name, last NM entry continues");
|
||||
ret = ISO_WRONG_RR;
|
||||
}
|
||||
if (linkdestcont != 0) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Incomplete link destination, last SL entry continues");
|
||||
ret = ISO_WRONG_RR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
free(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//TODO convert name to needed charset!!
|
||||
|
||||
} else {
|
||||
/* RR extensions are not read / used */
|
||||
atts.st_mode = fsdata->mode;
|
||||
atts.st_gid = fsdata->gid;
|
||||
atts.st_uid = fsdata->uid;
|
||||
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 name in directory record
|
||||
*/
|
||||
if (!name) {
|
||||
size_t len;
|
||||
|
||||
if (record->file_id[0] == 0) {
|
||||
/* "." entry, we can call this for root node, so... */
|
||||
if (!(atts.st_mode & S_IFDIR)) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_WRONG_IMG,
|
||||
"Wrong ISO file name. \".\" not dir");
|
||||
return ISO_WRONG_ECMA119;
|
||||
}
|
||||
} else {
|
||||
name = fsdata->get_name((char*)record->file_id, record->len_fi[0]);
|
||||
|
||||
/* remove trailing version number */
|
||||
len = strlen(name);
|
||||
if (len > 2 && name[len-2] == ';' && name[len-1] == '1') {
|
||||
name[len-2] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (relocated_dir) {
|
||||
|
||||
/*
|
||||
* We are dealing with a placeholder for a relocated dir.
|
||||
* Thus, we need to read attributes for this directory from the "."
|
||||
* entry of the relocated dir.
|
||||
*/
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
|
||||
ret = fsdata->src->read_block(fsdata->src, relocated_dir, buffer);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iso_file_source_new_ifs(fs, parent, (struct ecma119_dir_record*)
|
||||
buffer, src);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* but the real name is the name of the placeholder */
|
||||
ifsdata = (ImageFileSourceData*) (*src)->data;
|
||||
ifsdata->name = name;
|
||||
return ISO_SUCCESS;
|
||||
}
|
||||
|
||||
if (fsdata->rr != RR_EXT_112) {
|
||||
/*
|
||||
* Only RRIP 1.12 provides valid inode numbers. If not, it is not easy
|
||||
* to generate those serial numbers, and we use extend block instead.
|
||||
* It BREAKS POSIX SEMANTICS, but its suitable for our needs
|
||||
*/
|
||||
atts.st_ino = (ino_t) iso_read_bb(record->block, 4, NULL);
|
||||
if (fsdata->rr == 0) {
|
||||
atts.st_nlink = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/* Fill last entries */
|
||||
atts.st_dev = fsdata->id;
|
||||
atts.st_blksize = BLOCK_SIZE;
|
||||
atts.st_blocks = div_up(atts.st_size, BLOCK_SIZE);
|
||||
|
||||
//TODO more sanity checks!!
|
||||
if (S_ISLNK(atts.st_mode) && (linkdest == NULL)) {
|
||||
iso_msg_sorry(fsdata->messenger, LIBISO_RR_ERROR,
|
||||
"Link without destination.");
|
||||
free(name);
|
||||
return ISO_WRONG_RR;
|
||||
}
|
||||
|
||||
/* ok, we can now create the file source */
|
||||
ifsdata = calloc(1, sizeof(ImageFileSourceData));
|
||||
if (ifsdata == NULL) {
|
||||
ret = ISO_MEM_ERROR;
|
||||
goto ifs_cleanup;
|
||||
}
|
||||
ifsrc = calloc(1, sizeof(IsoFileSource));
|
||||
if (ifsrc == NULL) {
|
||||
ret = ISO_MEM_ERROR;
|
||||
goto ifs_cleanup;
|
||||
}
|
||||
|
||||
/* fill data */
|
||||
ifsdata->fs = fs;
|
||||
iso_filesystem_ref((IsoFilesystem*)fs);
|
||||
if (parent) {
|
||||
ifsdata->parent = parent;
|
||||
iso_file_source_ref(parent);
|
||||
}
|
||||
ifsdata->info = atts;
|
||||
ifsdata->name = name;
|
||||
ifsdata->block = iso_read_bb(record->block, 4, NULL);
|
||||
|
||||
if (S_ISLNK(atts.st_mode)) {
|
||||
ifsdata->data.content = linkdest;
|
||||
}
|
||||
|
||||
ifsrc->class = &ifs_class;
|
||||
ifsrc->data = ifsdata;
|
||||
ifsrc->refcount = 1;
|
||||
|
||||
*src = ifsrc;
|
||||
return ISO_SUCCESS;
|
||||
|
||||
ifs_cleanup: ;
|
||||
free(name);
|
||||
free(linkdest);
|
||||
free(ifsdata);
|
||||
free(ifsrc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_get_root(IsoFilesystem *fs, IsoFileSource **root)
|
||||
{
|
||||
//TODO ensure data source is opened
|
||||
|
||||
//TODO not implemented
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file)
|
||||
{
|
||||
//TODO not implemented
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int ifs_get_id(IsoFilesystem *fs)
|
||||
{
|
||||
return ISO_IMAGE_FS_ID;
|
||||
}
|
||||
|
||||
static
|
||||
int ifs_fs_open(IsoImageFilesystem *fs)
|
||||
{
|
||||
@ -396,6 +914,9 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts,
|
||||
iso_data_source_ref(src);
|
||||
data->open_count = 0; //TODO
|
||||
|
||||
/* get an id for the filesystem */
|
||||
data->id = ++fs_dev_id;
|
||||
|
||||
/* fill data from opts */
|
||||
data->gid = opts->gid;
|
||||
data->uid = opts->uid;
|
||||
@ -407,7 +928,10 @@ int iso_image_filesystem_new(IsoDataSource *src, struct iso_read_opts *opts,
|
||||
ifs->close = ifs_fs_close;
|
||||
|
||||
ifs->fs.data = data;
|
||||
ifs->fs.get_root = ifs_get_root;
|
||||
ifs->fs.get_by_path = ifs_get_by_path;
|
||||
ifs->fs.free = ifs_fs_free;
|
||||
ifs->fs.get_id = ifs_get_id;
|
||||
|
||||
/* read Volume Descriptors and ensure it is a valid image */
|
||||
|
||||
|
@ -409,6 +409,7 @@ Range "vreixo" : 0x00030000 to 0x0003ffff
|
||||
0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found
|
||||
0x00030111 (SORRY,HIGH) = Unsupported RR feature
|
||||
0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry
|
||||
0x00030113 (WARNING,MEDIUM)= Wrong/Damaged Rock Ridge
|
||||
|
||||
El-Torito:
|
||||
0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored
|
||||
|
@ -53,10 +53,13 @@
|
||||
#define LIBISO_SUSP_WRONG 0x00030102
|
||||
/* Unsupported multiple SUSP ER entries where found */
|
||||
#define LIBISO_SUSP_MULTIPLE_ER 0x00030103
|
||||
|
||||
/** Unsupported RR feature. */
|
||||
#define LIBISO_RR_UNSUPPORTED 0x00030111
|
||||
/** Error in a Rock Ridge entry. */
|
||||
#define LIBISO_RR_ERROR 0x00030112
|
||||
/* Wrong/Damaged Rock Ridge */
|
||||
#define LIBISO_RR_WARNING 0x00030113
|
||||
|
||||
/** Unsupported boot vol desc. */
|
||||
#define LIBISO_BOOT_VD_UNHANLED 0x00030201
|
||||
|
Loading…
Reference in New Issue
Block a user