Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

volume.c

/*
 * 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 */

Generated by  Doxygen 1.6.0   Back to index