2007-08-10 09:36:34 +00:00
|
|
|
/*
|
|
|
|
* This file contains functions related to the reading of SUSP and
|
|
|
|
* Rock Ridge extensions on an ECMA-119 image.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "ecma119.h"
|
|
|
|
#include "ecma119_read.h"
|
|
|
|
#include "ecma119_read_rr.h"
|
|
|
|
#include "util.h"
|
2007-08-27 22:51:48 +00:00
|
|
|
#include "messages.h"
|
2007-08-10 09:36:34 +00:00
|
|
|
|
|
|
|
#define BLOCK_SIZE 2048
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fills a struct stat with the values of a Rock Ridge PX entry
|
|
|
|
* On error, info->error is set propertly and the function returns != 0
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px,
|
|
|
|
struct stat *st)
|
|
|
|
{
|
|
|
|
assert( info && px && st);
|
|
|
|
assert( px->sig[0] == 'P' && px->sig[1] == 'X');
|
|
|
|
|
|
|
|
if ( info->rr == RR_EXT_112 && px->len_sue[0] != 44 ) {
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.12");
|
2007-08-10 09:36:34 +00:00
|
|
|
info->error = LIBISOFS_WRONG_RR;
|
|
|
|
return -1;
|
|
|
|
} else if ( info->rr == RR_EXT_110 && px->len_sue[0] != 36 ) {
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.10");
|
2007-08-10 09:36:34 +00:00
|
|
|
info->error = LIBISOFS_WRONG_RR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
if (info->rr == RR_EXT_112) {
|
|
|
|
st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL);
|
|
|
|
} else {
|
|
|
|
st->st_ino = ++info->ino;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fills a struct stat with the values of a Rock Ridge TF entry
|
|
|
|
* On error, info->error is set propertly and the function returns != 0
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf,
|
|
|
|
struct stat *st)
|
|
|
|
{
|
|
|
|
time_t time;
|
|
|
|
int s;
|
|
|
|
int nts = 0;
|
|
|
|
|
|
|
|
assert( info && tf && st);
|
|
|
|
assert( tf->sig[0] == 'T' && tf->sig[1] == 'F');
|
|
|
|
|
|
|
|
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) {
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short.");
|
2007-08-10 09:36:34 +00:00
|
|
|
info->error = LIBISOFS_WRONG_RR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
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) {
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short.");
|
2007-08-10 09:36:34 +00:00
|
|
|
info->error = LIBISOFS_WRONG_RR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
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) {
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short.");
|
2007-08-10 09:36:34 +00:00
|
|
|
info->error = LIBISOFS_WRONG_RR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
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 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
read_rr_NM(struct susp_sys_user_entry *nm, char *name)
|
|
|
|
{
|
|
|
|
assert(nm);
|
|
|
|
assert( nm->sig[0] == 'N' && nm->sig[1] == 'M');
|
|
|
|
|
|
|
|
/* concatenate the results */
|
|
|
|
if (name) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
read_rr_SL(struct susp_sys_user_entry *sl, char *dest)
|
|
|
|
{
|
|
|
|
int pos;
|
|
|
|
assert(sl);
|
|
|
|
assert( sl->sig[0] == 'S' && sl->sig[1] == 'L');
|
|
|
|
|
|
|
|
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) {
|
2007-08-27 22:51:48 +00:00
|
|
|
char msg[38];
|
|
|
|
sprintf(msg, "SL component flag %x not supported.", flags);
|
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, msg);
|
2007-08-10 09:36:34 +00:00
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
len = sl->data.SL.comps[pos + 1];
|
|
|
|
comp = (char*)&sl->data.SL.comps[pos + 2];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
int size = strlen(dest);
|
|
|
|
dest = realloc(dest, strlen(dest) + len + 2);
|
|
|
|
if ( dest[size-1] != '/' ) {
|
|
|
|
dest[size] = '/';
|
|
|
|
dest[size+1] = '\0';
|
|
|
|
}
|
|
|
|
strncat(dest, comp, len);
|
|
|
|
} else {
|
|
|
|
dest = strcopy(comp, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct susp_iterator {
|
|
|
|
|
|
|
|
uint8_t* base;
|
|
|
|
int pos;
|
|
|
|
int size;
|
|
|
|
struct iso_read_info *info;
|
|
|
|
|
|
|
|
uint32_t ce_block;
|
|
|
|
uint32_t ce_off;
|
|
|
|
uint32_t ce_len; /*< Length of the next continuation area, 0 if
|
|
|
|
no more CA are specified */
|
|
|
|
|
|
|
|
uint8_t *buffer; /*< If there are continuation areas */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct susp_iterator *
|
|
|
|
susp_iter_new(struct iso_read_info *info, struct ecma119_dir_record *record)
|
|
|
|
{
|
|
|
|
struct susp_iterator *iter = malloc(sizeof(struct susp_iterator));
|
|
|
|
int pad = (record->len_fi[0] + 1) % 2;
|
|
|
|
|
|
|
|
iter->base = record->file_id + record->len_fi[0] + pad;
|
|
|
|
iter->pos = info->len_skp; /* 0 in most cases */
|
|
|
|
iter->size = record->len_dr[0] - record->len_fi[0] - 33 -pad;
|
|
|
|
iter->info = info;
|
|
|
|
|
|
|
|
iter->ce_len = 0;
|
|
|
|
iter->buffer = NULL;
|
|
|
|
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct susp_sys_user_entry *
|
|
|
|
susp_iter_next(struct susp_iterator* iter)
|
|
|
|
{
|
|
|
|
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 block needed to cache the full CE */
|
|
|
|
for (block = 0; block < nblocks; ++block) {
|
|
|
|
if (iter->info->src->read_block(iter->info->src,
|
|
|
|
iter->ce_block + block,
|
|
|
|
iter->buffer + block * BLOCK_SIZE)) {
|
|
|
|
iter->info->error = LIBISOFS_READ_FAILURE;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->len_sue[0] == 0) {
|
|
|
|
/* a wrong image with this lead us to a infinity loop */
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "Damaged RR/SUSP information.");
|
2007-08-10 09:36:34 +00:00
|
|
|
iter->info->error = LIBISOFS_WRONG_RR;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
iter->pos += entry->len_sue[0];
|
|
|
|
|
|
|
|
if ( SUSP_SIG(entry, 'C', 'E') ) {
|
|
|
|
/* Continuation entry */
|
|
|
|
if (iter->ce_len) {
|
2007-08-27 22:51:48 +00:00
|
|
|
iso_msg_sorry(LIBISO_RR_ERROR, "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.\n"
|
|
|
|
"Ignoring last CE. Maybe the image is damaged.\n");
|
2007-08-10 09:36:34 +00:00
|
|
|
} 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);
|
|
|
|
} else if ( SUSP_SIG(entry, 'P', 'D') ) {
|
|
|
|
/* skip padding */
|
|
|
|
return susp_iter_next(iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
susp_iter_free(struct susp_iterator* iter)
|
|
|
|
{
|
|
|
|
free(iter->buffer);
|
|
|
|
free(iter);
|
|
|
|
}
|