|
|
|
/*
|
|
|
|
* Copyright (c) 2007 Vreixo Formoso
|
|
|
|
* Copyright (c) 2009 Thomas Schmitt
|
|
|
|
*
|
|
|
|
* This file is part of the libisofs project; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation. See COPYING file for details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Filesystem/FileSource implementation to access an ISO image, using an
|
|
|
|
* IsoDataSource to read image data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "libisofs.h"
|
|
|
|
#include "ecma119.h"
|
|
|
|
#include "messages.h"
|
|
|
|
#include "rockridge.h"
|
|
|
|
#include "image.h"
|
|
|
|
#include "tree.h"
|
|
|
|
#include "eltorito.h"
|
|
|
|
#include "node.h"
|
|
|
|
#include "aaip_0_2.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <langinfo.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Options for image reading.
|
|
|
|
* There are four kind of options:
|
|
|
|
* - Related to multisession support.
|
|
|
|
* In most cases, an image begins at LBA 0 of the data source. However,
|
|
|
|
* in multisession discs, the later image begins in the last session on
|
|
|
|
* disc. The block option can be used to specify the start of that last
|
|
|
|
* session.
|
|
|
|
* - Related to the tree that will be read.
|
|
|
|
* As default, when Rock Ridge extensions are present in the image, that
|
|
|
|
* will be used to get the tree. If RR extensions are not present, libisofs
|
|
|
|
* will use the Joliet extensions if available. Finally, the plain ISO-9660
|
|
|
|
* tree is used if neither RR nor Joliet extensions are available. With
|
|
|
|
* norock, nojoliet, and preferjoliet options, you can change this
|
|
|
|
* default behavior.
|
|
|
|
* - Related to default POSIX attributes.
|
|
|
|
* When Rock Ridege extensions are not used, libisofs can't figure out what
|
|
|
|
* are the the permissions, uid or gid for the files. You should supply
|
|
|
|
* default values for that.
|
|
|
|
*/
|
|
|
|
struct iso_read_opts
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Block where the image begins, usually 0, can be different on a
|
|
|
|
* multisession disc.
|
|
|
|
*/
|
|
|
|
uint32_t block;
|
|
|
|
|
|
|
|
unsigned int norock : 1; /*< Do not read Rock Ridge extensions */
|
|
|
|
unsigned int nojoliet : 1; /*< Do not read Joliet extensions */
|
|
|
|
unsigned int noiso1999 : 1; /*< Do not read ISO 9660:1999 enhanced tree */
|
|
|
|
unsigned int noaaip : 1; /* Do not read AAIP extension for xattr and ACL */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hand out new inode numbers and overwrite eventually read PX inode
|
|
|
|
* numbers. This will split apart any hardlinks.
|
|
|
|
*/
|
|
|
|
unsigned int make_new_ino : 1 ;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When both Joliet and RR extensions are present, the RR tree is used.
|
|
|
|
* If you prefer using Joliet, set this to 1.
|
|
|
|
*/
|
|
|
|
unsigned int preferjoliet : 1;
|
|
|
|
|
|
|
|
uid_t uid; /**< Default uid when no RR */
|
|
|
|
gid_t gid; /**< Default uid when no RR */
|
|
|
|
mode_t dir_mode; /**< Default mode when no RR (only permissions) */
|
|
|
|
mode_t file_mode;
|
|
|
|
/* TODO #00024 : option to convert names to lower case for iso reading */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Input charset for RR file names. NULL to use default locale charset.
|
|
|
|
*/
|
|
|
|
char *input_charset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable methods to automatically choose an input charset.
|
|
|
|
* This eventually overrides input_charset.
|
|
|
|
*
|
|
|
|
* bit0= allow to set the input character set automatically from
|
|
|
|
* attribute "isofs.cs" of root directory
|
|
|
|
*/
|
|
|
|
int auto_input_charset;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return information for image.
|
|
|
|
* Both size, hasRR and hasJoliet will be filled by libisofs with suitable
|
|
|
|
* values.
|
|
|
|
*/
|
|
|
|
struct iso_read_image_features
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Will be filled with the size (in 2048 byte block) of the image, as
|
|
|
|
* reported in the PVM.
|
|
|
|
*/
|
|
|
|
uint32_t size;
|
|
|
|
|
|
|
|
/** It will be set to 1 if RR extensions are present, to 0 if not. */
|
|
|
|
unsigned int hasRR :1;
|
|
|
|
|
|
|
|
/** It will be set to 1 if Joliet extensions are present, to 0 if not. */
|
|
|
|
unsigned int hasJoliet :1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* It will be set to 1 if the image is an ISO 9660:1999, i.e. it has
|
|
|
|
* a version 2 Enhanced Volume Descriptor.
|
|
|
|
*/
|
|
|
|
unsigned int hasIso1999 :1;
|
|
|
|
|
|
|
|
/** It will be set to 1 if El-Torito boot record is present, to 0 if not.*/
|
|
|
|
unsigned int hasElTorito :1;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ifs_fs_open(IsoImageFilesystem *fs);
|
|
|
|
static int ifs_fs_close(IsoImageFilesystem *fs);
|
|
|
|
static int iso_file_source_new_ifs(IsoImageFilesystem *fs,
|
|
|
|
IsoFileSource *parent, struct ecma119_dir_record *record,
|
|
|
|
IsoFileSource **src, int flag);
|
|
|
|
|
|
|
|
/** unique identifier for each image */
|
|
|
|
unsigned int fs_dev_id = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Should the RR extensions be read?
|
|
|
|
*/
|
|
|
|
enum read_rr_ext {
|
|
|
|
RR_EXT_NO = 0, /*< Do not use RR extensions */
|
|
|
|
RR_EXT_110 = 1, /*< RR extensions conforming version 1.10 */
|
|
|
|
RR_EXT_112 = 2 /*< RR extensions conforming version 1.12 */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data for the image IsoFilesystem
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
* 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 dir_mode; /**< Default mode when no RR (only permissions) */
|
|
|
|
mode_t file_mode;
|
|
|
|
|
|
|
|
int msgid;
|
|
|
|
|
|
|
|
char *input_charset; /**< Input charset for RR names */
|
|
|
|
char *local_charset; /**< For RR names, will be set to the locale one */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable methods to automatically choose an input charset.
|
|
|
|
* This eventually overrides input_charset.
|
|
|
|
*
|
|
|
|
* bit0= allow to set the input character set automatically from
|
|
|
|
* attribute "isofs.cs" of root directory
|
|
|
|
*/
|
|
|
|
int auto_input_charset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will be filled with the block lba of the extend for the root directory
|
|
|
|
* of the hierarchy that will be read, either from the PVD (ISO, RR) or
|
|
|
|
* from the SVD (Joliet)
|
|
|
|
*/
|
|
|
|
uint32_t iso_root_block;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will be filled with the block lba of the extend for the root directory,
|
|
|
|
* as read from the PVM
|
|
|
|
*/
|
|
|
|
uint32_t pvd_root_block;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will be filled with the block lba of the extend for the root directory,
|
|
|
|
* as read from the SVD
|
|
|
|
*/
|
|
|
|
uint32_t svd_root_block;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will be filled with the block lba of the extend for the root directory,
|
|
|
|
* as read from the enhanced volume descriptor (ISO 9660:1999)
|
|
|
|
*/
|
|
|
|
uint32_t evd_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;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
/* Volume attributes */
|
|
|
|
char *volset_id;
|
|
|
|
char *volume_id; /**< Volume identifier. */
|
|
|
|
char *publisher_id; /**< Volume publisher. */
|
|
|
|
char *data_preparer_id; /**< Volume data preparer. */
|
|
|
|
char *system_id; /**< Volume system identifier. */
|
|
|
|
char *application_id; /**< Volume application id */
|
|
|
|
char *copyright_file_id;
|
|
|
|
char *abstract_file_id;
|
|
|
|
char *biblio_file_id;
|
|
|
|
|
|
|
|
/* extension information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* RR version being used in image.
|
|
|
|
* 0 no RR extension, 1 RRIP 1.10, 2 RRIP 1.12
|
|
|
|
*/
|
|
|
|
enum read_rr_ext rr_version;
|
|
|
|
|
|
|
|
/** If Joliet extensions are available on image */
|
|
|
|
unsigned int joliet : 1;
|
|
|
|
|
|
|
|
/** If ISO 9660:1999 is available on image */
|
|
|
|
unsigned int iso1999 : 1;
|
|
|
|
|
|
|
|
/** Whether AAIP info shall be loaded if it is present.
|
|
|
|
* 1 = yes , 0 = no
|
|
|
|
*/
|
|
|
|
int aaip_load;
|
|
|
|
|
|
|
|
/** Whether AAIP is present. Version major.minor = major * 100 + minor
|
|
|
|
* Value -1 means that no AAIP ER was detected yet.
|
|
|
|
*/
|
|
|
|
int aaip_version;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Number of blocks of the volume, as reported in the PVM.
|
|
|
|
*/
|
|
|
|
uint32_t nblocks;
|
|
|
|
|
|
|
|
/* el-torito information */
|
|
|
|
unsigned int eltorito : 1; /* is el-torito available */
|
|
|
|
unsigned int bootable:1; /**< If the entry is bootable. */
|
|
|
|
unsigned char type; /**< The type of image */
|
|
|
|
unsigned char partition_type; /**< type of partition for HD-emul images */
|
|
|
|
short load_seg; /**< Load segment for the initial boot image. */
|
|
|
|
short load_size; /**< Number of sectors to load. */
|
|
|
|
uint32_t imgblock; /**< Block for El-Torito boot image */
|
|
|
|
uint32_t catblock; /**< Block for El-Torito catalog */
|
|
|
|
|
|
|
|
/* Whether inode numbers from PX entries shall be discarded */
|
|
|
|
unsigned int make_new_ino : 1 ;
|
|
|
|
|
|
|
|
/* Inode number generator counter */
|
|
|
|
ino_t inode_counter;
|
|
|
|
|
|
|
|
/* PX inode number status
|
|
|
|
bit0= there were nodes with PX inode numbers
|
|
|
|
bit1= there were nodes with PX but without inode numbers
|
|
|
|
bit2= there were nodes without PX
|
|
|
|
bit3= there were nodes with faulty PX
|
|
|
|
*/
|
|
|
|
int px_ino_status;
|
|
|
|
|
|
|
|
} _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 */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Location of file extents.
|
|
|
|
*/
|
|
|
|
struct iso_file_section *sections;
|
|
|
|
int nsections;
|
|
|
|
|
|
|
|
unsigned int opened : 2; /**< 0 not opened, 1 opened file, 2 opened dir */
|
|
|
|
|
|
|
|
#ifdef Libisofs_with_zliB
|
|
|
|
uint8_t header_size_div4;
|
|
|
|
uint8_t block_size_log2;
|
|
|
|
uint32_t uncompressed_size;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* info for content reading */
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* - 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.
|
|
|
|
*/
|
|
|
|
off_t offset;
|
|
|
|
} data;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* malloc() storage for the string of AAIP fields which represent
|
|
|
|
* ACLs and XFS-style Extended Attributes. (Not to be confused with
|
|
|
|
* ECMA-119 Extended Attributes.)
|
|
|
|
*/
|
|
|
|
unsigned char *aa_string;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
struct child_list
|
|
|
|
{
|
|
|
|
IsoFileSource *file;
|
|
|
|
struct child_list *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
void child_list_free(struct child_list *list)
|
|
|
|
{
|
|
|
|
struct child_list *temp;
|
|
|
|
struct child_list *next = list;
|
|
|
|
while (next != NULL) {
|
|
|
|
temp = next->next;
|
|
|
|
iso_file_source_unref(next->file);
|
|
|
|
free(next);
|
|
|
|
next = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 data->name == NULL ? NULL : 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)
|
|
|
|
{
|
|
|
|
ImageFileSourceData *data;
|
|
|
|
|
|
|
|
if (src == NULL || info == NULL || src->data == NULL) {
|
|
|
|
return ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = (ImageFileSourceData*)src->data;
|
|
|
|
|
|
|
|
if (S_ISLNK(data->info.st_mode)) {
|
|
|
|
/* TODO #00012 : support follow symlinks on image filesystem */
|
|
|
|
return ISO_FILE_BAD_PATH;
|
|
|
|
}
|
|
|
|
*info = data->info;
|
|
|
|
return ISO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int ifs_access(IsoFileSource *src)
|
|
|
|
{
|
|
|
|
/* we always have access, it is controlled by DataSource */
|
|
|
|
return ISO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read all directory records in a directory, and creates an IsoFileSource for
|
|
|
|
* each of them, storing them in the data field of the IsoFileSource for the
|
|
|
|
* given dir.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
int read_dir(ImageFileSourceData *data)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t block;
|
|
|
|
IsoImageFilesystem *fs;
|
|
|
|
_ImageFsData *fsdata;
|
|
|
|
struct ecma119_dir_record *record;
|
|
|
|
uint8_t buffer[BLOCK_SIZE];
|
|
|
|
IsoFileSource *child = NULL;
|
|
|
|
uint32_t pos = 0;
|
|
|
|
uint32_t tlen = 0;
|
|
|
|
|
|
|
|
if (data == NULL) {
|
|
|
|
return ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs = data->fs;
|
|
|
|
fsdata = fs->data;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "." entry, get size of the dir and skip */
|
|
|
|
record = (struct ecma119_dir_record *)(buffer + pos);
|
|
|
|
size = iso_read_bb(record->length, 4, NULL);
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
ret = fsdata->src->read_block(fsdata->src, ++block, buffer);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
tlen += 2048 - pos;
|
|
|
|
pos = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* What about ignoring files with existence flag?
|
|
|
|
* if (record->flags[0] & 0x01)
|
|
|
|
* continue;
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (data->parent == NULL && record->len_fi[0] == 8
|
|
|
|
&& !strncmp((char*)record->file_id, "RR_MOVED", 8)) {
|
|
|
|
|
|
|
|
iso_msg_debug(fsdata->msgid, "Skipping RR_MOVE entry.");
|
|
|
|
|
|
|
|
tlen += record->len_dr[0];
|
|
|
|
pos += record->len_dr[0];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We pass a NULL parent instead of dir, to prevent the circular
|
|
|
|
* reference from child to parent.
|
|
|
|
*/
|
|
|
|
ret = iso_file_source_new_ifs(fs, NULL, record, &child, 0);
|
|
|
|
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 == 1) {
|
|
|
|
struct child_list *node;
|
|
|
|
node = malloc(sizeof(struct child_list));
|
|
|
|
if (node == NULL) {
|
|
|
|
iso_file_source_unref(child);
|
|
|
|
return ISO_OUT_OF_MEM;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Note that we insert in reverse order. This leads to faster
|
|
|
|
* addition here, but also when adding to the tree, as insertion
|
|
|
|
* will be done, sorted, in the first position of the list.
|
|
|
|
*/
|
|
|
|
node->next = data->data.content;
|
|
|
|
node->file = child;
|
|
|
|
data->data.content = node;
|
|
|
|
child = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tlen += record->len_dr[0];
|
|
|
|
pos += record->len_dr[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return ISO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int ifs_open(IsoFileSource *src)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
ImageFileSourceData *data;
|
|
|
|
|
|
|
|
if (src == NULL || src->data == NULL) {
|
|
|
|
return ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
data = (ImageFileSourceData*)src->data;
|
|
|
|
|
|
|
|
if (data->opened) {
|
|
|
|
return ISO_FILE_ALREADY_OPENED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR(data->info.st_mode)) {
|
|
|
|
/* ensure fs is openned */
|
|
|
|
ret = data->fs->open(data->fs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cache all directory entries.
|
|
|
|
* This can waste more memory, but improves as disc is read in much more
|
|
|
|
* sequencially way, thus reducing jump between tracks on disc
|
|
|
|
*/
|
|
|
|
ret = read_dir(data);
|
|
|
|
data->fs->close(data->fs);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
/* free probably allocated children */
|
|
|
|
child_list_free((struct child_list*)data->data.content);
|
|
|
|
} else {
|
|
|
|
data->opened = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
} else if (S_ISREG(data->info.st_mode)) {
|
|
|
|
/* ensure fs is openned */
|
|
|
|
ret = data->fs->open(data->fs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
data->data.content = malloc(BLOCK_SIZE);
|
|
|
|
if (data->data.content == NULL) {
|
|
|
|
return ISO_OUT_OF_MEM;
|
|
|
|
}
|
|
|
|
data->data.offset = 0;
|
|
|
|
data->opened = 1;
|
|
|
|
} else {
|
|
|
|
/* symlinks and special files inside image can't be openned */
|
|
|
|
return ISO_FILE_ERROR;
|
|
|
|
}
|
|
|
|
return ISO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int ifs_close(IsoFileSource *src)
|
|
|
|
{
|
|
|
|
ImageFileSourceData *data;
|
|
|
|
|
|
|
|
if (src == NULL || src->data == NULL) {
|
|
|
|
return ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
data = (ImageFileSourceData*)src->data;
|
|
|
|
|
|
|
|
if (!data->opened) {
|
|
|
|
return ISO_FILE_NOT_OPENED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->opened == 2) {
|
|
|
|
/*
|
|
|
|
* close a dir, free all pending pre-allocated children.
|
|
|
|
* not that we don't need to close the filesystem, it was already
|
|
|
|
* closed
|
|
|
|
*/
|
|
|
|
child_list_free((struct child_list*) data->data.content);
|
|
|
|
data->data.content = NULL;
|
|
|
|
data->opened = 0;
|
|
|
|
} else if (data->opened == 1) {
|
|
|
|
/* close regular file */
|
|
|
|
free(data->data.content);
|
|
|
|
data->fs->close(data->fs);
|
|
|
|
data->data.content = NULL;
|
|
|
|
data->opened = 0;
|
|
|
|
} else {
|
|
|
|
/* TODO only dirs and files supported for now */
|
|
|
|
return ISO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* The file src must be open() before calling this, and close() when no
|
|
|
|
* more needed. Not valid for dirs. On symlinks it reads the destination
|
|
|
|
* file.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* number of bytes read, 0 if EOF, < 0 on error
|
|
|
|
* Error codes:
|
|
|
|
* ISO_FILE_ERROR
|
|
|
|
* ISO_NULL_POINTER
|
|
|
|
* ISO_FILE_NOT_OPENED
|
|
|
|
* ISO_FILE_IS_DIR
|
|
|
|
* ISO_OUT_OF_MEM
|
|
|
|
* ISO_INTERRUPTED
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
int ifs_read(IsoFileSource *src, void *buf, size_t count)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
ImageFileSourceData *data;
|
|
|
|
uint32_t read = 0;
|
|
|
|
|
|
|
|
if (src == NULL || src->data == NULL || buf == NULL) {
|
|
|
|
return ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
if (count == 0) {
|
|
|
|
return ISO_WRONG_ARG_VALUE;
|
|
|
|
}
|
|
|
|
data = (ImageFileSourceData*)src->data;
|
|
|
|
|
|
|
|
if (!data->opened) {
|
|
|
|
return ISO_FILE_NOT_OPENED;
|
|
|
|
} else if (data->opened != 1) {
|
|
|
|
return ISO_FILE_IS_DIR;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (read < count && data->data.offset < data->info.st_size) {
|
|
|
|
size_t bytes;
|
|
|
|
uint8_t *orig;
|
|
|
|
|
|
|
|
if (block_offset(data->nsections, data->sections, data->data.offset) == 0) {
|
|
|
|
/* we need to buffer next block */
|
|
|
|
uint32_t block;
|
|
|
|
_ImageFsData *fsdata;
|
|
|
|
|
|
|
|
if (data->data.offset >= data->info.st_size) {
|
|
|
|
/* EOF */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fsdata = data->fs->data;
|
|
|
|
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) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* how much can I read */
|
|
|
|
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 += block_offset(data->nsections, data->sections, data->data.offset);
|
|
|
|
memcpy((uint8_t*)buf + read, orig, bytes);
|
|
|
|
read += bytes;
|
|
|
|
data->data.offset += (off_t)bytes;
|
|
|
|
}
|
|
|
|
return read;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
off_t ifs_lseek(IsoFileSource *src, off_t offset, int flag)
|
|
|
|
{
|
|
|
|
ImageFileSourceData *data;
|
|
|
|
|
|
|
|
if (src == NULL) {
|
|
|
|
return (off_t)ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
if (offset < (off_t)0) {
|
|
|
|
return (off_t)ISO_WRONG_ARG_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = src->data;
|
|
|
|
|
|
|
|
if (!data->opened) {
|
|
|
|
return (off_t)ISO_FILE_NOT_OPENED;
|
|
|
|
} else if (data->opened != 1) {
|
|
|
|
return (off_t)ISO_FILE_IS_DIR;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (flag) {
|
|
|
|
case 0: /* SEEK_SET */
|
|
|
|
data->data.offset = offset;
|
|
|
|
break;
|
|
|
|
case 1: /* SEEK_CUR */
|
|
|
|
data->data.offset += offset;
|
|
|
|
break;
|
|
|
|
case 2: /* SEEK_END */
|
|
|
|
/* do this make sense? */
|
|
|
|
data->data.offset = data->info.st_size + offset;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (off_t)ISO_WRONG_ARG_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
if (data->data.offset < data->info.st_size) {
|
|
|
|
int ret;
|
|
|
|
fsdata = data->fs->data;
|
|
|
|
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) {
|
|
|
|
return (off_t)ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data->data.offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int ifs_readdir(IsoFileSource *src, IsoFileSource **child)
|
|
|
|
{
|
|
|
|
ImageFileSourceData *data, *cdata;
|
|
|
|
struct child_list *children;
|
|
|
|
|
|
|
|
if (src == NULL || src->data == NULL || child == NULL) {
|
|
|
|
return ISO_NULL_POINTER;
|
|
|
|
}
|
|
|
|
data = (ImageFileSourceData*)src->data;
|
|
|
|
|
|
|
|
if (!data->opened) {
|
|
|
|
return ISO_FILE_NOT_OPENED;
|
|
|
|
} else if (data->opened != 2) {
|
|
|
|
return ISO_FILE_IS_NOT_DIR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return the first child and free it */
|
|
|
|
if (data->data.content == NULL) {
|
|
|
|
return 0; /* EOF */
|
|
|
|
}
|
|
|
|
|
|
|
|
children = (struct child_list*)data->data.content;
|
|
|
|
*child = children->file;
|
|
|
|
cdata = (ImageFileSourceData*)(*child)->data;
|
|
|
|
|
|
|
|
/* set the ref to the parent */
|
|
|
|
cdata->parent = src;
|
|
|
|
iso_file_source_ref(src);
|
|
|
|
|
|
|
|
/* free the first element of the list */
|
|
|
|
data->data.content = children->next;
|
|
|
|
free(children);
|
|
|
|
|
|
|
|
return ISO_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read the destination of a symlink. You don't need to open the file
|
|
|
|
* to call this.
|
|
|
|
*
|
|
|
|
* @param buf
|
|
|
|
* allocated buffer of at least bufsiz bytes.
|
|