aboutsummaryrefslogtreecommitdiff
path: root/fs/isofs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/isofs')
-rw-r--r--fs/isofs/Kconfig39
-rw-r--r--fs/isofs/compress.c533
-rw-r--r--fs/isofs/dir.c60
-rw-r--r--fs/isofs/export.c59
-rw-r--r--fs/isofs/inode.c419
-rw-r--r--fs/isofs/isofs.h49
-rw-r--r--fs/isofs/joliet.c36
-rw-r--r--fs/isofs/namei.c31
-rw-r--r--fs/isofs/rock.c40
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);