Merge to Fedora kernel-2.6.18-1.2255_FC5-vs2.0.2.2-rc9 patched with stable patch...
[linux-2.6.git] / fs / squashfs / inode.c
index 2348368..9e8ae26 100644 (file)
@@ -173,14 +173,15 @@ out:
 
 SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
                        long long index, unsigned int length,
-                       long long *next_index)
+                       long long *next_index, int srclength)
 {
        struct squashfs_sb_info *msblk = s->s_fs_info;
+       struct squashfs_super_block *sblk = &msblk->sblk;
        struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
                        msblk->devblksize_log2) + 2];
        unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
        unsigned int cur_index = index >> msblk->devblksize_log2;
-       int bytes, avail_bytes, b = 0, k;
+       int bytes, avail_bytes, b = 0, k = 0;
        char *c_buffer;
        unsigned int compressed;
        unsigned int c_byte = length;
@@ -191,8 +192,11 @@ SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
                c_buffer = compressed ? msblk->read_data : buffer;
                c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
 
-               TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
-                                       ? "" : "un", (unsigned int) c_byte);
+               TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
+                                       ? "" : "un", (unsigned int) c_byte, srclength);
+
+                if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
+                        goto read_failure;
 
                if (!(bh[0] = sb_getblk(s, cur_index)))
                        goto block_release;
@@ -204,6 +208,9 @@ SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
                }
                ll_rw_block(READ, b, bh);
        } else {
+                if (index < 0 || (index + 2) > sblk->bytes_used)
+                        goto read_failure;
+
                if (!(bh[0] = get_block_length(s, &cur_index, &offset,
                                                                &c_byte)))
                        goto read_failure;
@@ -216,6 +223,9 @@ SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
                TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
                                        ? "" : "un", (unsigned int) c_byte);
 
+                if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
+                        goto read_failure;
+
                for (b = 1; bytes < c_byte; b++) {
                        if (!(bh[b] = sb_getblk(s, ++cur_index)))
                                goto block_release;
@@ -227,7 +237,7 @@ SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
        if (compressed)
                down(&msblk->read_data_mutex);
 
-       for (bytes = 0, k = 0; k < b; k++) {
+       for (bytes = 0; k < b; k++) {
                avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
                                        msblk->devblksize - offset :
                                        c_byte - bytes;
@@ -249,7 +259,7 @@ SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
                msblk->stream.next_in = c_buffer;
                msblk->stream.avail_in = c_byte;
                msblk->stream.next_out = buffer;
-               msblk->stream.avail_out = msblk->read_size;
+               msblk->stream.avail_out = srclength;
 
                if (((zlib_err = zlib_inflateInit(&msblk->stream)) != Z_OK) ||
                                ((zlib_err = zlib_inflate(&msblk->stream, Z_FINISH))
@@ -271,8 +281,8 @@ SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
        return bytes;
 
 block_release:
-       while (--b >= 0)
-               brelse(bh[b]);
+       for (; k < b; k++)
+               brelse(bh[k]);
 
 read_failure:
        ERROR("sb_bread failed reading block 0x%x\n", cur_index);
@@ -336,14 +346,20 @@ SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
                        msblk->block_cache[i].block = SQUASHFS_USED_BLK;
                        up(&msblk->block_cache_mutex);
 
-                       if (!(msblk->block_cache[i].length =
-                                               squashfs_read_data(s,
-                                               msblk->block_cache[i].data,
-                                               block, 0, &next_index))) {
-                               ERROR("Unable to read cache block [%llx:%x]\n",
-                                               block, offset);
-                               goto out;
-                       }
+                        msblk->block_cache[i].length = squashfs_read_data(s,
+                                msblk->block_cache[i].data, block, 0, &next_index,
+                               SQUASHFS_METADATA_SIZE);
+
+                        if (msblk->block_cache[i].length == 0) {
+                                ERROR("Unable to read cache block [%llx:%x]\n",
+                                                block, offset);
+                                down(&msblk->block_cache_mutex);
+                                msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
+                                kfree(msblk->block_cache[i].data);
+                                wake_up(&msblk->waitq);
+                                up(&msblk->block_cache_mutex);
+                                goto out;
+                        }
 
                        down(&msblk->block_cache_mutex);
                        wake_up(&msblk->waitq);
@@ -357,7 +373,11 @@ SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
                        continue;
                }
 
-               if ((bytes = msblk->block_cache[i].length - offset) >= length) {
+                bytes = msblk->block_cache[i].length - offset;
+
+                if (bytes < 1)
+                        goto out;
+               else if (bytes >= length) {
                        if (buffer)
                                memcpy(buffer, msblk->block_cache[i].data +
                                                offset, length);
@@ -442,6 +462,7 @@ SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_blo
 {
        int i, n;
        struct squashfs_sb_info *msblk = s->s_fs_info;
+        struct squashfs_super_block *sblk = &msblk->sblk;
 
        while ( 1 ) {
                down(&msblk->fragment_mutex);
@@ -487,7 +508,8 @@ SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_blo
 
                        if (!(msblk->fragment[i].length = squashfs_read_data(s,
                                                msblk->fragment[i].data,
-                                               start_block, length, NULL))) {
+                                               start_block, length, NULL,
+                                               sblk->block_size))) {
                                ERROR("Unable to read fragment cache block "
                                                        "[%llx]\n", start_block);
                                msblk->fragment[i].locked = 0;
@@ -874,6 +896,10 @@ static int read_fragment_index_table(struct super_block *s)
 {
        struct squashfs_sb_info *msblk = s->s_fs_info;
        struct squashfs_super_block *sblk = &msblk->sblk;
+       unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
+
+       if (length == 0)
+               return 1;
 
        /* Allocate fragment index table */
        if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES
@@ -882,13 +908,9 @@ static int read_fragment_index_table(struct super_block *s)
                return 0;
        }
    
-       if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) &&
-                                       !squashfs_read_data(s, (char *)
-                                       msblk->fragment_index,
-                                       sblk->fragment_table_start,
-                                       SQUASHFS_FRAGMENT_INDEX_BYTES
-                                       (sblk->fragments) |
-                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+       if (!squashfs_read_data(s, (char *) msblk->fragment_index,
+                       sblk->fragment_table_start, length |
+                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
                ERROR("unable to read fragment index table\n");
                return 0;
        }
@@ -980,9 +1002,11 @@ static int squashfs_fill_super(struct super_block *s, void *data, int silent)
        init_waitqueue_head(&msblk->waitq);
        init_waitqueue_head(&msblk->fragment_wait_queue);
 
+       sblk->bytes_used = sizeof(struct squashfs_super_block);
        if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
                                        sizeof(struct squashfs_super_block) |
-                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL,
+                                       sizeof(struct squashfs_super_block))) {
                SERROR("unable to read superblock\n");
                goto failed_mount;
        }
@@ -1010,6 +1034,15 @@ static int squashfs_fill_super(struct super_block *s, void *data, int silent)
        if(!supported_squashfs_filesystem(msblk, silent))
                goto failed_mount;
 
+        /* Check the filesystem does not extend beyond the end of the
+           block device */
+        if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
+                goto failed_mount;
+
+        /* Check the root inode for sanity */
+        if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
+                goto failed_mount;
+
        TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
        TRACE("Inodes are %scompressed\n",
                                        SQUASHFS_UNCOMPRESSED_INODES
@@ -1079,7 +1112,9 @@ static int squashfs_fill_super(struct super_block *s, void *data, int silent)
                if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
                                        ((sblk->no_uids + sblk->no_guids) *
                                         sizeof(unsigned int)) |
-                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL,
+                                       (sblk->no_uids + sblk->no_guids) *
+                                       sizeof(unsigned int))) {
                        ERROR("unable to read uid/gid table\n");
                        goto failed_mount;
                }
@@ -1090,7 +1125,9 @@ static int squashfs_fill_super(struct super_block *s, void *data, int silent)
                if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
                                        ((sblk->no_uids + sblk->no_guids) *
                                         sizeof(unsigned int)) |
-                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL,
+                                       (sblk->no_uids + sblk->no_guids) *
+                                       sizeof(unsigned int))) {
                        ERROR("unable to read uid/gid table\n");
                        goto failed_mount;
                }
@@ -1516,7 +1553,8 @@ static int squashfs_readpage(struct file *file, struct page *page)
                down(&msblk->read_page_mutex);
                
                if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
-                                       block, bsize, NULL))) {
+                                       block, bsize, NULL,
+                                       msblk->read_size))) {
                        ERROR("Unable to read page, block %llx, size %x\n", block,
                                        bsize);
                        up(&msblk->read_page_mutex);
@@ -1616,15 +1654,12 @@ static int squashfs_readpage4K(struct file *file, struct page *page)
 
        if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
                                        PAGE_CACHE_SHIFT)) {
-               pageaddr = kmap_atomic(page, KM_USER0);
                block_list = NULL;
                goto skip_read;
        }
 
        if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
                ERROR("Failed to allocate block_list\n");
