summaryrefslogtreecommitdiff
path: root/package/aboot/src/lib/isolib.c
diff options
context:
space:
mode:
authorWaldemar Brodkorb <wbx@openadk.org>2017-12-31 18:47:16 +0100
committerWaldemar Brodkorb <wbx@openadk.org>2017-12-31 18:47:25 +0100
commit3a96085b999220c4da0c5ef7d1f7ba26b9ddfb98 (patch)
tree77f1445aae2e6be5135594e95986b3278bbc061c /package/aboot/src/lib/isolib.c
parentcc28479164b8dc8afd4310716da32f16022f5974 (diff)
dec-multia: make netboot possible, add aboot bootloader
Diffstat (limited to 'package/aboot/src/lib/isolib.c')
-rw-r--r--package/aboot/src/lib/isolib.c1590
1 files changed, 1590 insertions, 0 deletions
diff --git a/package/aboot/src/lib/isolib.c b/package/aboot/src/lib/isolib.c
new file mode 100644
index 000000000..f0d186e30
--- /dev/null
+++ b/package/aboot/src/lib/isolib.c
@@ -0,0 +1,1590 @@
+/*
+ * 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-)
+ */
+#include <linux/stat.h>
+#include <sys/types.h>
+
+#include "string.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);
+ /* HK: iso_iget expects an "int" but root_inode is "long" ?? */
+ 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 0
+//See http://www.y-adagio.com/public/standards/iso_cdromr/sect_1.htm
+//Section 4.16 and 4.17 for explanation why this check is invalid
+ if (isonum_723 (h_pri->volume_set_size) != 1) {
+ printf("Multi-volume disks not (yet) supported.\n");
+ return -1;
+ };
+#endif
+ 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 0
+//See http://www.y-adagio.com/public/standards/iso_cdromr/sect_1.htm
+//Section 4.16 and 4.17 for explanation why this check is invalid
+ if (isonum_723 (pri->volume_set_size) != 1) {
+ printf("Multi-volume disks not (yet) supported.\n");
+ return -1;
+ }
+#endif
+ 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;
+ /* HK: isonum_733 returns an "int" but root_inode is a long ? */
+ 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);
+/* HK: iso_iget expects an "int" but root_inode is "long" ?? */
+ 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
+ break;
+ };
+ 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;
+}
+
+
+