diff options
Diffstat (limited to 'genisoimage/volume.c')
-rw-r--r-- | genisoimage/volume.c | 728 |
1 files changed, 728 insertions, 0 deletions
diff --git a/genisoimage/volume.c b/genisoimage/volume.c new file mode 100644 index 0000000..dd45336 --- /dev/null +++ b/genisoimage/volume.c @@ -0,0 +1,728 @@ +/* + * 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. + * + */ + +/* @(#)volume.c 1.14 04/07/09 joerg, Copyright 1997, 1998, 1999, 2000 James Pearson */ +/* + * Copyright (c) 1997, 1998, 1999, 2000 James Pearson + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * volume.c: prepare HFS volume for mkhybrid + * + * James Pearson 17/7/97 + * modified JCP 29/7/97 to improve allocation sizes to cut + * down on wasted space. Now uses the HFS "allocation" size rounded + * up to the nearest 2048 bytes. Savings can be significant with + * a large volume containing lots of smallish files. + * + * Updated for v1.12 - now uses the built in RELOCATED_DIRECTORY + * flag for finding the real directory location JCP 8/1/97 + */ + +#ifdef APPLE_HYB + +#include <mconfig.h> +#include "genisoimage.h" +#include <errno.h> + +#define HFS_MIN_SIZE 1600 /* 800k == 1600 HFS blocks */ + +static hfsvol *vol_save = 0; /* used to "destroy" an HFS volume */ + +static int AlcSiz(Ulong); +static int XClpSiz(Ulong); +static int get_vol_size(int); +int write_fork(hfsfile * hfp, long tot); +int make_mac_volume(struct directory *, int); +static int copy_to_mac_vol(hfsvol *, struct directory *); +static void set_dir_info(hfsvol *, struct directory *); + +/* + * AlcSiz: find allocation size for given volume size + */ +static int +AlcSiz(Ulong vlen) +{ + int lpa, + drAlBlkSiz; + + /* code extracted from hfs_format() */ + lpa = 1 + vlen / 65536; + drAlBlkSiz = lpa * HFS_BLOCKSZ; + + /* + * now set our "allocation size" to the allocation block rounded up to + * the nearest SECTOR_SIZE (2048 bytes) + */ + drAlBlkSiz = ROUND_UP(drAlBlkSiz, SECTOR_SIZE); + + return (drAlBlkSiz); +} + +/* + * XClpSiz: find the default size of the catalog/extent file + */ +static int +XClpSiz(Ulong vlen) +{ + int olpa, + lpa, + drNmAlBlks, + drAlBlkSiz; + int vbmsz, + drXTClpSiz; + + /* code extracted from hfs_format() */ + + /* get the lpa from our calculated allocation block size */ + drAlBlkSiz = AlcSiz(vlen); + lpa = drAlBlkSiz / HFS_BLOCKSZ; + + vbmsz = (vlen / lpa + 4095) / 4096; + drNmAlBlks = (vlen - 5 - vbmsz) / lpa; + drXTClpSiz = drNmAlBlks / 128 * drAlBlkSiz; + + /* override the drXTClpSiz size for large volumes */ + if (drXTClpSiz > hce->max_XTCsize) { + drXTClpSiz = hce->max_XTCsize; + } else { + /* + * make allowances because we have possibly rounded up the + * allocation size get the "original" lpa " + */ + olpa = 1 + vlen / 65536; + + /* adjust size upwards */ + drXTClpSiz = ((Ullong)drXTClpSiz * lpa) / olpa; + } + + /* round up to the nearest allocation size */ + drXTClpSiz = ROUND_UP(drXTClpSiz, drAlBlkSiz); + + return (drXTClpSiz); +} + +/* + * get_vol_size: get the size of the volume including the extent/catalog + */ +static int +get_vol_size(int vblen) +{ + int drXTClpSiz; + int drAlBlkSiz; + int new_vblen; + + /* + * try to estimate a "volume size" based on the code in hfs_format + * - we need the size of the catalog/extents and Desktop files included + * in the volume, as we add this to the end of the ISO volume + */ + drXTClpSiz = XClpSiz(vblen); + drAlBlkSiz = AlcSiz(vblen); + + /* + * catalog file is set at CTC times (default twice) the extents + * file size - hence the (ctc_size + 1) below. The Desktop starts of + * the same size as the "clump size" == 4 x drAlBlkSiz, + * plus a spare drAlBlkSiz for the alternative MDB + */ + new_vblen = vblen + + ((hce->ctc_size + 1) * drXTClpSiz + 5 * drAlBlkSiz) / HFS_BLOCKSZ; + + return (new_vblen); +} + +/* + * write_fork: "write" file data to the volume + * + * This is used to update the HFS file internal structures + * but no data is actually written (it's trapped deep down in + * libhfs). + */ +int +write_fork(hfsfile *hfp, long tot) +{ + char blk[HFS_BLOCKSZ]; + unsigned short start; + long len; + + len = tot; + /* we need to know where this fork starts */ + start = hfs_get_drAllocPtr(hfp); + + /* loop through the data a block at a time */ + while (len >= HFS_BLOCKSZ) { + if (hfs_write(hfp, blk, HFS_BLOCKSZ) < 0) + return (-1); + len -= HFS_BLOCKSZ; + } + /* write out anything left */ + if (len) + if (hfs_write(hfp, blk, len) < 0) + return (-1); + + /* + * set the start of the allocation search to be immediately after + * this fork + */ + hfs_set_drAllocPtr(hfp, start, tot); + + return (0); +} + +/* + * make_mac_volume: "create" an HFS volume using the ISO data + * + * The HFS volume structures are set up (but no data is written yet). + * + * ISO volumes have a allocation size of 2048 bytes - regardless + * of the size of the volume. HFS allocation size is depends on volume + * size, so we may have to update the ISO structures to add in any + * padding. + */ +int +make_mac_volume(struct directory *dpnt, int start_extent) +{ + char vol_name[HFS_MAX_VLEN + 1]; /* Mac volume name */ + hfsvol *vol; /* Mac volume */ + int vblen; /* vol length (HFS blocks) */ + int Csize, + lastCsize; /* allocation sizes */ + int ret = 0; /* return value */ + int loop = 1; + + /* umount volume if we have had a previous attempt */ + if (vol_save) + if (hfs_umount(vol_save, 0, hfs_lock) < 0) + return (-1); + + /* set the default clump size to the ISO block size */ + Csize = lastCsize = SECTOR_SIZE; + + if (verbose > 1) + fprintf(stderr, "Creating HFS Volume info\n"); + + /* name or copy ISO volume name to Mac Volume name */ + strncpy(vol_name, hfs_volume_id ? hfs_volume_id : volume_id, + HFS_MAX_VLEN); + vol_name[HFS_MAX_VLEN] = '\0'; + + /* get initial size of HFS volume (size of current ISO volume) */ + vblen = (last_extent - session_start) * HFS_BLK_CONV; + + /* make sure volume is at least 800k */ + if (vblen < HFS_MIN_SIZE) + vblen += insert_padding_file(HFS_MIN_SIZE - vblen); + + /* + * add on size of extents/catalog file, but this may mean the + * allocation size will change, so loop round until the + * allocation size doesn't change + */ + while (loop) { + hce->XTCsize = XClpSiz(vblen); + vblen = get_vol_size(vblen); + Csize = AlcSiz(vblen); + + if (Csize == lastCsize) { + /* allocation size hasn't changed, so carry on */ + loop = 0; + } else { + /* + * allocation size has changed, so update + * ISO volume size + */ + if ((vblen = get_adj_size(Csize)) < 0) { + sprintf(hce->error, + "too many files for HFS volume"); + return (-1); + } + vblen += + ROUND_UP((start_extent - session_start) * + HFS_BLK_CONV, Csize); + lastCsize = Csize; + } + } + + /* take off the label/map size */ + vblen -= hce->hfs_map_size; + + hce->hfs_vol_size = vblen; + + /* set the default allocation size for libhfs */ + hce->Csize = Csize; + + /* format and mount the "volume" */ + if (hfs_format(hce, 0, vol_name) < 0) { + sprintf(hce->error, "can't HFS format %s", vol_name); + return (-1); + } + /* + * update the ISO structures with new start extents and any + * padding required + */ + if (Csize != SECTOR_SIZE) { + last_extent = adj_size(Csize, start_extent, + hce->hfs_hdr_size + hce->hfs_map_size); + adj_size_other(dpnt); + } + if ((vol = hfs_mount(hce, 0, 0)) == 0) { + sprintf(hce->error, "can't HFS mount %s", vol_name); + return (-1); + } + /* save the volume for possible later use */ + vol_save = vol; + + /* + * Recursively "copy" the files to the volume + * - we need to know the first allocation block in the volume as + * starting blocks of files are relative to this. + */ + ret = copy_to_mac_vol(vol, dpnt); + if (ret < 0) + return (ret); + + /* + * make the Desktop files - I *think* this stops the Mac rebuilding the + * desktop when the CD is mounted on a Mac These will be ignored if they + * already exist + */ + if (create_dt) + ret = make_desktop(vol, + (last_extent - session_start) * HFS_BLK_CONV); + if (ret < 0) + return (ret); + + /* close the volume */ + if (hfs_flush(vol) < 0) + return (-1); + + /* unmount and set the start blocks for the catalog/extents files */ + if (hfs_umount(vol, (last_extent - session_start) * HFS_BLK_CONV, hfs_lock) < 0) + return (-1); + + return (Csize); +} + +#define TEN 10 /* well, it is! */ +#define LCHAR "_" + +/* + * copy_to_mac_vol: copy all files in a directory to corresponding + * Mac folder. + * + * Files are copied recursively to corresponding folders on the Mac + * volume. The caller routine needs to do a hfs_chdir before calling this + * routine. + */ +static int +copy_to_mac_vol(hfsvol *vol, struct directory *node) +{ + struct directory_entry *s_entry; /* ISO directory entry */ + struct directory_entry *s_entry1; /* tmp ISO directory entry */ + struct directory *dpnt; /* ISO directory */ + + hfsfile *hfp; /* HFS file */ + hfsdirent *ent; /* HFS file entities */ + long id; /* current HFS folder */ + long dext, + rext; /* real data/rsrc start blk */ + int ret; /* result code */ + int new_name; /* HFS file has modified name */ + + int tens; + int digits; + int i; + + /* store the current HFS directory ID */ + if ((id = hfs_getcwd(vol)) == 0) + return (-1); + + if (verbose > 1) + fprintf(stderr, "HFS scanning %s\n", node->whole_name); + + /* loop through the ISO directory entries and process files */ + for (s_entry = node->contents; s_entry; s_entry = s_entry->next) { + /* ignore directory and associated (rsrc) files */ + if (s_entry->isorec.flags[0] & (ISO_DIRECTORY|ISO_ASSOCIATED)) + continue; + + /* ignore any non-Mac type file */ + if (!s_entry->hfs_ent) + continue; + + /* + * ignore if from a previous session + * - should be trapped above + */ + if (s_entry->starting_block < session_start) + continue; + +#ifdef DEBUG + fprintf(stderr, " Name = %s", s_entry->whole_name); + fprintf(stderr, " Startb = %d\n", s_entry->starting_block); +#endif /* DEBUG */ + + ent = s_entry->hfs_ent; + + /* create file */ + i = HFS_MAX_FLEN - strlen(ent->name); + new_name = 0; + tens = TEN; + digits = 1; + + while (1) { + /* + * try to open file - if it exists, + * then append '_' to the name and try again + */ + errno = 0; + if ((hfs_create(vol, ent->name, ent->u.file.type, + ent->u.file.creator)) < 0) { + if (errno != EEXIST) { + /* + * not an "exist" error, or we can't + * append as the filename is already + * HFS_MAX_FLEN chars + */ + sprintf(hce->error, + "can't HFS create file %s", + s_entry->whole_name); + return (-1); + } else if (i == 0) { + /* + * File name at max HFS length + * - make unique name + */ + if (!new_name) + new_name++; + + sprintf(ent->name + + HFS_MAX_FLEN - digits - 1, + "%s%d", LCHAR, new_name); + new_name++; + if (new_name == tens) { + tens *= TEN; + digits++; + } + } else { + /* append '_' to get new name */ + strcat(ent->name, LCHAR); + i--; + new_name = 1; + } + } else + break; + } + + /* warn that we have a new name */ + if (new_name && verbose > 0) { + fprintf(stderr, "Using HFS name: %s for %s\n", + ent->name, + s_entry->whole_name); + } + /* open file */ + if ((hfp = hfs_open(vol, ent->name)) == 0) { + sprintf(hce->error, "can't HFS open %s", + s_entry->whole_name); + return (-1); + } + /* if it has a data fork, then "write" it out */ + if (ent->u.file.dsize) + write_fork(hfp, ent->u.file.dsize); + + /* if it has a resource fork, set the fork and "write" it out */ + if (ent->u.file.rsize) { + if ((hfs_setfork(hfp, 1)) < 0) + return (-1); + write_fork(hfp, ent->u.file.rsize); + } + + /* make file invisible if ISO9660 hidden */ + if (s_entry->de_flags & HIDDEN_FILE) + ent->fdflags |= HFS_FNDR_ISINVISIBLE; + + /* update any HFS file attributes */ + if ((hfs_fsetattr(hfp, ent)) < 0) { + sprintf(hce->error, "can't HFS set attributes %s", + s_entry->whole_name); + return (-1); + } + /* + * get the ISO starting block of data fork (may be zero) + * and convert to the equivalent HFS block + */ + if (ent->u.file.dsize) { + dext = (s_entry->starting_block - session_start) * + HFS_BLK_CONV; + } else { + dext = 0; + } + + /* + * if the file has a resource fork (associated file), + * get it's ISO starting block and convert as above + */ + if (s_entry->assoc && ent->u.file.rsize) { + rext = + (s_entry->assoc->starting_block - session_start) * + HFS_BLK_CONV; + } else { + rext = 0; + } + + /* close the file and update the starting blocks */ + if (hfs_close(hfp, dext, rext) < 0) { + sprintf(hce->error, "can't HFS close file %s", + s_entry->whole_name); + return (-1); + } + } + + /* set folder info and custom icon (if it exists) */ + set_dir_info(vol, node); + + /* + * process sub-directories - have a slight problem here, + * if the directory had been relocated, then we need to find the + * real directory - we do this by first finding the + * real directory_entry, and then finding it's directory info + */ + + /* following code taken from joliet.c */ + for (s_entry = node->contents; s_entry; s_entry = s_entry->next) { + if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) { + /* + * if the directory has been reloacted, then search the + * relocated directory for the real entry + */ + for (s_entry1 = reloc_dir->contents; s_entry1; + s_entry1 = s_entry1->next) { + if (s_entry1->parent_rec == s_entry) + break; + } + + /* have a problem - can't find the real directory */ + if (s_entry1 == NULL) { + sprintf(hce->error, + "can't locate relocated directory %s", + s_entry->whole_name); + return (-1); + } + } else + s_entry1 = s_entry; + + /* now have the correct entry - now find the actual directory */ + if ((s_entry1->isorec.flags[0] & ISO_DIRECTORY) && + strcmp(s_entry1->name, ".") && + strcmp(s_entry1->name, "..")) { + if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) + dpnt = reloc_dir->subdir; + else + dpnt = node->subdir; + + while (1) { + if (dpnt->self == s_entry1) + break; + dpnt = dpnt->next; + if (!dpnt) { + sprintf(hce->error, + "can't find directory location %s", + s_entry1->whole_name); + return (-1); + } + } + /* + * now have the correct directory + * - so do the HFS stuff + */ + ent = dpnt->hfs_ent; + + /* + * if we don't have hfs entries, then this is a "deep" + * directory - this will be processed later + */ + if (!ent) + continue; + + /* make sub-folder */ + i = HFS_MAX_FLEN - strlen(ent->name); + new_name = 0; + tens = TEN; + digits = 1; + + while (1) { + /* + * try to create new directory + * - if it exists, then append '_' to the name + * and try again + */ + errno = 0; + if (hfs_mkdir(vol, ent->name) < 0) { + if (errno != EEXIST) { + /* + * not an "exist" error, + * or we can't append as the + * filename is already + * HFS_MAX_FLEN chars + */ + sprintf(hce->error, + "can't HFS create folder %s", + s_entry->whole_name); + return (-1); + } else if (i == 0) { + /* + * File name at max HFS length + * - make unique name + */ + if (!new_name) + new_name++; + + sprintf(ent->name + + HFS_MAX_FLEN - digits - 1, + "%s%d", LCHAR, new_name); + new_name++; + if (new_name == tens) { + tens *= TEN; + digits++; + } + } else { + /* append '_' to get new name */ + strcat(ent->name, LCHAR); + i--; + new_name = 1; + } + } else + break; + } + + /* warn that we have a new name */ + if (new_name && verbose > 0) { + fprintf(stderr, "Using HFS name: %s for %s\n", + ent->name, + s_entry->whole_name); + } + /* see if we need to "bless" this folder */ + if (hfs_bless && strcmp(s_entry->whole_name, hfs_bless) + == 0) { + hfs_stat(vol, ent->name, ent); + hfs_vsetbless(vol, ent->cnid); + if (verbose > 0) { + fprintf(stderr, "Blessing %s (%s)\n", + ent->name, + s_entry->whole_name); + } + /* stop any further checks */ + hfs_bless = NULL; + } + /* change to sub-folder */ + if (hfs_chdir(vol, ent->name) < 0) + return (-1); + + /* recursively copy files ... */ + ret = copy_to_mac_vol(vol, dpnt); + if (ret < 0) + return (ret); + + /* change back to this folder */ + if (hfs_setcwd(vol, id) < 0) + return (-1); + } + } + + return (0); +} + +/* + * set_dir_info: Set directory info for a file - also use a custom + * Icon - if it exists. + * + * Sets folder' layout (window layout, view, scroll bars etc) + * + * Set the 'HFS_FNDR_HASCUSTOMICON' bit of the folder flags + * if a file called 'Icon\r' exists in the folder + * + * Also makes sure the Icon file is invisible + * Don't worry if any of this fails ... + * + * Thanks to Rob Leslie <rob@mars.org> for how to do this. + */ + +#define ICON "Icon" + +static void +set_dir_info(hfsvol *vol, struct directory *de) +{ + hfsdirent *ent = de->hfs_ent; + hfsdirent ent1; + char name[HFS_MAX_FLEN + 1]; + unsigned short flags = 0; + + memset(&ent1, 0, sizeof (hfsdirent)); + + sprintf(name, "%s\r", ICON); + + /* get the attributes for the Icon file */ + if (hfs_stat(vol, name, &ent1) == 0) { + + /* make sure it is invisible */ + ent1.fdflags |= HFS_FNDR_ISINVISIBLE; + + /* set the new attributes for the Icon file */ + hfs_setattr(vol, name, &ent1); + + /* flag the folder as having a custom icon */ + flags |= HFS_FNDR_HASCUSTOMICON; + } + + /* make the current folder invisible if ISO9660 hidden */ + if (de->self->de_flags & HIDDEN_FILE) { + flags |= HFS_FNDR_ISINVISIBLE; + } + + /* may not have an hfs_ent for this directory */ + if (ent == NULL) { + ent = &ent1; + memset(ent, 0, sizeof (hfsdirent)); + + /* get the attributes for the folder */ + if (hfs_stat(vol, ":", ent) < 0) + return; + } + + /* set HFS_FNDR_HASCUSTOMICON/HFS_FNDR_ISINVISIBLE if needed */ + ent->fdflags |= flags; + + /* set the new attributes for the folder */ + if (hfs_setattr(vol, ":", ent) < 0) { + /* + * Only needed if we add things after this if statement. + */ +/* return;*/ + } +} + +#endif /* APPLE_HYB */ |