diff options
Diffstat (limited to 'fs/isofs/inode.c')
-rw-r--r-- | fs/isofs/inode.c | 314 |
1 files changed, 176 insertions, 138 deletions
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 06de6ac7c..26c72dab9 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -320,7 +320,7 @@ static int parse_options(char *options, struct iso9660_options * popt) *value++ = 0; #ifdef CONFIG_JOLIET - if (!strcmp(this_char,"iocharset")) { + if (!strcmp(this_char,"iocharset") && value) { popt->iocharset = value; while (*value && *value != ',') value++; @@ -445,25 +445,31 @@ static unsigned int isofs_get_last_session(kdev_t dev) return vol_desc_start; } +/* + * Initialize the superblock and read the root inode. + * + * Note: a check_disk_change() has been done immediately prior + * to this call, so we don't need to check again. + */ struct super_block *isofs_read_super(struct super_block *s, void *data, int silent) { - struct buffer_head * bh = NULL, *pri_bh = NULL; - unsigned int blocksize; - unsigned int blocksize_bits; kdev_t dev = s->s_dev; + struct buffer_head * bh = NULL, *pri_bh = NULL; struct hs_primary_descriptor * h_pri = NULL; struct iso_primary_descriptor * pri = NULL; struct iso_supplementary_descriptor *sec = NULL; struct iso_directory_record * rootp; + int joliet_level = 0; int high_sierra; int iso_blknum, block; - int joliet_level = 0; int orig_zonesize; + int table; + unsigned int blocksize, blocksize_bits; unsigned int vol_desc_start; + unsigned long first_data_zone; struct inode * inode; struct iso9660_options opt; - int table; MOD_INC_USE_COUNT; /* lock before any blocking operations */ @@ -592,7 +598,6 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, root_found: brelse(pri_bh); - s->u.isofs_sb.s_joliet_level = joliet_level; if (joliet_level && opt.rock == 'n') { /* This is the case of Joliet with the norock mount flag. @@ -623,10 +628,7 @@ root_found: s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */ - /* RDE: convert log zone size to bit shift */ - orig_zonesize = s -> u.isofs_sb.s_log_zone_size; - /* * If the zone size is smaller than the hardware sector size, * this is a fatal error. This would occur if the disc drive @@ -637,6 +639,7 @@ root_found: if(blocksize != 0 && orig_zonesize < blocksize) goto out_bad_size; + /* 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; @@ -657,14 +660,15 @@ root_found: /* RDE: data zone now byte offset! */ - s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); + first_data_zone = ((isonum_733 (rootp->extent) + + isonum_711 (rootp->ext_attr_length)) + << s -> u.isofs_sb.s_log_zone_size); + s->u.isofs_sb.s_firstdatazone = first_data_zone; #ifndef BEQUIET printk(KERN_DEBUG "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(KERN_DEBUG "First datazone:%ld Root inode number %d\n", + printk(KERN_DEBUG "First datazone:%ld Root inode number:%ld\n", s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size, s->u.isofs_sb.s_firstdatazone); if(high_sierra) @@ -672,6 +676,29 @@ root_found: #endif /* + * If the Joliet level is set, we _may_ decide to use the + * secondary descriptor, but can't be sure until after we + * read the root inode. But before reading the root inode + * we may need to change the device blocksize, and would + * rather release the old buffer first. So, we cache the + * first_data_zone value from the secondary descriptor. + */ + if (joliet_level) { + pri = (struct iso_primary_descriptor *) sec; + rootp = (struct iso_directory_record *) + pri->root_directory_record; + first_data_zone = ((isonum_733 (rootp->extent) + + isonum_711 (rootp->ext_attr_length)) + << s -> u.isofs_sb.s_log_zone_size); + } + + /* + * We're all done using the volume descriptor, and may need + * to change the device blocksize, so release the buffer now. + */ + brelse(bh); + + /* * Force the blocksize to 512 for 512 byte sectors. The file * read primitives really get it wrong in a bad way if we don't * do this. @@ -688,22 +715,15 @@ root_found: * entries. By forcing the blocksize in this way, we ensure * that we will never be required to do this. */ - if( orig_zonesize != opt.blocksize ) - { - opt.blocksize = orig_zonesize; - blocksize_bits = 0; - { - int i = opt.blocksize; - while (i != 1){ - blocksize_bits++; - i >>=1; - } - } - set_blocksize(dev, opt.blocksize); + if ( orig_zonesize != opt.blocksize ) { + set_blocksize(dev, orig_zonesize); #ifndef BEQUIET - printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize); + printk(KERN_DEBUG + "ISOFS: Forcing new log zone size:%d\n", orig_zonesize); #endif - } + } + s->s_blocksize = orig_zonesize; + s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size; s->u.isofs_sb.s_nls_iocharset = NULL; @@ -732,8 +752,12 @@ root_found: * as suid, so we merely allow them to set the default permissions. */ s->u.isofs_sb.s_mode = opt.mode & 0777; - s->s_blocksize = opt.blocksize; - s->s_blocksize_bits = blocksize_bits; + + /* + * Read the root inode, which _may_ result in changing + * the s_rock flag. Once we have the final s_rock value, + * we then decide whether to use the Joliet descriptor. + */ inode = iget(s, s->u.isofs_sb.s_firstdatazone); /* @@ -744,21 +768,17 @@ root_found: * CD with Unicode names. Until someone sees such a beast, it * will not be supported. */ - if (opt.rock == 'y' && s->u.isofs_sb.s_rock == 1) { + if (s->u.isofs_sb.s_rock == 1) { joliet_level = 0; - } - if (joliet_level) { - iput(inode); - pri = (struct iso_primary_descriptor *) sec; - rootp = (struct iso_directory_record *) - pri->root_directory_record; - s->u.isofs_sb.s_firstdatazone = - ((isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); - inode = iget(s, s->u.isofs_sb.s_firstdatazone); + } else if (joliet_level) { s->u.isofs_sb.s_rock = 0; - opt.rock = 'n'; + if (s->u.isofs_sb.s_firstdatazone != first_data_zone) { + s->u.isofs_sb.s_firstdatazone = first_data_zone; + printk(KERN_DEBUG + "ISOFS: changing to secondary root\n"); + iput(inode); + inode = iget(s, s->u.isofs_sb.s_firstdatazone); + } } if (opt.check == 'u') { @@ -766,32 +786,46 @@ root_found: if (joliet_level) opt.check = 'r'; else opt.check = 's'; } + s->u.isofs_sb.s_joliet_level = joliet_level; + /* check the root inode */ + if (!inode) + goto out_no_root; + if (!inode->i_op) + goto out_bad_root; + /* get the root dentry */ s->s_root = d_alloc_root(inode, NULL); if (!(s->s_root)) goto out_no_root; + table = 0; if (joliet_level) table += 2; if (opt.check == 'r') table++; s->s_root->d_op = &isofs_dentry_ops[table]; - if(!check_disk_change(dev)) { - brelse(bh); - unlock_super(s); - return s; - } - /* - * Disk changed? Free the root dentry and clean up ... - */ - dput(s->s_root); - goto out_freechar; + unlock_super(s); + return s; /* - * Display error message + * Display error messages and free resources. */ -out_no_root: - printk(KERN_ERR "isofs_read_super: get root inode failed\n"); +out_bad_root: + printk(KERN_WARNING "isofs_read_super: root inode not initialized\n"); goto out_iput; +out_no_root: + printk(KERN_WARNING "isofs_read_super: get root inode failed\n"); +out_iput: + iput(inode); +#ifdef CONFIG_JOLIET + if (s->u.isofs_sb.s_nls_iocharset) + unload_nls(s->u.isofs_sb.s_nls_iocharset); +#endif + goto out_unlock; +out_no_read: + printk(KERN_WARNING "isofs_read_super: " + "bread failed, dev=%s, iso_blknum=%d, block=%d\n", + kdevname(dev), iso_blknum, block); + goto out_unlock; out_bad_zone_size: printk(KERN_WARNING "Bad logical zone size %ld\n", s->u.isofs_sb.s_log_zone_size); @@ -808,23 +842,7 @@ out_no_support: out_unknown_format: if (!silent) printk(KERN_WARNING "Unable to identify CD-ROM format.\n"); - goto out_freebh; -out_no_read: - printk(KERN_WARNING "isofs_read_super: " - "bread failed, dev=%s, iso_blknum=%d, block=%d\n", - kdevname(dev), iso_blknum, block); - goto out_unlock; - /* - * Cascaded error cleanup to ensure all resources are freed. - */ -out_iput: - iput(inode); -out_freechar: -#ifdef CONFIG_JOLIET - if (s->u.isofs_sb.s_nls_iocharset) - unload_nls(s->u.isofs_sb.s_nls_iocharset); -#endif out_freebh: brelse(bh); out_unlock: @@ -945,101 +963,113 @@ static void test_and_set_uid(uid_t *p, uid_t value) static int isofs_read_level3_size(struct inode * inode) { + unsigned long ino = inode->i_ino; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; struct buffer_head * bh = NULL; - struct iso_directory_record * raw_inode = NULL; /* quiet gcc */ - unsigned char *pnt = NULL; - void *cpnt = NULL; - int block = 0; /* Quiet GCC */ - unsigned long ino; - int i; + int block = 0; + int i = 0; + void *cpnt; + struct iso_directory_record * raw_inode; inode->i_size = 0; inode->u.isofs_i.i_next_section_ino = 0; - ino = inode->i_ino; - i = 0; do { - if(i > 100) { - printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n" - "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino); - return 0; - } + unsigned char *pnt; + unsigned int reclen; + int offset = (ino & (bufsize - 1)); - if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) { - if(bh) brelse(bh); + cpnt = NULL; + /* Check whether to update our buffer */ + if (block != ino >> ISOFS_BUFFER_BITS(inode)) { block = ino >> ISOFS_BUFFER_BITS(inode); - if (!(bh=bread(inode->i_dev,block, bufsize))) { - printk("unable to read i-node block"); - return 1; - } - } - pnt = ((unsigned char *) bh->b_data - + (ino & (bufsize - 1))); - - if ((ino & (bufsize - 1)) + *pnt > bufsize){ - int frag1, offset; - - offset = (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); - return 1; - } - 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 1; - } - offset += *pnt - bufsize; - memcpy((char *)cpnt+frag1, bh->b_data, offset); - pnt = ((unsigned char *) cpnt); + bh = bread(inode->i_dev, block, bufsize); + if (!bh) + goto out_noread; } - - if(*pnt == 0) { + pnt = ((unsigned char *) bh->b_data + offset); + raw_inode = ((struct iso_directory_record *) pnt); + /* + * Note: this is invariant even if the record + * spans buffers and must be copied ... + */ + reclen = *pnt; + + /* N.B. this test doesn't trigger the i++ code ... */ + if(reclen == 0) { ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE; continue; } - raw_inode = ((struct iso_directory_record *) pnt); + + /* Check whether the raw inode spans the buffer ... */ + if (offset + reclen > bufsize){ + int frag1 = bufsize - offset; + + cpnt = kmalloc(reclen, GFP_KERNEL); + if (cpnt == NULL) + goto out_nomem; + memcpy(cpnt, pnt, frag1); + brelse(bh); + bh = bread(inode->i_dev, ++block, bufsize); + if (!bh) + goto out_noread; + offset += reclen - bufsize; + memcpy((char *)cpnt+frag1, bh->b_data, offset); + raw_inode = ((struct iso_directory_record *) cpnt); + } inode->i_size += isonum_733 (raw_inode->size); if(i == 1) inode->u.isofs_i.i_next_section_ino = ino; - ino += *pnt; - if (cpnt) { + ino += reclen; + if (cpnt) kfree (cpnt); - cpnt = NULL; - } i++; - } while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80); + if(i > 100) + goto out_toomany; + } while(raw_inode->flags[-high_sierra] & 0x80); +out: brelse(bh); return 0; + +out_nomem: + printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino); + brelse(bh); + return 1; +out_noread: + printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block); + if (cpnt) + kfree(cpnt); + return 1; +out_toomany: + printk(KERN_INFO "isofs_read_level3_size: " + "More than 100 file sections ?!?, aborting...\n" + "isofs_read_level3_size: inode=%lu ino=%lu\n", + inode->i_ino, ino); + goto out; } void isofs_read_inode(struct inode * inode) { + struct super_block *sb = inode->i_sb; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode); + int high_sierra = sb->u.isofs_sb.s_high_sierra; struct buffer_head * bh; struct iso_directory_record * raw_inode; - unsigned char *pnt = NULL; - int high_sierra; - int block; - int volume_seq_no ; - int i; + unsigned char *pnt; + int volume_seq_no, 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; + bh = bread(inode->i_dev, block, bufsize); + if (!bh) { + printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); + 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 (raw_inode->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; @@ -1049,10 +1079,13 @@ void isofs_read_inode(struct inode * inode) easier to give 1 which tells find to do it the hard way. */ } else { - inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */ + /* Everybody gets to read the file. */ + inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; inode->i_nlink = 1; inode->i_mode |= S_IFREG; -/* If there are no periods in the name, then set the execute permission bit */ + /* 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; @@ -1133,14 +1166,16 @@ void isofs_read_inode(struct 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; /* get the volume sequence number */ volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ; /* + * All done with buffer ... no more references to buffer memory! + */ + brelse(bh); + + /* * Disable checking if we see any volume number other than 0 or 1. * We could use the cruft option, but that has multiple purposes, one * of which is limiting the file size to 16Mb. Thus we silently allow @@ -1152,6 +1187,8 @@ void isofs_read_inode(struct inode * inode) inode->i_sb->u.isofs_sb.s_cruft = 'y'; } + /* Install the inode operations vector */ + inode->i_op = NULL; #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS if (inode->i_sb->u.isofs_sb.s_cruft != 'y' && (volume_seq_no != 0) && (volume_seq_no != 1)) { @@ -1173,6 +1210,7 @@ void isofs_read_inode(struct inode * inode) init_fifo(inode); } return; + fail: /* With a data error we return this information */ inode->i_mtime = inode->i_atime = inode->i_ctime = 0; |