diff options
Diffstat (limited to 'fs/isofs')
| -rw-r--r-- | fs/isofs/Kconfig | 39 | ||||
| -rw-r--r-- | fs/isofs/compress.c | 533 | ||||
| -rw-r--r-- | fs/isofs/dir.c | 60 | ||||
| -rw-r--r-- | fs/isofs/export.c | 59 | ||||
| -rw-r--r-- | fs/isofs/inode.c | 419 | ||||
| -rw-r--r-- | fs/isofs/isofs.h | 49 | ||||
| -rw-r--r-- | fs/isofs/joliet.c | 36 | ||||
| -rw-r--r-- | fs/isofs/namei.c | 31 | ||||
| -rw-r--r-- | fs/isofs/rock.c | 40 |
9 files changed, 689 insertions, 577 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 defb932eee9..592e5115a56 100644 --- a/fs/isofs/compress.c +++ b/fs/isofs/compress.c @@ -36,286 +36,323 @@ static void *zisofs_zlib_workspace; 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_path.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; - - /* - * If this page is wholly outside i_size we just return zero; - * do_generic_file_read() will handle this for us - */ - if (page->index >= maxpage) { - SetPageUptodate(page); - unlock_page(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; } - - 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); + /* 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; + /* 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); - /* 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; + 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. + */ - indexblocks = ((blockptr^blockendptr) >> bufshift) ? 2 : 1; - ptrbh[0] = ptrbh[1] = NULL; + if (!bhs[0]) + goto b_eio; - 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; + wait_on_buffer(bhs[0]); + if (!buffer_uptodate(bhs[0])) { + *errp = -EIO; + goto b_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; - } - } - 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 - 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 ( !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; - mutex_lock(&zisofs_zlib_lock); - - 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; } - zlib_inflateEnd(&stream); + 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; + } + + 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; - z_eio: - mutex_unlock(&zisofs_zlib_lock); + 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 */ diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 1ba407c64df..b943cbd963b 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -10,7 +10,7 @@ * * isofs directory handling functions */ -#include <linux/smp_lock.h> +#include <linux/gfp.h> #include "isofs.h" int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) @@ -78,8 +78,8 @@ 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, +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); @@ -94,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) { @@ -108,7 +108,7 @@ 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 @@ -119,8 +119,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, 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; } @@ -145,6 +145,14 @@ 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, @@ -156,16 +164,16 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, 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; } @@ -173,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_path.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; } @@ -187,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; } @@ -223,10 +229,10 @@ 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; } @@ -240,32 +246,30 @@ 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_path.dentry->d_inode; + 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, - .readdir = isofs_readdir, + .iterate = isofs_readdir, }; /* diff --git a/fs/isofs/export.c b/fs/isofs/export.c index bb219138331..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,7 +22,7 @@ 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); @@ -32,12 +32,7 @@ isofs_export_iget(struct super_block *sb, iput(inode); return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return d_obtain_alias(inode); } /* This function is surprisingly simple. The trick is understanding @@ -51,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; @@ -104,38 +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 (IS_ERR(parent_inode)) { - rv = ERR_CAST(parent_inode); - if (rv != ERR_PTR(-ENOMEM)) - 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; @@ -147,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; } @@ -198,7 +175,7 @@ static struct dentry *isofs_fh_to_parent(struct super_block *sb, { struct isofs_fid *ifid = (struct isofs_fid *)fid; - if (fh_type != 2) + if (fh_len < 2 || fh_type != 2) return NULL; return isofs_export_iget(sb, diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 044a254d526..4556ce1af5b 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -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); @@ -68,19 +75,25 @@ static struct inode *isofs_alloc_inode(struct super_block *sb) 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(struct kmem_cache *cachep, void *foo) +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; 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), @@ -94,13 +107,19 @@ 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; } @@ -114,7 +133,7 @@ static const struct super_operations isofs_sops = { }; -static struct dentry_operations isofs_dentry_ops[] = { +static const struct dentry_operations isofs_dentry_ops[] = { { .d_hash = isofs_hash, .d_compare = isofs_dentry_cmp, @@ -136,21 +155,24 @@ 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 fmode; - mode_t dmode; - 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; @@ -160,7 +182,7 @@ struct iso9660_options{ * 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; @@ -181,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; @@ -206,100 +228,86 @@ isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) } /* - * Case insensitive compare of two isofs names. + * Compare of two isofs names. */ -static int isofs_dentry_cmpi_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 (strnicmp(a->name, b->name, alen) == 0) - return 0; - } - return 1; -} - -/* - * Case sensitive compare of two isofs names. - */ -static int isofs_dentry_cmp_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 (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 @@ -307,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_dmode, + 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 +343,7 @@ static match_table_t tokens = { {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,24 +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->fmode = popt->dmode = 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) @@ -388,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; @@ -444,12 +451,18 @@ 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)) @@ -461,6 +474,9 @@ static int parse_options(char *options, struct iso9660_options *popt) return 0; popt->dmode = option; break; + case Opt_overriderockperm: + popt->overriderockperm = 1; + break; case Opt_block: if (match_int(&args[0], &option)) return 0; @@ -540,6 +556,34 @@ static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) } /* + * 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 @@ -620,7 +664,7 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) 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 (opt.joliet) { if (sec->escape[2] == 0x40) joliet_level = 1; else if (sec->escape[2] == 0x43) @@ -645,7 +689,7 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) goto out_freebh; sbi->s_high_sierra = 1; - opt.rock = 'n'; + opt.rock = 0; h_pri = (struct hs_primary_descriptor *)vdp; goto root_found; } @@ -668,7 +712,7 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) root_found: - if (joliet_level && (pri == NULL || opt.rock == 'n')) { + 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 */ @@ -713,16 +757,12 @@ root_found: } 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) + * With multi-extent files, file size is only limited by the maximum + * size of a file system, which is 8 TB. */ - - s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */; + s->s_maxbytes = 0x80000000000LL; /* Set this for reference. Its not currently used except on write which we don't have .. */ @@ -797,22 +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_fmode = opt.fmode & 0777; - sbi->s_dmode = opt.dmode & 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 @@ -824,6 +873,18 @@ root_found: 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 * want to use Rock Ridge by default. This can be overridden * by using the norock mount option. There is still one other @@ -855,10 +916,6 @@ root_found: } sbi->s_joliet_level = joliet_level; - /* check the root inode */ - if (!inode->i_op) - goto out_bad_root; - /* Make sure the root inode is a directory */ if (!S_ISDIR(inode->i_mode)) { printk(KERN_WARNING @@ -867,17 +924,20 @@ root_found: goto out_iput; } - /* get the root dentry */ - s->s_root = d_alloc_root(inode); - 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]; + + 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); @@ -886,8 +946,6 @@ root_found: /* * Display error messages and free resources. */ -out_bad_root: - printk(KERN_WARNING "%s: root inode not initialized\n", __func__); out_iput: iput(inode); goto out_no_inode; @@ -897,14 +955,13 @@ out_no_root: 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 "%s: bread failed, dev=%s, iso_blknum=%d, block=%d\n", __func__, s->s_id, iso_blknum, block); - goto out_freesbi; + goto out_freebh; out_bad_zone_size: printk(KERN_WARNING "ISOFS: Bad logical zone size %ld\n", sbi->s_log_zone_size); @@ -919,6 +976,7 @@ out_unknown_format: out_freebh: brelse(bh); + brelse(pri_bh); out_freesbi: kfree(opt.iocharset); kfree(sbi); @@ -929,6 +987,7 @@ out_freesbi: 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; @@ -938,6 +997,8 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) 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; } @@ -947,27 +1008,23 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) * or getblk() if they are not. Returns the number of blocks inserted * (-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, error; struct iso_inode_info *ei = ISOFS_I(inode); - lock_kernel(); - error = -EIO; rv = 0; - if (iblock < 0 || iblock != iblock_s) { + 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; @@ -985,8 +1042,9 @@ 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(KERN_DEBUG "%s: block >= EOF (%ld, %ld)\n", - __func__, 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; } @@ -1012,9 +1070,9 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, if (++section > 100) { printk(KERN_DEBUG "%s: More than 100 file sections ?!?" " aborting...\n", __func__); - printk(KERN_DEBUG "%s: block=%ld firstext=%u sect_size=%u " + printk(KERN_DEBUG "%s: block=%lu firstext=%u sect_size=%u " "nextblk=%lu nextoff=%lu\n", __func__, - iblock, firstext, (unsigned) sect_size, + b_off, firstext, (unsigned) sect_size, nextblk, nextoff); goto abort; } @@ -1035,7 +1093,6 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, error = 0; abort: - unlock_kernel(); return rv != 0 ? rv : error; } @@ -1079,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) @@ -1089,22 +1152,10 @@ 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); @@ -1259,8 +1310,11 @@ static int isofs_read_inode(struct inode *inode) ei->i_file_format = isofs_file_normal; if (de->flags[-high_sierra] & 2) { - inode->i_mode = sbi->s_dmode | S_IFDIR; - inode->i_nlink = 1; /* + 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 @@ -1268,9 +1322,17 @@ static int isofs_read_inode(struct inode *inode) * do it the hard way. */ } else { - /* Everybody gets to read the file. */ - inode->i_mode = sbi->s_fmode | S_IFREG; - inode->i_nlink = 1; + 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; @@ -1298,7 +1360,7 @@ static int 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]) { @@ -1344,9 +1406,18 @@ static int 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)) { @@ -1448,20 +1519,24 @@ struct inode *isofs_iget(struct super_block *sb, 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) { @@ -1499,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 d1bdf8adb35..99167238518 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -35,29 +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_fmode; - mode_t s_dmode; - 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; @@ -78,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); @@ -113,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); diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index 92c14b850e9..a048de81c09 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -37,37 +37,6 @@ uni16_to_x8(unsigned char *ascii, __be16 *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,8 +48,9 @@ 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, (__be16 *) de->name, de->name_len[0] >> 1, nls); diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 344b247bc29..95295640d9c 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -6,7 +6,7 @@ * (C) 1991 Linus Torvalds - minix filesystem */ -#include <linux/smp_lock.h> +#include <linux/gfp.h> #include "isofs.h" /* @@ -37,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); } /* @@ -111,6 +111,13 @@ 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)))) { @@ -135,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); } @@ -155,7 +162,7 @@ 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 uninitialized_var(block); @@ -163,27 +170,17 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nam 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)); __free_page(page); - inode = NULL; - if (found) { - inode = isofs_iget(dir->i_sb, block, offset); - if (IS_ERR(inode)) { - unlock_kernel(); - return ERR_CAST(inode); - } - } - 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 6bd48f0a704..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'): { @@ -479,7 +496,7 @@ repeat: 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; @@ -500,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", @@ -644,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; @@ -656,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; @@ -731,7 +747,6 @@ repeat: goto fail; brelse(bh); *rpnt = '\0'; - unlock_kernel(); SetPageUptodate(page); kunmap(page); unlock_page(page); @@ -748,7 +763,6 @@ out_bad_span: printk("symlink spans iso9660 blocks\n"); fail: brelse(bh); - unlock_kernel(); error: SetPageError(page); kunmap(page); |
