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

isolib.c

/* 
 * This code is based on the ISO filesystem support in MILO (by
 * Dave Rusling).
 *
 * This is a set of functions that provides minimal filesystem
 * functionality to the Linux bootstrapper.  All we can do is
 * open and read files... but that's all we need 8-)
 */
#ifndef TESTING
#  include <linux/string.h>
#endif
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <asm/page.h>

#include <iso.h>
#include <isolib.h>
#include <utils.h>

/* iso9660 support code */

#define MAX_OPEN_FILES 5

static struct inode_table_entry {
    struct iso_inode inode;
    int inumber;
    int free;
    unsigned short old_mode;
    unsigned size;
    int nlink;
    int mode;
    void *start;
} inode_table[MAX_OPEN_FILES];

static unsigned long root_inode = 0;
static struct isofs_super_block sb;
static char data_block[1024];
static char big_data_block[2048];

#ifndef S_IRWXUGO
# define S_IRWXUGO      (S_IRWXU|S_IRWXG|S_IRWXO)
# define S_IXUGO  (S_IXUSR|S_IXGRP|S_IXOTH)
# define S_IRUGO  (S_IRUSR|S_IRGRP|S_IROTH)
#endif

extern long iso_dev_read (void * buf, long offset, long size);

static int parse_rock_ridge_inode(struct iso_directory_record * de,
                          struct iso_inode * inode);
static char *get_rock_ridge_symlink(struct iso_inode *inode);
static int get_rock_ridge_filename(struct iso_directory_record * de,
                           char * retname,
                           struct iso_inode * inode);

int
isonum_711 (char * p)
{
      return (*p & 0xff);
}


int
isonum_712 (char * p)
{
      int val;

      val = *p;
      if (val & 0x80)
            val |= 0xffffff00;
      return val;
}


int
isonum_721 (char * p)
{
      return ((p[0] & 0xff) | ((p[1] & 0xff) << 8));
}

int
isonum_722 (char * p)
{
      return (((p[0] & 0xff) << 8) | (p[1] & 0xff));
}


int
isonum_723 (char * p)
{
      return isonum_721(p);
}


int
isonum_731 (char * p)
{
      return ((p[0] & 0xff)
            | ((p[1] & 0xff) << 8)
            | ((p[2] & 0xff) << 16)
            | ((p[3] & 0xff) << 24));
}


int
isonum_732 (char * p)
{
      return (((p[0] & 0xff) << 24)
            | ((p[1] & 0xff) << 16)
            | ((p[2] & 0xff) << 8)
            | (p[3] & 0xff));
}


int
isonum_733 (char * p)
{
      return isonum_731(p);
}


static int
iso_bmap (struct iso_inode *inode, int block)
{
      if (block < 0) {
            printf("iso_bmap: block<0");
            return 0;
      }
      return (inode->i_first_extent >> sb.s_blocksize_bits) + block;
}

static int
iso_breadi (struct iso_inode *ip, long blkno, long nblks, char * buffer)
{
      long i_size, abs_blkno;

      /* do some error checking */
      if (!ip || !ip->i_first_extent)
            return -1;

      i_size = ((struct inode_table_entry *) ip)->size;
      /* as in ext2.c - cons_read() doesn't really cope well with
           EOF conditions - actually it should be fixed */
      if ((blkno+nblks) * sb.s_blocksize > i_size)
            nblks = ((i_size + sb.s_blocksize)
                   / sb.s_blocksize) - blkno;

      /* figure out which iso block number(s) we're being asked for */
      abs_blkno = iso_bmap(ip, blkno);
      if (!abs_blkno)
            return -1;
      /* now try and read them (easy since ISO files are continguous) */
      return iso_dev_read(buffer, abs_blkno * sb.s_blocksize,
                      nblks * sb.s_blocksize);
}


/*
 * Release our hold on an inode.  Since this is a read-only application,
 * don't worry about putting back any changes...
 */
static void
iso_iput (struct iso_inode *ip)
{
      struct inode_table_entry *itp;

      /* Find and free the inode table slot we used... */
      itp = (struct inode_table_entry *) ip;

      itp->inumber = 0;
      itp->free = 1;
}

/*
 * Read the specified inode from the disk and return it to the user.
 * Returns NULL if the inode can't be read...
 *
 * Uses data_block
 */
