diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:16 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:25 +0100 |
commit | 3a96085b999220c4da0c5ef7d1f7ba26b9ddfb98 (patch) | |
tree | 77f1445aae2e6be5135594e95986b3278bbc061c /package/aboot/src/lib/isolib.c | |
parent | cc28479164b8dc8afd4310716da32f16022f5974 (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.c | 1590 |
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; +} + + + |