diff options
Diffstat (limited to 'fs/isofs')
-rw-r--r-- | fs/isofs/Makefile | 30 | ||||
-rw-r--r-- | fs/isofs/dir.c | 258 | ||||
-rw-r--r-- | fs/isofs/file.c | 260 | ||||
-rw-r--r-- | fs/isofs/inode.c | 707 | ||||
-rw-r--r-- | fs/isofs/namei.c | 268 | ||||
-rw-r--r-- | fs/isofs/rock.c | 523 | ||||
-rw-r--r-- | fs/isofs/rock.h | 111 | ||||
-rw-r--r-- | fs/isofs/symlink.c | 106 | ||||
-rw-r--r-- | fs/isofs/util.c | 131 |
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; +} + |