static struct iso_inode *
iso_iget (int ino)
{
      int i;
      struct iso_inode *inode;
      struct inode_table_entry *itp;
      struct iso_directory_record * raw_inode;
      unsigned char *pnt = NULL;
      void *cpnt = NULL;
      int high_sierra;
      int block;

#ifdef DEBUG_ISO
      printf("iso_iget(ino=%d)\n", ino);
#endif

      /* find a free inode to play with */
      inode = NULL;
      itp = NULL;
      for (i = 0; i < MAX_OPEN_FILES; i++) {
            if (inode_table[i].free) {
                  itp = &(inode_table[i]);
                  inode = &(itp->inode);
                  break;
            }
      }
      if ((inode == NULL) || (itp == NULL)) {
            printf("iso9660 (iget): no free inodes\n");
            return (NULL);
      }

      block = ino >> sb.s_blocksize_bits;
      if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize)
          != sb.s_blocksize) {
            printf("iso9660: unable to read i-node block");
            return NULL;
      }

      pnt = ((unsigned char *) data_block + (ino & (sb.s_blocksize - 1)));
      raw_inode = ((struct iso_directory_record *) pnt);
      high_sierra = sb.s_high_sierra;

      if ((ino & (sb.s_blocksize - 1)) + *pnt > sb.s_blocksize){
            int frag1, offset;

            offset = (ino & (sb.s_blocksize - 1));
            frag1 = sb.s_blocksize - offset;
            cpnt = big_data_block;
            memcpy(cpnt, data_block + offset, frag1);
            offset += *pnt - sb.s_blocksize; /* DUH! pnt would get
                                                    wiped out by the
                                                    iso_dev_read here. */
            if (iso_dev_read(data_block, ++block * sb.s_blocksize,
                         sb.s_blocksize)
                != sb.s_blocksize) {
                  printf("unable to read i-node block");
                  return NULL;
            }
            memcpy((char *)cpnt+frag1, data_block, offset);
            pnt = ((unsigned char *) cpnt);
            raw_inode = ((struct iso_directory_record *) pnt);
      }

      if (raw_inode->flags[-high_sierra] & 2) {
            itp->mode = S_IRUGO | S_IXUGO | S_IFDIR;
            itp->nlink = 1; /* Set to 1.  We know there are 2, but
                           the find utility tries to optimize
                           if it is 2, and it screws up.  It is
                           easier to give 1 which tells find to
                           do it the hard way. */
      } else {
            itp->mode = sb.s_mode; /* Everybody gets to read the file. */
            itp->nlink = 1;
            itp->mode |= S_IFREG;
            /*
             * If there are no periods in the name, then set the
             * execute permission bit
             */
            for(i=0; i< raw_inode->name_len[0]; i++)
                  if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
                        break;
            if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';') 
                  itp->mode |= S_IXUGO; /* execute permission */
      }

      itp->size = isonum_733 (raw_inode->size);

      /* There are defective discs out there - we do this to protect
         ourselves.  A cdrom will never contain more than 700Mb */
      if((itp->size < 0 || itp->size > 700000000) &&
         sb.s_cruft == 'n')
      {
            printf("Warning: defective cdrom.  "
                   "Enabling \"cruft\" mount option.\n");
            sb.s_cruft = 'y';
      }

      /*
       * Some dipshit decided to store some other bit of information
       * in the high byte of the file length.  Catch this and
       * holler.  WARNING: this will make it impossible for a file
       * to be > 16Mb on the CDROM!!!
       */
      if(sb.s_cruft == 'y' && 
         itp->size & 0xff000000)
      {
            itp->size &= 0x00ffffff;
      }

      if (raw_inode->interleave[0]) {
            printf("Interleaved files not (yet) supported.\n");
            itp->size = 0;
      }

      /* I have no idea what file_unit_size is used for, so
         we will flag it for now */
      if (raw_inode->file_unit_size[0] != 0){
            printf("File unit size != 0 for ISO file (%d).\n", ino);
      }

      /* I have no idea what other flag bits are used for, so
         we will flag it for now */
#ifdef DEBUG_ISO
      if ((raw_inode->flags[-high_sierra] & ~2)!= 0){
            printf("Unusual flag settings for ISO file (%d %x).\n",
                   ino, raw_inode->flags[-high_sierra]);
      }
#endif

      inode->i_first_extent = (isonum_733 (raw_inode->extent) + 
                         isonum_711 (raw_inode->ext_attr_length))
                               << sb.s_log_zone_size;

      /* Now we check the Rock Ridge extensions for further info */

      if (sb.s_rock) 
            parse_rock_ridge_inode(raw_inode,inode);

      /* Will be used for previous directory */
      inode->i_backlink = 0xffffffff;
      switch (sb.s_conversion) {
            case 'a':
            inode->i_file_format = ISOFS_FILE_UNKNOWN; /* File type */
            break;
            case 'b':
            inode->i_file_format = ISOFS_FILE_BINARY; /* File type */
            break;
            case 't':
            inode->i_file_format = ISOFS_FILE_TEXT; /* File type */
            break;
            case 'm':
            inode->i_file_format = ISOFS_FILE_TEXT_M; /* File type */
            break;
      }

      /* keep our inode table correct */
      itp->free = 0;
      itp->inumber = ino;

      /* return a pointer to it */
      return inode;
}


/*
 * ok, we cannot use strncmp, as the name is not in our data space.
 * Thus we'll have to use iso_match. No big problem. Match also makes
 * some sanity tests.
 *
 * NOTE! unlike strncmp, iso_match returns 1 for success, 0 for failure.
 */
static int
iso_match (int len, const char *name, const char *compare, int dlen)
{
      if (!compare)
            return 0;

#ifdef DEBUG_ISO
      printf("iso_match: comparing %d chars of %s with %s\n",
             dlen, name, compare);
#endif

      /* check special "." and ".." files */
      if (dlen == 1) {
            /* "." */
            if (compare[0] == 0) {
                  if (!len)
                        return 1;
                  compare = ".";
            } else if (compare[0] == 1) {
                  compare = "..";
                  dlen = 2;
            }
      }
      if (dlen != len)
            return 0;
      return !memcmp(name, compare, len);
}

/*
 * Find an entry in the specified directory with the wanted name. It
 * returns the cache buffer in which the entry was found, and the entry
 * itself (as an inode number). It does NOT read the inode of the
 * entry - you'll have to do that yourself if you want to.
 *
 * uses data_block
 */
