summaryrefslogtreecommitdiffstats
path: root/fs/isofs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/isofs')
-rw-r--r--fs/isofs/Makefile30
-rw-r--r--fs/isofs/dir.c258
-rw-r--r--fs/isofs/file.c260
-rw-r--r--fs/isofs/inode.c707
-rw-r--r--fs/isofs/namei.c268
-rw-r--r--fs/isofs/rock.c523
-rw-r--r--fs/isofs/rock.h111
-rw-r--r--fs/isofs/symlink.c106
-rw-r--r--fs/isofs/util.c131
9 files changed, 2394 insertions, 0 deletions
diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile
new file mode 100644
index 000000000..a780af479
--- /dev/null
+++ b/fs/isofs/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the linux isofs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= namei.o inode.o file.o dir.o util.o rock.o symlink.o
+
+isofs.o: $(OBJS)
+ $(LD) -r -o isofs.o $(OBJS)
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
new file mode 100644
index 000000000..b1934db04
--- /dev/null
+++ b/fs/isofs/dir.c
@@ -0,0 +1,258 @@
+/*
+ * linux/fs/isofs/dir.c
+ *
+ * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * isofs directory handling functions
+ */
+
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+#include <linux/fs.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+3) & ~3)
+
+static int isofs_readdir(struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations isofs_dir_operations = {
+ NULL, /* lseek - default */
+ NULL, /* read */
+ NULL, /* write - bad */
+ isofs_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations isofs_dir_inode_operations = {
+ &isofs_dir_operations, /* default directory file-ops */
+ NULL, /* create */
+ isofs_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ isofs_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int isofs_readdir(struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
+ unsigned int block,offset,i, j;
+ char c = 0;
+ int inode_number;
+ struct buffer_head * bh;
+ void * cpnt = NULL;
+ unsigned int old_offset;
+ int dlen, rrflag;
+ int high_sierra = 0;
+ char * dpnt, *dpnt1;
+ struct iso_directory_record * de;
+
+ dpnt1 = NULL;
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+
+ offset = filp->f_pos & (bufsize - 1);
+ block = isofs_bmap(inode,filp->f_pos>>bufbits);
+
+ if(!block) return 0;
+
+ if(!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size)))
+ return 0;
+
+ while (filp->f_pos < inode->i_size) {
+#ifdef DEBUG
+ printk("Block, offset, f_pos: %x %x %x\n",
+ block, offset, filp->f_pos);
+#endif
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ inode_number = (block << bufbits) + (offset & (bufsize - 1));
+
+ /* If the length byte is zero, we should move on to the next
+ CDROM sector. If we are at the end of the directory, we
+ kick out of the while loop. */
+
+ if (*((unsigned char *) de) == 0) {
+ brelse(bh);
+ offset = 0;
+ filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1))
+ + ISOFS_BLOCK_SIZE);
+ block = isofs_bmap(inode,(filp->f_pos)>>bufbits);
+ if (!block
+ || !(bh = breada(inode->i_dev, block, bufsize, filp->f_pos,
+ inode->i_size)))
+ return 0;
+ continue;
+ }
+
+ /* Make sure that the entire directory record is in the
+ current bh block.
+ If not, we malloc a buffer, and put the two halves together,
+ so that we can cleanly read the block */
+
+ old_offset = offset;
+ offset += *((unsigned char *) de);
+ filp->f_pos += *((unsigned char *) de);
+
+ if (offset > bufsize) {
+ unsigned int frag1;
+ frag1 = bufsize - old_offset;
+ cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL);
+ if (!cpnt) return 0;
+ memcpy(cpnt, bh->b_data + old_offset, frag1);
+ de = (struct iso_directory_record *) ((char *)cpnt);
+ brelse(bh);
+ offset = filp->f_pos & (bufsize - 1);
+ block = isofs_bmap(inode,(filp->f_pos)>> bufbits);
+ if (!block
+ || !(bh = breada(inode->i_dev, block, bufsize,
+ filp->f_pos, inode->i_size))) {
+ kfree(cpnt);
+ return 0;
+ };
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ }
+
+ /* Handle the case of the '.' directory */
+
+ rrflag = 0;
+ i = 1;
+ if (de->name_len[0] == 1 && de->name[0] == 0) {
+ put_fs_byte('.',dirent->d_name);
+ inode_number = inode->i_ino;
+ dpnt = ".";
+ }
+
+ /* Handle the case of the '..' directory */
+
+ else if (de->name_len[0] == 1 && de->name[0] == 1) {
+ put_fs_byte('.',dirent->d_name);
+ put_fs_byte('.',dirent->d_name+1);
+ i = 2;
+ dpnt = "..";
+ if((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino)
+ inode_number = inode->u.isofs_i.i_backlink;
+ else
+ inode_number = inode->i_ino;
+
+ /* This should never happen, but who knows. Try to be forgiving */
+ if(inode_number == -1) {
+ inode_number =
+ isofs_lookup_grandparent(inode,
+ find_rock_ridge_relocation(de, inode));
+ if(inode_number == -1){ /* Should never happen */
+ printk("Backlink not properly set.\n");
+ goto out;
+ };
+ }
+ }
+
+ /* Handle everything else. Do name translation if there
+ is no Rock Ridge NM field. */
+
+ else {
+ /* Do not report hidden or associated files */
+ high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
+ if (de->flags[-high_sierra] & 5) {
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ };
+ continue;
+ }
+ dlen = de->name_len[0];
+ dpnt = de->name;
+ i = dlen;
+ rrflag = get_rock_ridge_filename(de, &dpnt, &dlen, inode);
+ if (rrflag) {
+ if (rrflag == -1) { /* This is a rock ridge reloc dir */
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ };
+ continue;
+ };
+ i = dlen;
+ }
+ else
+ if(inode->i_sb->u.isofs_sb.s_mapping == 'n') {
+ dpnt1 = dpnt;
+ dpnt = kmalloc(dlen, GFP_KERNEL);
+ if (!dpnt) goto out;
+ for (i = 0; i < dlen && i < NAME_MAX; i++) {
+ if (!(c = dpnt1[i])) break;
+ if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */
+ if (c == '.' && i == dlen-3 && de->name[i+1] == ';' && de->name[i+2] == '1')
+ break; /* Drop trailing '.;1' (ISO9660:1988 7.5.1 requires period) */
+ if (c == ';' && i == dlen-2 && de->name[i+1] == '1')
+ break; /* Drop trailing ';1' */
+ if (c == ';') c = '.'; /* Convert remaining ';' to '.' */
+ dpnt[i] = c;
+ }
+ }
+ for(j=0; j<i; j++)
+ put_fs_byte(dpnt[j],j+dirent->d_name); /* And save it */
+ if(dpnt1) {
+ kfree(dpnt);
+ dpnt = dpnt1;
+ }
+
+ dcache_add(inode, dpnt, i, inode_number);
+ };
+#if 0
+ printk("Nchar: %d\n",i);
+#endif
+
+ if (rrflag) kfree(dpnt);
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ };
+
+ if (i) {
+ put_fs_long(inode_number, &dirent->d_ino);
+ put_fs_byte(0,i+dirent->d_name);
+ put_fs_word(i,&dirent->d_reclen);
+ brelse(bh);
+ return ROUND_UP(NAME_OFFSET(dirent) + i + 1);
+ }
+ }
+ /* We go here for any condition we cannot handle. We also drop through
+ to here at the end of the directory. */
+ out:
+ if (cpnt)
+ kfree(cpnt);
+ brelse(bh);
+ return 0;
+}
+
+
+
diff --git a/fs/isofs/file.c b/fs/isofs/file.c
new file mode 100644
index 000000000..ee0877d7b
--- /dev/null
+++ b/fs/isofs/file.c
@@ -0,0 +1,260 @@
+/*
+ * linux/fs/isofs/file.c
+ *
+ * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * isofs regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#include <linux/dirent.h>
+
+#define NBUF 32
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#include <linux/fs.h>
+#include <linux/iso_fs.h>
+
+static int isofs_file_read(struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the isofs filesystem.
+ */
+static struct file_operations isofs_file_operations = {
+ NULL, /* lseek - default */
+ isofs_file_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations isofs_file_inode_operations = {
+ &isofs_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ isofs_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+/* This is a heuristic to determine if a file is text of binary. If it
+ * is text, then we translate all 0x0d characters to spaces. If the 0x0d
+ * character is not preceded or followed by a 0x0a, then we turn it into
+ * a 0x0a. A control-Z is also turned into a linefeed.
+ */
+
+static inline void unixify_to_fs(char * outbuf, char * buffer, int chars,
+ int mode)
+{
+ char outchar;
+
+ while(chars--){
+ outchar = *buffer;
+ if(outchar == 0x1a) outchar = 0x0a;
+ if(outchar == 0x0d){
+ if(mode == ISOFS_FILE_TEXT_M) outchar = 0x0a;
+ if(mode == ISOFS_FILE_TEXT) outchar = ' ';
+ }
+ put_fs_byte(outchar, outbuf++);
+ buffer++;
+ }
+}
+
+/*This function determines if a given file has a DOS-like text format or not*/
+
+static void isofs_determine_filetype(struct inode * inode)
+{
+ int block;
+ int result, i;
+ struct buffer_head * bh;
+ unsigned char * pnt;
+
+ block = isofs_bmap(inode,0);
+ if (block && (bh = bread(inode->i_dev,block, ISOFS_BUFFER_SIZE(inode)))) {
+ pnt = (unsigned char *) bh->b_data;
+ result = ISOFS_FILE_TEXT_M;
+ for(i=0;i<(inode->i_size < ISOFS_BUFFER_SIZE(inode) ? inode->i_size : ISOFS_BUFFER_SIZE(inode));
+ i++,pnt++){
+ if(*pnt & 0x80) {result = ISOFS_FILE_BINARY; break;};
+ if(*pnt >= 0x20 || *pnt == 0x1a) continue;
+ if(*pnt == 0x0a) {result = ISOFS_FILE_TEXT; continue;};
+ if(*pnt >= 0x9 && *pnt <= 0x0d) continue;
+ result = ISOFS_FILE_BINARY;
+ break;
+ }
+ brelse(bh);
+ inode->u.isofs_i.i_file_format = result;
+ }
+}
+
+static int isofs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ int read,left,chars;
+ int block, blocks, offset, total_blocks;
+ int bhrequest;
+ int ra_blocks, max_block, nextblock;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+
+ if (!inode) {
+ printk("isofs_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
+ printk("isofs_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ if (inode->u.isofs_i.i_file_format == ISOFS_FILE_UNKNOWN)
+ isofs_determine_filetype(inode);
+ if (filp->f_pos > inode->i_size)
+ left = 0;
+ else
+ left = inode->i_size - filp->f_pos;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ block = filp->f_pos >> ISOFS_BUFFER_BITS(inode);
+ offset = (inode->u.isofs_i.i_first_extent + filp->f_pos)
+ & (ISOFS_BUFFER_SIZE(inode)-1);
+ blocks = (left + offset + ISOFS_BUFFER_SIZE(inode) - 1) / ISOFS_BUFFER_SIZE(inode);
+ bhb = bhe = buflist;
+
+ ra_blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
+ if(ra_blocks > blocks) blocks = ra_blocks;
+
+ /*
+ * this is for stopping read ahead at EOF. It's important for
+ * reading PhotoCD's, because they have many small data tracks instead
+ * of one big. And between two data-tracks are some unreadable sectors.
+ * A read ahead after a EOF may try to read such an unreadable sector.
+ * kraxel@cs.tu-berlin.de (Gerd Knorr)
+ */
+ total_blocks = (inode->i_size + (1 << ISOFS_BUFFER_BITS(inode)) - 1)
+ >> ISOFS_BUFFER_BITS(inode);
+ if (block + blocks > total_blocks)
+ blocks = total_blocks - block;
+
+ max_block = (inode->i_size + BLOCK_SIZE - 1)/BLOCK_SIZE;
+ nextblock = -1;
+
+ /* We do this in a two stage process. We first try and request
+ as many blocks as we can, then we wait for the first one to
+ complete, and then we try and wrap up as many as are actually
+ done. This routine is rather generic, in that it can be used
+ in a filesystem by substituting the appropriate function in
+ for getblk.
+
+ This routine is optimized to make maximum use of the various
+ buffers and caches. */
+
+ do {
+ bhrequest = 0;
+ while (blocks) {
+ int uptodate;
+ --blocks;
+ *bhb = getblk(inode->i_dev,isofs_bmap(inode, block++), ISOFS_BUFFER_SIZE(inode));
+ uptodate = 1;
+ if (*bhb && !(*bhb)->b_uptodate) {
+ uptodate = 0;
+ bhreq[bhrequest++] = *bhb;
+ };
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ /* If the block we have on hand is uptodate, go ahead
+ and complete processing. */
+ if(uptodate) break;
+
+ if (bhb == bhe)
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest)
+ ll_rw_block(READ, bhrequest, bhreq);
+
+ do{ /* Finish off all I/O that has actually completed */
+ if (*bhe) {/* test for valid buffer */
+ wait_on_buffer(*bhe);
+ if (!(*bhe)->b_uptodate) {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ left = 0;
+ break;
+ }
+ }
+
+ if (left < ISOFS_BUFFER_SIZE(inode) - offset)
+ chars = left;
+ else
+ chars = ISOFS_BUFFER_SIZE(inode) - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (*bhe) {
+ if (inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT ||
+ inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT_M)
+ unixify_to_fs(buf, offset+(*bhe)->b_data, chars,
+ inode->u.isofs_i.i_file_format);
+ else
+ memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ brelse(*bhe);
+ buf += chars;
+ } else {
+ while (chars-->0)
+ put_fs_byte(0,buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while( bhe != bhb && (*bhe == 0 || !(*bhe)->b_lock) &&
+ (left > 0));
+ } while (left > 0);
+
+/* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ if (*bhe) brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ };
+
+ filp->f_reada = 1;
+
+ if (!read)
+ return -EIO;
+ return read;
+}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
new file mode 100644
index 000000000..c1754e337
--- /dev/null
+++ b/fs/isofs/inode.c
@@ -0,0 +1,707 @@
+/*
+ * linux/fs/isofs/inode.c
+ *
+ * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#ifdef LEAK_CHECK
+static int check_malloc = 0;
+static int check_bread = 0;
+#endif
+
+void isofs_put_super(struct super_block *sb)
+{
+ lock_super(sb);
+
+#ifdef LEAK_CHECK
+ printk("Outstanding mallocs:%d, outstanding buffers: %d\n",
+ check_malloc, check_bread);
+#endif
+ sb->s_dev = 0;
+ unlock_super(sb);
+ return;
+}
+
+static struct super_operations isofs_sops = {
+ isofs_read_inode,
+ NULL, /* notify_change */
+ NULL, /* write_inode */
+ NULL, /* put_inode */
+ isofs_put_super,
+ NULL, /* write_super */
+ isofs_statfs,
+ NULL
+};
+
+struct iso9660_options{
+ char map;
+ char rock;
+ char cruft;
+ unsigned char conversion;
+ unsigned int blocksize;
+ gid_t gid;
+ uid_t uid;
+};
+
+static int parse_options(char *options, struct iso9660_options * popt)
+{
+ char *this_char,*value;
+
+ popt->map = 'n';
+ popt->rock = 'y';
+ popt->cruft = 'n';
+ popt->conversion = 'a';
+ popt->blocksize = 1024;
+ popt->gid = 0;
+ popt->uid = 0;
+ if (!options) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if (strncmp(this_char,"norock",6) == 0) {
+ popt->rock = 'n';
+ continue;
+ };
+ if (strncmp(this_char,"cruft",5) == 0) {
+ popt->cruft = 'y';
+ continue;
+ };
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"map") && value) {
+ if (value[0] && !value[1] && strchr("on",*value))
+ popt->map = *value;
+ else if (!strcmp(value,"off")) popt->map = 'o';
+ else if (!strcmp(value,"normal")) popt->map = 'n';
+ else return 0;
+ }
+ else if (!strcmp(this_char,"conv") && value) {
+ if (value[0] && !value[1] && strchr("btma",*value))
+ popt->conversion = *value;
+ else if (!strcmp(value,"binary")) popt->conversion = 'b';
+ else if (!strcmp(value,"text")) popt->conversion = 't';
+ else if (!strcmp(value,"mtext")) popt->conversion = 'm';
+ else if (!strcmp(value,"auto")) popt->conversion = 'a';
+ else return 0;
+ }
+ else if (value &&
+ (!strcmp(this_char,"block") ||
+ !strcmp(this_char,"uid") ||
+ !strcmp(this_char,"gid"))) {
+ char * vpnt = value;
+ unsigned int ivalue;
+ ivalue = 0;
+ while(*vpnt){
+ if(*vpnt < '0' || *vpnt > '9') break;
+ ivalue = ivalue * 10 + (*vpnt - '0');
+ vpnt++;
+ };
+ if (*vpnt) return 0;
+ switch(*this_char) {
+ case 'b':
+ if (ivalue != 1024 && ivalue != 2048) return 0;
+ popt->blocksize = ivalue;
+ break;
+ case 'u':
+ popt->uid = ivalue;
+ break;
+ case 'g':
+ popt->gid = ivalue;
+ break;
+ }
+ }
+ else return 0;
+ }
+ return 1;
+}
+
+struct super_block *isofs_read_super(struct super_block *s,void *data,
+ int silent)
+{
+ struct buffer_head *bh;
+ int iso_blknum;
+ unsigned int blocksize_bits;
+ int high_sierra;
+ int dev=s->s_dev;
+ 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;
+
+ struct iso9660_options opt;
+
+ if (!parse_options((char *) data,&opt)) {
+ s->s_dev = 0;
+ return NULL;
+ }
+
+#if 0
+ printk("map = %c\n", opt.map);
+ printk("rock = %c\n", opt.rock);
+ printk("cruft = %c\n", opt.cruft);
+ printk("conversion = %c\n", opt.conversion);
+ printk("blocksize = %d\n", opt.blocksize);
+ printk("gid = %d\n", opt.gid);
+ printk("uid = %d\n", opt.uid);
+#endif
+
+ blocksize_bits = 0;
+ {
+ int i = opt.blocksize;
+ while (i != 1){
+ blocksize_bits++;
+ i >>=1;
+ };
+ };
+ set_blocksize(dev, opt.blocksize);
+
+ lock_super(s);
+
+ s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
+
+ for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
+ if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) {
+ s->s_dev=0;
+ printk("isofs_read_super: bread failed, dev 0x%x iso_blknum %d\n",
+ dev, iso_blknum);
+ unlock_super(s);
+ return NULL;
+ }
+
+ vdp = (struct iso_volume_descriptor *)bh->b_data;
+ hdp = (struct hs_volume_descriptor *)bh->b_data;
+
+
+ if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
+ if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
+ goto out;
+ if (isonum_711 (hdp->type) == ISO_VD_END)
+ goto out;
+
+ s->u.isofs_sb.s_high_sierra = 1;
+ high_sierra = 1;
+ opt.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)
+ goto out;
+ if (isonum_711 (vdp->type) == ISO_VD_END)
+ goto out;
+
+ pri = (struct iso_primary_descriptor *)vdp;
+ break;
+ };
+
+ brelse(bh);
+ }
+ if(iso_blknum == 100) {
+ if (!silent)
+ printk("Unable to identify CD-ROM format.\n");
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
+ };
+
+
+ if(high_sierra){
+ rootp = (struct iso_directory_record *) h_pri->root_directory_record;
+ if (isonum_723 (h_pri->volume_set_size) != 1) {
+ printk("Multi-volume disks not (yet) supported.\n");
+ goto out;
+ };
+ s->u.isofs_sb.s_nzones = isonum_733 (h_pri->volume_space_size);
+ s->u.isofs_sb.s_log_zone_size = isonum_723 (h_pri->logical_block_size);
+ s->u.isofs_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) {
+ printk("Multi-volume disks not (yet) supported.\n");
+ goto out;
+ };
+ s->u.isofs_sb.s_nzones = isonum_733 (pri->volume_space_size);
+ s->u.isofs_sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
+ s->u.isofs_sb.s_max_size = isonum_733(pri->volume_space_size);
+ }
+
+ s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */
+
+ /* RDE: convert log zone size to bit shift */
+
+ switch (s -> u.isofs_sb.s_log_zone_size)
+ { case 512: s -> u.isofs_sb.s_log_zone_size = 9; break;
+ case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
+ case 2048: s -> u.isofs_sb.s_log_zone_size = 11; break;
+
+ default:
+ printk("Bad logical zone size %ld\n", s -> u.isofs_sb.s_log_zone_size);
+ goto out;
+ }
+
+ /* RDE: data zone now byte offset! */
+
+ s->u.isofs_sb.s_firstdatazone = (isonum_733( rootp->extent)
+ << s -> u.isofs_sb.s_log_zone_size);
+ s->s_magic = ISOFS_SUPER_MAGIC;
+
+ /* 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) */
+
+ s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */;
+
+ brelse(bh);
+
+ printk("Max size:%ld Log zone size:%ld\n",
+ s->u.isofs_sb.s_max_size,
+ 1UL << s->u.isofs_sb.s_log_zone_size);
+ printk("First datazone:%ld Root inode number %d\n",
+ s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
+ isonum_733 (rootp->extent) << s -> u.isofs_sb.s_log_zone_size);
+ if(high_sierra) printk("Disc in High Sierra format.\n");
+ unlock_super(s);
+ /* set up enough so that it can read an inode */
+
+ s->s_dev = dev;
+ s->s_op = &isofs_sops;
+ s->u.isofs_sb.s_mapping = opt.map;
+ s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 1 : 0);
+ s->u.isofs_sb.s_conversion = opt.conversion;
+ s->u.isofs_sb.s_cruft = opt.cruft;
+ s->u.isofs_sb.s_uid = opt.uid;
+ s->u.isofs_sb.s_gid = opt.gid;
+ s->s_blocksize = opt.blocksize;
+ s->s_blocksize_bits = blocksize_bits;
+ s->s_mounted = iget(s, isonum_733 (rootp->extent) << s -> u.isofs_sb.s_log_zone_size);
+ unlock_super(s);
+
+ if (!(s->s_mounted)) {
+ s->s_dev=0;
+ printk("get root inode failed\n");
+ return NULL;
+ }
+
+ if(!check_disk_change(s->s_dev)) return s;
+ out: /* Kick out for various error conditions */
+ brelse(bh);
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
+}
+
+void isofs_statfs (struct super_block *sb, struct statfs *buf)
+{
+ put_fs_long(ISOFS_SUPER_MAGIC, &buf->f_type);
+ put_fs_long(1 << ISOFS_BLOCK_BITS, &buf->f_bsize);
+ put_fs_long(sb->u.isofs_sb.s_nzones, &buf->f_blocks);
+ put_fs_long(0, &buf->f_bfree);
+ put_fs_long(0, &buf->f_bavail);
+ put_fs_long(sb->u.isofs_sb.s_ninodes, &buf->f_files);
+ put_fs_long(0, &buf->f_ffree);
+ put_fs_long(NAME_MAX, &buf->f_namelen);
+ /* Don't know what value to put in buf->f_fsid */
+}
+
+int isofs_bmap(struct inode * inode,int block)
+{
+
+ if (block<0) {
+ printk("_isofs_bmap: block<0");
+ return 0;
+ }
+ return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
+}
+
+void isofs_read_inode(struct inode * inode)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ struct buffer_head * bh;
+ struct iso_directory_record * raw_inode;
+ unsigned char *pnt = NULL;
+ void *cpnt = NULL;
+ int high_sierra;
+ int block;
+ int i;
+
+ block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
+ if (!(bh=bread(inode->i_dev,block, bufsize))) {
+ printk("unable to read i-node block");
+ goto fail;
+ }
+
+ pnt = ((unsigned char *) bh->b_data
+ + (inode->i_ino & (bufsize - 1)));
+ raw_inode = ((struct iso_directory_record *) pnt);
+ high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
+
+ if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
+ int frag1, offset;
+
+ offset = (inode->i_ino & (bufsize - 1));
+ frag1 = bufsize - offset;
+ cpnt = kmalloc(*pnt,GFP_KERNEL);
+ if (cpnt == NULL) {
+ printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
+ brelse(bh);
+ goto fail;
+ }
+ memcpy(cpnt, bh->b_data + offset, frag1);
+ brelse(bh);
+ if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+ kfree(cpnt);
+ printk("unable to read i-node block");
+ goto fail;
+ }
+ offset += *pnt - bufsize;
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ pnt = ((unsigned char *) cpnt);
+ raw_inode = ((struct iso_directory_record *) pnt);
+ }
+
+ inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */
+ inode->i_nlink = 1;
+
+ if (raw_inode->flags[-high_sierra] & 2) {
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ inode->i_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 {
+ inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */
+ inode->i_nlink = 1;
+ inode->i_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] == ';')
+ inode->i_mode |= S_IXUGO; /* execute permission */
+ }
+ inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
+ inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
+ inode->i_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((inode->i_size < 0 || inode->i_size > 700000000) &&
+ inode->i_sb->u.isofs_sb.s_cruft == 'n') {
+ printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n");
+ inode->i_sb->u.isofs_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(inode->i_sb->u.isofs_sb.s_cruft == 'y' &&
+ inode->i_size & 0xff000000){
+/* printk("Illegal format on cdrom. Pester manufacturer.\n"); */
+ inode->i_size &= 0x00ffffff;
+ }
+
+ if (raw_inode->interleave[0]) {
+ printk("Interleaved files not (yet) supported.\n");
+ inode->i_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){
+ printk("File unit size != 0 for ISO file (%ld).\n",inode->i_ino);
+ }
+
+ /* I have no idea what other flag bits are used for, so
+ we will flag it for now */
+#ifdef DEBUG
+ if((raw_inode->flags[-high_sierra] & ~2)!= 0){
+ printk("Unusual flag settings for ISO file (%ld %x).\n",
+ inode->i_ino, raw_inode->flags[-high_sierra]);
+ }
+#endif
+
+#ifdef DEBUG
+ printk("Get inode %d: %d %d: %d\n",inode->i_ino, block,
+ ((int)pnt) & 0x3ff, inode->i_size);
+#endif
+
+ inode->i_mtime = inode->i_atime = inode->i_ctime =
+ iso_date(raw_inode->date, high_sierra);
+
+ inode->u.isofs_i.i_first_extent = (isonum_733 (raw_inode->extent) +
+ isonum_711 (raw_inode->ext_attr_length))
+ << inode -> i_sb -> u.isofs_sb.s_log_zone_size;
+
+ inode->u.isofs_i.i_backlink = 0xffffffff; /* Will be used for previous directory */
+ switch (inode->i_sb->u.isofs_sb.s_conversion){
+ case 'a':
+ inode->u.isofs_i.i_file_format = ISOFS_FILE_UNKNOWN; /* File type */
+ break;
+ case 'b':
+ inode->u.isofs_i.i_file_format = ISOFS_FILE_BINARY; /* File type */
+ break;
+ case 't':
+ inode->u.isofs_i.i_file_format = ISOFS_FILE_TEXT; /* File type */
+ break;
+ case 'm':
+ inode->u.isofs_i.i_file_format = ISOFS_FILE_TEXT_M; /* File type */
+ break;
+ }
+
+/* Now test for possible Rock Ridge extensions which will override some of
+ these numbers in the inode structure. */
+
+ if (!high_sierra)
+ parse_rock_ridge_inode(raw_inode, inode);
+
+#ifdef DEBUG
+ printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent);
+#endif
+ brelse(bh);
+
+ inode->i_op = NULL;
+
+ /* A volume number of 0 is nonsense. Disable checking if we see
+ this */
+ if (inode->i_sb->u.isofs_sb.s_cruft == 'n' &&
+ isonum_723 (raw_inode->volume_sequence_number) == 0) {
+ printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n");
+ inode->i_sb->u.isofs_sb.s_cruft = 'y';
+ }
+
+ if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
+ isonum_723 (raw_inode->volume_sequence_number) != 1) {
+ printk("Multi volume CD somehow got mounted.\n");
+ } else {
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &isofs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &isofs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &isofs_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+ }
+ if (cpnt) {
+ kfree (cpnt);
+ cpnt = NULL;
+ }
+ return;
+ fail:
+ /* With a data error we return this information */
+ inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ inode->u.isofs_i.i_first_extent = 0;
+ inode->u.isofs_i.i_backlink = 0xffffffff;
+ inode->i_size = 0;
+ inode->i_nlink = 1;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_mode = S_IFREG; /*Regular file, no one gets to read*/
+ inode->i_op = NULL;
+ return;
+}
+
+/* There are times when we need to know the inode number of a parent of
+ a particular directory. When control passes through a routine that
+ has access to the parent information, it fills it into the inode structure,
+ but sometimes the inode gets flushed out of the queue, and someone
+ remembers the number. When they try to open up again, we have lost
+ the information. The '..' entry on the disc points to the data area
+ for a particular inode, so we can follow these links back up, but since
+ we do not know the inode number, we do not actually know how large the
+ directory is. The disc is almost always correct, and there is
+ enough error checking on the drive itself, but an open ended search
+ makes me a little nervous.
+
+ The bsd iso filesystem uses the extent number for an inode, and this
+ would work really nicely for us except that the read_inode function
+ would not have any clean way of finding the actual directory record
+ that goes with the file. If we had such info, then it would pay
+ to change the inode numbers and eliminate this function.
+*/
+
+int isofs_lookup_grandparent(struct inode * parent, int extent)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(parent);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(parent);
+ unsigned int block,offset;
+ int parent_dir, inode_number;
+ int old_offset;
+ void * cpnt = NULL;
+ int result;
+ int directory_size;
+ struct buffer_head * bh;
+ struct iso_directory_record * de;
+
+ offset = 0;
+ block = extent << (ISOFS_BLOCK_BITS - bufbits);
+ if (!(bh = bread(parent->i_dev, block, bufsize))) return -1;
+
+ while (1 == 1) {
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ if (*((unsigned char *) de) == 0)
+ {
+ brelse(bh);
+ return -1;
+ }
+
+ offset += *((unsigned char *) de);
+
+ if (offset >= bufsize)
+ {
+ printk(".. Directory not in first block"
+ " of directory.\n");
+ brelse(bh);
+ return -1;
+ }
+
+ if (de->name_len[0] == 1 && de->name[0] == 1)
+ {
+ parent_dir = find_rock_ridge_relocation(de, parent);
+ directory_size = isonum_733 (de->size);
+ brelse(bh);
+ break;
+ }
+ }
+#ifdef DEBUG
+ printk("Parent dir:%x\n",parent_dir);
+#endif
+ /* Now we know the extent where the parent dir starts on. */
+
+ result = -1;
+
+ offset = 0;
+ block = parent_dir << (ISOFS_BLOCK_BITS - bufbits);
+ if (!block || !(bh = bread(parent->i_dev,block, bufsize)))
+ return -1;
+
+ for(;;)
+ {
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ inode_number = (block << bufbits)+(offset & (bufsize - 1));
+
+ /* If the length byte is zero, we should move on to the next
+ CDROM sector. If we are at the end of the directory, we
+ kick out of the while loop. */
+
+ if (*((unsigned char *) de) == 0)
+ {
+ brelse(bh);
+ offset = 0;
+ block++;
+ directory_size -= bufsize;
+ if(directory_size < 0) return -1;
+ if((block & 1) && (ISOFS_BLOCK_BITS - bufbits))
+ return -1;
+ if (!block
+ || !(bh = bread(parent->i_dev,block, bufsize)))
+ return -1;
+ continue;
+ }
+
+ /* Make sure that the entire directory record is in the current
+ bh block. If not, we malloc a buffer, and put the two
+ halves together, so that we can cleanly read the block. */
+
+ old_offset = offset;
+ offset += *((unsigned char *) de);
+
+ if (offset >= bufsize)
+ {
+ unsigned int frag1;
+ frag1 = bufsize - old_offset;
+ cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL);
+ if (!cpnt) return -1;
+ memcpy(cpnt, bh->b_data + old_offset, frag1);
+ de = (struct iso_directory_record *) ((char *)cpnt);
+ brelse(bh);
+ offset -= bufsize;
+ directory_size -= bufsize;
+ if(directory_size < 0) return -1;
+ block++;
+ if(!(bh = bread(parent->i_dev,block,bufsize))) {
+ kfree(cpnt);
+ return -1;
+ };
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ }
+
+ if (find_rock_ridge_relocation(de, parent) == extent){
+ result = inode_number;
+ goto out;
+ }
+
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ }
+ }
+
+ /* We go here for any condition we cannot handle.
+ We also drop through to here at the end of the directory. */
+
+ out:
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ }
+ brelse(bh);
+#ifdef DEBUG
+ printk("Resultant Inode %d\n",result);
+#endif
+ return result;
+}
+
+#ifdef LEAK_CHECK
+#undef malloc
+#undef free_s
+#undef bread
+#undef brelse
+
+void * leak_check_malloc(unsigned int size){
+ void * tmp;
+ check_malloc++;
+ tmp = kmalloc(size, GFP_KERNEL);
+ return tmp;
+}
+
+void leak_check_free_s(void * obj, int size){
+ check_malloc--;
+ return kfree_s(obj, size);
+}
+
+struct buffer_head * leak_check_bread(int dev, int block, int size){
+ check_bread++;
+ return bread(dev, block, size);
+}
+
+void leak_check_brelse(struct buffer_head * bh){
+ check_bread--;
+ return brelse(bh);
+}
+
+#endif
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
new file mode 100644
index 000000000..1473b1b7f
--- /dev/null
+++ b/fs/isofs/namei.c
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/isofs/namei.c
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <asm/segment.h>
+#include <linux/malloc.h>
+
+#include <linux/errno.h>
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use isofs_match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, isofs_match returns 1 for success, 0 for failure.
+ */
+static int isofs_match(int len,const char * name, char * compare, int dlen)
+{
+ if (!compare)
+ return 0;
+
+ /* 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 0
+ if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen);
+#endif
+
+ if (dlen != len)
+ return 0;
+ return !memcmp(name, compare, len);
+}
+
+/*
+ * isofs_find_entry()
+ *
+ * finds 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.
+ */
+static struct buffer_head * isofs_find_entry(struct inode * dir,
+ const char * name, int namelen, unsigned long * ino, unsigned long * ino_back)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
+ unsigned int block, i, f_pos, offset, inode_number;
+ struct buffer_head * bh;
+ void * cpnt = NULL;
+ unsigned int old_offset;
+ unsigned int backlink;
+ int dlen, rrflag, match;
+ int high_sierra = 0;
+ char * dpnt;
+ struct iso_directory_record * de;
+ char c;
+
+ *ino = 0;
+ if (!dir) return NULL;
+
+ if (!(block = dir->u.isofs_i.i_first_extent)) return NULL;
+
+ f_pos = 0;
+
+ offset = f_pos & (bufsize - 1);
+ block = isofs_bmap(dir,f_pos >> bufbits);
+
+ if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL;
+
+ while (f_pos < dir->i_size) {
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ backlink = dir->i_ino;
+ 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) {
+ brelse(bh);
+ offset = 0;
+ f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1))
+ + ISOFS_BLOCK_SIZE);
+ block = isofs_bmap(dir,f_pos>>bufbits);
+ if (!block || !(bh = bread(dir->i_dev,block,bufsize)))
+ return 0;
+ 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 = kmalloc(*((unsigned char *) de),GFP_KERNEL);
+ if (!cpnt) return 0;
+ memcpy(cpnt, bh->b_data + old_offset, frag1);
+
+ de = (struct iso_directory_record *) cpnt;
+ brelse(bh);
+ offset = f_pos & (bufsize - 1);
+ block = isofs_bmap(dir,f_pos>>bufbits);
+ if (!block || !(bh = bread(dir->i_dev,block,bufsize))) {
+ kfree(cpnt);
+ return 0;
+ };
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ }
+
+ /* Handle the '.' case */
+
+ if (de->name[0]==0 && de->name_len[0]==1) {
+ inode_number = dir->i_ino;
+ backlink = 0;
+ }
+
+ /* Handle the '..' case */
+
+ if (de->name[0]==1 && de->name_len[0]==1) {
+#if 0
+ printk("Doing .. (%d %d)",
+ dir->i_sb->s_firstdatazone,
+ dir->i_ino);
+#endif
+ if((dir->i_sb->u.isofs_sb.s_firstdatazone) != dir->i_ino)
+ inode_number = dir->u.isofs_i.i_backlink;
+ else
+ inode_number = dir->i_ino;
+ backlink = 0;
+ }
+
+ /* Do not report hidden or associated files */
+ high_sierra = dir->i_sb->u.isofs_sb.s_high_sierra;
+ if (de->flags[-high_sierra] & 5) {
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ };
+ continue;
+ }
+
+ dlen = de->name_len[0];
+ dpnt = de->name;
+ /* Now convert the filename in the buffer to lower case */
+ rrflag = get_rock_ridge_filename(de, &dpnt, &dlen, dir);
+ if (rrflag) {
+ if (rrflag == -1) goto out; /* Relocated deep directory */
+ } else {
+ if(dir->i_sb->u.isofs_sb.s_mapping == 'n') {
+ for (i = 0; i < dlen; i++) {
+ c = dpnt[i];
+ if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */
+ if (c == ';' && i == dlen-2 && dpnt[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(dpnt[dlen-1] == '.' && namelen == dlen-1)
+ dlen--;
+ }
+ }
+ match = isofs_match(namelen,name,dpnt,dlen);
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ }
+
+ if(rrflag) kfree(dpnt);
+ if (match) {
+ if(inode_number == -1) {
+ /* Should only happen for the '..' entry */
+ inode_number =
+ isofs_lookup_grandparent(dir,
+ find_rock_ridge_relocation(de,dir));
+ if(inode_number == -1){
+ /* Should never happen */
+ printk("Backlink not properly set.\n");
+ goto out;
+ }
+ }
+ *ino = inode_number;
+ *ino_back = backlink;
+ return bh;
+ }
+ }
+ out:
+ if (cpnt)
+ kfree(cpnt);
+ brelse(bh);
+ return NULL;
+}
+
+int isofs_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result)
+{
+ unsigned long ino, ino_back;
+ struct buffer_head * bh;
+
+#ifdef DEBUG
+ printk("lookup: %x %d\n",dir->i_ino, len);
+#endif
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+
+ ino = 0;
+
+ if (dcache_lookup(dir, name, len, &ino)) ino_back = dir->i_ino;
+
+ if (!ino) {
+ if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (ino_back == dir->i_ino)
+ dcache_add(dir, name, len, ino);
+ brelse(bh);
+ };
+
+ if (!(*result = iget(dir->i_sb,ino))) {
+ iput(dir);
+ return -EACCES;
+ }
+
+ /* 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).
+ */
+
+ if (ino_back && !(*result)->i_pipe && (*result)->i_sb == dir->i_sb) {
+ (*result)->u.isofs_i.i_backlink = ino_back;
+ }
+
+ iput(dir);
+ return 0;
+}
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
new file mode 100644
index 000000000..686c1d910
--- /dev/null
+++ b/fs/isofs/rock.c
@@ -0,0 +1,523 @@
+/*
+ * linux/fs/isofs/rock.c
+ *
+ * (C) 1992, 1993 Eric Youngdale
+ *
+ * Rock Ridge Extensions to iso9660
+ */
+#include <linux/config.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include "rock.h"
+
+/* These functions are designed to read the system areas of a directory record
+ * and extract relevant information. There are different functions provided
+ * depending upon what information we need at the time. One function fills
+ * out an inode structure, a second one extracts a filename, a third one
+ * returns a symbolic link name, and a fourth one returns the extent number
+ * for the file. */
+
+#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,DEV) \
+ {if (buffer) kfree(buffer); \
+ if (cont_extent){ \
+ int block, offset, offset1; \
+ struct buffer_head * bh; \
+ buffer = kmalloc(cont_size,GFP_KERNEL); \
+ if (!buffer) goto out; \
+ block = cont_extent; \
+ offset = cont_offset; \
+ offset1 = 0; \
+ if(ISOFS_BUFFER_SIZE(DEV) == 1024) { \
+ block <<= 1; \
+ if (offset >= 1024) block++; \
+ offset &= 1023; \
+ if(offset + cont_size >= 1024) { \
+ bh = bread(DEV->i_dev, block++, ISOFS_BUFFER_SIZE(DEV)); \
+ if(!bh) {printk("Unable to read continuation Rock Ridge record\n"); \
+ kfree(buffer); \
+ buffer = NULL; } else { \
+ memcpy(buffer, bh->b_data + offset, 1024 - offset); \
+ brelse(bh); \
+ offset1 = 1024 - offset; \
+ offset = 0;} \
+ } \
+ }; \
+ if(buffer) { \
+ bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
+ if(bh){ \
+ memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \
+ brelse(bh); \
+ chr = (unsigned char *) buffer; \
+ len = cont_size; \
+ cont_extent = 0; \
+ cont_size = 0; \
+ cont_offset = 0; \
+ goto LABEL; \
+ }; \
+ } \
+ printk("Unable to read rock-ridge attributes\n"); \
+ }}
+
+/* This is the inner layer of the get filename routine, and is called
+ for each system area and continuation record related to the file */
+
+int find_rock_ridge_relocation(struct iso_directory_record * de,
+ struct inode * inode) {
+ int flag;
+ int len;
+ int retval;
+ unsigned char * chr;
+ CONTINUE_DECLS;
+ flag = 0;
+
+ /* If this is a '..' then we are looking for the parent, otherwise we
+ are looking for the child */
+
+ if (de->name[0]==1 && de->name_len[0]==1) flag = 1;
+ /* Return value if we do not find appropriate record. */
+ retval = isonum_733 (de->extent);
+
+ if (!inode->i_sb->u.isofs_sb.s_rock) return retval;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+ {
+ int rrflag, sig;
+ struct rock_ridge * rr;
+
+ 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'):
+ rrflag = rr->u.RR.flags[0];
+ if (flag && !(rrflag & RR_PL)) goto out;
+ if (!flag && !(rrflag & RR_CL)) goto out;
+ break;
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','L'):
+#ifdef DEBUG
+ printk("RR: CL\n");
+#endif
+ if (flag == 0) {
+ retval = isonum_733(rr->u.CL.location);
+ goto out;
+ };
+ break;
+ case SIG('P','L'):
+#ifdef DEBUG
+ printk("RR: PL\n");
+#endif
+ if (flag != 0) {
+ retval = isonum_733(rr->u.PL.location);
+ goto out;
+ };
+ break;
+ case SIG('C','E'):
+ CHECK_CE; /* This tells is if there is a continuation record */
+ break;
+ default:
+ break;
+ }
+ };
+ };
+ MAYBE_CONTINUE(repeat, inode);
+ return retval;
+ out:
+ if(buffer) kfree(buffer);
+ return retval;
+}
+
+int get_rock_ridge_filename(struct iso_directory_record * de,
+ char ** name, int * namlen, struct inode * inode)
+{
+ int len;
+ unsigned char * chr;
+ CONTINUE_DECLS;
+ char * retname = NULL;
+ int retnamlen = 0, truncate=0;
+
+ if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+ {
+ struct rock_ridge * rr;
+ int sig;
+
+ 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'):
+ if((rr->u.RR.flags[0] & RR_NM) == 0) goto out;
+ break;
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','E'):
+ CHECK_CE;
+ break;
+ case SIG('N','M'):
+ if (truncate) break;
+ if (rr->u.NM.flags & ~1) {
+ printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
+ break;
+ };
+ if (!retname){
+ retname = (char *) kmalloc (255,GFP_KERNEL);
+ /* This may be a waste, but we only
+ need this for a moment. The layers
+ that call this function should
+ deallocate the mem fairly soon
+ after control is returned */
+
+ if (!retname) goto out;
+ *retname = 0; /* Zero length string */
+ retnamlen = 0;
+ };
+ if((strlen(retname) + rr->len - 5) >= 254) {
+ truncate = 1;
+ break;
+ };
+ strncat(retname, rr->u.NM.name, rr->len - 5);
+ retnamlen += rr->len - 5;
+ break;
+ case SIG('R','E'):
+#ifdef DEBUG
+ printk("RR: RE (%x)\n", inode->i_ino);
+#endif
+ if (buffer) kfree(buffer);
+ if (retname) kfree(retname);
+ return -1;
+ default:
+ break;
+ }
+ };
+ }
+ MAYBE_CONTINUE(repeat,inode);
+ if(retname){
+ *name = retname;
+ *namlen = retnamlen;
+ return 1;
+ };
+ return 0; /* This file did not have a NM field */
+ out:
+ if(buffer) kfree(buffer);
+ if (retname) kfree(retname);
+ return 0;
+}
+
+int parse_rock_ridge_inode(struct iso_directory_record * de,
+ struct inode * inode){
+ int len;
+ unsigned char * chr;
+ CONTINUE_DECLS;
+
+ if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+ {
+ int cnt, sig;
+ struct 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'):
+ if((rr->u.RR.flags[0] &
+ (RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out;
+ break;
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','E'):
+ CHECK_CE;
+ break;
+ case SIG('E','R'):
+ printk("ISO9660 Extensions: ");
+ { int p;
+ for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
+ };
+ printk("\n");
+ break;
+ case SIG('P','X'):
+ inode->i_mode = isonum_733(rr->u.PX.mode);
+ inode->i_nlink = isonum_733(rr->u.PX.n_links);
+ inode->i_uid = isonum_733(rr->u.PX.uid);
+ inode->i_gid = isonum_733(rr->u.PX.gid);
+ break;
+ case SIG('P','N'):
+ { int high, low;
+ high = isonum_733(rr->u.PN.dev_high);
+ low = isonum_733(rr->u.PN.dev_low);
+ inode->i_rdev = ((high << 8) | (low & 0xff)) & 0xffff;
+ };
+ break;
+ case SIG('T','F'):
+ /* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
+ Try and handle this correctly for either case. */
+ cnt = 0; /* Rock ridge never appears on a High Sierra disk */
+ if(rr->u.TF.flags & TF_CREATE)
+ inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ if(rr->u.TF.flags & TF_MODIFY)
+ inode->i_mtime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ if(rr->u.TF.flags & TF_ACCESS)
+ inode->i_atime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ if(rr->u.TF.flags & TF_ATTRIBUTES)
+ inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ break;
+ case SIG('S','L'):
+ {int slen;
+ struct SL_component * slp;
+ slen = rr->len - 5;
+ slp = &rr->u.SL.link;
+ inode->i_size = 0;
+ while (slen > 1){
+ rootflag = 0;
+ switch(slp->flags &~1){
+ case 0:
+ inode->i_size += slp->len;
+ break;
+ case 2:
+ inode->i_size += 1;
+ break;
+ case 4:
+ inode->i_size += 2;
+ break;
+ case 8:
+ rootflag = 1;
+ inode->i_size += 1;
+ break;
+ default:
+ printk("Symlink component flag not implemented\n");
+ };
+ slen -= slp->len + 2;
+ slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+ if(slen < 2) break;
+ if(!rootflag) inode->i_size += 1;
+ };
+ };
+ break;
+ case SIG('R','E'):
+ printk("Attempt to read inode for relocated directory\n");
+ goto out;
+ case SIG('C','L'):
+#ifdef DEBUG
+ printk("RR CL (%x)\n",inode->i_ino);
+#endif
+ inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) <<
+ inode -> i_sb -> u.isofs_sb.s_log_zone_size;
+ reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent);
+ inode->i_mode = reloc->i_mode;
+ inode->i_nlink = reloc->i_nlink;
+ inode->i_uid = reloc->i_uid;
+ inode->i_gid = reloc->i_gid;
+ inode->i_rdev = reloc->i_rdev;
+ inode->i_size = reloc->i_size;
+ inode->i_atime = reloc->i_atime;
+ inode->i_ctime = reloc->i_ctime;
+ inode->i_mtime = reloc->i_mtime;
+ iput(reloc);
+ break;
+ default:
+ break;
+ }
+ };
+ }
+ MAYBE_CONTINUE(repeat,inode);
+ return 0;
+ out:
+ if(buffer) kfree(buffer);
+ return 0;
+}
+
+
+/* Returns the name of the file that this inode is symlinked to. This is
+ in malloc'd memory, so it needs to be freed, once we are through with it */
+
+char * get_rock_ridge_symlink(struct inode * inode)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
+ struct buffer_head * bh;
+ unsigned char * pnt;
+ void * cpnt = NULL;
+ char * rpnt;
+ struct iso_directory_record * raw_inode;
+ CONTINUE_DECLS;
+ int block;
+ int sig;
+ int rootflag;
+ int len;
+ unsigned char * chr;
+ struct rock_ridge * rr;
+
+ if (!inode->i_sb->u.isofs_sb.s_rock)
+ panic("Cannot have symlink with high sierra variant of iso filesystem\n");
+
+ rpnt = 0;
+
+ block = inode->i_ino >> bufbits;
+ if (!(bh=bread(inode->i_dev,block, bufsize))) {
+ printk("unable to read i-node block");
+ return NULL;
+ };
+
+ pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1));
+
+ raw_inode = ((struct iso_directory_record *) pnt);
+
+ if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
+ int frag1, offset;
+
+ offset = (inode->i_ino & (bufsize - 1));
+ frag1 = bufsize - offset;
+ cpnt = kmalloc(*pnt,GFP_KERNEL);
+ if(!cpnt) return NULL;
+ memcpy(cpnt, bh->b_data + offset, frag1);
+ brelse(bh);
+ if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+ kfree(cpnt);
+ printk("unable to read i-node block");
+ return NULL;
+ };
+ offset += *pnt - bufsize;
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ pnt = ((unsigned char *) cpnt);
+ raw_inode = ((struct iso_directory_record *) pnt);
+ };
+
+ /* 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 */
+ if (rpnt) break;
+ 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'):
+ 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 * slp;
+ slen = rr->len - 5;
+ slp = &rr->u.SL.link;
+ while (slen > 1){
+ if (!rpnt){
+ rpnt = (char *) kmalloc (inode->i_size +1, GFP_KERNEL);
+ 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:
+ printk("Symlink component flag not implemented (%d)\n",slen);
+ };
+ slen -= slp->len + 2;
+ slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+ if(slen < 2) break;
+ if(!rootflag) strcat(rpnt,"/");
+ };
+ break;
+ default:
+ break;
+ }
+ };
+ };
+ MAYBE_CONTINUE(repeat,inode);
+ brelse(bh);
+
+ if (cpnt) {
+ kfree(cpnt);
+ cpnt = NULL;
+ };
+
+ return rpnt;
+ out:
+ if(buffer) kfree(buffer);
+ return 0;
+}
+
+
+
+
+
+
diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h
new file mode 100644
index 000000000..36057b8fa
--- /dev/null
+++ b/fs/isofs/rock.h
@@ -0,0 +1,111 @@
+/* These structs are used by the system-use-sharing protocol, in which the
+ Rock Ridge extensions are embedded. It is quite possible that other
+ extensions are present on the disk, and this is fine as long as they
+ all use SUSP */
+
+struct SU_SP{
+ unsigned char magic[2];
+ unsigned char skip;
+};
+
+struct SU_CE{
+ char extent[8];
+ char offset[8];
+ char size[8];
+};
+
+struct SU_ER{
+ unsigned char len_id;
+ unsigned char len_des;
+ unsigned char len_src;
+ unsigned char ext_ver;
+ char data[0];
+};
+
+struct RR_RR{
+ char flags[1];
+};
+
+struct RR_PX{
+ char mode[8];
+ char n_links[8];
+ char uid[8];
+ char gid[8];
+};
+
+struct RR_PN{
+ char dev_high[8];
+ char dev_low[8];
+};
+
+
+struct SL_component{
+ unsigned char flags;
+ unsigned char len;
+ char text[0];
+};
+
+struct RR_SL{
+ unsigned char flags;
+ struct SL_component link;
+};
+
+struct RR_NM{
+ unsigned char flags;
+ char name[0];
+};
+
+struct RR_CL{
+ char location[8];
+};
+
+struct RR_PL{
+ char location[8];
+};
+
+struct stamp{
+ char time[7];
+};
+
+struct RR_TF{
+ char flags;
+ struct stamp times[0]; /* Variable number of these beasts */
+};
+
+/* These are the bits and their meanings for flags in the TF structure. */
+#define TF_CREATE 1
+#define TF_MODIFY 2
+#define TF_ACCESS 4
+#define TF_ATTRIBUTES 8
+#define TF_BACKUP 16
+#define TF_EXPIRATION 32
+#define TF_EFFECTIVE 64
+#define TF_LONG_FORM 128
+
+struct rock_ridge{
+ char signature[2];
+ unsigned char len;
+ unsigned char version;
+ union{
+ struct SU_SP SP;
+ struct SU_CE CE;
+ struct SU_ER ER;
+ struct RR_RR RR;
+ struct RR_PX PX;
+ struct RR_PN PN;
+ struct RR_SL SL;
+ struct RR_NM NM;
+ struct RR_CL CL;
+ struct RR_PL PL;
+ struct RR_TF TF;
+ } u;
+};
+
+#define RR_PX 1 /* POSIX attributes */
+#define RR_PN 2 /* POSIX devices */
+#define RR_SL 4 /* Symbolic link */
+#define RR_NM 8 /* Alternate Name */
+#define RR_CL 16 /* Child link */
+#define RR_PL 32 /* Parent link */
+#define RR_RE 64 /* Relocation directory */
+#define RR_TF 128 /* Timestamps */
diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c
new file mode 100644
index 000000000..fa4a45ba6
--- /dev/null
+++ b/fs/isofs/symlink.c
@@ -0,0 +1,106 @@
+/*
+ * linux/fs/isofs/symlink.c
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * isofs symlink handling code. This is only used with the Rock Ridge
+ * extensions to iso9660
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/iso_fs.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>
+
+static int isofs_readlink(struct inode *, char *, int);
+static int isofs_follow_link(struct inode *, struct inode *, int, int, struct inode **);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations isofs_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ isofs_readlink, /* readlink */
+ isofs_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int isofs_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ int error;
+ char * pnt;
+
+ if (!dir) {
+ dir = current->fs->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ *res_inode = NULL;
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if ((current->link_count > 5) ||
+ !(pnt = get_rock_ridge_symlink(inode))) {
+ iput(dir);
+ iput(inode);
+ *res_inode = NULL;
+ return -ELOOP;
+ }
+ iput(inode);
+ current->link_count++;
+ error = open_namei(pnt,flag,mode,res_inode,dir);
+ current->link_count--;
+ kfree(pnt);
+ return error;
+}
+
+static int isofs_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ char * pnt;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+
+ if (buflen > 1023)
+ buflen = 1023;
+ pnt = get_rock_ridge_symlink(inode);
+
+ iput(inode);
+ if (!pnt)
+ return 0;
+ i = 0;
+
+ while (i<buflen && (c = pnt[i])) {
+ i++;
+ put_fs_byte(c,buffer++);
+ }
+ kfree(pnt);
+ return i;
+}
diff --git a/fs/isofs/util.c b/fs/isofs/util.c
new file mode 100644
index 000000000..dbeee868d
--- /dev/null
+++ b/fs/isofs/util.c
@@ -0,0 +1,131 @@
+/*
+ * linux/fs/isofs/util.c
+ *
+ * The special functions in the file are numbered according to the section
+ * of the iso 9660 standard in which they are described. isonum_733 will
+ * convert numbers according to section 7.3.3, etc.
+ *
+ * isofs special functions. This file was lifted in its entirety from
+ * the bsd386 iso9660 filesystem, by Pace Williamson.
+ */
+
+
+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)
+{
+#if 0
+ if (p[0] != p[3] || p[1] != p[2]) {
+ fprintf (stderr, "invalid format 7.2.3 number\n");
+ exit (1);
+ }
+#endif
+ 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)
+{
+#if 0
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (p[i] != p[7-i]) {
+ fprintf (stderr, "bad format 7.3.3 number\n");
+ exit (1);
+ }
+ }
+#endif
+ return (isonum_731 (p));
+}
+
+/* We have to convert from a MM/DD/YY format to the unix ctime format. We have to
+ take into account leap years and all of that good stuff. Unfortunately, the kernel
+ does not have the information on hand to take into account daylight savings time,
+ so there will be cases (roughly half the time) where the dates are off by one hour. */
+int iso_date(char * p, int flag)
+{
+ int year, month, day, hour ,minute, second, tz;
+ int crtime, days, i;
+
+ year = p[0] - 70;
+ month = p[1];
+ day = p[2];
+ hour = p[3];
+ minute = p[4];
+ second = p[5];
+ if (flag == 0) tz = p[6]; /* High sierra has no time zone */
+ else tz = 0;
+
+ if (year < 0) {
+ crtime = 0;
+ } else {
+ int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+ days = year * 365;
+ if (year > 2)
+ days += (year+1) / 4;
+ for (i = 1; i < month; i++)
+ days += monlen[i-1];
+ if (((year+2) % 4) == 0 && month > 2)
+ days++;
+ days += day - 1;
+ crtime = ((((days * 24) + hour) * 60 + minute) * 60)
+ + second;
+
+ /* sign extend */
+ if (tz & 0x80)
+ tz |= (-1 << 8);
+
+ /* timezone offset is unreliable on some disks */
+ if (-48 <= tz && tz <= 52)
+ crtime += tz * 15 * 60;
+ }
+ return crtime;
+}
+