diff options
Diffstat (limited to 'fs/isofs')
| -rw-r--r-- | fs/isofs/Kconfig | 39 | ||||
| -rw-r--r-- | fs/isofs/compress.c | 549 | ||||
| -rw-r--r-- | fs/isofs/dir.c | 144 | ||||
| -rw-r--r-- | fs/isofs/export.c | 132 | ||||
| -rw-r--r-- | fs/isofs/inode.c | 917 | ||||
| -rw-r--r-- | fs/isofs/isofs.h | 53 | ||||
| -rw-r--r-- | fs/isofs/joliet.c | 54 | ||||
| -rw-r--r-- | fs/isofs/namei.c | 59 | ||||
| -rw-r--r-- | fs/isofs/rock.c | 44 |
9 files changed, 1075 insertions, 916 deletions
diff --git a/fs/isofs/Kconfig b/fs/isofs/Kconfig new file mode 100644 index 00000000000..8ab9878e367 --- /dev/null +++ b/fs/isofs/Kconfig @@ -0,0 +1,39 @@ +config ISO9660_FS + tristate "ISO 9660 CDROM file system support" + help + This is the standard file system used on CD-ROMs. It was previously + known as "High Sierra File System" and is called "hsfs" on other + Unix systems. The so-called Rock-Ridge extensions which allow for + long Unix filenames and symbolic links are also supported by this + driver. If you have a CD-ROM drive and want to do more with it than + just listen to audio CDs and watch its LEDs, say Y (and read + <file:Documentation/filesystems/isofs.txt> and the CD-ROM-HOWTO, + available from <http://www.tldp.org/docs.html#howto>), thereby + enlarging your kernel by about 27 KB; otherwise say N. + + To compile this file system support as a module, choose M here: the + module will be called isofs. + +config JOLIET + bool "Microsoft Joliet CDROM extensions" + depends on ISO9660_FS + select NLS + help + Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system + which allows for long filenames in unicode format (unicode is the + new 16 bit character code, successor to ASCII, which encodes the + characters of almost all languages of the world; see + <http://www.unicode.org/> for more information). Say Y here if you + want to be able to read Joliet CD-ROMs under Linux. + +config ZISOFS + bool "Transparent decompression extension" + depends on ISO9660_FS + select ZLIB_INFLATE + help + This is a Linux-specific extension to RockRidge which lets you store + data in compressed form on a CD-ROM and have it transparently + decompressed when the CD-ROM is accessed. See + <http://www.kernel.org/pub/linux/utils/fs/zisofs/> for the tools + necessary to create such a filesystem. Say Y here if you want to be + able to read such compressed CD-ROMs. diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c index 731816332b1..592e5115a56 100644 --- a/fs/isofs/compress.c +++ b/fs/isofs/compress.c @@ -33,278 +33,326 @@ static char zisofs_sink_page[PAGE_CACHE_SIZE]; * allocation; this avoids failures at block-decompression time. */ static void *zisofs_zlib_workspace; -static struct semaphore zisofs_zlib_semaphore; +static DEFINE_MUTEX(zisofs_zlib_lock); /* - * When decompressing, we typically obtain more than one page - * per reference. We inject the additional pages into the page - * cache as a form of readahead. + * Read data of @inode from @block_start to @block_end and uncompress + * to one zisofs block. Store the data in the @pages array with @pcount + * entries. Start storing at offset @poffset of the first page. */ -static int zisofs_readpage(struct file *file, struct page *page) +static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, + loff_t block_end, int pcount, + struct page **pages, unsigned poffset, + int *errp) { - struct inode *inode = file->f_dentry->d_inode; - struct address_space *mapping = inode->i_mapping; - unsigned int maxpage, xpage, fpage, blockindex; - unsigned long offset; - unsigned long blockptr, blockendptr, cstart, cend, csize; - struct buffer_head *bh, *ptrbh[2]; - unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); - unsigned int bufshift = ISOFS_BUFFER_BITS(inode); - unsigned long bufmask = bufsize - 1; - int err = -EIO; - int i; - unsigned int header_size = ISOFS_I(inode)->i_format_parm[0]; unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1]; - /* unsigned long zisofs_block_size = 1UL << zisofs_block_shift; */ - unsigned int zisofs_block_page_shift = zisofs_block_shift-PAGE_CACHE_SHIFT; - unsigned long zisofs_block_pages = 1UL << zisofs_block_page_shift; - unsigned long zisofs_block_page_mask = zisofs_block_pages-1; - struct page *pages[zisofs_block_pages]; - unsigned long index = page->index; - int indexblocks; - - /* We have already been given one page, this is the one - we must do. */ - xpage = index & zisofs_block_page_mask; - pages[xpage] = page; - - /* The remaining pages need to be allocated and inserted */ - offset = index & ~zisofs_block_page_mask; - blockindex = offset >> zisofs_block_page_shift; - maxpage = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - maxpage = min(zisofs_block_pages, maxpage-offset); - - for ( i = 0 ; i < maxpage ; i++, offset++ ) { - if ( i != xpage ) { - pages[i] = grab_cache_page_nowait(mapping, offset); - } - page = pages[i]; - if ( page ) { - ClearPageError(page); - kmap(page); + unsigned int bufsize = ISOFS_BUFFER_SIZE(inode); + unsigned int bufshift = ISOFS_BUFFER_BITS(inode); + unsigned int bufmask = bufsize - 1; + int i, block_size = block_end - block_start; + z_stream stream = { .total_out = 0, + .avail_in = 0, + .avail_out = 0, }; + int zerr; + int needblocks = (block_size + (block_start & bufmask) + bufmask) + >> bufshift; + int haveblocks; + blkcnt_t blocknum; + struct buffer_head *bhs[needblocks + 1]; + int curbh, curpage; + + if (block_size > deflateBound(1UL << zisofs_block_shift)) { + *errp = -EIO; + return 0; + } + /* Empty block? */ + if (block_size == 0) { + for ( i = 0 ; i < pcount ; i++ ) { + if (!pages[i]) + continue; + memset(page_address(pages[i]), 0, PAGE_CACHE_SIZE); + flush_dcache_page(pages[i]); + SetPageUptodate(pages[i]); } + return ((loff_t)pcount) << PAGE_CACHE_SHIFT; } - /* This is the last page filled, plus one; used in case of abort. */ - fpage = 0; - - /* Find the pointer to this specific chunk */ - /* Note: we're not using isonum_731() here because the data is known aligned */ - /* Note: header_size is in 32-bit words (4 bytes) */ - blockptr = (header_size + blockindex) << 2; - blockendptr = blockptr + 4; - - indexblocks = ((blockptr^blockendptr) >> bufshift) ? 2 : 1; - ptrbh[0] = ptrbh[1] = NULL; - - if ( isofs_get_blocks(inode, blockptr >> bufshift, ptrbh, indexblocks) != indexblocks ) { - if ( ptrbh[0] ) brelse(ptrbh[0]); - printk(KERN_DEBUG "zisofs: Null buffer on reading block table, inode = %lu, block = %lu\n", - inode->i_ino, blockptr >> bufshift); - goto eio; - } - ll_rw_block(READ, indexblocks, ptrbh); - - bh = ptrbh[0]; - if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) { - printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu\n", - inode->i_ino, blockptr >> bufshift); - if ( ptrbh[1] ) - brelse(ptrbh[1]); - goto eio; - } - cstart = le32_to_cpu(*(__le32 *)(bh->b_data + (blockptr & bufmask))); - - if ( indexblocks == 2 ) { - /* We just crossed a block boundary. Switch to the next block */ - brelse(bh); - bh = ptrbh[1]; - if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) { - printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu\n", - inode->i_ino, blockendptr >> bufshift); - goto eio; - } + /* Because zlib is not thread-safe, do all the I/O at the top. */ + blocknum = block_start >> bufshift; + memset(bhs, 0, (needblocks + 1) * sizeof(struct buffer_head *)); + haveblocks = isofs_get_blocks(inode, blocknum, bhs, needblocks); + ll_rw_block(READ, haveblocks, bhs); + + curbh = 0; + curpage = 0; + /* + * First block is special since it may be fractional. We also wait for + * it before grabbing the zlib mutex; odds are that the subsequent + * blocks are going to come in in short order so we don't hold the zlib + * mutex longer than necessary. + */ + + if (!bhs[0]) + goto b_eio; + + wait_on_buffer(bhs[0]); + if (!buffer_uptodate(bhs[0])) { + *errp = -EIO; + goto b_eio; } - cend = le32_to_cpu(*(__le32 *)(bh->b_data + (blockendptr & bufmask))); - brelse(bh); - if (cstart > cend) - goto eio; + stream.workspace = zisofs_zlib_workspace; + mutex_lock(&zisofs_zlib_lock); - csize = cend-cstart; - - if (csize > deflateBound(1UL << zisofs_block_shift)) - goto eio; - - /* Now page[] contains an array of pages, any of which can be NULL, - and the locks on which we hold. We should now read the data and - release the pages. If the pages are NULL the decompressed data - for that particular page should be discarded. */ - - if ( csize == 0 ) { - /* This data block is empty. */ - - for ( fpage = 0 ; fpage < maxpage ; fpage++ ) { - if ( (page = pages[fpage]) != NULL ) { - memset(page_address(page), 0, PAGE_CACHE_SIZE); - - flush_dcache_page(page); - SetPageUptodate(page); - kunmap(page); - unlock_page(page); - if ( fpage == xpage ) - err = 0; /* The critical page */ - else - page_cache_release(page); + zerr = zlib_inflateInit(&stream); + if (zerr != Z_OK) { + if (zerr == Z_MEM_ERROR) + *errp = -ENOMEM; + else + *errp = -EIO; + printk(KERN_DEBUG "zisofs: zisofs_inflateInit returned %d\n", + zerr); + goto z_eio; + } + + while (curpage < pcount && curbh < haveblocks && + zerr != Z_STREAM_END) { + if (!stream.avail_out) { + if (pages[curpage]) { + stream.next_out = page_address(pages[curpage]) + + poffset; + stream.avail_out = PAGE_CACHE_SIZE - poffset; + poffset = 0; + } else { + stream.next_out = (void *)&zisofs_sink_page; + stream.avail_out = PAGE_CACHE_SIZE; } } - } else { - /* This data block is compressed. */ - z_stream stream; - int bail = 0, left_out = -1; - int zerr; - int needblocks = (csize + (cstart & bufmask) + bufmask) >> bufshift; - int haveblocks; - struct buffer_head *bhs[needblocks+1]; - struct buffer_head **bhptr; - - /* Because zlib is not thread-safe, do all the I/O at the top. */ - - blockptr = cstart >> bufshift; - memset(bhs, 0, (needblocks+1)*sizeof(struct buffer_head *)); - haveblocks = isofs_get_blocks(inode, blockptr, bhs, needblocks); - ll_rw_block(READ, haveblocks, bhs); - - bhptr = &bhs[0]; - bh = *bhptr++; - - /* First block is special since it may be fractional. - We also wait for it before grabbing the zlib - semaphore; odds are that the subsequent blocks are - going to come in in short order so we don't hold - the zlib semaphore longer than necessary. */ - - if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) { - printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld\n", - fpage, xpage, csize); - goto b_eio; - } - stream.next_in = bh->b_data + (cstart & bufmask); - stream.avail_in = min(bufsize-(cstart & bufmask), csize); - csize -= stream.avail_in; - - stream.workspace = zisofs_zlib_workspace; - down(&zisofs_zlib_semaphore); - - zerr = zlib_inflateInit(&stream); - if ( zerr != Z_OK ) { - if ( err && zerr == Z_MEM_ERROR ) - err = -ENOMEM; - printk(KERN_DEBUG "zisofs: zisofs_inflateInit returned %d\n", - zerr); - goto z_eio; + if (!stream.avail_in) { + wait_on_buffer(bhs[curbh]); + if (!buffer_uptodate(bhs[curbh])) { + *errp = -EIO; + break; + } + stream.next_in = bhs[curbh]->b_data + + (block_start & bufmask); + stream.avail_in = min_t(unsigned, bufsize - + (block_start & bufmask), + block_size); + block_size -= stream.avail_in; + block_start = 0; } - while ( !bail && fpage < maxpage ) { - page = pages[fpage]; - if ( page ) - stream.next_out = page_address(page); - else - stream.next_out = (void *)&zisofs_sink_page; - stream.avail_out = PAGE_CACHE_SIZE; - - while ( stream.avail_out ) { - int ao, ai; - if ( stream.avail_in == 0 && left_out ) { - if ( !csize ) { - printk(KERN_WARNING "zisofs: ZF read beyond end of input\n"); - bail = 1; - break; - } else { - bh = *bhptr++; - if ( !bh || - (wait_on_buffer(bh), !buffer_uptodate(bh)) ) { - /* Reached an EIO */ - printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld\n", - fpage, xpage, csize); - - bail = 1; - break; - } - stream.next_in = bh->b_data; - stream.avail_in = min(csize,bufsize); - csize -= stream.avail_in; - } - } - ao = stream.avail_out; ai = stream.avail_in; - zerr = zlib_inflate(&stream, Z_SYNC_FLUSH); - left_out = stream.avail_out; - if ( zerr == Z_BUF_ERROR && stream.avail_in == 0 ) - continue; - if ( zerr != Z_OK ) { - /* EOF, error, or trying to read beyond end of input */ - if ( err && zerr == Z_MEM_ERROR ) - err = -ENOMEM; - if ( zerr != Z_STREAM_END ) - printk(KERN_DEBUG "zisofs: zisofs_inflate returned %d, inode = %lu, index = %lu, fpage = %d, xpage = %d, avail_in = %d, avail_out = %d, ai = %d, ao = %d\n", - zerr, inode->i_ino, index, - fpage, xpage, - stream.avail_in, stream.avail_out, - ai, ao); - bail = 1; - break; + while (stream.avail_out && stream.avail_in) { + zerr = zlib_inflate(&stream, Z_SYNC_FLUSH); + if (zerr == Z_BUF_ERROR && stream.avail_in == 0) + break; + if (zerr == Z_STREAM_END) + break; + if (zerr != Z_OK) { + /* EOF, error, or trying to read beyond end of input */ + if (zerr == Z_MEM_ERROR) + *errp = -ENOMEM; + else { + printk(KERN_DEBUG + "zisofs: zisofs_inflate returned" + " %d, inode = %lu," + " page idx = %d, bh idx = %d," + " avail_in = %d," + " avail_out = %d\n", + zerr, inode->i_ino, curpage, + curbh, stream.avail_in, + stream.avail_out); + *errp = -EIO; } + goto inflate_out; } + } - if ( stream.avail_out && zerr == Z_STREAM_END ) { - /* Fractional page written before EOF. This may - be the last page in the file. */ - memset(stream.next_out, 0, stream.avail_out); - stream.avail_out = 0; + if (!stream.avail_out) { + /* This page completed */ + if (pages[curpage]) { + flush_dcache_page(pages[curpage]); + SetPageUptodate(pages[curpage]); } + curpage++; + } + if (!stream.avail_in) + curbh++; + } +inflate_out: + zlib_inflateEnd(&stream); - if ( !stream.avail_out ) { - /* This page completed */ - if ( page ) { - flush_dcache_page(page); - SetPageUptodate(page); - kunmap(page); - unlock_page(page); - if ( fpage == xpage ) - err = 0; /* The critical page */ - else - page_cache_release(page); - } - fpage++; - } +z_eio: + mutex_unlock(&zisofs_zlib_lock); + +b_eio: + for (i = 0; i < haveblocks; i++) + brelse(bhs[i]); + return stream.total_out; +} + +/* + * Uncompress data so that pages[full_page] is fully uptodate and possibly + * fills in other pages if we have data for them. + */ +static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount, + struct page **pages) +{ + loff_t start_off, end_off; + loff_t block_start, block_end; + unsigned int header_size = ISOFS_I(inode)->i_format_parm[0]; + unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1]; + unsigned int blockptr; + loff_t poffset = 0; + blkcnt_t cstart_block, cend_block; + struct buffer_head *bh; + unsigned int blkbits = ISOFS_BUFFER_BITS(inode); + unsigned int blksize = 1 << blkbits; + int err; + loff_t ret; + + BUG_ON(!pages[full_page]); + + /* + * We want to read at least 'full_page' page. Because we have to + * uncompress the whole compression block anyway, fill the surrounding + * pages with the data we have anyway... + */ + start_off = page_offset(pages[full_page]); + end_off = min_t(loff_t, start_off + PAGE_CACHE_SIZE, inode->i_size); + + cstart_block = start_off >> zisofs_block_shift; + cend_block = (end_off + (1 << zisofs_block_shift) - 1) + >> zisofs_block_shift; + + WARN_ON(start_off - (full_page << PAGE_CACHE_SHIFT) != + ((cstart_block << zisofs_block_shift) & PAGE_CACHE_MASK)); + + /* Find the pointer to this specific chunk */ + /* Note: we're not using isonum_731() here because the data is known aligned */ + /* Note: header_size is in 32-bit words (4 bytes) */ + blockptr = (header_size + cstart_block) << 2; + bh = isofs_bread(inode, blockptr >> blkbits); + if (!bh) + return -EIO; + block_start = le32_to_cpu(*(__le32 *) + (bh->b_data + (blockptr & (blksize - 1)))); + + while (cstart_block < cend_block && pcount > 0) { + /* Load end of the compressed block in the file */ + blockptr += 4; + /* Traversed to next block? */ + if (!(blockptr & (blksize - 1))) { + brelse(bh); + + bh = isofs_bread(inode, blockptr >> blkbits); + if (!bh) + return -EIO; + } + block_end = le32_to_cpu(*(__le32 *) + (bh->b_data + (blockptr & (blksize - 1)))); + if (block_start > block_end) { + brelse(bh); + return -EIO; + } + err = 0; + ret = zisofs_uncompress_block(inode, block_start, block_end, + pcount, pages, poffset, &err); + poffset += ret; + pages += poffset >> PAGE_CACHE_SHIFT; + pcount -= poffset >> PAGE_CACHE_SHIFT; + full_page -= poffset >> PAGE_CACHE_SHIFT; + poffset &= ~PAGE_CACHE_MASK; + + if (err) { + brelse(bh); + /* + * Did we finish reading the page we really wanted + * to read? + */ + if (full_page < 0) + return 0; + return err; } - zlib_inflateEnd(&stream); - z_eio: - up(&zisofs_zlib_semaphore); + block_start = block_end; + cstart_block++; + } + + if (poffset && *pages) { + memset(page_address(*pages) + poffset, 0, + PAGE_CACHE_SIZE - poffset); + flush_dcache_page(*pages); + SetPageUptodate(*pages); + } + return 0; +} + +/* + * When decompressing, we typically obtain more than one page + * per reference. We inject the additional pages into the page + * cache as a form of readahead. + */ +static int zisofs_readpage(struct file *file, struct page *page) +{ + struct inode *inode = file_inode(file); + struct address_space *mapping = inode->i_mapping; + int err; + int i, pcount, full_page; + unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1]; + unsigned int zisofs_pages_per_cblock = + PAGE_CACHE_SHIFT <= zisofs_block_shift ? + (1 << (zisofs_block_shift - PAGE_CACHE_SHIFT)) : 0; + struct page *pages[max_t(unsigned, zisofs_pages_per_cblock, 1)]; + pgoff_t index = page->index, end_index; + + end_index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + /* + * If this page is wholly outside i_size we just return zero; + * do_generic_file_read() will handle this for us + */ + if (index >= end_index) { + SetPageUptodate(page); + unlock_page(page); + return 0; + } - b_eio: - for ( i = 0 ; i < haveblocks ; i++ ) { - if ( bhs[i] ) - brelse(bhs[i]); + if (PAGE_CACHE_SHIFT <= zisofs_block_shift) { + /* We have already been given one page, this is the one + we must do. */ + full_page = index & (zisofs_pages_per_cblock - 1); + pcount = min_t(int, zisofs_pages_per_cblock, + end_index - (index & ~(zisofs_pages_per_cblock - 1))); + index -= full_page; + } else { + full_page = 0; + pcount = 1; + } + pages[full_page] = page; + + for (i = 0; i < pcount; i++, index++) { + if (i != full_page) + pages[i] = grab_cache_page_nowait(mapping, index); + if (pages[i]) { + ClearPageError(pages[i]); + kmap(pages[i]); } } -eio: + err = zisofs_fill_pages(inode, full_page, pcount, pages); /* Release any residual pages, do not SetPageUptodate */ - while ( fpage < maxpage ) { - page = pages[fpage]; - if ( page ) { - flush_dcache_page(page); - if ( fpage == xpage ) - SetPageError(page); - kunmap(page); - unlock_page(page); - if ( fpage != xpage ) - page_cache_release(page); + for (i = 0; i < pcount; i++) { + if (pages[i]) { + flush_dcache_page(pages[i]); + if (i == full_page && err) + SetPageError(pages[i]); + kunmap(pages[i]); + unlock_page(pages[i]); + if (i != full_page) + page_cache_release(pages[i]); } - fpage++; } /* At this point, err contains 0 or -EIO depending on the "critical" page */ @@ -317,31 +365,16 @@ const struct address_space_operations zisofs_aops = { /* No bmap operation supported */ }; -static int initialized; - int __init zisofs_init(void) { - if ( initialized ) { - printk("zisofs_init: called more than once\n"); - return 0; - } - zisofs_zlib_workspace = vmalloc(zlib_inflate_workspacesize()); if ( !zisofs_zlib_workspace ) return -ENOMEM; - init_MUTEX(&zisofs_zlib_semaphore); - initialized = 1; return 0; } void zisofs_cleanup(void) { - if ( !initialized ) { - printk("zisofs_cleanup: called without initialization\n"); - return; - } - vfree(zisofs_zlib_workspace); - initialized = 0; } diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 27e276987fd..b943cbd963b 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -7,34 +7,18 @@ * * Steve Beynon : Missing last directory entries fixed * (stephen@askone.demon.co.uk) : 21st June 1996 - * + * * isofs directory handling functions */ -#include <linux/smp_lock.h> +#include <linux/gfp.h> #include "isofs.h" -static int isofs_readdir(struct file *, void *, filldir_t); - -const struct file_operations isofs_dir_operations = -{ - .read = generic_read_dir, - .readdir = isofs_readdir, -}; - -/* - * directories can handle most operations... - */ -struct inode_operations isofs_dir_inode_operations = -{ - .lookup = isofs_lookup, -}; - int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) { char * old = de->name; int len = de->name_len[0]; int i; - + for (i = 0; i < len; i++) { unsigned char c = old[i]; if (!c) @@ -62,22 +46,27 @@ int isofs_name_translate(struct iso_directory_record *de, char *new, struct inod } /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */ -int get_acorn_filename(struct iso_directory_record * de, - char * retname, struct inode * inode) +int get_acorn_filename(struct iso_directory_record *de, + char *retname, struct inode *inode) { int std; - unsigned char * chr; + unsigned char *chr; int retnamlen = isofs_name_translate(de, retname, inode); - if (retnamlen == 0) return 0; + + if (retnamlen == 0) + return 0; std = sizeof(struct iso_directory_record) + de->name_len[0]; - if (std & 1) std++; - if ((*((unsigned char *) de) - std) != 32) return retnamlen; + if (std & 1) + std++; + if ((*((unsigned char *) de) - std) != 32) + return retnamlen; chr = ((unsigned char *) de) + std; - if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen; - if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!'; + if (strncmp(chr, "ARCHIMEDES", 10)) + return retnamlen; + if ((*retname == '_') && ((chr[19] & 1) == 1)) + *retname = '!'; if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff) - && ((chr[12] & 0xf0) == 0xf0)) - { + && ((chr[12] & 0xf0) == 0xf0)) { retname[retnamlen] = ','; sprintf(retname+retnamlen+1, "%3.3x", ((chr[12] & 0xf) << 8) | chr[11]); @@ -89,9 +78,9 @@ int get_acorn_filename(struct iso_directory_record * de, /* * This should _really_ be cleaned up some day.. */ -static int do_isofs_readdir(struct inode *inode, struct file *filp, - void *dirent, filldir_t filldir, - char * tmpname, struct iso_directory_record * tmpde) +static int do_isofs_readdir(struct inode *inode, struct file *file, + struct dir_context *ctx, + char *tmpname, struct iso_directory_record *tmpde) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); @@ -105,10 +94,10 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, struct iso_directory_record *de; struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); - offset = filp->f_pos & (bufsize - 1); - block = filp->f_pos >> bufbits; + offset = ctx->pos & (bufsize - 1); + block = ctx->pos >> bufbits; - while (filp->f_pos < inode->i_size) { + while (ctx->pos < inode->i_size) { int de_len; if (!bh) { @@ -119,17 +108,19 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, de = (struct iso_directory_record *) (bh->b_data + offset); - de_len = *(unsigned char *) de; + de_len = *(unsigned char *)de; - /* 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 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 (de_len == 0) { brelse(bh); bh = NULL; - filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); - block = filp->f_pos >> bufbits; + ctx->pos = (ctx->pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); + block = ctx->pos >> bufbits; offset = 0; continue; } @@ -154,28 +145,35 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, } de = tmpde; } + /* Basic sanity check, whether name doesn't exceed dir entry */ + if (de_len < de->name_len[0] + + sizeof(struct iso_directory_record)) { + printk(KERN_NOTICE "iso9660: Corrupted directory entry" + " in block %lu of inode %lu\n", block, + inode->i_ino); + return -EIO; + } if (first_de) { isofs_normalize_block_and_offset(de, - &block_saved, - &offset_saved); + &block_saved, + &offset_saved); inode_number = isofs_get_ino(block_saved, - offset_saved, - bufbits); + offset_saved, bufbits); } if (de->flags[-sbi->s_high_sierra] & 0x80) { first_de = 0; - filp->f_pos += de_len; + ctx->pos += de_len; continue; } first_de = 1; /* Handle the case of the '.' directory */ if (de->name_len[0] == 1 && de->name[0] == 0) { - if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) + if (!dir_emit_dot(file, ctx)) break; - filp->f_pos += de_len; + ctx->pos += de_len; continue; } @@ -183,10 +181,9 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, /* Handle the case of the '..' directory */ if (de->name_len[0] == 1 && de->name[0] == 1) { - inode_number = parent_ino(filp->f_dentry); - if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0) + if (!dir_emit_dotdot(file, ctx)) break; - filp->f_pos += de_len; + ctx->pos += de_len; continue; } @@ -197,11 +194,10 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, * Do not report hidden files if so instructed, or associated * files unless instructed to do so */ - if ((sbi->s_hide == 'y' && - (de->flags[-sbi->s_high_sierra] & 1)) || - (sbi->s_showassoc =='n' && + if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) || + (!sbi->s_showassoc && (de->flags[-sbi->s_high_sierra] & 4))) { - filp->f_pos += de_len; + ctx->pos += de_len; continue; } @@ -233,14 +229,15 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, } } if (len > 0) { - if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0) + if (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN)) break; } - filp->f_pos += de_len; + ctx->pos += de_len; continue; } - if (bh) brelse(bh); + if (bh) + brelse(bh); return 0; } @@ -249,25 +246,38 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, * handling split directory entries.. The real work is done by * "do_isofs_readdir()". */ -static int isofs_readdir(struct file *filp, - void *dirent, filldir_t filldir) +static int isofs_readdir(struct file *file, struct dir_context *ctx) { int result; - char * tmpname; - struct iso_directory_record * tmpde; - struct inode *inode = filp->f_dentry->d_inode; - + char *tmpname; + struct iso_directory_record *tmpde; + struct inode *inode = file_inode(file); tmpname = (char *)__get_free_page(GFP_KERNEL); if (tmpname == NULL) return -ENOMEM; - lock_kernel(); tmpde = (struct iso_directory_record *) (tmpname+1024); - result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); + result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde); free_page((unsigned long) tmpname); - unlock_kernel(); return result; } + +const struct file_operations isofs_dir_operations = +{ + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = isofs_readdir, +}; + +/* + * directories can handle most operations... + */ +const struct inode_operations isofs_dir_inode_operations = +{ + .lookup = isofs_lookup, +}; + + diff --git a/fs/isofs/export.c b/fs/isofs/export.c index 4af856a7fda..12088d8de3f 100644 --- a/fs/isofs/export.c +++ b/fs/isofs/export.c @@ -9,7 +9,7 @@ * * The following files are helpful: * - * Documentation/filesystems/Exporting + * Documentation/filesystems/nfs/Exporting * fs/exportfs/expfs.c. */ @@ -22,34 +22,17 @@ isofs_export_iget(struct super_block *sb, __u32 generation) { struct inode *inode; - struct dentry *result; + if (block == 0) return ERR_PTR(-ESTALE); inode = isofs_iget(sb, block, offset); - if (inode == NULL) - return ERR_PTR(-ENOMEM); - if (is_bad_inode(inode) - || (generation && inode->i_generation != generation)) - { + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (generation && inode->i_generation != generation) { iput(inode); return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; -} - -static struct dentry * -isofs_export_get_dentry(struct super_block *sb, void *vobjp) -{ - __u32 *objp = vobjp; - unsigned long block = objp[0]; - unsigned long offset = objp[1]; - __u32 generation = objp[2]; - return isofs_export_iget(sb, block, offset, generation); + return d_obtain_alias(inode); } /* This function is surprisingly simple. The trick is understanding @@ -63,7 +46,6 @@ static struct dentry *isofs_export_get_parent(struct dentry *child) unsigned long parent_offset = 0; struct inode *child_inode = child->d_inode; struct iso_inode_info *e_child_inode = ISOFS_I(child_inode); - struct inode *parent_inode = NULL; struct iso_directory_record *de = NULL; struct buffer_head * bh = NULL; struct dentry *rv = NULL; @@ -116,36 +98,20 @@ static struct dentry *isofs_export_get_parent(struct dentry *child) /* Normalize */ isofs_normalize_block_and_offset(de, &parent_block, &parent_offset); - /* Get the inode. */ - parent_inode = isofs_iget(child_inode->i_sb, - parent_block, - parent_offset); - if (parent_inode == NULL) { - rv = ERR_PTR(-EACCES); - goto out; - } - - /* Allocate the dentry. */ - rv = d_alloc_anon(parent_inode); - if (rv == NULL) { - rv = ERR_PTR(-ENOMEM); - goto out; - } - + rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block, + parent_offset)); out: - if (bh) { + if (bh) brelse(bh); - } return rv; } static int -isofs_export_encode_fh(struct dentry *dentry, +isofs_export_encode_fh(struct inode *inode, __u32 *fh32, int *max_len, - int connectable) + struct inode *parent) { - struct inode * inode = dentry->d_inode; struct iso_inode_info * ei = ISOFS_I(inode); int len = *max_len; int type = 1; @@ -157,24 +123,25 @@ isofs_export_encode_fh(struct dentry *dentry, * offset of the inode and the upper 16 bits of fh32[1] to * hold the offset of the parent. */ - - if (len < 3 || (connectable && len < 5)) - return 255; + if (parent && (len < 5)) { + *max_len = 5; + return FILEID_INVALID; + } else if (len < 3) { + *max_len = 3; + return FILEID_INVALID; + } len = 3; fh32[0] = ei->i_iget5_block; fh16[2] = (__u16)ei->i_iget5_offset; /* fh16 [sic] */ + fh16[3] = 0; /* avoid leaking uninitialized data */ fh32[2] = inode->i_generation; - if (connectable && !S_ISDIR(inode->i_mode)) { - struct inode *parent; + if (parent) { struct iso_inode_info *eparent; - spin_lock(&dentry->d_lock); - parent = dentry->d_parent->d_inode; eparent = ISOFS_I(parent); fh32[3] = eparent->i_iget5_block; fh16[3] = (__u16)eparent->i_iget5_offset; /* fh16 [sic] */ fh32[4] = parent->i_generation; - spin_unlock(&dentry->d_lock); len = 5; type = 2; } @@ -182,43 +149,44 @@ isofs_export_encode_fh(struct dentry *dentry, return type; } +struct isofs_fid { + u32 block; + u16 offset; + u16 parent_offset; + u32 generation; + u32 parent_block; + u32 parent_generation; +}; -static struct dentry * -isofs_export_decode_fh(struct super_block *sb, - __u32 *fh32, - int fh_len, - int fileid_type, - int (*acceptable)(void *context, struct dentry *de), - void *context) +static struct dentry *isofs_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) { - __u16 *fh16 = (__u16*)fh32; - __u32 child[3]; /* The child is what triggered all this. */ - __u32 parent[3]; /* The parent is just along for the ride. */ + struct isofs_fid *ifid = (struct isofs_fid *)fid; - if (fh_len < 3 || fileid_type > 2) + if (fh_len < 3 || fh_type > 2) return NULL; - child[0] = fh32[0]; - child[1] = fh16[2]; /* fh16 [sic] */ - child[2] = fh32[2]; - - parent[0] = 0; - parent[1] = 0; - parent[2] = 0; - if (fileid_type == 2) { - if (fh_len > 2) parent[0] = fh32[3]; - parent[1] = fh16[3]; /* fh16 [sic] */ - if (fh_len > 4) parent[2] = fh32[4]; - } - - return sb->s_export_op->find_exported_dentry(sb, child, parent, - acceptable, context); + return isofs_export_iget(sb, ifid->block, ifid->offset, + ifid->generation); } +static struct dentry *isofs_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct isofs_fid *ifid = (struct isofs_fid *)fid; + + if (fh_len < 2 || fh_type != 2) + return NULL; + + return isofs_export_iget(sb, + fh_len > 2 ? ifid->parent_block : 0, + ifid->parent_offset, + fh_len > 4 ? ifid->parent_generation : 0); +} -struct export_operations isofs_export_ops = { - .decode_fh = isofs_export_decode_fh, +const struct export_operations isofs_export_ops = { .encode_fh = isofs_export_encode_fh, - .get_dentry = isofs_export_get_dentry, + .fh_to_dentry = isofs_fh_to_dentry, + .fh_to_parent = isofs_fh_to_parent, .get_parent = isofs_export_get_parent, }; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 4527692f432..4556ce1af5b 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -3,7 +3,7 @@ * * (C) 1991 Linus Torvalds - minix filesystem * 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem. - * 1994 Eberhard Moenkeberg - multi session handling. + * 1994 Eberhard Mönkeberg - multi session handling. * 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs. * 1997 Gordon Chaffee - Joliet CDs * 1998 Eric Lammerts - ISO 9660 Level 3 @@ -17,36 +17,43 @@ #include <linux/slab.h> #include <linux/nls.h> #include <linux/ctype.h> -#include <linux/smp_lock.h> #include <linux/statfs.h> #include <linux/cdrom.h> #include <linux/parser.h> +#include <linux/mpage.h> +#include <linux/user_namespace.h> #include "isofs.h" #include "zisofs.h" #define BEQUIET -static int isofs_hashi(struct dentry *parent, struct qstr *qstr); -static int isofs_hash(struct dentry *parent, struct qstr *qstr); -static int isofs_dentry_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int isofs_dentry_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int isofs_hashi(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash(const struct dentry *parent, struct qstr *qstr); +static int isofs_dentry_cmpi(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); +static int isofs_dentry_cmp(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); #ifdef CONFIG_JOLIET -static int isofs_hashi_ms(struct dentry *parent, struct qstr *qstr); -static int isofs_hash_ms(struct dentry *parent, struct qstr *qstr); -static int isofs_dentry_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); -static int isofs_dentry_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int isofs_hashi_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_dentry_cmpi_ms(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); +static int isofs_dentry_cmp_ms(const struct dentry *parent, + const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name); #endif static void isofs_put_super(struct super_block *sb) { struct isofs_sb_info *sbi = ISOFS_SB(sb); + #ifdef CONFIG_JOLIET - if (sbi->s_nls_iocharset) { - unload_nls(sbi->s_nls_iocharset); - sbi->s_nls_iocharset = NULL; - } + unload_nls(sbi->s_nls_iocharset); #endif kfree(sbi); @@ -54,41 +61,45 @@ static void isofs_put_super(struct super_block *sb) return; } -static void isofs_read_inode(struct inode *); +static int isofs_read_inode(struct inode *); static int isofs_statfs (struct dentry *, struct kstatfs *); -static kmem_cache_t *isofs_inode_cachep; +static struct kmem_cache *isofs_inode_cachep; static struct inode *isofs_alloc_inode(struct super_block *sb) { struct iso_inode_info *ei; - ei = kmem_cache_alloc(isofs_inode_cachep, SLAB_KERNEL); + ei = kmem_cache_alloc(isofs_inode_cachep, GFP_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; } -static void isofs_destroy_inode(struct inode *inode) +static void isofs_i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode)); } -static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags) +static void isofs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, isofs_i_callback); +} + +static void init_once(void *foo) { struct iso_inode_info *ei = foo; - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) - inode_init_once(&ei->vfs_inode); + inode_init_once(&ei->vfs_inode); } - -static int init_inodecache(void) + +static int __init init_inodecache(void) { isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", - sizeof(struct iso_inode_info), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - init_once, NULL); + sizeof(struct iso_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); if (isofs_inode_cachep == NULL) return -ENOMEM; return 0; @@ -96,27 +107,33 @@ static int init_inodecache(void) static void destroy_inodecache(void) { + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); kmem_cache_destroy(isofs_inode_cachep); } static int isofs_remount(struct super_block *sb, int *flags, char *data) { - /* we probably want a lot more here */ - *flags |= MS_RDONLY; + sync_filesystem(sb); + if (!(*flags & MS_RDONLY)) + return -EROFS; return 0; } -static struct super_operations isofs_sops = { +static const struct super_operations isofs_sops = { .alloc_inode = isofs_alloc_inode, .destroy_inode = isofs_destroy_inode, - .read_inode = isofs_read_inode, .put_super = isofs_put_super, .statfs = isofs_statfs, .remount_fs = isofs_remount, + .show_options = generic_show_options, }; -static struct dentry_operations isofs_dentry_ops[] = { +static const struct dentry_operations isofs_dentry_ops[] = { { .d_hash = isofs_hash, .d_compare = isofs_dentry_cmp, @@ -138,30 +155,34 @@ static struct dentry_operations isofs_dentry_ops[] = { }; struct iso9660_options{ - char map; - char rock; - char joliet; - char cruft; - char hide; - char showassoc; - char nocompress; + unsigned int rock:1; + unsigned int joliet:1; + unsigned int cruft:1; + unsigned int hide:1; + unsigned int showassoc:1; + unsigned int nocompress:1; + unsigned int overriderockperm:1; + unsigned int uid_set:1; + unsigned int gid_set:1; + unsigned int utf8:1; + unsigned char map; unsigned char check; unsigned int blocksize; - mode_t mode; - gid_t gid; - uid_t uid; + umode_t fmode; + umode_t dmode; + kgid_t gid; + kuid_t uid; char *iocharset; - unsigned char utf8; - /* LVE */ - s32 session; - s32 sbsector; + /* LVE */ + s32 session; + s32 sbsector; }; /* * Compute the hash for the isofs name corresponding to the dentry. */ static int -isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms) +isofs_hash_common(struct qstr *qstr, int ms) { const char *name; int len; @@ -182,7 +203,7 @@ isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms) * Compute the hash for the isofs name corresponding to the dentry. */ static int -isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) +isofs_hashi_common(struct qstr *qstr, int ms) { const char *name; int len; @@ -199,7 +220,7 @@ isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) hash = init_name_hash(); while (len--) { c = tolower(*name++); - hash = partial_name_hash(tolower(c), hash); + hash = partial_name_hash(c, hash); } qstr->hash = end_name_hash(hash); @@ -207,100 +228,86 @@ isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) } /* - * Case insensitive compare of two isofs names. - */ -static int isofs_dentry_cmpi_common(struct dentry *dentry, struct qstr *a, - struct qstr *b, int ms) -{ - int alen, blen; - - /* A filename cannot end in '.' or we treat it like it has none */ - alen = a->len; - blen = b->len; - if (ms) { - while (alen && a->name[alen-1] == '.') - alen--; - while (blen && b->name[blen-1] == '.') - blen--; - } - if (alen == blen) { - if (strnicmp(a->name, b->name, alen) == 0) - return 0; - } - return 1; -} - -/* - * Case sensitive compare of two isofs names. + * Compare of two isofs names. */ -static int isofs_dentry_cmp_common(struct dentry *dentry, struct qstr *a, - struct qstr *b, int ms) +static int isofs_dentry_cmp_common( + unsigned int len, const char *str, + const struct qstr *name, int ms, int ci) { int alen, blen; /* A filename cannot end in '.' or we treat it like it has none */ - alen = a->len; - blen = b->len; + alen = name->len; + blen = len; if (ms) { - while (alen && a->name[alen-1] == '.') + while (alen && name->name[alen-1] == '.') alen--; - while (blen && b->name[blen-1] == '.') + while (blen && str[blen-1] == '.') blen--; } if (alen == blen) { - if (strncmp(a->name, b->name, alen) == 0) - return 0; + if (ci) { + if (strnicmp(name->name, str, alen) == 0) + return 0; + } else { + if (strncmp(name->name, str, alen) == 0) + return 0; + } } return 1; } static int -isofs_hash(struct dentry *dentry, struct qstr *qstr) +isofs_hash(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hash_common(dentry, qstr, 0); + return isofs_hash_common(qstr, 0); } static int -isofs_hashi(struct dentry *dentry, struct qstr *qstr) +isofs_hashi(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hashi_common(dentry, qstr, 0); + return isofs_hashi_common(qstr, 0); } static int -isofs_dentry_cmp(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmp(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmp_common(dentry, a, b, 0); + return isofs_dentry_cmp_common(len, str, name, 0, 0); } static int -isofs_dentry_cmpi(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmpi_common(dentry, a, b, 0); + return isofs_dentry_cmp_common(len, str, name, 0, 1); } #ifdef CONFIG_JOLIET static int -isofs_hash_ms(struct dentry *dentry, struct qstr *qstr) +isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hash_common(dentry, qstr, 1); + return isofs_hash_common(qstr, 1); } static int -isofs_hashi_ms(struct dentry *dentry, struct qstr *qstr) +isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr) { - return isofs_hashi_common(dentry, qstr, 1); + return isofs_hashi_common(qstr, 1); } static int -isofs_dentry_cmp_ms(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmp_ms(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmp_common(dentry, a, b, 1); + return isofs_dentry_cmp_common(len, str, name, 1, 0); } static int -isofs_dentry_cmpi_ms(struct dentry *dentry,struct qstr *a,struct qstr *b) +isofs_dentry_cmpi_ms(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) { - return isofs_dentry_cmpi_common(dentry, a, b, 1); + return isofs_dentry_cmp_common(len, str, name, 1, 1); } #endif @@ -308,10 +315,10 @@ enum { Opt_block, Opt_check_r, Opt_check_s, Opt_cruft, Opt_gid, Opt_ignore, Opt_iocharset, Opt_map_a, Opt_map_n, Opt_map_o, Opt_mode, Opt_nojoliet, Opt_norock, Opt_sb, Opt_session, Opt_uid, Opt_unhide, Opt_utf8, Opt_err, - Opt_nocompress, Opt_hide, Opt_showassoc, + Opt_nocompress, Opt_hide, Opt_showassoc, Opt_dmode, Opt_overriderockperm, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_norock, "norock"}, {Opt_nojoliet, "nojoliet"}, {Opt_unhide, "unhide"}, @@ -335,6 +342,8 @@ static match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_mode, "mode=%u"}, + {Opt_dmode, "dmode=%u"}, + {Opt_overriderockperm, "overriderockperm"}, {Opt_block, "block=%u"}, {Opt_ignore, "conv=binary"}, {Opt_ignore, "conv=b"}, @@ -354,22 +363,22 @@ static int parse_options(char *options, struct iso9660_options *popt) int option; popt->map = 'n'; - popt->rock = 'y'; - popt->joliet = 'y'; - popt->cruft = 'n'; - popt->hide = 'n'; - popt->showassoc = 'n'; + popt->rock = 1; + popt->joliet = 1; + popt->cruft = 0; + popt->hide = 0; + popt->showassoc = 0; popt->check = 'u'; /* unset */ popt->nocompress = 0; popt->blocksize = 1024; - popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could - be shared with DOS machines so - virtually anything could be - a valid executable. */ - popt->gid = 0; - popt->uid = 0; + popt->fmode = popt->dmode = ISOFS_INVALID_MODE; + popt->uid_set = 0; + popt->gid_set = 0; + popt->gid = GLOBAL_ROOT_GID; + popt->uid = GLOBAL_ROOT_UID; popt->iocharset = NULL; popt->utf8 = 0; + popt->overriderockperm = 0; popt->session=-1; popt->sbsector=-1; if (!options) @@ -386,20 +395,20 @@ static int parse_options(char *options, struct iso9660_options *popt) token = match_token(p, tokens, args); switch (token) { case Opt_norock: - popt->rock = 'n'; + popt->rock = 0; break; case Opt_nojoliet: - popt->joliet = 'n'; + popt->joliet = 0; break; case Opt_hide: - popt->hide = 'y'; + popt->hide = 1; break; case Opt_unhide: case Opt_showassoc: - popt->showassoc = 'y'; + popt->showassoc = 1; break; case Opt_cruft: - popt->cruft = 'y'; + popt->cruft = 1; break; case Opt_utf8: popt->utf8 = 1; @@ -442,17 +451,31 @@ static int parse_options(char *options, struct iso9660_options *popt) case Opt_uid: if (match_int(&args[0], &option)) return 0; - popt->uid = option; + popt->uid = make_kuid(current_user_ns(), option); + if (!uid_valid(popt->uid)) + return 0; + popt->uid_set = 1; break; case Opt_gid: if (match_int(&args[0], &option)) return 0; - popt->gid = option; + popt->gid = make_kgid(current_user_ns(), option); + if (!gid_valid(popt->gid)) + return 0; + popt->gid_set = 1; break; case Opt_mode: if (match_int(&args[0], &option)) return 0; - popt->mode = option; + popt->fmode = option; + break; + case Opt_dmode: + if (match_int(&args[0], &option)) + return 0; + popt->dmode = option; + break; + case Opt_overriderockperm: + popt->overriderockperm = 1; break; case Opt_block: if (match_int(&args[0], &option)) @@ -505,34 +528,62 @@ static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) Te.cdte_format=CDROM_LBA; i = ioctl_by_bdev(bdev, CDROMREADTOCENTRY, (unsigned long) &Te); if (!i) { - printk(KERN_DEBUG "Session %d start %d type %d\n", - session, Te.cdte_addr.lba, - Te.cdte_ctrl&CDROM_DATA_TRACK); + printk(KERN_DEBUG "ISOFS: Session %d start %d type %d\n", + session, Te.cdte_addr.lba, + Te.cdte_ctrl&CDROM_DATA_TRACK); if ((Te.cdte_ctrl&CDROM_DATA_TRACK) == 4) return Te.cdte_addr.lba; } - - printk(KERN_ERR "Invalid session number or type of track\n"); + + printk(KERN_ERR "ISOFS: Invalid session number or type of track\n"); } i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); if (session > 0) - printk(KERN_ERR "Invalid session number\n"); + printk(KERN_ERR "ISOFS: Invalid session number\n"); #if 0 - printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); + printk(KERN_DEBUG "isofs.inode: CDROMMULTISESSION: rc=%d\n",i); if (i==0) { - printk("isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no"); - printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); + printk(KERN_DEBUG "isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no"); + printk(KERN_DEBUG "isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); } #endif if (i==0) #if WE_OBEY_THE_WRITTEN_STANDARDS - if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ + if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif - vol_desc_start=ms_info.addr.lba; + vol_desc_start=ms_info.addr.lba; return vol_desc_start; } /* + * Check if root directory is empty (has less than 3 files). + * + * Used to detect broken CDs where ISO root directory is empty but Joliet root + * directory is OK. If such CD has Rock Ridge extensions, they will be disabled + * (and Joliet used instead) or else no files would be visible. + */ +static bool rootdir_empty(struct super_block *sb, unsigned long block) +{ + int offset = 0, files = 0, de_len; + struct iso_directory_record *de; + struct buffer_head *bh; + + bh = sb_bread(sb, block); + if (!bh) + return true; + while (files < 3) { + de = (struct iso_directory_record *) (bh->b_data + offset); + de_len = *(unsigned char *) de; + if (de_len == 0) + break; + files++; + offset += de_len; + } + brelse(bh); + return files < 3; +} + +/* * Initialize the superblock and read the root inode. * * Note: a check_disk_change() has been done immediately prior @@ -540,20 +591,22 @@ static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) */ static int isofs_fill_super(struct super_block *s, void *data, int silent) { - struct buffer_head * bh = NULL, *pri_bh = NULL; - struct hs_primary_descriptor * h_pri = NULL; - struct iso_primary_descriptor * pri = NULL; + 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 iso_blknum, block; - int orig_zonesize; - int table; - unsigned int vol_desc_start; - unsigned long first_data_zone; - struct inode * inode; - struct iso9660_options opt; - struct isofs_sb_info * sbi; + struct iso_directory_record *rootp; + struct inode *inode; + struct iso9660_options opt; + struct isofs_sb_info *sbi; + unsigned long first_data_zone; + int joliet_level = 0; + int iso_blknum, block; + int orig_zonesize; + int table, error = -EINVAL; + unsigned int vol_desc_start; + + save_mount_options(s, data); sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -579,72 +632,73 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) vol_desc_start = (opt.sbsector != -1) ? opt.sbsector : isofs_get_last_session(s,opt.session); - for (iso_blknum = vol_desc_start+16; - iso_blknum < vol_desc_start+100; iso_blknum++) - { - struct hs_volume_descriptor * hdp; - struct iso_volume_descriptor * vdp; - - block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits); - if (!(bh = sb_bread(s, block))) - goto out_no_read; - - vdp = (struct iso_volume_descriptor *)bh->b_data; - hdp = (struct hs_volume_descriptor *)bh->b_data; - - /* Due to the overlapping physical location of the descriptors, - * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure - * proper identification in this case, we first check for ISO. - */ - if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) { - if (isonum_711 (vdp->type) == ISO_VD_END) - break; - if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) { - if (pri == NULL) { - pri = (struct iso_primary_descriptor *)vdp; - /* Save the buffer in case we need it ... */ - pri_bh = bh; - bh = NULL; - } - } + for (iso_blknum = vol_desc_start+16; + iso_blknum < vol_desc_start+100; iso_blknum++) { + struct hs_volume_descriptor *hdp; + struct iso_volume_descriptor *vdp; + + block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits); + if (!(bh = sb_bread(s, block))) + goto out_no_read; + + vdp = (struct iso_volume_descriptor *)bh->b_data; + hdp = (struct hs_volume_descriptor *)bh->b_data; + + /* + * Due to the overlapping physical location of the descriptors, + * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure + * proper identification in this case, we first check for ISO. + */ + if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) { + if (isonum_711(vdp->type) == ISO_VD_END) + break; + if (isonum_711(vdp->type) == ISO_VD_PRIMARY) { + if (pri == NULL) { + pri = (struct iso_primary_descriptor *)vdp; + /* Save the buffer in case we need it ... */ + pri_bh = bh; + bh = NULL; + } + } #ifdef CONFIG_JOLIET - else if (isonum_711 (vdp->type) == ISO_VD_SUPPLEMENTARY) { - sec = (struct iso_supplementary_descriptor *)vdp; - if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { - if (opt.joliet == 'y') { - if (sec->escape[2] == 0x40) { - joliet_level = 1; - } else if (sec->escape[2] == 0x43) { - joliet_level = 2; - } else if (sec->escape[2] == 0x45) { - joliet_level = 3; - } - printk(KERN_DEBUG"ISO 9660 Extensions: Microsoft Joliet Level %d\n", - joliet_level); + else if (isonum_711(vdp->type) == ISO_VD_SUPPLEMENTARY) { + sec = (struct iso_supplementary_descriptor *)vdp; + if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { + if (opt.joliet) { + if (sec->escape[2] == 0x40) + joliet_level = 1; + else if (sec->escape[2] == 0x43) + joliet_level = 2; + else if (sec->escape[2] == 0x45) + joliet_level = 3; + + printk(KERN_DEBUG "ISO 9660 Extensions: " + "Microsoft Joliet Level %d\n", + joliet_level); + } + goto root_found; + } else { + /* Unknown supplementary volume descriptor */ + sec = NULL; + } } - goto root_found; - } else { - /* Unknown supplementary volume descriptor */ - sec = NULL; - } - } #endif - } else { - if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { - if (isonum_711 (hdp->type) != ISO_VD_PRIMARY) - goto out_freebh; - - sbi->s_high_sierra = 1; - opt.rock = 'n'; - h_pri = (struct hs_primary_descriptor *)vdp; - goto root_found; + } else { + if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { + if (isonum_711(hdp->type) != ISO_VD_PRIMARY) + goto out_freebh; + + sbi->s_high_sierra = 1; + opt.rock = 0; + h_pri = (struct hs_primary_descriptor *)vdp; + goto root_found; + } } - } - /* Just skip any volume descriptors we don't recognize */ + /* Just skip any volume descriptors we don't recognize */ - brelse(bh); - bh = NULL; + brelse(bh); + bh = NULL; } /* * If we fall through, either no volume descriptor was found, @@ -658,25 +712,25 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) root_found: - if (joliet_level && (pri == NULL || opt.rock == 'n')) { - /* This is the case of Joliet with the norock mount flag. - * A disc with both Joliet and Rock Ridge is handled later - */ - pri = (struct iso_primary_descriptor *) sec; + if (joliet_level && (pri == NULL || !opt.rock)) { + /* This is the case of Joliet with the norock mount flag. + * A disc with both Joliet and Rock Ridge is handled later + */ + pri = (struct iso_primary_descriptor *) sec; } if(sbi->s_high_sierra){ - rootp = (struct iso_directory_record *) h_pri->root_directory_record; - sbi->s_nzones = isonum_733 (h_pri->volume_space_size); - sbi->s_log_zone_size = isonum_723 (h_pri->logical_block_size); - sbi->s_max_size = isonum_733(h_pri->volume_space_size); + rootp = (struct iso_directory_record *) h_pri->root_directory_record; + sbi->s_nzones = isonum_733(h_pri->volume_space_size); + sbi->s_log_zone_size = isonum_723(h_pri->logical_block_size); + sbi->s_max_size = isonum_733(h_pri->volume_space_size); } else { - if (!pri) - goto out_freebh; - rootp = (struct iso_directory_record *) pri->root_directory_record; - sbi->s_nzones = isonum_733 (pri->volume_space_size); - sbi->s_log_zone_size = isonum_723 (pri->logical_block_size); - sbi->s_max_size = isonum_733(pri->volume_space_size); + if (!pri) + goto out_freebh; + rootp = (struct iso_directory_record *) pri->root_directory_record; + sbi->s_nzones = isonum_733(pri->volume_space_size); + sbi->s_log_zone_size = isonum_723(pri->logical_block_size); + sbi->s_max_size = isonum_733(pri->volume_space_size); } sbi->s_ninodes = 0; /* No way to figure this out easily */ @@ -689,42 +743,39 @@ root_found: * blocks that were 512 bytes (which should only very rarely * happen.) */ - if(orig_zonesize < opt.blocksize) + if (orig_zonesize < opt.blocksize) goto out_bad_size; /* RDE: convert log zone size to bit shift */ - switch (sbi->s_log_zone_size) - { case 512: sbi->s_log_zone_size = 9; break; - case 1024: sbi->s_log_zone_size = 10; break; - case 2048: sbi->s_log_zone_size = 11; break; + switch (sbi->s_log_zone_size) { + case 512: sbi->s_log_zone_size = 9; break; + case 1024: sbi->s_log_zone_size = 10; break; + case 2048: sbi->s_log_zone_size = 11; break; - default: + default: goto out_bad_zone_size; - } + } s->s_magic = ISOFS_SUPER_MAGIC; - s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */ - - /* 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 */; + /* + * With multi-extent files, file size is only limited by the maximum + * size of a file system, which is 8 TB. + */ + s->s_maxbytes = 0x80000000000LL; /* Set this for reference. Its not currently used except on write which we don't have .. */ - - first_data_zone = isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length); + + first_data_zone = isonum_733(rootp->extent) + + isonum_711(rootp->ext_attr_length); sbi->s_firstdatazone = first_data_zone; #ifndef BEQUIET - printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n", - sbi->s_max_size, - 1UL << sbi->s_log_zone_size); - printk(KERN_DEBUG "First datazone:%ld\n", sbi->s_firstdatazone); + printk(KERN_DEBUG "ISOFS: Max size:%ld Log zone size:%ld\n", + sbi->s_max_size, 1UL << sbi->s_log_zone_size); + printk(KERN_DEBUG "ISOFS: First datazone:%ld\n", sbi->s_firstdatazone); if(sbi->s_high_sierra) - printk(KERN_DEBUG "Disc in High Sierra format.\n"); + printk(KERN_DEBUG "ISOFS: Disc in High Sierra format.\n"); #endif /* @@ -739,8 +790,8 @@ root_found: 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); + first_data_zone = isonum_733(rootp->extent) + + isonum_711(rootp->ext_attr_length); } /* @@ -773,7 +824,7 @@ root_found: #ifdef CONFIG_JOLIET if (joliet_level && opt.utf8 == 0) { - char * p = opt.iocharset ? opt.iocharset : CONFIG_NLS_DEFAULT; + char *p = opt.iocharset ? opt.iocharset : CONFIG_NLS_DEFAULT; sbi->s_nls_iocharset = load_nls(p); if (! sbi->s_nls_iocharset) { /* Fail only if explicit charset specified */ @@ -786,21 +837,31 @@ root_found: s->s_op = &isofs_sops; s->s_export_op = &isofs_export_ops; sbi->s_mapping = opt.map; - sbi->s_rock = (opt.rock == 'y' ? 2 : 0); + sbi->s_rock = (opt.rock ? 2 : 0); sbi->s_rock_offset = -1; /* initial offset, will guess until SP is found*/ sbi->s_cruft = opt.cruft; sbi->s_hide = opt.hide; sbi->s_showassoc = opt.showassoc; sbi->s_uid = opt.uid; sbi->s_gid = opt.gid; + sbi->s_uid_set = opt.uid_set; + sbi->s_gid_set = opt.gid_set; sbi->s_utf8 = opt.utf8; sbi->s_nocompress = opt.nocompress; + sbi->s_overriderockperm = opt.overriderockperm; /* * It would be incredibly stupid to allow people to mark every file * on the disk as suid, so we merely allow them to set the default * permissions. */ - sbi->s_mode = opt.mode & 0777; + if (opt.fmode != ISOFS_INVALID_MODE) + sbi->s_fmode = opt.fmode & 0777; + else + sbi->s_fmode = ISOFS_INVALID_MODE; + if (opt.dmode != ISOFS_INVALID_MODE) + sbi->s_dmode = opt.dmode & 0777; + else + sbi->s_dmode = ISOFS_INVALID_MODE; /* * Read the root inode, which _may_ result in changing @@ -808,6 +869,20 @@ root_found: * we then decide whether to use the Joliet descriptor. */ inode = isofs_iget(s, sbi->s_firstdatazone, 0); + if (IS_ERR(inode)) + goto out_no_root; + + /* + * Fix for broken CDs with Rock Ridge and empty ISO root directory but + * correct Joliet root directory. + */ + if (sbi->s_rock == 1 && joliet_level && + rootdir_empty(s, sbi->s_firstdatazone)) { + printk(KERN_NOTICE + "ISOFS: primary root directory is empty. " + "Disabling Rock Ridge and switching to Joliet."); + sbi->s_rock = 0; + } /* * If this disk has both Rock Ridge and Joliet on it, then we @@ -823,34 +898,46 @@ root_found: sbi->s_rock = 0; if (sbi->s_firstdatazone != first_data_zone) { sbi->s_firstdatazone = first_data_zone; - printk(KERN_DEBUG + printk(KERN_DEBUG "ISOFS: changing to secondary root\n"); iput(inode); inode = isofs_iget(s, sbi->s_firstdatazone, 0); + if (IS_ERR(inode)) + goto out_no_root; } } if (opt.check == 'u') { /* Only Joliet is case insensitive by default */ - if (joliet_level) opt.check = 'r'; - else opt.check = 's'; + if (joliet_level) + opt.check = 'r'; + else + opt.check = 's'; } sbi->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); - if (!(s->s_root)) - goto out_no_root; + /* Make sure the root inode is a directory */ + if (!S_ISDIR(inode->i_mode)) { + printk(KERN_WARNING + "isofs_fill_super: root inode is not a directory. " + "Corrupted media?\n"); + goto out_iput; + } table = 0; - if (joliet_level) table += 2; - if (opt.check == 'r') table++; - s->s_root->d_op = &isofs_dentry_ops[table]; + if (joliet_level) + table += 2; + if (opt.check == 'r') + table++; + + s->s_d_op = &isofs_dentry_ops[table]; + + /* get the root dentry */ + s->s_root = d_make_root(inode); + if (!(s->s_root)) { + error = -ENOMEM; + goto out_no_inode; + } kfree(opt.iocharset); @@ -859,56 +946,59 @@ root_found: /* * Display error messages and free resources. */ -out_bad_root: - printk(KERN_WARNING "isofs_fill_super: root inode not initialized\n"); - goto out_iput; -out_no_root: - printk(KERN_WARNING "isofs_fill_super: get root inode failed\n"); out_iput: iput(inode); + goto out_no_inode; +out_no_root: + error = PTR_ERR(inode); + if (error != -ENOMEM) + printk(KERN_WARNING "%s: get root inode failed\n", __func__); +out_no_inode: #ifdef CONFIG_JOLIET - if (sbi->s_nls_iocharset) - unload_nls(sbi->s_nls_iocharset); + unload_nls(sbi->s_nls_iocharset); #endif goto out_freesbi; out_no_read: - printk(KERN_WARNING "isofs_fill_super: " - "bread failed, dev=%s, iso_blknum=%d, block=%d\n", - s->s_id, iso_blknum, block); - goto out_freesbi; + printk(KERN_WARNING "%s: bread failed, dev=%s, iso_blknum=%d, block=%d\n", + __func__, s->s_id, iso_blknum, block); + goto out_freebh; out_bad_zone_size: - printk(KERN_WARNING "Bad logical zone size %ld\n", + printk(KERN_WARNING "ISOFS: Bad logical zone size %ld\n", sbi->s_log_zone_size); goto out_freebh; out_bad_size: - printk(KERN_WARNING "Logical zone size(%d) < hardware blocksize(%u)\n", + printk(KERN_WARNING "ISOFS: Logical zone size(%d) < hardware blocksize(%u)\n", orig_zonesize, opt.blocksize); goto out_freebh; out_unknown_format: if (!silent) - printk(KERN_WARNING "Unable to identify CD-ROM format.\n"); + printk(KERN_WARNING "ISOFS: Unable to identify CD-ROM format.\n"); out_freebh: brelse(bh); + brelse(pri_bh); out_freesbi: kfree(opt.iocharset); kfree(sbi); s->s_fs_info = NULL; - return -EINVAL; + return error; } static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); buf->f_type = ISOFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = (ISOFS_SB(sb)->s_nzones - << (ISOFS_SB(sb)->s_log_zone_size - sb->s_blocksize_bits)); + << (ISOFS_SB(sb)->s_log_zone_size - sb->s_blocksize_bits)); buf->f_bfree = 0; buf->f_bavail = 0; buf->f_files = ISOFS_SB(sb)->s_ninodes; buf->f_ffree = 0; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); buf->f_namelen = NAME_MAX; return 0; } @@ -916,37 +1006,34 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) /* * Get a set of blocks; filling in buffer_heads if already allocated * or getblk() if they are not. Returns the number of blocks inserted - * (0 == error.) + * (-ve == error.) */ -int isofs_get_blocks(struct inode *inode, sector_t iblock_s, +int isofs_get_blocks(struct inode *inode, sector_t iblock, struct buffer_head **bh, unsigned long nblocks) { - unsigned long b_off; + unsigned long b_off = iblock; unsigned offset, sect_size; unsigned int firstext; unsigned long nextblk, nextoff; - long iblock = (long)iblock_s; - int section, rv; + int section, rv, error; struct iso_inode_info *ei = ISOFS_I(inode); - lock_kernel(); - + error = -EIO; rv = 0; - if (iblock < 0 || iblock != iblock_s) { - printk("isofs_get_blocks: block number too large\n"); + if (iblock != b_off) { + printk(KERN_DEBUG "%s: block number too large\n", __func__); goto abort; } - b_off = iblock; - - offset = 0; - firstext = ei->i_first_extent; + + offset = 0; + firstext = ei->i_first_extent; sect_size = ei->i_section_size >> ISOFS_BUFFER_BITS(inode); - nextblk = ei->i_next_section_block; - nextoff = ei->i_next_section_offset; - section = 0; + nextblk = ei->i_next_section_block; + nextoff = ei->i_next_section_offset; + section = 0; - while ( nblocks ) { + while (nblocks) { /* If we are *way* beyond the end of the file, print a message. * Access beyond the end of the file up to the next page boundary * is normal, however because of the way the page cache works. @@ -955,43 +1042,47 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, * I/O errors. */ if (b_off > ((inode->i_size + PAGE_CACHE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) { - printk("isofs_get_blocks: block >= EOF (%ld, %ld)\n", - iblock, (unsigned long) inode->i_size); + printk(KERN_DEBUG "%s: block >= EOF (%lu, %llu)\n", + __func__, b_off, + (unsigned long long)inode->i_size); goto abort; } - - if (nextblk) { - while (b_off >= (offset + sect_size)) { - struct inode *ninode; - - offset += sect_size; - if (nextblk == 0) - goto abort; - ninode = isofs_iget(inode->i_sb, nextblk, nextoff); - if (!ninode) - goto abort; - firstext = ISOFS_I(ninode)->i_first_extent; - sect_size = ISOFS_I(ninode)->i_section_size >> ISOFS_BUFFER_BITS(ninode); - nextblk = ISOFS_I(ninode)->i_next_section_block; - nextoff = ISOFS_I(ninode)->i_next_section_offset; - iput(ninode); - - if (++section > 100) { - printk("isofs_get_blocks: More than 100 file sections ?!?, aborting...\n"); - printk("isofs_get_blocks: block=%ld firstext=%u sect_size=%u " - "nextblk=%lu nextoff=%lu\n", - iblock, firstext, (unsigned) sect_size, - nextblk, nextoff); - goto abort; - } + + /* On the last section, nextblk == 0, section size is likely to + * exceed sect_size by a partial block, and access beyond the + * end of the file will reach beyond the section size, too. + */ + while (nextblk && (b_off >= (offset + sect_size))) { + struct inode *ninode; + + offset += sect_size; + ninode = isofs_iget(inode->i_sb, nextblk, nextoff); + if (IS_ERR(ninode)) { + error = PTR_ERR(ninode); + goto abort; + } + firstext = ISOFS_I(ninode)->i_first_extent; + sect_size = ISOFS_I(ninode)->i_section_size >> ISOFS_BUFFER_BITS(ninode); + nextblk = ISOFS_I(ninode)->i_next_section_block; + nextoff = ISOFS_I(ninode)->i_next_section_offset; + iput(ninode); + + if (++section > 100) { + printk(KERN_DEBUG "%s: More than 100 file sections ?!?" + " aborting...\n", __func__); + printk(KERN_DEBUG "%s: block=%lu firstext=%u sect_size=%u " + "nextblk=%lu nextoff=%lu\n", __func__, + b_off, firstext, (unsigned) sect_size, + nextblk, nextoff); + goto abort; } } - - if ( *bh ) { + + if (*bh) { map_bh(*bh, inode->i_sb, firstext + b_off - offset); } else { *bh = sb_getblk(inode->i_sb, firstext+b_off-offset); - if ( !*bh ) + if (!*bh) goto abort; } bh++; /* Next buffer head */ @@ -1000,9 +1091,9 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, rv++; } + error = 0; abort: - unlock_kernel(); - return rv; + return rv != 0 ? rv : error; } /* @@ -1011,12 +1102,15 @@ abort: static int isofs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { + int ret; + if (create) { - printk("isofs_get_block: Kernel tries to allocate a block\n"); + printk(KERN_DEBUG "%s: Kernel tries to allocate a block\n", __func__); return -EROFS; } - return isofs_get_blocks(inode, iblock, &bh_result, 1) ? 0 : -EIO; + ret = isofs_get_blocks(inode, iblock, &bh_result, 1); + return ret < 0 ? ret : 0; } static int isofs_bmap(struct inode *inode, sector_t block) @@ -1042,7 +1136,13 @@ struct buffer_head *isofs_bread(struct inode *inode, sector_t block) static int isofs_readpage(struct file *file, struct page *page) { - return block_read_full_page(page,isofs_get_block); + return mpage_readpage(page, isofs_get_block); +} + +static int isofs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, isofs_get_block); } static sector_t _isofs_bmap(struct address_space *mapping, sector_t block) @@ -1052,31 +1152,19 @@ static sector_t _isofs_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations isofs_aops = { .readpage = isofs_readpage, - .sync_page = block_sync_page, + .readpages = isofs_readpages, .bmap = _isofs_bmap }; -static inline void test_and_set_uid(uid_t *p, uid_t value) -{ - if (value) - *p = value; -} - -static inline void test_and_set_gid(gid_t *p, gid_t value) -{ - if (value) - *p = value; -} - static int isofs_read_level3_size(struct inode *inode) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); int high_sierra = ISOFS_SB(inode->i_sb)->s_high_sierra; - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; unsigned long block, offset, block_saved, offset_saved; int i = 0; int more_entries = 0; - struct iso_directory_record * tmpde = NULL; + struct iso_directory_record *tmpde = NULL; struct iso_inode_info *ei = ISOFS_I(inode); inode->i_size = 0; @@ -1091,7 +1179,7 @@ static int isofs_read_level3_size(struct inode *inode) offset = ei->i_iget5_offset; do { - struct iso_directory_record * de; + struct iso_directory_record *de; unsigned int de_len; if (!bh) { @@ -1165,26 +1253,26 @@ out_noread: return -EIO; out_toomany: - printk(KERN_INFO "isofs_read_level3_size: " - "More than 100 file sections ?!?, aborting...\n" - "isofs_read_level3_size: inode=%lu\n", - inode->i_ino); + printk(KERN_INFO "%s: More than 100 file sections ?!?, aborting...\n" + "isofs_read_level3_size: inode=%lu\n", + __func__, inode->i_ino); goto out; } -static void isofs_read_inode(struct inode *inode) +static int isofs_read_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct isofs_sb_info *sbi = ISOFS_SB(sb); unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned long block; int high_sierra = sbi->s_high_sierra; - struct buffer_head * bh = NULL; - struct iso_directory_record * de; - struct iso_directory_record * tmpde = NULL; + struct buffer_head *bh = NULL; + struct iso_directory_record *de; + struct iso_directory_record *tmpde = NULL; unsigned int de_len; unsigned long offset; struct iso_inode_info *ei = ISOFS_I(inode); + int ret = -EIO; block = ei->i_iget5_block; bh = sb_bread(inode->i_sb, block); @@ -1201,7 +1289,8 @@ static void isofs_read_inode(struct inode *inode) tmpde = kmalloc(de_len, GFP_KERNEL); if (tmpde == NULL) { - printk(KERN_INFO "isofs_read_inode: out of memory\n"); + printk(KERN_INFO "%s: out of memory\n", __func__); + ret = -ENOMEM; goto fail; } memcpy(tmpde, bh->b_data + offset, frag1); @@ -1214,24 +1303,36 @@ static void isofs_read_inode(struct inode *inode) } inode->i_ino = isofs_get_ino(ei->i_iget5_block, - ei->i_iget5_offset, - ISOFS_BUFFER_BITS(inode)); + ei->i_iget5_offset, + ISOFS_BUFFER_BITS(inode)); /* Assume it is a normal-format file unless told otherwise */ ei->i_file_format = isofs_file_normal; if (de->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. */ + if (sbi->s_dmode != ISOFS_INVALID_MODE) + inode->i_mode = S_IFDIR | sbi->s_dmode; + else + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + set_nlink(inode, 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 { - /* Everybody gets to read the file. */ - inode->i_mode = sbi->s_mode; - inode->i_nlink = 1; - inode->i_mode |= S_IFREG; + if (sbi->s_fmode != ISOFS_INVALID_MODE) { + inode->i_mode = S_IFREG | sbi->s_fmode; + } else { + /* + * Set default permissions: r-x for all. The disc + * could be shared with DOS machines so virtually + * anything could be a valid executable. + */ + inode->i_mode = S_IFREG | S_IRUGO | S_IXUGO; + } + set_nlink(inode, 1); } inode->i_uid = sbi->s_uid; inode->i_gid = sbi->s_gid; @@ -1241,13 +1342,16 @@ static void isofs_read_inode(struct inode *inode) ei->i_format_parm[1] = 0; ei->i_format_parm[2] = 0; - ei->i_section_size = isonum_733 (de->size); + ei->i_section_size = isonum_733(de->size); if (de->flags[-high_sierra] & 0x80) { - if(isofs_read_level3_size(inode)) goto fail; + ret = isofs_read_level3_size(inode); + if (ret < 0) + goto fail; + ret = -EIO; } else { ei->i_next_section_block = 0; ei->i_next_section_offset = 0; - inode->i_size = isonum_733 (de->size); + inode->i_size = isonum_733(de->size); } /* @@ -1256,27 +1360,28 @@ static void isofs_read_inode(struct inode *inode) * this CDROM was mounted with the cruft option. */ - if (sbi->s_cruft == 'y') + if (sbi->s_cruft) inode->i_size &= 0x00ffffff; if (de->interleave[0]) { - printk("Interleaved files not (yet) supported.\n"); + printk(KERN_DEBUG "ISOFS: 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 (de->file_unit_size[0] != 0) { - printk("File unit size != 0 for ISO file (%ld).\n", - inode->i_ino); + printk(KERN_DEBUG "ISOFS: 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((de->flags[-high_sierra] & ~2)!= 0){ - printk("Unusual flag settings for ISO file (%ld %x).\n", - inode->i_ino, de->flags[-high_sierra]); + printk(KERN_DEBUG "ISOFS: Unusual flag settings for ISO file " + "(%ld %x).\n", + inode->i_ino, de->flags[-high_sierra]); } #endif @@ -1287,11 +1392,11 @@ static void isofs_read_inode(struct inode *inode) inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0; - ei->i_first_extent = (isonum_733 (de->extent) + - isonum_711 (de->ext_attr_length)); + ei->i_first_extent = (isonum_733(de->extent) + + isonum_711(de->ext_attr_length)); /* Set the number of blocks for stat() - should be done before RR */ - inode->i_blocks = (inode->i_size + 511) >> 9; + inode->i_blocks = (inode->i_size + 511) >> 9; /* * Now test for possible Rock Ridge extensions which will override @@ -1301,14 +1406,23 @@ static void isofs_read_inode(struct inode *inode) if (!high_sierra) { parse_rock_ridge_inode(de, inode); /* if we want uid/gid set, override the rock ridge setting */ - test_and_set_uid(&inode->i_uid, sbi->s_uid); - test_and_set_gid(&inode->i_gid, sbi->s_gid); + if (sbi->s_uid_set) + inode->i_uid = sbi->s_uid; + if (sbi->s_gid_set) + inode->i_gid = sbi->s_gid; } + /* Now set final access rights if overriding rock ridge setting */ + if (S_ISDIR(inode->i_mode) && sbi->s_overriderockperm && + sbi->s_dmode != ISOFS_INVALID_MODE) + inode->i_mode = S_IFDIR | sbi->s_dmode; + if (S_ISREG(inode->i_mode) && sbi->s_overriderockperm && + sbi->s_fmode != ISOFS_INVALID_MODE) + inode->i_mode = S_IFREG | sbi->s_fmode; /* Install the inode operations vector */ if (S_ISREG(inode->i_mode)) { inode->i_fop = &generic_ro_fops; - switch ( ei->i_file_format ) { + switch (ei->i_file_format) { #ifdef CONFIG_ZISOFS case isofs_file_compressed: inode->i_data.a_ops = &zisofs_aops; @@ -1328,16 +1442,16 @@ static void isofs_read_inode(struct inode *inode) /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ init_special_inode(inode, inode->i_mode, inode->i_rdev); + ret = 0; out: kfree(tmpde); if (bh) brelse(bh); - return; + return ret; out_badread: printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); fail: - make_bad_inode(inode); goto out; } @@ -1352,7 +1466,7 @@ static int isofs_iget5_test(struct inode *ino, void *data) struct isofs_iget5_callback_data *d = (struct isofs_iget5_callback_data*)data; return (i->i_iget5_block == d->block) - && (i->i_iget5_offset == d->offset); + && (i->i_iget5_offset == d->offset); } static int isofs_iget5_set(struct inode *ino, void *data) @@ -1376,9 +1490,10 @@ struct inode *isofs_iget(struct super_block *sb, unsigned long hashval; struct inode *inode; struct isofs_iget5_callback_data data; + long ret; if (offset >= 1ul << sb->s_blocksize_bits) - return NULL; + return ERR_PTR(-EINVAL); data.block = block; data.offset = offset; @@ -1386,30 +1501,42 @@ struct inode *isofs_iget(struct super_block *sb, hashval = (block << sb->s_blocksize_bits) | offset; inode = iget5_locked(sb, hashval, &isofs_iget5_test, - &isofs_iget5_set, &data); + &isofs_iget5_set, &data); + + if (!inode) + return ERR_PTR(-ENOMEM); - if (inode && (inode->i_state & I_NEW)) { - sb->s_op->read_inode(inode); - unlock_new_inode(inode); + if (inode->i_state & I_NEW) { + ret = isofs_read_inode(inode); + if (ret < 0) { + iget_failed(inode); + inode = ERR_PTR(ret); + } else { + unlock_new_inode(inode); + } } return inode; } -static int isofs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) +static struct dentry *isofs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - return get_sb_bdev(fs_type, flags, dev_name, data, isofs_fill_super, - mnt); + /* We don't support read-write mounts */ + if (!(flags & MS_RDONLY)) + return ERR_PTR(-EACCES); + return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super); } static struct file_system_type iso9660_fs_type = { .owner = THIS_MODULE, .name = "iso9660", - .get_sb = isofs_get_sb, + .mount = isofs_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; +MODULE_ALIAS_FS("iso9660"); +MODULE_ALIAS("iso9660"); static int __init init_iso9660_fs(void) { @@ -1447,5 +1574,3 @@ static void __exit exit_iso9660_fs(void) module_init(init_iso9660_fs) module_exit(exit_iso9660_fs) MODULE_LICENSE("GPL"); -/* Actual filesystem name is iso9660, as requested in filesystems.c */ -MODULE_ALIAS("iso9660"); diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index e6308c8b573..99167238518 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -1,5 +1,6 @@ #include <linux/fs.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/iso_fs.h> #include <asm/unaligned.h> @@ -34,28 +35,30 @@ struct isofs_sb_info { unsigned long s_log_zone_size; unsigned long s_max_size; - unsigned char s_high_sierra; /* A simple flag */ - unsigned char s_mapping; int s_rock_offset; /* offset of SUSP fields within SU area */ - unsigned char s_rock; unsigned char s_joliet_level; - unsigned char s_utf8; - unsigned char s_cruft; /* Broken disks with high - byte of length containing - junk */ - unsigned char s_unhide; - unsigned char s_nosuid; - unsigned char s_nodev; - unsigned char s_nocompress; - unsigned char s_hide; - unsigned char s_showassoc; + unsigned char s_mapping; + unsigned int s_high_sierra:1; + unsigned int s_rock:2; + unsigned int s_utf8:1; + unsigned int s_cruft:1; /* Broken disks with high byte of length + * containing junk */ + unsigned int s_nocompress:1; + unsigned int s_hide:1; + unsigned int s_showassoc:1; + unsigned int s_overriderockperm:1; + unsigned int s_uid_set:1; + unsigned int s_gid_set:1; - mode_t s_mode; - gid_t s_gid; - uid_t s_uid; + umode_t s_fmode; + umode_t s_dmode; + kgid_t s_gid; + kuid_t s_uid; struct nls_table *s_nls_iocharset; /* Native language support table */ }; +#define ISOFS_INVALID_MODE ((umode_t) -1) + static inline struct isofs_sb_info *ISOFS_SB(struct super_block *sb) { return sb->s_fs_info; @@ -76,29 +79,29 @@ static inline int isonum_712(char *p) } static inline unsigned int isonum_721(char *p) { - return le16_to_cpu(get_unaligned((__le16 *)p)); + return get_unaligned_le16(p); } static inline unsigned int isonum_722(char *p) { - return be16_to_cpu(get_unaligned((__le16 *)p)); + return get_unaligned_be16(p); } static inline unsigned int isonum_723(char *p) { /* Ignore bigendian datum due to broken mastering programs */ - return le16_to_cpu(get_unaligned((__le16 *)p)); + return get_unaligned_le16(p); } static inline unsigned int isonum_731(char *p) { - return le32_to_cpu(get_unaligned((__le32 *)p)); + return get_unaligned_le32(p); } static inline unsigned int isonum_732(char *p) { - return be32_to_cpu(get_unaligned((__le32 *)p)); + return get_unaligned_be32(p); } static inline unsigned int isonum_733(char *p) { /* Ignore bigendian datum due to broken mastering programs */ - return le32_to_cpu(get_unaligned((__le32 *)p)); + return get_unaligned_le32(p); } extern int iso_date(char *, int); @@ -111,7 +114,7 @@ extern int isofs_name_translate(struct iso_directory_record *, char *, struct in int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *); int get_acorn_filename(struct iso_directory_record *, char *, struct inode *); -extern struct dentry *isofs_lookup(struct inode *, struct dentry *, struct nameidata *); +extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int flags); extern struct buffer_head *isofs_bread(struct inode *, sector_t); extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); @@ -174,7 +177,7 @@ isofs_normalize_block_and_offset(struct iso_directory_record* de, } } -extern struct inode_operations isofs_dir_inode_operations; +extern const struct inode_operations isofs_dir_inode_operations; extern const struct file_operations isofs_dir_operations; extern const struct address_space_operations isofs_symlink_aops; -extern struct export_operations isofs_export_ops; +extern const struct export_operations isofs_export_ops; diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index 81a90e170ac..a048de81c09 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -14,9 +14,9 @@ * Convert Unicode 16 to UTF-8 or ASCII. */ static int -uni16_to_x8(unsigned char *ascii, u16 *uni, int len, struct nls_table *nls) +uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls) { - wchar_t *ip, ch; + __be16 *ip, ch; unsigned char *op; ip = uni; @@ -24,8 +24,8 @@ uni16_to_x8(unsigned char *ascii, u16 *uni, int len, struct nls_table *nls) while ((ch = get_unaligned(ip)) && len) { int llen; - ch = be16_to_cpu(ch); - if ((llen = nls->uni2char(ch, op, NLS_MAX_CHARSET_SIZE)) > 0) + llen = nls->uni2char(be16_to_cpu(ch), op, NLS_MAX_CHARSET_SIZE); + if (llen > 0) op += llen; else *op++ = '?'; @@ -37,37 +37,6 @@ uni16_to_x8(unsigned char *ascii, u16 *uni, int len, struct nls_table *nls) return (op - ascii); } -/* Convert big endian wide character string to utf8 */ -static int -wcsntombs_be(__u8 *s, const __u8 *pwcs, int inlen, int maxlen) -{ - const __u8 *ip; - __u8 *op; - int size; - __u16 c; - - op = s; - ip = pwcs; - while ((*ip || ip[1]) && (maxlen > 0) && (inlen > 0)) { - c = (*ip << 8) | ip[1]; - if (c > 0x7f) { - size = utf8_wctomb(op, c, maxlen); - if (size == -1) { - /* Ignore character and move on */ - maxlen--; - } else { - op += size; - maxlen -= size; - } - } else { - *op++ = (__u8) c; - } - ip += 2; - inlen--; - } - return (op - s); -} - int get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode) { @@ -79,23 +48,22 @@ get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, st nls = ISOFS_SB(inode->i_sb)->s_nls_iocharset; if (utf8) { - len = wcsntombs_be(outname, de->name, - de->name_len[0] >> 1, PAGE_SIZE); + len = utf16s_to_utf8s((const wchar_t *) de->name, + de->name_len[0] >> 1, UTF16_BIG_ENDIAN, + outname, PAGE_SIZE); } else { - len = uni16_to_x8(outname, (u16 *) de->name, - de->name_len[0] >> 1, nls); + len = uni16_to_x8(outname, (__be16 *) de->name, + de->name_len[0] >> 1, nls); } - if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) { + if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) len -= 2; - } /* * Windows doesn't like periods at the end of a name, * so neither do we */ - while (len >= 2 && (outname[len-1] == '.')) { + while (len >= 2 && (outname[len-1] == '.')) len--; - } return len; } diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index e7ba0c30e07..95295640d9c 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -6,8 +6,7 @@ * (C) 1991 Linus Torvalds - minix filesystem */ -#include <linux/config.h> /* Joliet? */ -#include <linux/smp_lock.h> +#include <linux/gfp.h> #include "isofs.h" /* @@ -16,7 +15,7 @@ * some sanity tests. */ static int -isofs_cmp(struct dentry * dentry, const char * compare, int dlen) +isofs_cmp(struct dentry *dentry, const char *compare, int dlen) { struct qstr qstr; @@ -38,7 +37,7 @@ isofs_cmp(struct dentry * dentry, const char * compare, int dlen) qstr.name = compare; qstr.len = dlen; - return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr); + return dentry->d_op->d_compare(NULL, NULL, dentry->d_name.len, dentry->d_name.name, &qstr); } /* @@ -49,24 +48,24 @@ isofs_cmp(struct dentry * dentry, const char * compare, int dlen) */ static unsigned long isofs_find_entry(struct inode *dir, struct dentry *dentry, - unsigned long *block_rv, unsigned long* offset_rv, - char * tmpname, struct iso_directory_record * tmpde) + unsigned long *block_rv, unsigned long *offset_rv, + char *tmpname, struct iso_directory_record *tmpde) { unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); unsigned long block, f_pos, offset, block_saved, offset_saved; - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); if (!ISOFS_I(dir)->i_first_extent) return 0; - + f_pos = 0; offset = 0; block = 0; while (f_pos < dir->i_size) { - struct iso_directory_record * de; + struct iso_directory_record *de; int de_len, match, i, dlen; char *dpnt; @@ -112,10 +111,17 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, dlen = de->name_len[0]; dpnt = de->name; + /* Basic sanity check, whether name doesn't exceed dir entry */ + if (de_len < dlen + sizeof(struct iso_directory_record)) { + printk(KERN_NOTICE "iso9660: Corrupted directory entry" + " in block %lu of inode %lu\n", block, + dir->i_ino); + return 0; + } if (sbi->s_rock && ((i = get_rock_ridge_filename(de, tmpname, dir)))) { - dlen = i; /* possibly -1 */ + dlen = i; /* possibly -1 */ dpnt = tmpname; #ifdef CONFIG_JOLIET } else if (sbi->s_joliet_level) { @@ -136,9 +142,9 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, */ match = 0; if (dlen > 0 && - (sbi->s_hide =='n' || + (!sbi->s_hide || (!(de->flags[-sbi->s_high_sierra] & 1))) && - (sbi->s_showassoc =='y' || + (sbi->s_showassoc || (!(de->flags[-sbi->s_high_sierra] & 4)))) { match = (isofs_cmp(dentry, dpnt, dlen) == 0); } @@ -146,8 +152,8 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, isofs_normalize_block_and_offset(de, &block_saved, &offset_saved); - *block_rv = block_saved; - *offset_rv = offset_saved; + *block_rv = block_saved; + *offset_rv = offset_saved; brelse(bh); return 1; } @@ -156,34 +162,25 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, return 0; } -struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd) +struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { int found; - unsigned long block, offset; + unsigned long uninitialized_var(block); + unsigned long uninitialized_var(offset); struct inode *inode; struct page *page; - dentry->d_op = dir->i_sb->s_root->d_op; - page = alloc_page(GFP_USER); if (!page) return ERR_PTR(-ENOMEM); - lock_kernel(); found = isofs_find_entry(dir, dentry, - &block, &offset, - page_address(page), - 1024 + page_address(page)); + &block, &offset, + page_address(page), + 1024 + page_address(page)); __free_page(page); - inode = NULL; - if (found) { - inode = isofs_iget(dir->i_sb, block, offset); - if (!inode) { - unlock_kernel(); - return ERR_PTR(-EACCES); - } - } - unlock_kernel(); + inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL; + return d_splice_alias(inode, dentry); } diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index f3a1db3098d..c0bf42472e4 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -8,7 +8,6 @@ #include <linux/slab.h> #include <linux/pagemap.h> -#include <linux/smp_lock.h> #include "isofs.h" #include "rock.h" @@ -209,6 +208,11 @@ repeat: while (rs.len > 2) { /* There may be one byte for padding somewhere */ rr = (struct rock_ridge *)rs.chr; + /* + * Ignore rock ridge info if rr->len is out of range, but + * don't return -EIO because that would make the file + * invisible. + */ if (rr->len < 3) goto out; /* Something got screwed up here */ sig = isonum_721(rs.chr); @@ -216,8 +220,12 @@ repeat: goto eio; rs.chr += rr->len; rs.len -= rr->len; + /* + * As above, just ignore the rock ridge info if rr->len + * is bogus. + */ if (rs.len < 0) - goto eio; /* corrupted isofs */ + goto out; /* Something got screwed up here */ switch (sig) { case SIG('R', 'R'): @@ -307,6 +315,11 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de, repeat: while (rs.len > 2) { /* There may be one byte for padding somewhere */ rr = (struct rock_ridge *)rs.chr; + /* + * Ignore rock ridge info if rr->len is out of range, but + * don't return -EIO because that would make the file + * invisible. + */ if (rr->len < 3) goto out; /* Something got screwed up here */ sig = isonum_721(rs.chr); @@ -314,8 +327,12 @@ repeat: goto eio; rs.chr += rr->len; rs.len -= rr->len; + /* + * As above, just ignore the rock ridge info if rr->len + * is bogus. + */ if (rs.len < 0) - goto eio; /* corrupted isofs */ + goto out; /* Something got screwed up here */ switch (sig) { #ifndef CONFIG_ZISOFS /* No flag for SF or ZF */ @@ -346,9 +363,9 @@ repeat: 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); + set_nlink(inode, isonum_733(rr->u.PX.n_links)); + i_uid_write(inode, isonum_733(rr->u.PX.uid)); + i_gid_write(inode, isonum_733(rr->u.PX.gid)); break; case SIG('P', 'N'): { @@ -474,10 +491,12 @@ repeat: isofs_iget(inode->i_sb, ISOFS_I(inode)->i_first_extent, 0); - if (!reloc) + if (IS_ERR(reloc)) { + ret = PTR_ERR(reloc); goto out; + } inode->i_mode = reloc->i_mode; - inode->i_nlink = reloc->i_nlink; + set_nlink(inode, reloc->i_nlink); inode->i_uid = reloc->i_uid; inode->i_gid = reloc->i_gid; inode->i_rdev = reloc->i_rdev; @@ -498,8 +517,7 @@ repeat: if (algo == SIG('p', 'z')) { int block_shift = isonum_711(&rr->u.ZF.parms[1]); - if (block_shift < PAGE_CACHE_SHIFT - || block_shift > 17) { + if (block_shift > 17) { printk(KERN_WARNING "isofs: " "Can't handle ZF block " "size of 2^%d\n", @@ -642,6 +660,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; struct iso_inode_info *ei = ISOFS_I(inode); + struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); char *link = kmap(page); unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); struct buffer_head *bh; @@ -654,12 +673,11 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) struct rock_state rs; int ret; - if (!ISOFS_SB(inode->i_sb)->s_rock) + if (!sbi->s_rock) goto error; init_rock_state(&rs, inode); block = ei->i_iget5_block; - lock_kernel(); bh = sb_bread(inode->i_sb, block); if (!bh) goto out_noread; @@ -729,7 +747,6 @@ repeat: goto fail; brelse(bh); *rpnt = '\0'; - unlock_kernel(); SetPageUptodate(page); kunmap(page); unlock_page(page); @@ -746,7 +763,6 @@ out_bad_span: printk("symlink spans iso9660 blocks\n"); fail: brelse(bh); - unlock_kernel(); error: SetPageError(page); kunmap(page); |