static int
iso_find_entry (struct iso_inode *dir, const char *name, int namelen,
            unsigned long *ino, unsigned long *ino_back)
{
      unsigned long bufsize = sb.s_blocksize;
      unsigned char bufbits = sb.s_blocksize_bits;
      unsigned int block, f_pos, offset, inode_number = 0; /*shut up, gcc*/
      void * cpnt = NULL;
      unsigned int old_offset;
      unsigned int backlink;
      int dlen, match, i;
      struct iso_directory_record * de;
      char c;
      struct inode_table_entry *itp = (struct inode_table_entry *) dir;

      *ino = 0;
      if (!dir) return -1;
      
      if (!(block = dir->i_first_extent)) return -1;
  
      f_pos = 0;
      offset = 0;
      block = iso_bmap(dir,f_pos >> bufbits);
      if (!block) return -1;

      if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize)
          != sb.s_blocksize) return -1;
  
      while (f_pos < itp->size) {
            de = (struct iso_directory_record *) (data_block + offset);
            backlink = itp->inumber;
            inode_number = (block << bufbits) + (offset & (bufsize - 1));

            /* If byte is zero, this is the end of file, or time to move to
               the next sector. Usually 2048 byte boundaries. */
            
            if (*((unsigned char *) de) == 0) {
                  offset = 0;

                  /* round f_pos up to the nearest blocksize */
                  f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1))
                         + ISOFS_BLOCK_SIZE);
                  block = iso_bmap(dir,f_pos>>bufbits);
                  if (!block) return -1;
                  if (iso_dev_read(data_block,
                               block * sb.s_blocksize,
                               sb.s_blocksize)
                        != sb.s_blocksize) return -1;
                  continue; /* Will kick out if past end of directory */
            }

            old_offset = offset;
            offset += *((unsigned char *) de);
            f_pos += *((unsigned char *) de);

            /* Handle case where the directory entry spans two blocks.
               Usually 1024 byte boundaries */
            if (offset >= bufsize) {
                    unsigned int frag1;
                  frag1 = bufsize - old_offset;
                  cpnt = big_data_block;
                  memcpy(cpnt, data_block + old_offset, frag1);

                  de = (struct iso_directory_record *) cpnt;
                  offset = f_pos & (bufsize - 1);
                  block = iso_bmap(dir,f_pos>>bufbits);
                  if (!block) return -1;
                        if (iso_dev_read(data_block,
                               block * sb.s_blocksize,
                               sb.s_blocksize)
                        != sb.s_blocksize) return 0;
                  memcpy((char *)cpnt+frag1, data_block, offset);
            }
            
            /* Handle the '.' case */
            
            if (de->name[0]==0 && de->name_len[0]==1) {
              inode_number = itp->inumber;
              backlink = 0;
            }
            
            /* Handle the '..' case */

            if (de->name[0]==1 && de->name_len[0]==1) {

              if((int) sb.s_firstdatazone != itp->inumber)
                inode_number = (isonum_733(de->extent) + 
                            isonum_711(de->ext_attr_length))
                            << sb.s_log_zone_size;
              else
                inode_number = itp->inumber;
              backlink = 0;
            }
    
            {
              /* should be sufficient, since get_rock_ridge_filename
               * truncates at 254 chars */
              char retname[256]; 
              dlen = get_rock_ridge_filename(de, retname, dir);
              if (dlen) {
                strcpy(de->name, retname);
              } else {
                dlen = isonum_711(de->name_len);
                if(sb.s_mapping == 'n') {
                  for (i = 0; i < dlen; i++) {
                  c = de->name[i];
                  if (c >= 'A' && c <= 'Z') c |= 0x20;  /* lower case */
                  if (c == ';' && i == dlen-2
                      && de->name[i+1] == '1') {
                    dlen -= 2;
                    break;
                  }
                  if (c == ';') c = '.';
                  de->name[i] = c;
                  }
                  /* This allows us to match with and without a trailing
                   period.  */
                  if(de->name[dlen-1] == '.' && namelen == dlen-1)
                  dlen--;
                }
              }
            }
            /*
             * Skip hidden or associated files unless unhide is set
             */
            match = 0;
            if(   !(de->flags[-sb.s_high_sierra] & 5)
               || sb.s_unhide == 'y' ) 
            {
              match = iso_match(namelen, name, de->name, dlen);
            }

            if (cpnt) cpnt = NULL;

            if (match) {
              if ((int) inode_number == -1) {
                /* Should never happen */
                printf("iso9660: error inode_number = -1\n");
                return -1;
              }

              *ino = inode_number;
              *ino_back = backlink;
#ifdef DEBUG_ISO
              printf("iso_find_entry returning successfully (ino = %d)\n",
                   inode_number);
#endif
              return 0;
            }
      }
#ifdef DEBUG_ISO
              printf("iso_find_entry returning unsuccessfully (ino = %d)\n",
                   inode_number);
#endif
      return -1;
}


/*
 *  Look up name in the current directory and return its corresponding
 *  inode if it can be found.
 *
 */
struct iso_inode *
iso_lookup(struct iso_inode *dir, const char *name)
{
      struct inode_table_entry *itp = (struct inode_table_entry *) dir;
      unsigned long ino, ino_back;
      struct iso_inode *result = NULL;
      int first, last;

#ifdef DEBUG_ISO
      printf("iso_lookup: %s\n", name);
#endif

      /* is the current inode a directory? */
      if (!S_ISDIR(itp->mode)) {
#ifdef DEBUG_ISO
            printf("iso_lookup: inode %d not a directory\n", itp->inumber);
#endif
            iso_iput(dir);
            return NULL;
      }

      /* work through the name finding each directory in turn */
      ino = 0;
      first = last = 0;
      while (last < (int) strlen(name)) {
            if (name[last] == '/') {
                  if (iso_find_entry(dir, &name[first], last - first, 
                                 &ino, &ino_back)) 
                        return NULL;
                  /* throw away the old directory inode, we
                     don't need it anymore */
                  iso_iput(dir);

                  if (!(dir = iso_iget(ino))) 
                        return NULL;
                  first = last + 1;
                  last = first;
            } else
                  last++;
      }
      {
            int rv;
            if ((rv = iso_find_entry(dir, &name[first], last - first, &ino, &ino_back))) {
                  iso_iput(dir);
                  return NULL;
            }
      }
      if (!(result = iso_iget(ino))) {
            iso_iput(dir);
            return NULL;
      }
      /*
       * We need this backlink for the ".." entry unless the name
       * that we are looking up traversed a mount point (in which
       * case the inode may not even be on an iso9660 filesystem,
       * and writing to u.isofs_i would only cause memory
       * corruption).
       */
      result->i_backlink = ino_back; 
      
      iso_iput(dir);
      return result;
}

