Thomas Schmitt
d20da80767
and using these numbers in PX entries during next image generation. This also answers the concerns about PX without ino in RRIP 1.12 and PX with ino in RRIP 1.10 images produced by mkisofs.
614 lines
17 KiB
C
614 lines
17 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* This file contains functions related to the reading of SUSP,
|
|
* Rock Ridge and AAIP extensions on an ECMA-119 image.
|
|
*/
|
|
|
|
#include "libisofs.h"
|
|
#include "ecma119.h"
|
|
#include "util.h"
|
|
#include "rockridge.h"
|
|
#include "messages.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct susp_iterator
|
|
{
|
|
uint8_t* base;
|
|
int pos;
|
|
int size;
|
|
IsoDataSource *src;
|
|
int msgid;
|
|
|
|
/* block and offset for next continuation area */
|
|
uint32_t ce_block;
|
|
uint32_t ce_off;
|
|
|
|
/** Length of the next continuation area, 0 if no more CA are specified */
|
|
uint32_t ce_len;
|
|
|
|
uint8_t *buffer; /*< If there are continuation areas */
|
|
};
|
|
|
|
SuspIterator*
|
|
susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record,
|
|
uint8_t len_skp, int msgid)
|
|
{
|
|
int pad = (record->len_fi[0] + 1) % 2;
|
|
struct susp_iterator *iter = malloc(sizeof(struct susp_iterator));
|
|
if (iter == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
iter->base = record->file_id + record->len_fi[0] + pad;
|
|
iter->pos = len_skp; /* 0 in most cases */
|
|
iter->size = record->len_dr[0] - record->len_fi[0] - 33 - pad;
|
|
iter->src = src;
|
|
iter->msgid = msgid;
|
|
|
|
iter->ce_len = 0;
|
|
iter->buffer = NULL;
|
|
|
|
return iter;
|
|
}
|
|
|
|
int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue)
|
|
{
|
|
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 blocks needed to cache the full CE */
|
|
for (block = 0; block < nblocks; ++block) {
|
|
int ret;
|
|
ret = iter->src->read_block(iter->src, iter->ce_block + block,
|
|
iter->buffer + block * BLOCK_SIZE);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
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 0;
|
|
}
|
|
}
|
|
|
|
if (entry->len_sue[0] == 0) {
|
|
/* a wrong image with this lead us to a infinity loop */
|
|
iso_msg_submit(iter->msgid, ISO_WRONG_RR, 0,
|
|
"Damaged RR/SUSP information.");
|
|
return ISO_WRONG_RR;
|
|
}
|
|
|
|
iter->pos += entry->len_sue[0];
|
|
|
|
if (SUSP_SIG(entry, 'C', 'E')) {
|
|
/* Continuation entry */
|
|
if (iter->ce_len) {
|
|
int ret;
|
|
ret = iso_msg_submit(iter->msgid, ISO_UNSUPPORTED_SUSP, 0,
|
|
"More than one CE System user entry has found in a single "
|
|
"System Use field or continuation area. This breaks SUSP "
|
|
"standard and it's not supported. Ignoring last CE. Maybe "
|
|
"the image is damaged.");
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
} 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, sue);
|
|
} else if (SUSP_SIG(entry, 'P', 'D')) {
|
|
/* skip padding */
|
|
return susp_iter_next(iter, sue);
|
|
}
|
|
|
|
*sue = entry;
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
void susp_iter_free(SuspIterator *iter)
|
|
{
|
|
free(iter->buffer);
|
|
free(iter);
|
|
}
|
|
|
|
/**
|
|
* Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1).
|
|
*
|
|
* @return
|
|
* 1 on success, < 0 on error
|
|
*/
|
|
int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st)
|
|
{
|
|
if (px == NULL || st == NULL) {
|
|
return ISO_NULL_POINTER;
|
|
}
|
|
if (px->sig[0] != 'P' || px->sig[1] != 'X') {
|
|
return ISO_WRONG_ARG_VALUE;
|
|
}
|
|
|
|
if (px->len_sue[0] != 44 && px->len_sue[0] != 36) {
|
|
return ISO_WRONG_RR;
|
|
}
|
|
|
|
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);
|
|
st->st_ino = 0;
|
|
if (px->len_sue[0] == 44) {
|
|
/* this corresponds to RRIP 1.12, so we have inode serial number */
|
|
st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL);
|
|
|
|
/* ts A90426 : Indicate that st_ino is valid */
|
|
return 2;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6)
|
|
*
|
|
* @return
|
|
* 1 on success, < 0 on error
|
|
*/
|
|
int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st)
|
|
{
|
|
time_t time;
|
|
int s;
|
|
int nts = 0;
|
|
|
|
if (tf == NULL || st == NULL) {
|
|
return ISO_NULL_POINTER;
|
|
}
|
|
if (tf->sig[0] != 'T' || tf->sig[1] != 'F') {
|
|
return ISO_WRONG_ARG_VALUE;
|
|
}
|
|
|
|
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) {
|
|
/* RR TF entry too short. */
|
|
return ISO_WRONG_RR;
|
|
}
|
|
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) {
|
|
/* RR TF entry too short. */
|
|
return ISO_WRONG_RR;
|
|
}
|
|
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) {
|
|
/* RR TF entry too short. */
|
|
return ISO_WRONG_RR;
|
|
}
|
|
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 ISO_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to
|
|
* the given name. You can pass a pointer to NULL as name.
|
|
*
|
|
* @return
|
|
* 1 on success, < 0 on error
|
|
*/
|
|
int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont)
|
|
{
|
|
if (nm == NULL || name == NULL) {
|
|
return ISO_NULL_POINTER;
|
|
}
|
|
if (nm->sig[0] != 'N' || nm->sig[1] != 'M') {
|
|
return ISO_WRONG_ARG_VALUE;
|
|
}
|
|
|
|
if (nm->len_sue[0] == 5) {
|
|
if (nm->data.NM.flags[0] & 0x2) {
|
|
/* it is a "." entry */
|
|
if (*name == NULL) {
|
|
return ISO_SUCCESS;
|
|
} else {
|
|
/* we can't have a previous not-NULL name */
|
|
return ISO_WRONG_RR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nm->len_sue[0] <= 5) {
|
|
/* ".." entry is an error, as we will never call it */
|
|
return ISO_WRONG_RR;
|
|
}
|
|
|
|
/* concatenate the results */
|
|
if (*cont) {
|
|
*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);
|
|
}
|
|
if (*name == NULL) {
|
|
return ISO_OUT_OF_MEM;
|
|
}
|
|
|
|
/* and set cond according to the value of CONTINUE flag */
|
|
*cont = nm->data.NM.flags[0] & 0x01;
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues.
|
|
*
|
|
* @param cont
|
|
* 0 not continue, 1 continue, 2 continue component
|
|
* @return
|
|
* 1 on success, < 0 on error
|
|
*/
|
|
int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont)
|
|
{
|
|
int pos;
|
|
|
|
if (sl == NULL || dest == NULL) {
|
|
return ISO_NULL_POINTER;
|
|
}
|
|
if (sl->sig[0] != 'S' || sl->sig[1] != 'L') {
|
|
return ISO_WRONG_ARG_VALUE;
|
|
}
|
|
|
|
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) {
|
|
/* unsupported flag component */
|
|
return ISO_UNSUPPORTED_RR;
|
|
} else {
|
|
len = sl->data.SL.comps[pos + 1];
|
|
comp = (char*)&sl->data.SL.comps[pos + 2];
|
|
}
|
|
|
|
if (*cont == 1) {
|
|
/* new component */
|
|
size_t size = strlen(*dest);
|
|
*dest = realloc(*dest, strlen(*dest) + len + 2);
|
|
if (*dest == NULL) {
|
|
return ISO_OUT_OF_MEM;
|
|
}
|
|
/* it is a new compoenent, add the '/' */
|
|
if ((*dest)[size-1] != '/') {
|
|
(*dest)[size] = '/';
|
|
(*dest)[size+1] = '\0';
|
|
}
|
|
strncat(*dest, comp, len);
|
|
} else if (*cont == 2) {
|
|
/* the component continues */
|
|
*dest = realloc(*dest, strlen(*dest) + len + 1);
|
|
if (*dest == NULL) {
|
|
return ISO_OUT_OF_MEM;
|
|
}
|
|
/* we don't have to add the '/' */
|
|
strncat(*dest, comp, len);
|
|
} else {
|
|
*dest = strcopy(comp, len);
|
|
}
|
|
if (*dest == NULL) {
|
|
return ISO_OUT_OF_MEM;
|
|
}
|
|
/* do the component continue or not? */
|
|
*cont = (flags & 0x01) ? 2 : 1;
|
|
}
|
|
|
|
if (*cont == 2) {
|
|
/* TODO check that SL flag is set to continute too ?*/
|
|
} else {
|
|
*cont = sl->data.SL.flags[0] & 0x1 ? 1 : 0;
|
|
}
|
|
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2).
|
|
*
|
|
* @return
|
|
* 1 on success, < 0 on error
|
|
*/
|
|
int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st)
|
|
{
|
|
int high_shift= 0;
|
|
|
|
if (pn == NULL || pn == NULL) {
|
|
return ISO_NULL_POINTER;
|
|
}
|
|
if (pn->sig[0] != 'P' || pn->sig[1] != 'N') {
|
|
return ISO_WRONG_ARG_VALUE;
|
|
}
|
|
|
|
if (pn->len_sue[0] != 20) {
|
|
return ISO_WRONG_RR;
|
|
}
|
|
|
|
/* (dev_t << 32) causes compiler warnings on FreeBSD
|
|
because sizeof(dev_t) is 4.
|
|
*/
|
|
st->st_rdev = (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL);
|
|
if (sizeof(st->st_rdev) > 4) {
|
|
high_shift = 32;
|
|
st->st_rdev |= (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) <<
|
|
high_shift);
|
|
}
|
|
|
|
/* was originally:
|
|
st->st_rdev = (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << 32)
|
|
| (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL);
|
|
*/
|
|
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
|
|
/* AA is the field signature of AAIP versions < 2.0
|
|
*/
|
|
int read_aaip_AA(struct susp_sys_user_entry *sue,
|
|
unsigned char **aa_string, size_t *aa_size, size_t *aa_len,
|
|
size_t *prev_field, int *is_done, int flag)
|
|
{
|
|
unsigned char *aapt;
|
|
|
|
if (*is_done) {
|
|
|
|
/* To coexist with Apple ISO :
|
|
Gracefully react on eventually trailing Apple AA
|
|
*/
|
|
if (sue->version[0] != 1 || sue->len_sue[0] == 7)
|
|
return ISO_SUCCESS;
|
|
|
|
return ISO_WRONG_RR;
|
|
}
|
|
|
|
|
|
/* Eventually create or grow storage */
|
|
if (*aa_size == 0 || *aa_string == NULL) {
|
|
|
|
/* Gracefully react on eventually leading Apple AA
|
|
*/
|
|
if (sue->version[0] != 1 || sue->len_sue[0] < 9) {
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
*aa_size = *aa_len + sue->len_sue[0];
|
|
*aa_string = calloc(*aa_size, 1);
|
|
*aa_len = 0;
|
|
} else if (*aa_len + sue->len_sue[0] > *aa_size) {
|
|
|
|
if (sue->version[0] != 1) {
|
|
/* Apple ISO within the AAIP field group is not AAIP compliant
|
|
*/
|
|
return ISO_WRONG_RR;
|
|
}
|
|
|
|
*aa_size += *aa_len + sue->len_sue[0];
|
|
*aa_string = realloc(*aa_string, *aa_size);
|
|
}
|
|
if (*aa_string == NULL)
|
|
return ISO_OUT_OF_MEM;
|
|
|
|
if (*aa_len > 0) {
|
|
/* Mark prev_field as being continued */
|
|
(*aa_string)[*prev_field + 4] = 1;
|
|
}
|
|
|
|
*prev_field = *aa_len;
|
|
|
|
/* Compose new SUSP header with signature aa[], cont == 0 */
|
|
aapt = *aa_string + *aa_len;
|
|
|
|
aapt[0] = 'A';
|
|
|
|
#ifdef Libisofs_aaip_2_0
|
|
aapt[1] = 'L';
|
|
#else /* Libisofs_aaip_2_0 */
|
|
aapt[1] = 'A';
|
|
#endif /* ! Libisofs_aaip_2_0 */
|
|
|
|
aapt[2] = sue->len_sue[0];
|
|
aapt[3] = 1;
|
|
aapt[4] = 0;
|
|
/* Append sue payload */
|
|
|
|
#ifdef Libisofs_aaip_2_0
|
|
memcpy(aapt + 5, sue->data.AL.comps, sue->len_sue[0] - 5);
|
|
*is_done = !(sue->data.AL.flags[0] & 1);
|
|
#else /* Libisofs_aaip_2_0 */
|
|
memcpy(aapt + 5, sue->data.AA.comps, sue->len_sue[0] - 5);
|
|
*is_done = !(sue->data.AA.flags[0] & 1);
|
|
#endif /* ! Libisofs_aaip_2_0 */
|
|
|
|
*aa_len += sue->len_sue[0];
|
|
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
|
|
/* AL is the obsolete field signature of AAIP versions >= 2.0
|
|
*/
|
|
int read_aaip_AL(struct susp_sys_user_entry *sue,
|
|
unsigned char **aa_string, size_t *aa_size, size_t *aa_len,
|
|
size_t *prev_field, int *is_done, int flag)
|
|
{
|
|
unsigned char *aapt;
|
|
|
|
if (*is_done)
|
|
return ISO_WRONG_RR;
|
|
if (sue->version[0] != 1)
|
|
return ISO_WRONG_RR;
|
|
|
|
/* Eventually create or grow storage */
|
|
if (*aa_size == 0 || *aa_string == NULL) {
|
|
*aa_size = *aa_len + sue->len_sue[0];
|
|
*aa_string = calloc(*aa_size, 1);
|
|
*aa_len = 0;
|
|
} else if (*aa_len + sue->len_sue[0] > *aa_size) {
|
|
*aa_size += *aa_len + sue->len_sue[0];
|
|
*aa_string = realloc(*aa_string, *aa_size);
|
|
}
|
|
if (*aa_string == NULL)
|
|
return ISO_OUT_OF_MEM;
|
|
|
|
if (*aa_len > 0) {
|
|
/* Mark prev_field as being continued */
|
|
(*aa_string)[*prev_field + 4] = 1;
|
|
}
|
|
|
|
*prev_field = *aa_len;
|
|
|
|
/* Compose new SUSP header with signature aa[], cont == 0 */
|
|
aapt = *aa_string + *aa_len;
|
|
|
|
aapt[0] = 'A';
|
|
|
|
#ifdef Libisofs_aaip_2_0
|
|
aapt[1] = 'L';
|
|
#else /* Libisofs_aaip_2_0 */
|
|
aapt[1] = 'A';
|
|
#endif /* ! Libisofs_aaip_2_0 */
|
|
|
|
aapt[2] = sue->len_sue[0];
|
|
aapt[3] = 1;
|
|
aapt[4] = 0;
|
|
/* Append sue payload */
|
|
|
|
#ifdef Libisofs_aaip_2_0
|
|
memcpy(aapt + 5, sue->data.AL.comps, sue->len_sue[0] - 5);
|
|
*is_done = !(sue->data.AL.flags[0] & 1);
|
|
#else /* Libisofs_aaip_2_0 */
|
|
memcpy(aapt + 5, sue->data.AA.comps, sue->len_sue[0] - 5);
|
|
*is_done = !(sue->data.AA.flags[0] & 1);
|
|
#endif /* ! Libisofs_aaip_2_0 */
|
|
|
|
*aa_len += sue->len_sue[0];
|
|
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Reads the zisofs parameters from a ZF field (see doc/zisofs_format.txt).
|
|
*
|
|
* @return
|
|
* 1 on success, < 0 on error
|
|
*/
|
|
int read_zisofs_ZF(struct susp_sys_user_entry *zf, uint8_t algorithm[2],
|
|
uint8_t *header_size_div4, uint8_t *block_size_log2,
|
|
uint32_t *uncompressed_size, int flag)
|
|
{
|
|
if (zf == NULL) {
|
|
return ISO_NULL_POINTER;
|
|
}
|
|
if (zf->sig[0] != 'Z' || zf->sig[1] != 'F') {
|
|
return ISO_WRONG_ARG_VALUE;
|
|
}
|
|
if (zf->len_sue[0] != 16) {
|
|
return ISO_WRONG_RR;
|
|
}
|
|
algorithm[0] = zf->data.ZF.parameters[0];
|
|
algorithm[1] = zf->data.ZF.parameters[1];
|
|
*header_size_div4 = zf->data.ZF.parameters[2];
|
|
*block_size_log2 = zf->data.ZF.parameters[3];
|
|
*uncompressed_size = iso_read_bb(&(zf->data.ZF.parameters[4]), 4, NULL);
|
|
return ISO_SUCCESS;
|
|
}
|
|
|
|
|