/* * This file contains functions related to the reading of SUSP and * Rock Ridge extensions on an ECMA-119 image. */ #include #include #include #include #include #include #include #include "ecma119.h" #include "ecma119_read.h" #include "ecma119_read_rr.h" #include "util.h" #include "messages.h" #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 ) { iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.12"); info->error = LIBISOFS_WRONG_RR; return -1; } else if ( info->rr == RR_EXT_110 && px->len_sue[0] != 36 ) { iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.10"); 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) { iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short."); 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) { iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short."); 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) { iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short."); 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) { char msg[38]; sprintf(msg, "SL component flag %x not supported.", flags); iso_msg_sorry(LIBISO_RR_ERROR, msg); 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 */ iso_msg_sorry(LIBISO_RR_ERROR, "Damaged RR/SUSP information."); 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) { 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"); } 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); }