/* follow a symbolic link, returning the inode of the file it points to */
static struct iso_inode *
iso_follow_link(struct iso_inode *from, const char *basename) 
{
      struct inode_table_entry *itp = (struct inode_table_entry *)from;
      struct iso_inode *root = iso_iget(root_inode);
      struct iso_inode *result = NULL;
      char *linkto;
      
#ifdef DEBUG_ISO
      printf("iso_follow_link(%s): ",basename);
#endif

      if (!S_ISLNK(itp->mode)) /* Hey, that's not a link! */
            return NULL;

      if (!itp->size) 
            return NULL;
      
      if (!(linkto = get_rock_ridge_symlink(from)))
            return NULL;

      linkto[itp->size]='\0';
#ifdef DEBUG_ISO
      printf("%s->%s\n",basename,linkto ? linkto : "[failed]");
#endif
      
      /* Resolve relative links. */

      if (linkto[0] !='/') {
            char *end = strrchr(basename, '/');
            if (end) {
                  char fullname[(end - basename + 1) + strlen(linkto) + 1];
                  strncpy(fullname, basename, end - basename + 1);
                  fullname[end - basename + 1] = '\0';
                  strcat(fullname, linkto);
#ifdef DEBUG_ISO
                  printf("resolved to %s\n", fullname);
#endif
                  result = iso_lookup(root,fullname);
            } else {
                  /* Assume it's in the root */
                  result = iso_lookup(root,linkto);
            }
      } else {
            result = iso_lookup(root,linkto);
      }
      free(linkto);
      iso_iput(root);
      return result;
}

/*
 * look if the driver can tell the multi session redirection value
 */
static inline unsigned int
iso_get_last_session (void)
{
#ifdef DEBUG_ISO 
      printf("iso_get_last_session() called\n");
#endif      
      return 0;
}


int
iso_read_super (void *data, int silent)
{
      static int first_time = 1;
      int high_sierra;
      unsigned int iso_blknum, vol_desc_start;
      char rock = 'y';
      int i;

      struct iso_volume_descriptor *vdp;
      struct hs_volume_descriptor *hdp;

      struct iso_primary_descriptor *pri = NULL;
      struct hs_primary_descriptor *h_pri = NULL;

      struct iso_directory_record *rootp;

      /* Initialize the inode table */
      for (i = 0; i < MAX_OPEN_FILES; i++) {
            inode_table[i].free = 1;
            inode_table[i].inumber = 0;
      }

#ifdef DEBUG_ISO 
      printf("iso_read_super() called\n");
#endif      

      /* set up the block size */
      sb.s_blocksize = 1024;
      sb.s_blocksize_bits = 10;

      sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */

      vol_desc_start = iso_get_last_session();
      
      for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; 
           iso_blknum++) {
#ifdef DEBUG_ISO
            printf("iso_read_super: iso_blknum=%d\n", iso_blknum);
#endif
            if (iso_dev_read(data_block, iso_blknum * 2048,
                         sb.s_blocksize) != sb.s_blocksize)
            {
                  printf("iso_read_super: bread failed, dev "
                         "iso_blknum %d\n", iso_blknum);
                  return -1;
            }
            vdp = (struct iso_volume_descriptor *)data_block;
            hdp = (struct hs_volume_descriptor *)data_block;

            if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
                  if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
                        return -1;
                  if (isonum_711 (hdp->type) == ISO_VD_END)
                        return -1;

                  sb.s_high_sierra = 1;
                  high_sierra = 1;
                  rock = 'n';
                  h_pri = (struct hs_primary_descriptor *)vdp;
                  break;
            }

            if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
                  if (isonum_711 (vdp->type) != ISO_VD_PRIMARY)
                        return -1;
                  if (isonum_711 (vdp->type) == ISO_VD_END)
                        return -1;
                  
                  pri = (struct iso_primary_descriptor *)vdp;
                  break;
            }
      }
      if(iso_blknum == vol_desc_start + 100) {
            if (!silent)
                  printf("iso: Unable to identify CD-ROM format.\n");
            return -1;
      }
      
      if (high_sierra) {
            rootp = (struct iso_directory_record *)
                  h_pri->root_directory_record;
            if (isonum_723 (h_pri->volume_set_size) != 1) {
                  printf("Multi-volume disks not (yet) supported.\n");
                  return -1;
            };
            sb.s_nzones = isonum_733 (h_pri->volume_space_size);
            sb.s_log_zone_size = 
                  isonum_723 (h_pri->logical_block_size);
            sb.s_max_size = isonum_733(h_pri->volume_space_size);
      } else {
            rootp = (struct iso_directory_record *)
                  pri->root_directory_record;
            if (isonum_723 (pri->volume_set_size) != 1) {
                  printf("Multi-volume disks not (yet) supported.\n");
                  return -1;
            }
            sb.s_nzones = isonum_733 (pri->volume_space_size);
            sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
            sb.s_max_size = isonum_733(pri->volume_space_size);
      }

      sb.s_ninodes = 0; /* No way to figure this out easily */

      /* RDE: convert log zone size to bit shift */

      switch (sb.s_log_zone_size) {
            case  512: sb.s_log_zone_size =  9; break;
            case 1024: sb.s_log_zone_size = 10; break;
            case 2048: sb.s_log_zone_size = 11; break;

            default:
            printf("Bad logical zone size %ld\n", sb.s_log_zone_size);
            return -1;
      }

      /* RDE: data zone now byte offset! */

      sb.s_firstdatazone = (isonum_733( rootp->extent) 
                               << sb.s_log_zone_size);
      /*
       * The CDROM is read-only, has no nodes (devices) on it, and
       * since all of the files appear to be owned by root, we
       * really do not want to allow suid.  (suid or devices will
       * not show up unless we have Rock Ridge extensions).
       */
      if (first_time) {
            first_time = 0;
            printf("iso: Max size:%ld   Log zone size:%ld\n",
                   sb.s_max_size, 1UL << sb.s_log_zone_size);
            printf("iso: First datazone:%ld   Root inode number %d\n",
                   sb.s_firstdatazone >> sb.s_log_zone_size,
                   isonum_733 (rootp->extent) << sb.s_log_zone_size);
            if (high_sierra)
                  printf("iso: Disc in High Sierra format.\n");
      }

      /* set up enough so that it can read an inode */

      sb.s_mapping = 'n';
      sb.s_rock = (rock == 'y' ? 1 : 0);
      sb.s_conversion = 'b';
      sb.s_cruft = 'n';
      sb.s_unhide = 'n';
      /*
       * It would be incredibly stupid to allow people to mark every file 
       * on the disk as suid, so we merely allow them to set the default 
       * permissions.
       */
      sb.s_mode = S_IRUGO & 0777;

      /* return successfully */
      root_inode = isonum_733 (rootp->extent) << sb.s_log_zone_size;
      return 0;
}