-               pageaddr = kmap_atomic(page, KM_USER0);
-               block_list = NULL;
                goto skip_read;
        }
 
@@ -1636,11 +1671,12 @@ static int squashfs_readpage4K(struct file *file, struct page *page)
 
                down(&msblk->read_page_mutex);
                bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
-                                       bsize, NULL);
-               pageaddr = kmap_atomic(page, KM_USER0);
-               if (bytes)
+                                       bsize, NULL, msblk->read_size);
+               if (bytes) {
+                       pageaddr = kmap_atomic(page, KM_USER0);
                        memcpy(pageaddr, msblk->read_page, bytes);
-               else
+                       kunmap_atomic(pageaddr, KM_USER0);
+               } else
                        ERROR("Unable to read page, block %llx, size %x\n",
                                        block, bsize);
                up(&msblk->read_page_mutex);
@@ -1650,11 +1686,12 @@ static int squashfs_readpage4K(struct file *file, struct page *page)
                                        SQUASHFS_I(inode)->
                                        u.s1.fragment_start_block,
                                        SQUASHFS_I(inode)-> u.s1.fragment_size);
-               pageaddr = kmap_atomic(page, KM_USER0);
                if (fragment) {
                        bytes = i_size_read(inode) & (sblk->block_size - 1);
+                       pageaddr = kmap_atomic(page, KM_USER0);
                        memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
                                        u.s1.fragment_offset, bytes);
+                       kunmap_atomic(pageaddr, KM_USER0);
                        release_cached_fragment(msblk, fragment);
                } else
                        ERROR("Unable to read page, block %llx, size %x\n",
@@ -1664,6 +1701,7 @@ static int squashfs_readpage4K(struct file *file, struct page *page)
        }
 
 skip_read:
+       pageaddr = kmap_atomic(page, KM_USER0);
        memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
        kunmap_atomic(pageaddr, KM_USER0);
        flush_dcache_page(page);