diff options
Diffstat (limited to 'genisoimage/multi.c')
-rw-r--r-- | genisoimage/multi.c | 1912 |
1 files changed, 1912 insertions, 0 deletions
diff --git a/genisoimage/multi.c b/genisoimage/multi.c new file mode 100644 index 0000000..e069c91 --- /dev/null +++ b/genisoimage/multi.c @@ -0,0 +1,1912 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)multi.c 1.68 05/05/15 joerg */ +/* + * File multi.c - scan existing iso9660 image and merge into + * iso9660 filesystem. Used for multisession support. + * + * Written by Eric Youngdale (1996). + * Copyright (c) 1999-2003 J. Schilling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <mconfig.h> +#include "genisoimage.h" +#include <timedefs.h> +#include <errno.h> +#include <utypes.h> +#include <schily.h> +#include <ctype.h> /* Needed for printasc() */ + +#ifdef VMS + +#include <sys/file.h> +#include <vms/fabdef.h> +#include "vms.h" +#endif + +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +/* + * Cannot debug memset() with gdb on Linux, so use fillbytes() + */ +/*#define memset(s, c, n) fillbytes(s, n, c)*/ + +#define TF_CREATE 1 +#define TF_MODIFY 2 +#define TF_ACCESS 4 +#define TF_ATTRIBUTES 8 + +static int isonum_711(unsigned char *p); +static int isonum_721(unsigned char *p); +static int isonum_723(unsigned char *p); +static int isonum_731(unsigned char *p); + +static void printasc(char *txt, unsigned char *p, int len); +static void prbytes(char *txt, unsigned char *p, int len); +unsigned char *parse_xa(unsigned char *pnt, int *lenp, + struct directory_entry *dpnt); +int rr_flags(struct iso_directory_record *idr); +static int parse_rrflags(Uchar *pnt, int len, int cont_flag); +static BOOL find_rr(struct iso_directory_record *idr, Uchar **pntp, + int *lenp); +static int parse_rr(unsigned char *pnt, int len, + struct directory_entry *dpnt); +static int check_rr_dates(struct directory_entry *dpnt, + struct directory_entry *current, + struct stat *statbuf, + struct stat *lstatbuf); +static struct directory_entry ** + read_merging_directory(struct iso_directory_record *, int *); +static int free_mdinfo(struct directory_entry **, int len); +static void free_directory_entry(struct directory_entry * dirp); +static void merge_remaining_entries(struct directory *, + struct directory_entry **, int); + +static int merge_old_directory_into_tree(struct directory_entry *, + struct directory *); +static void check_rr_relocation(struct directory_entry *de); + +static int +isonum_711(unsigned char *p) +{ + return (*p & 0xff); +} + +static int +isonum_721(unsigned char *p) +{ + return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); +} + +static int +isonum_723(unsigned char *p) +{ +#if 0 + if (p[0] != p[3] || p[1] != p[2]) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "invalid format 7.2.3 number\n"); +#else + fprintf(stderr, "invalid format 7.2.3 number\n"); + exit(1); +#endif + } +#endif + return (isonum_721(p)); +} + +static int +isonum_731(unsigned char *p) +{ + return ((p[0] & 0xff) + | ((p[1] & 0xff) << 8) + | ((p[2] & 0xff) << 16) + | ((p[3] & 0xff) << 24)); +} + +int +isonum_733(unsigned char *p) +{ + return (isonum_731(p)); +} + +FILE *in_image = NULL; + +#ifndef USE_SCG +/* + * Don't define readsecs if genisoimage is linked with + * the SCSI library. + * readsecs() will be implemented as SCSI command in this case. + * + * Use global var in_image directly in readsecs() + * the SCSI equivalent will not use a FILE* for I/O. + * + * The main point of this pointless abstraction is that Solaris won't let + * you read 2K sectors from the cdrom driver. The fact that 99.9% of the + * discs out there have a 2K sectorsize doesn't seem to matter that much. + * Anyways, this allows the use of a scsi-generics type of interface on + * Solaris. + */ +static int +readsecs(int startsecno, void *buffer, int sectorcount) +{ + int f = fileno(in_image); + + if (lseek(f, (off_t) startsecno * SECTOR_SIZE, SEEK_SET) == (off_t) - 1) { +#ifdef USE_LIBSCHILY + comerr(" Seek error on old image\n"); +#else + fprintf(stderr, " Seek error on old image\n"); + exit(10); +#endif + } + if (read(f, buffer, (sectorcount * SECTOR_SIZE)) + != (sectorcount * SECTOR_SIZE)) { +#ifdef USE_LIBSCHILY + comerr(" Read error on old image\n"); +#else + fprintf(stderr, " Read error on old image\n"); + exit(10); +#endif + } + return (sectorcount * SECTOR_SIZE); +} + +#endif + +static void +printasc(char *txt, unsigned char *p, int len) +{ + int i; + + fprintf(stderr, "%s ", txt); + for (i = 0; i < len; i++) { + if (isprint(p[i])) + fprintf(stderr, "%c", p[i]); + else + fprintf(stderr, "."); + } + fprintf(stderr, "\n"); +} + +static void +prbytes(char *txt, register Uchar *p, register int len) +{ + fprintf(stderr, "%s", txt); + while (--len >= 0) + fprintf(stderr, " %02X", *p++); + fprintf(stderr, "\n"); +} + +unsigned char * +parse_xa(unsigned char *pnt, int *lenp, struct directory_entry *dpnt) +{ + struct iso_xa_dir_record *xadp; + int len = *lenp; +static int did_xa = 0; + +/*fprintf(stderr, "len: %d\n", len);*/ + + if (len >= 14) { + xadp = (struct iso_xa_dir_record *)pnt; + +/* if (dpnt) prbytes("XA ", pnt, len);*/ + if (xadp->signature[0] == 'X' && xadp->signature[1] == 'A' && + xadp->reserved[0] == '\0') { + len -= 14; + pnt += 14; + *lenp = len; + if (!did_xa) { + did_xa = 1; + errmsgno(EX_BAD, "Found XA directory extension record.\n"); + } + } else if (pnt[2] == 0) { + char *cp = NULL; + + if (dpnt) + cp = (char *)&dpnt->isorec; + if (cp) { + prbytes("ISOREC:", (Uchar *)cp, 33+cp[32]); + printasc("ISOREC:", (Uchar *)cp, 33+cp[32]); + prbytes("XA REC:", pnt, len); + printasc("XA REC:", pnt, len); + } + if (no_rr == 0) { + errmsgno(EX_BAD, "Disabling RR / XA / AA.\n"); + no_rr = 1; + } + *lenp = 0; + if (cp) { + errmsgno(EX_BAD, "Problems with old ISO directory entry for file: '%s'.\n", &cp[33]); + } + errmsgno(EX_BAD, "Illegal extended directory attributes found (bad XA disk?).\n"); +/* errmsgno(EX_BAD, "Disabling Rock Ridge for old session.\n");*/ + comerrno(EX_BAD, "Try again using the -no-rr option.\n"); + } + } + if (len >= 4 && pnt[3] != 1 && pnt[3] != 2) { + prbytes("BAD RR ATTRIBUTES:", pnt, len); + printasc("BAD RR ATTRIBUTES:", pnt, len); + } + return (pnt); +} + +static BOOL +find_rr(struct iso_directory_record *idr, Uchar **pntp, int *lenp) +{ + struct iso_xa_dir_record *xadp; + int len; + unsigned char *pnt; + BOOL ret = FALSE; + + len = idr->length[0] & 0xff; + len -= sizeof (struct iso_directory_record); + len += sizeof (idr->name); + len -= idr->name_len[0]; + + pnt = (unsigned char *) idr; + pnt += sizeof (struct iso_directory_record); + pnt -= sizeof (idr->name); + pnt += idr->name_len[0]; + if ((idr->name_len[0] & 1) == 0) { + pnt++; + len--; + } + if (len >= 14) { + xadp = (struct iso_xa_dir_record *)pnt; + + if (xadp->signature[0] == 'X' && xadp->signature[1] == 'A' && + xadp->reserved[0] == '\0') { + len -= 14; + pnt += 14; + ret = TRUE; + } + } + *pntp = pnt; + *lenp = len; + return (ret); +} + +static int +parse_rrflags(Uchar *pnt, int len, int cont_flag) +{ + int ncount; + int cont_extent; + int cont_offset; + int cont_size; + int flag1; + int flag2; + + cont_extent = cont_offset = cont_size = 0; + + ncount = 0; + flag1 = flag2 = 0; + while (len >= 4) { + if (pnt[3] != 1 && pnt[3] != 2) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "**BAD RRVERSION (%d) for %c%c\n", + pnt[3], pnt[0], pnt[1]); +#else + fprintf(stderr, + "**BAD RRVERSION (%d) for %c%c\n", + pnt[3], pnt[0], pnt[1]); +#endif + return (0); /* JS ??? Is this right ??? */ + } + ncount++; + if (pnt[0] == 'R' && pnt[1] == 'R') + flag1 = pnt[4] & 0xff; + + if (strncmp((char *) pnt, "PX", 2) == 0) /* POSIX attributes */ + flag2 |= 1; + if (strncmp((char *) pnt, "PN", 2) == 0) /* POSIX device number */ + flag2 |= 2; + if (strncmp((char *) pnt, "SL", 2) == 0) /* Symlink */ + flag2 |= 4; + if (strncmp((char *) pnt, "NM", 2) == 0) /* Alternate Name */ + flag2 |= 8; + if (strncmp((char *) pnt, "CL", 2) == 0) /* Child link */ + flag2 |= 16; + if (strncmp((char *) pnt, "PL", 2) == 0) /* Parent link */ + flag2 |= 32; + if (strncmp((char *) pnt, "RE", 2) == 0) /* Relocated Direcotry */ + flag2 |= 64; + if (strncmp((char *) pnt, "TF", 2) == 0) /* Time stamp */ + flag2 |= 128; + if (strncmp((char *) pnt, "SP", 2) == 0) { /* SUSP record */ + flag2 |= 1024; +/* su_version = pnt[3] & 0xff;*/ + } + if (strncmp((char *) pnt, "AA", 2) == 0) { /* Apple Signature record */ + flag2 |= 2048; +/* aa_version = pnt[3] & 0xff;*/ + } + + if (strncmp((char *)pnt, "CE", 2) == 0) { /* Continuation Area */ + cont_extent = isonum_733(pnt+4); + cont_offset = isonum_733(pnt+12); + cont_size = isonum_733(pnt+20); + } + + len -= pnt[2]; + pnt += pnt[2]; + if (len <= 3 && cont_extent) { + unsigned char sector[SECTOR_SIZE]; + + readsecs(cont_extent, sector, 1); + flag2 |= parse_rrflags(§or[cont_offset], cont_size, 1); + } + } + return (flag2); +} + +int +rr_flags(struct iso_directory_record *idr) +{ + int len; + unsigned char *pnt; + int ret = 0; + + if (find_rr(idr, &pnt, &len)) + ret |= 4096; + ret |= parse_rrflags(pnt, len, 0); + return (ret); +} + +/* + * Parse the RR attributes so we can find the file name. + */ +static int +parse_rr(unsigned char *pnt, int len, struct directory_entry *dpnt) +{ + int cont_extent; + int cont_offset; + int cont_size; + char name_buf[256]; + + cont_extent = cont_offset = cont_size = 0; + + pnt = parse_xa(pnt, &len, dpnt /* 0 */); + + while (len >= 4) { + if (pnt[3] != 1 && pnt[3] != 2) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "**BAD RRVERSION (%d) for %c%c\n", + pnt[3], pnt[0], pnt[1]); +#else + fprintf(stderr, + "**BAD RRVERSION (%d) for %c%c\n", + pnt[3], pnt[0], pnt[1]); +#endif + return (-1); + } + if (strncmp((char *) pnt, "NM", 2) == 0) { + strncpy(name_buf, (char *) pnt + 5, pnt[2] - 5); + name_buf[pnt[2] - 5] = 0; + if (dpnt->name) { + size_t nlen = strlen(dpnt->name); + + /* + * append to name from previous NM records + */ + dpnt->name = realloc(dpnt->name, nlen + + strlen(name_buf) + 1); + strcpy(dpnt->name + nlen, name_buf); + } else { + dpnt->name = strdup(name_buf); + dpnt->got_rr_name = 1; + } + /* continue searching for more NM records */ + } else if (strncmp((char *) pnt, "CE", 2) == 0) { + cont_extent = isonum_733(pnt + 4); + cont_offset = isonum_733(pnt + 12); + cont_size = isonum_733(pnt + 20); + } + + len -= pnt[2]; + pnt += pnt[2]; + if (len <= 3 && cont_extent) { + unsigned char sector[SECTOR_SIZE]; + + readsecs(cont_extent, sector, 1); + if (parse_rr(§or[cont_offset], + cont_size, dpnt) == -1) + return (-1); + } + } + + /* Fall back to the iso name if no RR name found */ + if (dpnt->name == NULL) { + char *cp; + + strcpy(name_buf, dpnt->isorec.name); + cp = strchr(name_buf, ';'); + if (cp != NULL) { + *cp = '\0'; + } + dpnt->name = strdup(name_buf); + } + return (0); +}/* parse_rr */ + + +/* + * Returns 1 if the two files are identical + * Returns 0 if the two files differ + */ +static int +check_rr_dates(struct directory_entry *dpnt, + struct directory_entry *current, + struct stat *statbuf, + struct stat *lstatbuf) +{ + int cont_extent; + int cont_offset; + int cont_size; + int offset; + unsigned char *pnt; + int len; + int same_file; + int same_file_type; + mode_t mode; + char time_buf[7]; + + + cont_extent = cont_offset = cont_size = 0; + same_file = 1; + same_file_type = 1; + + pnt = dpnt->rr_attributes; + len = dpnt->rr_attr_size; + /* + * We basically need to parse the rr attributes again, and dig out the + * dates and file types. + */ + pnt = parse_xa(pnt, &len, /* dpnt */ 0); + while (len >= 4) { + if (pnt[3] != 1 && pnt[3] != 2) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "**BAD RRVERSION (%d) for %c%c\n", + pnt[3], pnt[0], pnt[1]); +#else + fprintf(stderr, + "**BAD RRVERSION (%d) for %c%c\n", + pnt[3], pnt[0], pnt[1]); +#endif + return (-1); + } + + /* + * If we have POSIX file modes, make sure that the file type is + * the same. If it isn't, then we must always write the new + * file. + */ + if (strncmp((char *) pnt, "PX", 2) == 0) { + mode = isonum_733(pnt + 4); + if ((lstatbuf->st_mode & S_IFMT) != (mode & S_IFMT)) { + same_file_type = 0; + same_file = 0; + } + } + if (strncmp((char *) pnt, "TF", 2) == 0) { + offset = 5; + if (pnt[4] & TF_CREATE) { + iso9660_date((char *) time_buf, + lstatbuf->st_ctime); + if (memcmp(time_buf, pnt + offset, 7) != 0) + same_file = 0; + offset += 7; + } + if (pnt[4] & TF_MODIFY) { + iso9660_date((char *) time_buf, + lstatbuf->st_mtime); + if (memcmp(time_buf, pnt + offset, 7) != 0) + same_file = 0; + offset += 7; + } + } + if (strncmp((char *) pnt, "CE", 2) == 0) { + cont_extent = isonum_733(pnt + 4); + cont_offset = isonum_733(pnt + 12); + cont_size = isonum_733(pnt + 20); + } + + len -= pnt[2]; + pnt += pnt[2]; + if (len <= 3 && cont_extent) { + unsigned char sector[SECTOR_SIZE]; + + readsecs(cont_extent, sector, 1); + /* + * Continue to scan the extension record. + * Note that this has not been tested yet, but it is + * definitely more correct that calling parse_rr() + * as done in Eric's old code. + */ + pnt = §or[cont_offset]; + len = cont_size; + /* + * Clear the "pending extension record" state as + * we did already read it now. + */ + cont_extent = cont_offset = cont_size = 0; + } + } + + /* + * If we have the same fundamental file type, then it is clearly safe + * to reuse the TRANS.TBL entry. + */ + if (same_file_type) { + current->de_flags |= SAFE_TO_REUSE_TABLE_ENTRY; + } + return (same_file); +} + +static struct directory_entry ** +read_merging_directory(struct iso_directory_record *mrootp, int *nentp) +{ + unsigned char *cpnt; + unsigned char *cpnt1; + char *p; + char *dirbuff; + int i; + struct iso_directory_record *idr; + int len; + int nbytes; + int nent; + struct directory_entry **pnt; + int rlen; + struct directory_entry **rtn; + int seen_rockridge; + unsigned char *tt_buf; + int tt_extent; + int tt_size; + + static int warning_given = 0; + + /* + * This is the number of sectors we will need to read. We need to + * round up to get the last fractional sector - we are asking for the + * data in terms of a number of sectors. + */ + nbytes = roundup(isonum_733((unsigned char *) mrootp->size), + SECTOR_SIZE); + + /* + * First, allocate a buffer large enough to read in the entire + * directory. + */ + dirbuff = (char *) e_malloc(nbytes); + + readsecs(isonum_733((unsigned char *) mrootp->extent), dirbuff, + nbytes / SECTOR_SIZE); + + /* + * Next look over the directory, and count up how many entries we have. + */ + len = isonum_733((unsigned char *) mrootp->size); + i = 0; + *nentp = 0; + nent = 0; + while (i < len) { + idr = (struct iso_directory_record *) & dirbuff[i]; + if (idr->length[0] == 0) { + i = ISO_ROUND_UP(i); + continue; + } + nent++; + i += idr->length[0]; + } + + /* + * Now allocate the buffer which will hold the array we are about to + * return. + */ + rtn = (struct directory_entry **) e_malloc(nent * sizeof (*rtn)); + + /* + * Finally, scan the directory one last time, and pick out the relevant + * bits of information, and store it in the relevant bits of the + * structure. + */ + i = 0; + pnt = rtn; + tt_extent = 0; + seen_rockridge = 0; + tt_size = 0; + while (i < len) { + idr = (struct iso_directory_record *) & dirbuff[i]; + if (idr->length[0] == 0) { + i = ISO_ROUND_UP(i); + continue; + } + *pnt = (struct directory_entry *) e_malloc(sizeof (**rtn)); + (*pnt)->next = NULL; +#ifdef DEBUG + fprintf(stderr, "IDR name: '%s' ist: %d soll: %d\n", + idr->name, strlen(idr->name), idr->name_len[0]); +#endif + (*pnt)->isorec = *idr; + (*pnt)->starting_block = + isonum_733((unsigned char *) idr->extent); + (*pnt)->size = isonum_733((unsigned char *) idr->size); + (*pnt)->priority = 0; + (*pnt)->name = NULL; + (*pnt)->got_rr_name = 0; + (*pnt)->table = NULL; + (*pnt)->whole_name = NULL; + (*pnt)->filedir = NULL; + (*pnt)->parent_rec = NULL; + /* + * Set this information so that we correctly cache previous + * session bits of information. + */ + (*pnt)->inode = (*pnt)->starting_block; + (*pnt)->dev = PREV_SESS_DEV; + (*pnt)->rr_attributes = NULL; + (*pnt)->rr_attr_size = 0; + (*pnt)->total_rr_attr_size = 0; + (*pnt)->de_flags = SAFE_TO_REUSE_TABLE_ENTRY; +#ifdef APPLE_HYB + (*pnt)->assoc = NULL; + (*pnt)->hfs_ent = NULL; +#endif /* APPLE_HYB */ + + /* + * Check for and parse any RR attributes for the file. All we + * are really looking for here is the original name of the + * file. + */ + rlen = idr->length[0] & 0xff; + cpnt = (unsigned char *) idr; + + rlen -= offsetof(struct iso_directory_record, name[0]); + cpnt += offsetof(struct iso_directory_record, name[0]); + + rlen -= idr->name_len[0]; + cpnt += idr->name_len[0]; + + if ((idr->name_len[0] & 1) == 0) { + cpnt++; + rlen--; + } + + if (no_rr) + rlen = 0; + if (rlen > 0) { + (*pnt)->total_rr_attr_size = + (*pnt)->rr_attr_size = rlen; + (*pnt)->rr_attributes = e_malloc(rlen); + memcpy((*pnt)->rr_attributes, cpnt, rlen); + seen_rockridge = 1; + } +#ifdef DEBUG + fprintf(stderr, "INT name: '%s' ist: %d soll: %d\n", + (*pnt)->isorec.name, strlen((*pnt)->isorec.name), + idr->name_len[0]); +#endif + + if (idr->name_len[0] < sizeof ((*pnt)->isorec.name)) { + /* + * Now zero out the remainder of the name field. + */ + cpnt = (unsigned char *) (*pnt)->isorec.name; + cpnt += idr->name_len[0]; + memset(cpnt, 0, + sizeof ((*pnt)->isorec.name) - idr->name_len[0]); + } else { + /* + * Simple sanity work to make sure that we have no + * illegal data structures in our tree. + */ + (*pnt)->isorec.name[MAX_ISONAME] = '\0'; + (*pnt)->isorec.name_len[0] = MAX_ISONAME; + } + /* + * If the filename len from the old session is more + * then 31 chars, there is a high risk of hard violations + * if the ISO9660 standard. + * Run it through our name canonication machine.... + */ + if (idr->name_len[0] > LEN_ISONAME || check_oldnames) { + iso9660_check(idr, *pnt); + } + + if (parse_rr((*pnt)->rr_attributes, rlen, *pnt) == -1) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Cannot parse Rock Ridge attributes for '%s'.\n", + idr->name); +#else + fprintf(stderr, + "Cannot parse Rock Ridge attributes for '%s'.\n", + idr->name); + exit(1); +#endif + } + if (((*pnt)->isorec.name_len[0] == 1) && + (((*pnt)->isorec.name[0] == 0) || /* "." entry */ + ((*pnt)->isorec.name[0] == 1))) { /* ".." entry */ + + if ((*pnt)->name != NULL) { + free((*pnt)->name); + } + if ((*pnt)->whole_name != NULL) { + free((*pnt)->whole_name); + } + if ((*pnt)->isorec.name[0] == 0) { + (*pnt)->name = strdup("."); + } else { + (*pnt)->name = strdup(".."); + } + } +#ifdef DEBUG + fprintf(stderr, "got DE name: %s\n", (*pnt)->name); +#endif + + if (strncmp(idr->name, trans_tbl, strlen(trans_tbl)) == 0) { + if ((*pnt)->name != NULL) { + free((*pnt)->name); + } + if ((*pnt)->whole_name != NULL) { + free((*pnt)->whole_name); + } +/* (*pnt)->name = strdup("<translation table>");*/ + (*pnt)->name = strdup(trans_tbl); + tt_extent = isonum_733((unsigned char *) idr->extent); + tt_size = isonum_733((unsigned char *) idr->size); + if (tt_extent == 0) + tt_size = 0; + } + pnt++; + i += idr->length[0]; + } +#ifdef APPLE_HYB + /* + * If we find an associated file, check if there is a file + * with same ISO name and link it to this entry + */ + for (pnt = rtn, i = 0; i < nent; i++, pnt++) { + int j; + + rlen = isonum_711((*pnt)->isorec.name_len); + if ((*pnt)->isorec.flags[0] & ISO_ASSOCIATED) { + for (j = 0; j < nent; j++) { + if (strncmp(rtn[j]->isorec.name, + (*pnt)->isorec.name, rlen) == 0 && + (rtn[j]->isorec.flags[0] & ISO_ASSOCIATED) == 0) { + rtn[j]->assoc = *pnt; + + /* + * don't want this entry to be + * in the Joliet tree + */ + (*pnt)->de_flags |= INHIBIT_JOLIET_ENTRY; + + /* + * as we have associated files, then + * assume we are are dealing with + * Apple's extensions - if not already + * set + */ + if (apple_both == 0) { + apple_both = apple_ext = 1; + } + break; + } + } + } + } +#endif /* APPLE_HYB */ + + /* + * If there was a TRANS.TBL;1 entry, then grab it, read it, and use it + * to get the filenames of the files. Also, save the table info, just + * in case we need to use it. + * + * The entries look something like: F ISODUMP.;1 isodump + */ + if (tt_extent != 0 && tt_size != 0) { + nbytes = roundup(tt_size, SECTOR_SIZE); + tt_buf = (unsigned char *) e_malloc(nbytes); + readsecs(tt_extent, tt_buf, nbytes / SECTOR_SIZE); + + /* + * Loop through the file, examine each entry, and attempt to + * attach it to the correct entry. + */ + cpnt = tt_buf; + cpnt1 = tt_buf; + while (cpnt - tt_buf < tt_size) { + /* Skip to a line terminator, or end of the file. */ + while ((cpnt1 - tt_buf < tt_size) && + (*cpnt1 != '\n') && + (*cpnt1 != '\0')) { + cpnt1++; + } + /* Zero terminate this particular line. */ + if (cpnt1 - tt_buf < tt_size) { + *cpnt1 = '\0'; + } + /* + * Now dig through the actual directories, and try and + * find the attachment for this particular filename. + */ + for (pnt = rtn, i = 0; i < nent; i++, pnt++) { + rlen = isonum_711((*pnt)->isorec.name_len); + + /* + * If this filename is so long that it would + * extend past the end of the file, it cannot + * be the one we want. + */ + if (cpnt + 2 + rlen - tt_buf >= tt_size) { + continue; + } + /* + * Now actually compare the name, and make sure + * that the character at the end is a ' '. + */ + if (strncmp((char *) cpnt + 2, + (*pnt)->isorec.name, rlen) == 0 && + cpnt[2 + rlen] == ' ' && + (p = strchr((char *)&cpnt[2 + rlen], '\t'))) { + p++; + /* + * This is a keeper. Now determine the + * correct table entry that we will + * use on the new image. + */ + if (strlen(p) > 0) { + (*pnt)->table = + e_malloc(strlen(p) + 4); + sprintf((*pnt)->table, + "%c\t%s\n", + *cpnt, p); + } + if (!(*pnt)->got_rr_name) { + if ((*pnt)->name != NULL) { + free((*pnt)->name); + } + (*pnt)->name = strdup(p); + } + break; + } + } + cpnt = cpnt1 + 1; + cpnt1 = cpnt; + } + + free(tt_buf); + } else if (!seen_rockridge && !warning_given) { + /* + * Warn the user that iso-9660 names were used because neither + * Rock Ridge (-R) nor TRANS.TBL (-T) name translations were + * found. + */ + fprintf(stderr, + "Warning: Neither Rock Ridge (-R) nor TRANS.TBL (-T) \n"); + fprintf(stderr, + "name translations were found on previous session.\n"); + fprintf(stderr, + "ISO-9660 file names have been used instead.\n"); + warning_given = 1; + } + if (dirbuff != NULL) { + free(dirbuff); + } + *nentp = nent; + return (rtn); +}/* read_merging_directory */ + +/* + * Free any associated data related to the structures. + */ +static int +free_mdinfo(struct directory_entry **ptr, int len) +{ + int i; + struct directory_entry **p; + + p = ptr; + for (i = 0; i < len; i++, p++) { + /* + * If the tree-handling code decided that it needed an entry, it + * will have removed it from the list. Thus we must allow for + * null pointers here. + */ + if (*p == NULL) { + continue; + } + free_directory_entry(*p); + } + + free(ptr); + return (0); +} + +static void +free_directory_entry(struct directory_entry *dirp) +{ + if (dirp->name != NULL) + free(dirp->name); + + if (dirp->whole_name != NULL) + free(dirp->whole_name); + + if (dirp->rr_attributes != NULL) + free(dirp->rr_attributes); + + if (dirp->table != NULL) + free(dirp->table); + + free(dirp); +} + +/* + * Search the list to see if we have any entries from the previous + * session that match this entry. If so, copy the extent number + * over so we don't bother to write it out to the new session. + */ +int +check_prev_session(struct directory_entry **ptr, int len, + struct directory_entry *curr_entry, + struct stat *statbuf, + struct stat *lstatbuf, + struct directory_entry **odpnt) +{ + int i; + int rr; + int retcode = 0; /* Default not found */ + + for (i = 0; i < len; i++) { + if (ptr[i] == NULL) { /* Used or empty entry skip */ + continue; + } +#if 0 + if (ptr[i]->name != NULL && ptr[i]->isorec.name_len[0] == 1 && + ptr[i]->name[0] == '\0') { + continue; + } + if (ptr[i]->name != NULL && ptr[i]->isorec.name_len[0] == 1 && + ptr[i]->name[0] == 1) { + continue; + } +#else + if (ptr[i]->name != NULL && strcmp(ptr[i]->name, ".") == 0) { + continue; + } + if (ptr[i]->name != NULL && strcmp(ptr[i]->name, "..") == 0) { + continue; + } +#endif + + if (ptr[i]->name != NULL && + strcmp(ptr[i]->name, curr_entry->name) != 0) { + /* Not the same name continue */ + continue; + } + /* + * It's a directory so we must always merge it with the new + * session. Never ever reuse directory extents. See comments + * in tree.c for an explaination of why this must be the case. + */ + if ((curr_entry->isorec.flags[0] & ISO_DIRECTORY) != 0) { + retcode = 2; /* Flag directory case */ + goto found_it; + } + /* + * We know that the files have the same name. If they also + * have the same file type (i.e. file, dir, block, etc), then + * we can safely reuse the TRANS.TBL entry for this file. The + * check_rr_dates() function will do this for us. + * + * Verify that the file type and dates are consistent. If not, + * we probably have a different file, and we need to write it + * out again. + */ + retcode = 1; /* We found a non directory */ + + if (ptr[i]->rr_attributes != NULL) { + if ((rr = check_rr_dates(ptr[i], curr_entry, statbuf, + lstatbuf)) == -1) + return (-1); + + if (rr == 0) { /* Different files */ + goto found_it; + } + } + /* + * Verify size and timestamp. If rock ridge is in use, we + * need to compare dates from RR too. Directories are special, + * we calculate their size later. + */ + if (ptr[i]->size != curr_entry->size) { + /* Different files */ + goto found_it; + } + if (memcmp(ptr[i]->isorec.date, + curr_entry->isorec.date, 7) != 0) { + /* Different files */ + goto found_it; + } + /* We found it and we can reuse the extent */ + memcpy(curr_entry->isorec.extent, ptr[i]->isorec.extent, 8); + curr_entry->starting_block = isonum_733((unsigned char *)ptr[i]->isorec.extent); + curr_entry->de_flags |= SAFE_TO_REUSE_TABLE_ENTRY; + goto found_it; + } + return (retcode); + +found_it: + if (odpnt != NULL) { + *odpnt = ptr[i]; + } else { + free(ptr[i]); + } + ptr[i] = NULL; + return (retcode); +} + +/* + * open_merge_image: Open an existing image. + */ +int +open_merge_image(char *path) +{ +#ifndef USE_SCG + in_image = fopen(path, "rb"); + if (in_image == NULL) { + return (-1); + } +#else + in_image = fopen(path, "rb"); + if (in_image == NULL) { + if (scsidev_open(path) < 0) + return (-1); + } +#endif + return (0); +} + +/* + * close_merge_image: Close an existing image. + */ +int +close_merge_image() +{ +#ifdef USE_SCG + return (scsidev_close()); +#else + return (fclose(in_image)); +#endif +} + +/* + * merge_isofs: Scan an existing image, and return a pointer + * to the root directory for this image. + */ +struct iso_directory_record * +merge_isofs(char *path) +{ + char buffer[SECTOR_SIZE]; + int file_addr; + int i; + struct iso_primary_descriptor *pri = NULL; + struct iso_directory_record *rootp; + struct iso_volume_descriptor *vdp; + + /* + * Start by searching for the volume header. Ultimately, we need to + * search for volume headers in multiple places because we might be + * starting with a multisession image. FIXME(eric). + */ + get_session_start(&file_addr); + + for (i = 0; i < 100; i++) { + if (readsecs(file_addr, buffer, + sizeof (buffer) / SECTOR_SIZE) != sizeof (buffer)) { +#ifdef USE_LIBSCHILY + comerr(" Read error on old image %s\n", path); +#else + fprintf(stderr, " Read error on old image %s\n", path); + exit(10); +#endif + } + vdp = (struct iso_volume_descriptor *) buffer; + + if ((strncmp(vdp->id, ISO_STANDARD_ID, sizeof (vdp->id)) == 0) && + (isonum_711((unsigned char *) vdp->type) == ISO_VD_PRIMARY)) { + break; + } + file_addr += 1; + } + + if (i == 100) { + return (NULL); + } + pri = (struct iso_primary_descriptor *) vdp; + + /* Check the blocksize of the image to make sure it is compatible. */ + if (isonum_723((unsigned char *) pri->logical_block_size) != SECTOR_SIZE) { + errmsgno(EX_BAD, + "Previous session has incompatible sector size %d.\n", + isonum_723((unsigned char *) pri->logical_block_size)); + return (NULL); + } + if (isonum_723((unsigned char *) pri->volume_set_size) != 1) { + errmsgno(EX_BAD, + "Previous session has volume set size %d (must be 1).\n", + isonum_723((unsigned char *) pri->volume_set_size)); + return (NULL); + } + /* Get the location and size of the root directory. */ + rootp = (struct iso_directory_record *) + e_malloc(sizeof (struct iso_directory_record)); + + memcpy(rootp, pri->root_directory_record, sizeof (*rootp)); + + return (rootp); +} + +static void +merge_remaining_entries(struct directory *this_dir, + struct directory_entry **pnt, int n_orig) +{ + int i; + struct directory_entry *s_entry; + unsigned int ttbl_extent = 0; + unsigned int ttbl_index = 0; + char whole_path[PATH_MAX]; + + /* + * Whatever is leftover in the list needs to get merged back into the + * directory. + */ + for (i = 0; i < n_orig; i++) { + if (pnt[i] == NULL) { + continue; + } + if (pnt[i]->name != NULL && pnt[i]->whole_name == NULL) { + /* Set the name for this directory. */ + strcpy(whole_path, this_dir->de_name); + strcat(whole_path, SPATH_SEPARATOR); + strcat(whole_path, pnt[i]->name); + + pnt[i]->whole_name = strdup(whole_path); + } + if (pnt[i]->name != NULL && +/* strcmp(pnt[i]->name, "<translation table>") == 0 )*/ + strcmp(pnt[i]->name, trans_tbl) == 0) { + ttbl_extent = + isonum_733((unsigned char *)pnt[i]->isorec.extent); + ttbl_index = i; + continue; + } + + /* + * Skip directories for now - these need to be treated + * differently. + */ + if ((pnt[i]->isorec.flags[0] & ISO_DIRECTORY) != 0) { + /* + * FIXME - we need to insert this directory into the + * tree, so that the path tables we generate will be + * correct. + */ + if ((strcmp(pnt[i]->name, ".") == 0) || + (strcmp(pnt[i]->name, "..") == 0)) { + free_directory_entry(pnt[i]); + pnt[i] = NULL; + continue; + } else { + merge_old_directory_into_tree(pnt[i], this_dir); + } + } + pnt[i]->next = this_dir->contents; + pnt[i]->filedir = this_dir; + this_dir->contents = pnt[i]; + pnt[i] = NULL; + } + + + /* + * If we don't have an entry for the translation table, then don't + * bother trying to copy the starting extent over. Note that it is + * possible that if we are copying the entire directory, the entry for + * the translation table will have already been inserted into the + * linked list and removed from the old entries list, in which case we + * want to leave the extent number as it was before. + */ + if (ttbl_extent == 0) { + return; + } + /* + * Finally, check the directory we are creating to see whether there + * are any new entries in it. If there are not, we can reuse the same + * translation table. + */ + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { + /* + * Don't care about '.' or '..'. They are never in the table + * anyways. + */ + if (s_entry->name != NULL && strcmp(s_entry->name, ".") == 0) { + continue; + } + if (s_entry->name != NULL && strcmp(s_entry->name, "..") == 0) { + continue; + } +/* if (strcmp(s_entry->name, "<translation table>") == 0)*/ + if (strcmp(s_entry->name, trans_tbl) == 0) { + continue; + } + if ((s_entry->de_flags & SAFE_TO_REUSE_TABLE_ENTRY) == 0) { + return; + } + } + + /* + * Locate the translation table, and re-use the same extent. It isn't + * clear that there should ever be one in there already so for now we + * try and muddle through the best we can. + */ + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { +/* if (strcmp(s_entry->name, "<translation table>") == 0)*/ + if (strcmp(s_entry->name, trans_tbl) == 0) { + fprintf(stderr, "Should never get here\n"); + set_733(s_entry->isorec.extent, ttbl_extent); + return; + } + } + + pnt[ttbl_index]->next = this_dir->contents; + pnt[ttbl_index]->filedir = this_dir; + this_dir->contents = pnt[ttbl_index]; + pnt[ttbl_index] = NULL; +} + + +/* + * Here we have a case of a directory that has completely disappeared from + * the face of the earth on the tree we are mastering from. Go through and + * merge it into the tree, as well as everything beneath it. + * + * Note that if a directory has been moved for some reason, this will + * incorrectly pick it up and attempt to merge it back into the old + * location. FIXME(eric). + */ +static int +merge_old_directory_into_tree(struct directory_entry *dpnt, + struct directory *parent) +{ + struct directory_entry **contents = NULL; + int i; + int n_orig; + struct directory *this_dir, + *next_brother; + char whole_path[PATH_MAX]; + + this_dir = (struct directory *) e_malloc(sizeof (struct directory)); + memset(this_dir, 0, sizeof (struct directory)); + this_dir->next = NULL; + this_dir->subdir = NULL; + this_dir->self = dpnt; + this_dir->contents = NULL; + this_dir->size = 0; + this_dir->extent = 0; + this_dir->depth = parent->depth + 1; + this_dir->parent = parent; + if (!parent->subdir) + parent->subdir = this_dir; + else { + next_brother = parent->subdir; + while (next_brother->next) + next_brother = next_brother->next; + next_brother->next = this_dir; + } + + /* Set the name for this directory. */ + strcpy(whole_path, parent->de_name); + strcat(whole_path, SPATH_SEPARATOR); + strcat(whole_path, dpnt->name); + this_dir->de_name = strdup(whole_path); + this_dir->whole_name = strdup(whole_path); + + /* + * Now fill this directory using information from the previous session. + */ + contents = read_merging_directory(&dpnt->isorec, &n_orig); + /* + * Start by simply copying the '.', '..' and non-directory entries to + * this directory. Technically we could let merge_remaining_entries + * handle this, but it gets rather confused by the '.' and '..' entries + */ + for (i = 0; i < n_orig; i++) { + /* + * We can always reuse the TRANS.TBL in this particular case. + */ + contents[i]->de_flags |= SAFE_TO_REUSE_TABLE_ENTRY; + + if (((contents[i]->isorec.flags[0] & ISO_DIRECTORY) != 0) && + (i >= 2)) { + continue; + } + /* If we have a directory, don't reuse the extent number. */ + if ((contents[i]->isorec.flags[0] & ISO_DIRECTORY) != 0) { + memset(contents[i]->isorec.extent, 0, 8); + + if (strcmp(contents[i]->name, ".") == 0) + this_dir->dir_flags |= DIR_HAS_DOT; + + if (strcmp(contents[i]->name, "..") == 0) + this_dir->dir_flags |= DIR_HAS_DOTDOT; + } + /* + * for regilar files, we do it here. + * If it has CL or RE attributes, remember its extent + */ + check_rr_relocation(contents[i]); + + /* + * Set the whole name for this file. + */ + strcpy(whole_path, this_dir->whole_name); + strcat(whole_path, SPATH_SEPARATOR); + strcat(whole_path, contents[i]->name); + + contents[i]->whole_name = strdup(whole_path); + + contents[i]->next = this_dir->contents; + contents[i]->filedir = this_dir; + this_dir->contents = contents[i]; + contents[i] = NULL; + } + + /* + * and for directories, we do it here. + * If it has CL or RE attributes, remember its extent + */ + check_rr_relocation(dpnt); + + /* + * Zero the extent number for ourselves. + */ + memset(dpnt->isorec.extent, 0, 8); + + /* + * Anything that is left are other subdirectories that need to be + * merged. + */ + merge_remaining_entries(this_dir, contents, n_orig); + free_mdinfo(contents, n_orig); +#if 0 + /* + * This is no longer required. The post-scan sort will handle all of + * this for us. + */ + sort_n_finish(this_dir); +#endif + + return (0); +} + + +char *cdrecord_data = NULL; + +int +get_session_start(int *file_addr) +{ + char *pnt; + +#ifdef CDRECORD_DETERMINES_FIRST_WRITABLE_ADDRESS + /* + * FIXME(eric). We need to coordinate with cdrecord to obtain the + * parameters. For now, we assume we are writing the 2nd session, so + * we start from the session that starts at 0. + */ + if (file_addr != NULL) + *file_addr = 16; + + /* + * We need to coordinate with cdrecord to get the next writable address + * from the device. Here is where we use it. + */ + session_start = last_extent = last_extent_written = cdrecord_result(); +#else + + if (file_addr != NULL) + *file_addr = 0L; + session_start = last_extent = last_extent_written = 0L; + if (check_session && cdrecord_data == NULL) + return (0); + + if (cdrecord_data == NULL) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Special parameters for cdrecord not specified with -C\n"); +#else + fprintf(stderr, + "Special parameters for cdrecord not specified with -C\n"); + exit(1); +#endif + } + /* + * Next try and find the ',' in there which delimits the two numbers. + */ + pnt = strchr(cdrecord_data, ','); + if (pnt == NULL) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Malformed cdrecord parameters\n"); +#else + fprintf(stderr, "Malformed cdrecord parameters\n"); + exit(1); +#endif + } + + *pnt = '\0'; + if (file_addr != NULL) { + *file_addr = atol(cdrecord_data); + } + pnt++; + + session_start = last_extent = last_extent_written = atol(pnt); + + pnt--; + *pnt = ','; + +#endif + return (0); +} + +/* + * This function scans the directory tree, looking for files, and it makes + * note of everything that is found. We also begin to construct the ISO9660 + * directory entries, so that we can determine how large each directory is. + */ +int +merge_previous_session(struct directory *this_dir, + struct iso_directory_record *mrootp, + char *reloc_root, + char *reloc_old_root) +{ + struct directory_entry **orig_contents = NULL; + struct directory_entry *odpnt = NULL; + int n_orig; + struct directory_entry *s_entry; + int status; + int lstatus; + struct stat statbuf, + lstatbuf; + int retcode; + + /* skip leading slash */ + while (reloc_old_root && reloc_old_root[0] == PATH_SEPARATOR) { + reloc_old_root++; + } + while (reloc_root && reloc_root[0] == PATH_SEPARATOR) { + reloc_root++; + } + + /* + * Parse the same directory in the image that we are merging for + * multisession stuff. + */ + orig_contents = read_merging_directory(mrootp, &n_orig); + if (orig_contents == NULL) { + if (reloc_old_root) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Reading old session failed, cannot execute -old-root.\n"); +#else + fprintf(stderr, + "Reading old session failed, cannot execute -old-root.\n"); + exit(1); +#endif + } + return (0); + } + + if (reloc_old_root && reloc_old_root[0]) { + struct directory_entry **new_orig_contents = orig_contents; + int new_n_orig = n_orig; + + /* decend until we reach the original root */ + while (reloc_old_root[0]) { + int i; + char *next; + int last; + + for (next = reloc_old_root; *next && *next != PATH_SEPARATOR; next++); + if (*next) { + last = 0; + *next = 0; + next++; + } else { + last = 1; + } + while (*next == PATH_SEPARATOR) { + next++; + } + + for (i = 0; i < new_n_orig; i++) { + struct iso_directory_record subroot; + + if (new_orig_contents[i]->name != NULL && + strcmp(new_orig_contents[i]->name, reloc_old_root) != 0) { + /* Not the same name continue */ + continue; + } + /* + * enter directory, free old one only if not the top level, + * which is still needed + */ + subroot = new_orig_contents[i]->isorec; + if (new_orig_contents != orig_contents) { + free_mdinfo(new_orig_contents, new_n_orig); + } + new_orig_contents = read_merging_directory(&subroot, &new_n_orig); + + if (!new_orig_contents) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Reading directory %s in old session failed, cannot execute -old-root.\n", + reloc_old_root); +#else + fprintf(stderr, + "Reading directory %s in old session failed, cannot execute -old-root.\n", + reloc_old_root); + exit(1); +#endif + } + i = -1; + break; + } + + if (i == new_n_orig) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "-old-root (sub)directory %s not found in old session.\n", + reloc_old_root); +#else + fprintf(stderr, + "-old-root (sub)directory %s not found in old session.\n", + reloc_old_root); + exit(1); +#endif + } + + /* restore string, proceed to next sub directory */ + if (!last) { + reloc_old_root[strlen(reloc_old_root)] = PATH_SEPARATOR; + } + reloc_old_root = next; + } + + /* + * preserve the old session, skipping those dirs/files that are found again + * in the new root + */ + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { + status = stat_filter(s_entry->whole_name, &statbuf); + lstatus = lstat_filter(s_entry->whole_name, &lstatbuf); + + /* + * check_prev_session() will search for s_entry and remove it from + * orig_contents if found + */ + retcode = check_prev_session(orig_contents, n_orig, s_entry, + &statbuf, &lstatbuf, NULL); + if (retcode == -1) + return (-1); + } + merge_remaining_entries(this_dir, orig_contents, n_orig); + + /* use new directory */ + free_mdinfo(orig_contents, n_orig); + orig_contents = new_orig_contents; + n_orig = new_n_orig; + + if (reloc_root && reloc_root[0]) { + /* also decend into new root before searching for files */ + this_dir = find_or_create_directory(this_dir, reloc_root, NULL, TRUE, NULL); + if (!this_dir) { + return (-1); + } + } + } + + + /* + * Now we scan the directory itself, and look at what is inside of it. + */ + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { + status = stat_filter(s_entry->whole_name, &statbuf); + lstatus = lstat_filter(s_entry->whole_name, &lstatbuf); + + /* + * We always should create an entirely new directory tree + * whenever we generate a new session, unless there were + * *no* changes whatsoever to any of the directories, in which + * case it would be kind of pointless to generate a new + * session. + * I believe it is possible to rigorously prove that any change + * anywhere in the filesystem will force the entire tree to be + * regenerated because the modified directory will get a new + * extent number. Since each subdirectory of the changed + * directory has a '..' entry, all of them will need to be + * rewritten too, and since the parent directory of the + * modified directory will have an extent pointer to the + * directory it too will need to be rewritten. Thus we will + * never be able to reuse any directory information when + * writing new sessions. + * + * We still check the previous session so we can mark off the + * equivalent entry in the list we got from the original disc, + * however. + */ + + /* + * The check_prev_session function looks for an identical + * entry in the previous session. If we see it, then we copy + * the extent number to s_entry, and cross it off the list. + * It returns 2 if it's a directory + */ + retcode = check_prev_session(orig_contents, n_orig, s_entry, + &statbuf, &lstatbuf, &odpnt); + if (retcode == -1) + return (-1); + + if (retcode == 2 && odpnt != NULL) { + int dflag; + + if (strcmp(s_entry->name, ".") != 0 && + strcmp(s_entry->name, "..") != 0) { + struct directory *child; + + /* + * XXX It seems that the tree that has been + * XXX read from the previous session does not + * XXX carry whole_name entries. We provide a + * XXX hack in + * XXX multi.c:find_or_create_directory() + * XXX that should be removed when a + * XXX reasonable method could be found. + */ + child = find_or_create_directory(this_dir, + s_entry->whole_name, + s_entry, 1, NULL); + dflag = merge_previous_session(child, + &odpnt->isorec, + NULL, reloc_old_root); + if (dflag == -1) { + return (-1); + } + free(odpnt); + odpnt = NULL; + } + } + } + + if (!reloc_old_root) { + /* + * Whatever is left over, are things which are no longer in the tree on + * disk. We need to also merge these into the tree. + */ + merge_remaining_entries(this_dir, orig_contents, n_orig); + } + free_mdinfo(orig_contents, n_orig); + return (1); +} + +/* + * This code deals with relocated directories which may exist + * in the previous session. + */ +struct dir_extent_link { + unsigned int extent; + struct directory_entry *de; + struct dir_extent_link *next; +}; + +static struct dir_extent_link *cl_dirs = NULL; +static struct dir_extent_link *re_dirs = NULL; + +static void +check_rr_relocation(struct directory_entry *de) +{ + unsigned char sector[SECTOR_SIZE]; + unsigned char *pnt = de->rr_attributes; + int len = de->rr_attr_size; + int cont_extent = 0, + cont_offset = 0, + cont_size = 0; + + pnt = parse_xa(pnt, &len, /* dpnt */ 0); + while (len >= 4) { + if (pnt[3] != 1 && pnt[3] != 2) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); +#else + fprintf(stderr, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); +#endif + } + if (strncmp((char *) pnt, "CL", 2) == 0) { + struct dir_extent_link *dlink = e_malloc(sizeof (*dlink)); + + dlink->extent = isonum_733(pnt + 4); + dlink->de = de; + dlink->next = cl_dirs; + cl_dirs = dlink; + + } else if (strncmp((char *) pnt, "RE", 2) == 0) { + struct dir_extent_link *dlink = e_malloc(sizeof (*dlink)); + + dlink->extent = de->starting_block; + dlink->de = de; + dlink->next = re_dirs; + re_dirs = dlink; + + } else if (strncmp((char *) pnt, "CE", 2) == 0) { + cont_extent = isonum_733(pnt + 4); + cont_offset = isonum_733(pnt + 12); + cont_size = isonum_733(pnt + 20); + + } else if (strncmp((char *) pnt, "ST", 2) == 0) { + len = pnt[2]; + } + len -= pnt[2]; + pnt += pnt[2]; + if (len <= 3 && cont_extent) { + /* ??? What if cont_offset+cont_size > SECTOR_SIZE */ + readsecs(cont_extent, sector, 1); + pnt = sector + cont_offset; + len = cont_size; + cont_extent = cont_offset = cont_size = 0; + } + } + +} + +void +match_cl_re_entries() +{ + struct dir_extent_link *re = re_dirs; + + /* for each relocated directory */ + for (; re; re = re->next) { + struct dir_extent_link *cl = cl_dirs; + + for (; cl; cl = cl->next) { + /* find a place where it was relocated from */ + if (cl->extent == re->extent) { + /* set link to that place */ + re->de->parent_rec = cl->de; + re->de->filedir = cl->de->filedir; + + /* + * see if it is in rr_moved + */ + if (reloc_dir != NULL) { + struct directory_entry *rr_moved_e = reloc_dir->contents; + + for (; rr_moved_e; rr_moved_e = rr_moved_e->next) { + /* yes it is */ + if (re->de == rr_moved_e) { + /* forget it */ + re->de = NULL; + } + } + } + break; + } + } + } +} + +void +finish_cl_pl_for_prev_session() +{ + struct dir_extent_link *re = re_dirs; + + /* for those that were relocated, but NOT to rr_moved */ + re = re_dirs; + for (; re; re = re->next) { + if (re->de != NULL) { + /* + * here we have hypothetical case when previous session + * was not created by genisoimage and contains relocations + */ + struct directory_entry *s_entry = re->de; + struct directory_entry *s_entry1; + struct directory *d_entry = reloc_dir->subdir; + + /* do the same as finish_cl_pl_entries */ + if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { + continue; + } + while (d_entry) { + if (d_entry->self == s_entry) + break; + d_entry = d_entry->next; + } + if (!d_entry) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Unable to locate directory parent\n"); +#else + fprintf(stderr, "Unable to locate directory parent\n"); + exit(1); +#endif + } + + if (s_entry->filedir != NULL && s_entry->parent_rec != NULL) { + char *rr_attr; + + /* + * First fix the PL pointer in the directory in the + * rr_reloc dir + */ + s_entry1 = d_entry->contents->next; + rr_attr = find_rr_attribute(s_entry1->rr_attributes, + s_entry1->total_rr_attr_size, "PL"); + if (rr_attr != NULL) + set_733(rr_attr + 4, s_entry->filedir->extent); + + /* Now fix the CL pointer */ + s_entry1 = s_entry->parent_rec; + + rr_attr = find_rr_attribute(s_entry1->rr_attributes, + s_entry1->total_rr_attr_size, "CL"); + if (rr_attr != NULL) + set_733(rr_attr + 4, d_entry->extent); + } + } + } + /* free memory */ + re = re_dirs; + while (re) { + struct dir_extent_link *next = re->next; + + free(re); + re = next; + } + re = cl_dirs; + while (re) { + struct dir_extent_link *next = re->next; + + free(re); + re = next; + } +} |