int
iso_bread (int fd, long blkno, long nblks, char * buffer)
{
      struct iso_inode *inode;

      /* find the inode for this file */
      inode = &inode_table[fd].inode;
      return iso_breadi(inode, blkno, nblks, buffer); 
}


int
iso_open (const char *filename)
{
      struct iso_inode *inode, *oldinode;
      struct iso_inode *root;

      /* get the root directory */
      root = iso_iget(root_inode);
      if (!root) {
            printf("iso9660: get root inode failed\n");
            return -1;
      }

      /* lookup the file */
      inode = iso_lookup(root, filename);

#ifdef DEBUG_ISO
      if (inode && S_ISLNK(((struct inode_table_entry *)inode)->mode)) {
            printf("%s is a link (len %u)\n",filename,
                   ((struct inode_table_entry *)inode)->size);
      } else {
            printf("%s is not a link\n",filename);
      }
#endif

      while ((inode) && (S_ISLNK(((struct inode_table_entry *)inode)->mode))) {
            oldinode = inode;
            inode = iso_follow_link(oldinode, filename);
            iso_iput(oldinode);
      }
      if (inode == NULL) 
            return -1;
      else {
            struct inode_table_entry * itp =
                  (struct inode_table_entry *) inode;
            return (itp - inode_table);
      }
}


void
iso_close (int fd)
{
      iso_iput(&inode_table[fd].inode);
}


long
iso_map (int fd, long block)
{
      return iso_bmap(&inode_table[fd].inode, block) * sb.s_blocksize;
}

#include <linux/stat.h>
int
iso_fstat (int fd, struct stat * buf)
{
      struct inode_table_entry * ip = inode_table + fd;

      if (fd >= MAX_OPEN_FILES)
            return -1;

      memset(buf, 0, sizeof(struct stat));
      /* fill in relevant fields */
      buf->st_ino   = ip->inumber;
      buf->st_mode  = ip->mode;
      buf->st_nlink = ip->nlink;
      buf->st_size  = ip->size;
      return 0;
}

/*
 * NOTE: mixing calls to this and calls to any other function that reads from
 * the filesystem will clobber the buffers and cause much wailing and gnashing
 * of teeth.
 *
 * Sorry this function is so ugly. It was written as a way for me to
 * learn how the ISO filesystem stuff works. 
 * 
 * Will Woods, 2/2001
 *
 * Uses data_block
 */
char *iso_readdir_i(int fd, int rewind) {
    struct inode_table_entry *itp = &(inode_table[fd]);
    struct iso_directory_record *dirent = 0;
    unsigned int fraglen = 0, block, dirent_len, name_len = 0, oldoffset;
    static unsigned int blockoffset = 0, diroffset = 0;
    
    if (!S_ISDIR(itp->mode)) {
      printf("Not a directory\n");
      return NULL;
    }
    
    /* Initial read to this directory, get the first block */
    if (rewind) {
      blockoffset = diroffset = 0;
      block = iso_bmap(&itp->inode,0); 
#ifdef DEBUG_ISO
      printf("fd #%d, inode %d, first_extent %d, block %u\n",
             fd,itp->inumber,itp->inode.i_first_extent,block);
#endif
      if (!block) return NULL;
      
      if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize) 
          != sb.s_blocksize) return NULL;
    }

    /* keep doing this until we get a filename or we fail */ 
    while (!name_len) {
      /* set up our dirent pointer into the block of data we've read */
      dirent = (struct iso_directory_record *) (data_block + blockoffset);
      dirent_len = isonum_711(dirent->length);
#ifdef DEBUG_ISO
      printf("diroffset=%u, blockoffset=%u, length=%u\n",
             diroffset,blockoffset,dirent_len);
#endif
    
      /* End of directory listing or end of sector */
      if (dirent_len == 0) { 
          /* round diroffset up to the nearest blocksize */
          diroffset = ((diroffset & ~(ISOFS_BLOCK_SIZE - 1))
                   + ISOFS_BLOCK_SIZE);
#ifdef DEBUG_ISO
          printf("dirent_len == 0. diroffset=%u, itp->size=%u. ",
               diroffset,itp->size);
#endif
          if (diroffset >= itp->size) {
#ifdef DEBUG_ISO
            printf("End of directory.\n");
#endif
            return NULL;
          } else {
#ifdef DEBUG_ISO
            printf("End of sector. Need next block.\n");
#endif
            /* Get the next block. */
            block = iso_bmap(&itp->inode, diroffset>>sb.s_blocksize_bits);
            if (!block) return NULL;
            if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize) 
                != sb.s_blocksize) return NULL;

            /* set the offsets and the pointers properly */
            blockoffset = 0;
            dirent = (struct iso_directory_record *) data_block;
            dirent_len = isonum_711(dirent->length);
#ifdef DEBUG_ISO
            printf("diroffset=%u, blockoffset=%u, length=%u\n",
                   diroffset,blockoffset,dirent_len);
#endif
          }
      }

      /* update the offsets for the next read */
      oldoffset = blockoffset;
      blockoffset += dirent_len;
      diroffset += dirent_len;
      
      /* 
       * directory entry spans two blocks - 
       * get next block and glue the two halves together 
       */
      if (blockoffset >= sb.s_blocksize) {
          fraglen = sb.s_blocksize - oldoffset; 
#ifdef DEBUG_ISO
          printf("fragmented block: blockoffset = %u, fraglen = %u\n",
               blockoffset, fraglen);
#endif
          /* copy the fragment at end of old block to front of new buffer */
          memcpy(big_data_block, data_block + oldoffset, fraglen);
          
          /* read the next block into the buffer after the old fragment */
          block = iso_bmap(&itp->inode, diroffset >> sb.s_blocksize_bits);
          if (!block) return NULL;
          if (iso_dev_read(big_data_block + fraglen, block * sb.s_blocksize, sb.s_blocksize) 
            != sb.s_blocksize) return NULL;
#ifdef DEBUG_ISO
          printf("read %u bytes from offset %u\n",
               sb.s_blocksize, block * sb.s_blocksize);
#endif
          
          blockoffset = 0;
          dirent = (struct iso_directory_record *) big_data_block;
      }
      
      /* 
       * Everything's cool, let's get the filename. 
       * First we need to figure out the length. 
       */
      name_len = isonum_711(dirent->name_len);
