summaryrefslogtreecommitdiff
path: root/genisoimage/volume.c
diff options
context:
space:
mode:
Diffstat (limited to 'genisoimage/volume.c')
-rw-r--r--genisoimage/volume.c728
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 */