diff --git a/src/fs_image.c b/src/fs_image.c index 0b75275..c20b5f0 100644 --- a/src/fs_image.c +++ b/src/fs_image.c @@ -20,6 +20,11 @@ #include #include +#define ISO_IMAGE_FS_ID 2 + +/** unique identifier for each image */ +unsigned int fs_dev_id = 0; + /** * Should the RR extensions be read? */ @@ -36,6 +41,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 @@ -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 */ diff --git a/src/libiso_msgs.h b/src/libiso_msgs.h index ceff4ad..c96992f 100644 --- a/src/libiso_msgs.h +++ b/src/libiso_msgs.h @@ -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 diff --git a/src/messages.h b/src/messages.h index 4d3efa4..1659f31 100644 --- a/src/messages.h +++ b/src/messages.h @@ -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