#ifdef DEBUG_ISO
      if (name_len==0) printf("dirent->name_len = 0, skipping.\n");
#endif
      
      /* skip '.' and '..' */
      if (name_len == 1) {
          if (dirent->name[0] == (char)0) name_len = 0;
          if (dirent->name[0] == (char)1) name_len = 0;
      } 


      if (sb.s_unhide == 'n') {
          /* sb.s_high_sierra is the offset for the position of the flags.
             this adjusts for differences between iso9660 and high sierra.
             
             if bit 0 (exists) or bit 2 (associated) are set, we ignore
             this record. */
          if (dirent->flags[-sb.s_high_sierra] & 5) name_len = 0;
      }

      /* if we have a real filename here.. */
      if (name_len) {
          /* should be sufficient, since get_rock_ridge_filename truncates
             at 254 characters */
          char rrname[256]; 
          if ((name_len = get_rock_ridge_filename(dirent, rrname, &itp->inode))) {
            rrname[name_len] = '\0';
            /* it's okay if rrname is longer than dirent->name, because
               we're just overwriting parts of the now-useless dirent */
            strcpy(dirent->name, rrname); 
          } else {
              int i;
              char c;
            if (sb.s_mapping == 'n') { /* downcase the name */
                for (i = 0; i < name_len; i++) {
                  c = dirent->name[i];
                  
                        /* lower case */ 
                  if ((c >= 'A') && (c <= 'Z')) c |= 0x20; 
                  
                  /* Drop trailing '.;1' */
                  if ((c == '.') && (i == name_len-3) && 
                      (dirent->name[i+1] == ';') && 
                            (dirent->name[i+2] == '1')) {
                      name_len -= 3 ; break; 
                  }
                  
                  /* Drop trailing ';1' */
                  if ((c == ';') && (i == name_len-2) && 
                            (dirent->name[i+1] == '1')) {
                      name_len -= 2; break;
                  }
                  
                  /* convert ';' to '.' */
                  if (c == ';') 
                      c = '.';
                  
                  dirent->name[i] = c;
                }
                dirent->name[name_len] = '\0';
            }
          }
      }
      /* now that we're done using it, and it's smaller than a full block, 
       * copy big_data_block back into data_block */
      if (fraglen) { 
            int len = sb.s_blocksize - dirent_len;
          memcpy(data_block, big_data_block + dirent_len, len);
#ifdef DEBUG_ISO
          printf("copied %u bytes of data back to data_block\n", len);
#endif
          blockoffset = 0;
          fraglen = 0;
      }
    }
    return dirent->name;
}

/********************************************************************** 
 * 
 * Rock Ridge functions and definitions, from the Linux kernel source.
 * linux/fs/isofs/rock.c, (c) 1992, 1993 Eric Youngdale.
 * 
 **********************************************************************/

#define SIG(A,B) ((A << 8) | B)

/* This is a way of ensuring that we have something in the system
   use fields that is compatible with Rock Ridge */
#define CHECK_SP(FAIL)                          \
      if(rr->u.SP.magic[0] != 0xbe) FAIL; \
      if(rr->u.SP.magic[1] != 0xef) FAIL;

/* We define a series of macros because each function must do exactly the
   same thing in certain places.  We use the macros to ensure that everything
   is done correctly */

#define CONTINUE_DECLS \
  int cont_extent = 0, cont_offset = 0, cont_size = 0;   \
  void * buffer = 0

#define CHECK_CE                          \
      {cont_extent = isonum_733(rr->u.CE.extent); \
      cont_offset = isonum_733(rr->u.CE.offset); \
      cont_size = isonum_733(rr->u.CE.size);}

#define SETUP_ROCK_RIDGE(DE,CHR,LEN)                                    \
  {LEN= sizeof(struct iso_directory_record) + DE->name_len[0];    \
  if(LEN & 1) LEN++;                                  \
  CHR = ((unsigned char *) DE) + LEN;                       \
  LEN = *((unsigned char *) DE) - LEN;}

#define MAYBE_CONTINUE(LABEL) \
  {if (buffer) free(buffer); \
  if (cont_extent){ \
    buffer = malloc(cont_size); \
    if (!buffer) goto out; \
    if (iso_dev_read(buffer, cont_extent * ISOFS_BLOCK_SIZE + cont_offset, cont_size) \
        == cont_size) { \
        chr = (unsigned char *) buffer; \
        len = cont_size; \
        cont_extent = cont_size = cont_offset = 0; \
        goto LABEL; \
    }; \
    printf("Unable to read rock-ridge attributes\n");    \
  }}

int get_rock_ridge_filename(struct iso_directory_record * de,
                      char * retname,
                      struct iso_inode * inode)
{
  int len;
  unsigned char * chr;
  int retnamlen = 0, truncate=0;
  int cont_extent = 0, cont_offset = 0, cont_size = 0;
  void *buffer = 0;

  /* No rock ridge? well then... */
  if (!sb.s_rock) return 0;
  *retname = '\0';

  len = sizeof(struct iso_directory_record) + isonum_711(de->name_len);
  if (len & 1) len++;
  chr = ((unsigned char *) de) + len;
  len = *((unsigned char *) de) - len;
  {
    struct rock_ridge * rr;
    int sig;
    
  repeat:
    while (len > 1){ /* There may be one byte for padding somewhere */
      rr = (struct rock_ridge *) chr;
      if (rr->len == 0) break; /* Something got screwed up here */

      sig = (chr[0] << 8) + chr[1];
      chr += rr->len; 
      len -= rr->len;

      switch(sig){
      case SIG('R','R'):
        if((rr->u.RR.flags[0] & RR_NM) == 0) goto out;
        break;
      case SIG('S','P'):
        if (rr->u.SP.magic[0] != 0xbe ||
            rr->u.SP.magic[1] != 0xef)
            goto out;
        break;
      case SIG('C','E'):
        cont_extent = isonum_733(rr->u.CE.extent);
        cont_offset = isonum_733(rr->u.CE.offset);
        cont_size = isonum_733(rr->u.CE.size);
        break;
      case SIG('N','M'):
        if (truncate) break;
        /*
       * If the flags are 2 or 4, this indicates '.' or '..'.
       * We don't want to do anything with this, because it
       * screws up the code that calls us.  We don't really
       * care anyways, since we can just use the non-RR
       * name.
       */
      if (rr->u.NM.flags & 6) {
          break;
      }

      if (rr->u.NM.flags & ~1) {
        printf("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
        break;
      };
      if((strlen(retname) + rr->len - 5) >= 254) {
          int i = 254-strlen(retname);
          strncat(retname, rr->u.NM.name, i); 
          retnamlen += i;
          truncate = 1;
          break;
      };
      strncat(retname, rr->u.NM.name, rr->len - 5);
      retnamlen += rr->len - 5;
      break;
      case SIG('R','E'):
        goto out;
      default:
        break;
      }
    };
  }
  if (buffer) free(buffer);
  if (cont_extent) { /* we had a continued record */
      buffer = malloc(cont_size);
      if (!buffer) goto out;
      if (iso_dev_read(buffer, cont_extent * ISOFS_BLOCK_SIZE + cont_offset, cont_size) 
        != cont_size) goto out;
      chr = buffer + cont_offset;
      len = cont_size;
      cont_extent = cont_size = cont_offset = 0;
      goto repeat;
  }
  return retnamlen; /* If 0, this file did not have a NM field */
 out:
  if (buffer) free(buffer);
  return 0;
}

static int parse_rock_ridge_inode(struct iso_directory_record * de,
                          struct iso_inode * inode){
  int len;
  unsigned char *chr;
  int symlink_len = 0;
  struct inode_table_entry *itp = (struct inode_table_entry *) inode;
  CONTINUE_DECLS;

#ifdef DEBUG_ROCK
  printf("parse_rock_ridge_inode(%u)\n",itp->inumber);
#endif

  if (!sb.s_rock) return 0;

  SETUP_ROCK_RIDGE(de, chr, len);
 repeat:
  {
    int sig;
    /* struct iso_inode * reloc; */
    struct rock_ridge * rr;
    int rootflag;
    
    while (len > 1){ /* There may be one byte for padding somewhere */
      rr = (struct rock_ridge *) chr;
      if (rr->len == 0) goto out; /* Something got screwed up here */
      sig = (chr[0] << 8) + chr[1];
      chr += rr->len; 
      len -= rr->len;
      
      switch(sig){
      case SIG('R','R'):
#ifdef DEBUG_ROCK
  printf("RR ");
#endif
      if((rr->u.RR.flags[0] & 
          (RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out;
      break;
      case SIG('S','P'):
#ifdef DEBUG_ROCK
  printf("SP ");
#endif
      CHECK_SP(goto out);
      break;
      case SIG('C','E'):
#ifdef DEBUG_ROCK
  printf("CE ");
#endif
      CHECK_CE;
      break;
      case SIG('E','R'):
#ifdef DEBUG_ROCK
      printf("ISO 9660 Extensions: ");
      { int p;
        for(p=0;p<rr->u.ER.len_id;p++) printf("%c",rr->u.ER.data[p]);
      };
      printf("\n");
#endif
      break;
      case SIG('P','X'):
#ifdef DEBUG_ROCK
  printf("PX ");
#endif
      itp->mode  = isonum_733(rr->u.PX.mode);
      itp->nlink = isonum_733(rr->u.PX.n_links);
      /* Ignore uid and gid. We're only a simple bootloader, after all. */
      break;
      case SIG('P','N'):
      /* Ignore device files. */
      break;
      case SIG('T','F'):
        /* create/modify/access times are uninteresting to us. */
      break;
      case SIG('S','L'):
#ifdef DEBUG_ROCK
  printf("SL ");
#endif
      {int slen;
       struct SL_component * slp;
       struct SL_component * oldslp;
       slen = rr->len - 5;
       slp = &rr->u.SL.link;
       itp->size = symlink_len;
       while (slen > 1){
         rootflag = 0;
         switch(slp->flags &~1){
         case 0:
           itp->size += slp->len;
           break;
         case 2:
           itp->size += 1;
           break;
         case 4:
           itp->size += 2;
           break;
         case 8:
           rootflag = 1;
           itp->size += 1;
           break;
         default:
           printf("Symlink component flag not implemented\n");
         };
         slen -= slp->len + 2;
         oldslp = slp;
         slp = (struct SL_component *) (((char *) slp) + slp->len + 2);

         if(slen < 2) {
           if(    ((rr->u.SL.flags & 1) != 0) 
                && ((oldslp->flags & 1) == 0) ) itp->size += 1;
           break;
         }

         /*
          * If this component record isn't continued, then append a '/'.
          */
         if(   (!rootflag)
             && ((oldslp->flags & 1) == 0) ) itp->size += 1;
       }
      }
      symlink_len = itp->size;
      break;
      case SIG('R','E'):
      printf("Attempt to read inode for relocated directory\n");
      goto out;
      case SIG('C','L'):
#ifdef DEBUG_ROCK
  printf("CL(!) ");
#endif
        /* I'm unsure as to the function of this signature.
         We'll ignore it and hope that everything will be OK.
      */
#if 0
#ifdef DEBUG
      printf("RR CL (%x)\n",inode->i_ino);
#endif
      inode->inode.first_extent = isonum_733(rr->u.CL.location);
      reloc = iso_iget(inode->i_sb,
                   (inode->u.isofs_i.i_first_extent <<
                    inode -> i_sb -> u.isofs_sb.s_log_zone_size));
      if (!reloc)
            goto out;
      inode->mode = reloc->mode;
      inode->nlink = reloc->nlink;
      inode->size = reloc->size;
      iso_iput(reloc);
#endif /* 0 */
      break;
      default:
      break;
      }
    };
  }
  MAYBE_CONTINUE(repeat);
#ifdef DEBUG_ROCK
  printf("\nparse_rock_ridge_inode(): ok\n");
#endif
  return 1;
 out:
  if(buffer) free(buffer);
#ifdef DEBUG_ROCK
  printf("\nparse_rock_ridge_inode(): failed\n");
#endif
  return 0;
}

/* Returns the name of the file that this inode is symlinked to.  This is
   in malloc memory, so we have to free it when we're done */

static char * get_rock_ridge_symlink(struct iso_inode *inode)
{
  int blocksize = ISOFS_BLOCK_SIZE;
  int blockbits = ISOFS_BLOCK_BITS;
  char * rpnt = NULL;
  unsigned char * pnt;
  struct iso_directory_record * raw_inode;
  struct inode_table_entry *itp = (struct inode_table_entry *)inode;
  CONTINUE_DECLS;
  int block, blockoffset;
  int sig;
  int rootflag;
  int len;
  unsigned char * chr, * buf = NULL;
  struct rock_ridge * rr;

#ifdef DEBUG_ROCK
  printf("get_rock_ridge_symlink(%u): link is %u bytes long\n",itp->inumber, itp->size);
#endif
  
  if (!sb.s_rock) goto out;

  block = itp->inumber >> blockbits;
  blockoffset = itp->inumber & (blocksize - 1);

  buf=malloc(blocksize);

  if (iso_dev_read(buf, block << blockbits, blocksize) != blocksize)
        goto out_noread;

  pnt = ((unsigned char *) buf) + blockoffset;
  
  raw_inode = ((struct iso_directory_record *) pnt);
  
  /*
   * If we go past the end of the buffer, there is some sort of error.
   */
  if (blockoffset + *pnt > blocksize)
      goto out_bad_span;
  
  /* Now test for possible Rock Ridge extensions which will override some of
     these numbers in the inode structure. */
  
  SETUP_ROCK_RIDGE(raw_inode, chr, len);

 repeat:
  while (len > 1){ /* There may be one byte for padding somewhere */
    rr = (struct rock_ridge *) chr;
    if (rr->len == 0) goto out; /* Something got screwed up here */
    sig = (chr[0] << 8) + chr[1];
    chr += rr->len; 
    len -= rr->len;
    
#ifdef DEBUG_ROCK
    printf("%c%c ",chr[0],chr[1]);
#endif
    switch(sig){
    case SIG('R','R'):
      if((rr->u.RR.flags[0] & RR_SL) == 0) goto out;
      break;
    case SIG('S','P'):
      CHECK_SP(goto out);
      break;
    case SIG('S','L'):
      {int slen;
       struct SL_component * oldslp;
       struct SL_component * slp;
       slen = rr->len - 5;
       slp = &rr->u.SL.link;
       while (slen > 1){
       if (!rpnt){
         rpnt = (char *) malloc (itp->size +1);
         if (!rpnt) goto out;
         *rpnt = 0;
       };
       rootflag = 0;
       switch(slp->flags &~1){
       case 0:
         strncat(rpnt,slp->text, slp->len);
         break;
       case 2:
         strcat(rpnt,".");
         break;
       case 4:
         strcat(rpnt,"..");
         break;
       case 8:
         rootflag = 1;
         strcat(rpnt,"/");
         break;
       default:
#ifdef DEBUG_ROCK
         printf("Symlink component flag not implemented (%d)\n",slen);
#endif
       };
       slen -= slp->len + 2;
       oldslp = slp;
       slp = (struct SL_component *) (((char *) slp) + slp->len + 2);

       if(slen < 2) {
         /*
          * If there is another SL record, and this component record
          * isn't continued, then add a slash.
          */
         if(    ((rr->u.SL.flags & 1) != 0) 
             && ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/");
         break;
       }

       /*
        * If this component record isn't continued, then append a '/'.
        */
       if(   (!rootflag)
          && ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/");

       };
       break;
     case SIG('C','E'):
       CHECK_CE; /* This tells is if there is a continuation record */
       break;
     default:
       break;
     }
    };
  };
  MAYBE_CONTINUE(repeat);
  
 out_freebh:
#ifdef DEBUG_ROCK
  printf("\nget_rock_ridge_symlink() exiting\n");
#endif
        if (buf)
            free(buf);
      return rpnt;

      /* error exit from macro */
out:
#ifdef DEBUG_ROCK
      printf("abort");
#endif
      if(buffer)
            free(buffer);
      if(rpnt)
            free(rpnt);
      rpnt = NULL;
      goto out_freebh;
out_noread:
      printf("unable to read block");
      goto out_freebh;
out_bad_span:
      printf("symlink spans iso9660 blocks\n");
      goto out_freebh;
}




Generated by  Doxygen 1.6.0   Back to index