# HG changeset patch # User Pascal Bellard # Date 1588583112 0 # Node ID 36e9a3dcd5de642da255f9bd7b1198d9704966ca # Parent d98ac734626df90860af0c3da6c6b58fbd1ee4fd Up linux-cloop (4.12) diff -r d98ac734626d -r 36e9a3dcd5de fusecloop/stuff/fusecloop.u --- a/fusecloop/stuff/fusecloop.u Mon May 04 07:56:50 2020 +0100 +++ b/fusecloop/stuff/fusecloop.u Mon May 04 09:05:12 2020 +0000 @@ -734,7 +734,7 @@ + tail.table_size = ntohl(len); + pos += len + sizeof(tail); + n = pos & 511; -+ if (n) write(STDOUT_FILENO, padding, 512 - n); ++ // if (n) write(STDOUT_FILENO, padding, 512 - n); + write(STDOUT_FILENO, compressed, len); + write(STDOUT_FILENO, &tail, sizeof(tail)); + exit(sig != 0); diff -r d98ac734626d -r 36e9a3dcd5de linux-cloop/receipt --- a/linux-cloop/receipt Mon May 04 07:56:50 2020 +0100 +++ b/linux-cloop/receipt Mon May 04 09:05:12 2020 +0000 @@ -2,13 +2,15 @@ PACKAGE="linux-cloop" SOURCE="cloop" -VERSION="2.639-2" +_VERSION="2.639-2" +#VERSION="$(sed '/+#define CLOOP_VERSION/!d;s|.* "\(.*\)"|\1|' stuff/cloop.u)" +VERSION="4.12" CATEGORY="base-system" MAINTAINER="pascal.bellard@slitaz.org" LICENSE="GPL2" SHORT_DESC="The read-only compressed loop device kernel module." WEB_SITE="http://knoppix.net/wiki/Cloop" -TARBALL="${SOURCE}_${VERSION}.tar.gz" +TARBALL="${SOURCE}_${_VERSION}.tar.gz" WGET_URL="http://debian-knoppix.alioth.debian.org/packages/$SOURCE/$TARBALL" DEPENDS="linux" @@ -19,9 +21,7 @@ compile_rules() { - patch -p0 < $stuff/cloop.u # 3.2.98 - sed -i -e 's|file->f_path.mnt, file->f_path.dentry|\&file->f_path|' \ - -e 's|bvec->|bvec.|g;s|*bvec|bvec|' cloop.c + patch -p0 < $stuff/cloop.u make KERNEL_DIR="/usr/src/linux" cloop.ko && xz cloop.ko } diff -r d98ac734626d -r 36e9a3dcd5de linux-cloop/stuff/cloop.u --- a/linux-cloop/stuff/cloop.u Mon May 04 07:56:50 2020 +0100 +++ b/linux-cloop/stuff/cloop.u Mon May 04 09:05:12 2020 +0000 @@ -1,80 +1,221 @@ --- cloop.h +++ cloop.h -@@ -20,6 +20,80 @@ +@@ -1,15 +1,50 @@ ++#define CLOOP_SIGNATURE "#!/bin/sh" /* @ offset 0 */ ++#define CLOOP_SIGNATURE_SIZE 9 ++#define CLOOP_SIGNATURE_OFFSET 0x0 ++ + #ifndef _COMPRESSED_LOOP_H + #define _COMPRESSED_LOOP_H + +-#define CLOOP_HEADROOM 128 ++/*************************************************************************\ ++* Starting with Format V4.0 (cloop version 4.x), cloop can now have two * ++* alternative structures: * ++* * ++* 1. Header first: "robust" format, handles missing blocks well * ++* 2. Footer (header last): "streaming" format, easier to create * ++* * ++* The cloop kernel module autodetects both formats, and can (currently) * ++* still handle the V2.0 format as well. * ++* * ++* 1. Header first: * ++* +---------------------------- FIXED SIZE ---------------------------+ * ++* |Signature (128 bytes) | * ++* |block_size (32bit number, network order) | * ++* |num_blocks (32bit number, network order) | * ++* +--------------------------- VARIABLE SIZE -------------------------+ * ++* |num_blocks * FlagsOffset (upper 4 bits flags, lower 64 bits offset)| * ++* |compressed data blocks of variable size ... | * ++* +-------------------------------------------------------------------+ * ++* * ++* 2. Footer (header last): * ++* +--------------------------- VARIABLE SIZE -------------------------+ * ++* |compressed data blocks of variable size ... | * ++* |num_blocks * FlagsOffset (upper 4 bits flags, lower 64 bits offset)| * ++* +---------------------------- FIXED SIZE ---------------------------+ * ++* |Signature (128 bytes) | * ++* |block_size (32bit number, network order) | * ++* |num_blocks (32bit number, network order) | * ++* +-------------------------------------------------------------------+ * ++* * ++* Offsets are always relative to beginning of file, in all formats. * ++* The block index contains num_blocks+1 offsets, followed (1) or * ++* preceded (2) by the compressed blocks. * ++\*************************************************************************/ + +-/* The cloop header usually looks like this: */ +-/* #!/bin/sh */ +-/* #V2.00 Format */ +-/* ...padding up to CLOOP_HEADROOM... */ +-/* block_size (32bit number, network order) */ +-/* num_blocks (32bit number, network order) */ ++#include /* u_int32_t */ ++ ++#define CLOOP_HEADROOM 128 + ++/* Header of fixed length, can be located at beginning or end of file */ + struct cloop_head + { + char preamble[CLOOP_HEADROOM]; +@@ -17,9 +52,163 @@ + u_int32_t num_blocks; + }; + ++/************************************************************************\ ++* CLOOP4 flags for each compressed block * ++* Value Meaning * ++* 0 GZIP/7ZIP compression (compatible with V2.0 Format) * ++* 1 no compression (incompressible data) * ++* 2 xz compression (currently best space saver) * ++* 3 lz4 compression * ++* 4 lzo compression (fastest) * ++\************************************************************************/ ++ ++typedef uint64_t cloop_block_ptr; ++ ++/* Get value of first 4 bits */ ++#define CLOOP_BLOCK_FLAGS(x) ((unsigned int)(((x) & 0xf000000000000000LLU) >> 60)) ++/* Get value of last 60 bits */ ++#define CLOOP_BLOCK_OFFSET(x) ((x) & 0x0fffffffffffffffLLU) ++ ++#define CLOOP_COMPRESSOR_ZLIB 0x0 ++#define CLOOP_COMPRESSOR_NONE 0x1 ++#define CLOOP_COMPRESSOR_XZ 0x2 ++#define CLOOP_COMPRESSOR_LZ4 0x3 ++#define CLOOP_COMPRESSOR_LZO1X 0x4 ++ ++#define CLOOP_COMPRESSOR_VALID(x) ((x) >= CLOOP_COMPRESSOR_ZLIB && (x) <= CLOOP_COMPRESSOR_LZO1X) ++ ++#define CLOOP_COMPRESSOR_LINK 0xF ++ ++ /* data_index (num_blocks 64bit pointers, network order)... */ /* compressed data (gzip block compressed format)... */ +struct cloop_tail +{ -+ u_int32_t table_size; -+ u_int32_t index_size; ++ u_int32_t table_size; ++ u_int32_t index_size; /* size:4 comp:3 ctrl-c:1 lastlen:24 */ ++#define CLOOP3_INDEX_SIZE(x) ((unsigned int)((x) & 0xF)) ++#define CLOOP3_BLOCKS_FLAGS(x) ((unsigned int)((x) & 0x70) >> 4) ++#define CLOOP3_TRUNCATED(x) ((unsigned int)((x) & 0x80) >> 7) ++#define CLOOP3_LASTLEN(x) (unsigned int)((x) >> 8) + u_int32_t num_blocks; +}; + ++#define GZIP_MAX_BUFFER(n) ((n) + (n)/1000 + 12) ++ +struct block_info +{ + loff_t offset; /* 64-bit offsets of compressed block */ + u_int32_t size; /* 32-bit compressed block size */ -+ u_int32_t optidx; /* 32-bit index number */ ++ u_int32_t flags; /* 32-bit compression flags */ +}; + -+static inline char *build_index(struct block_info *offsets, unsigned long n) ++static inline char *build_index(struct block_info *offsets, unsigned long n, ++ unsigned long block_size, unsigned global_flags) +{ + u_int32_t *ofs32 = (u_int32_t *) offsets; + loff_t *ofs64 = (loff_t *) offsets; -+ ++ ++ /* v3 64bits bug: v1 assumed */ ++ unsigned long v3_64 = (n+1)/2; ++ loff_t prev; ++ ++ if (ofs32[0] != 0 && ofs32[1] == 0) { ++ for (prev=__le64_to_cpu(ofs64[v3_64]); ++ v3_64 > 0 && __le64_to_cpu(ofs64[--v3_64]) < prev; ++ prev=__le64_to_cpu(ofs64[v3_64])); ++ } ++ + if (ofs32[0] == 0) { + if (ofs32[2]) { /* ACCELERATED KNOPPIX V1.0 */ + while (n--) { + offsets[n].offset = __be64_to_cpu(offsets[n].offset); + offsets[n].size = ntohl(offsets[n].size); ++ offsets[n].flags = 0; + } + return (char *) "128BE accelerated knoppix 1.0"; + } -+ else { /* V2.0 */ -+ loff_t last = __be64_to_cpu(ofs64[n - 1]); -+ while (n--) { ++ else { /* V2.0/V4.0 */ ++ loff_t last = CLOOP_BLOCK_OFFSET(__be64_to_cpu(ofs64[n])); ++ u_int32_t flags; ++ static char v4[11]; ++ unsigned long i = n; ++ ++ for (flags = 0; n-- ;) { ++ loff_t data = __be64_to_cpu(ofs64[n]); ++ + offsets[n].size = last - -+ (offsets[n].offset = __be64_to_cpu(ofs64[n])); ++ (offsets[n].offset = CLOOP_BLOCK_OFFSET(data)); + last = offsets[n].offset; ++ offsets[n].flags = CLOOP_BLOCK_FLAGS(data); ++ flags |= 1 << offsets[n].flags; + } -+ return (char *) "64BE v2.0"; ++ if (flags < 2) return (char *) "64BE v2.0"; ++ while (i--) { ++ if (offsets[i].flags == CLOOP_COMPRESSOR_LINK) { ++ offsets[i] = offsets[offsets[i].offset]; ++ } ++ } ++ strcpy(v4, (char *) "64BE v4.0a"); ++ v4[10] = 'a' + ((flags-1) & 0xF); // compressors used ++ if (flags > 0x10) { // with links ? ++ v4[10] += 'A' - 'a'; ++ } ++ return v4; + } + } -+ else if (ofs32[1] == 0) { /* V1.0 */ -+ loff_t last = __le64_to_cpu(ofs64[n - 1]); ++ else if (ofs32[1] == 0 && v3_64 == 0) { /* V1.0 */ ++ loff_t last = __le64_to_cpu(ofs64[n]); + while (n--) { + offsets[n].size = last - + (offsets[n].offset = __le64_to_cpu(ofs64[n])); + last = offsets[n].offset; ++ offsets[n].flags = 0; + } + return (char *) "64LE v1.0"; + } -+ else if (ntohl(ofs32[0]) == (4*n) + 0x8C) { /* V0.68 */ -+ loff_t last = ntohl(ofs32[n - 1]); -+ while (n--) { -+ offsets[n].size = last - -+ (offsets[n].offset = ntohl(ofs32[n])); -+ last = offsets[n].offset; -+ } -+ return (char *) "32BE v0.68"; -+ } -+ else { /* V3.0 */ ++ else { /* V3.0 or V0.68 */ + unsigned long i; + loff_t j; ++ static char v3[11]; + ++ for (i = 0; i < n && ntohl(ofs32[i]) < ntohl(ofs32[i+1]); i++); ++ if (i == n && ntohl(ofs32[0]) == (4*n) + 0x8C) { /* V0.68 */ ++ loff_t last = ntohl(ofs32[n]); ++ while (n--) { ++ offsets[n].size = last - ++ (offsets[n].offset = ntohl(ofs32[n])); ++ last = offsets[n].offset; ++ offsets[n].flags = 0; ++ } ++ return (char *) "32BE v0.68"; ++ } ++ ++ v3_64 = (ofs32[1] == 0); + for (i = n; i-- != 0; ) -+ offsets[i].size = ntohl(ofs32[i]); ++ offsets[i].size = ntohl(ofs32[i << v3_64]); + for (i = 0, j = sizeof(struct cloop_head); i < n; i++) { + offsets[i].offset = j; ++ offsets[i].flags = global_flags; ++ if (offsets[i].size == 0xFFFFFFFF) { ++ offsets[i].flags = CLOOP_COMPRESSOR_NONE; ++ offsets[i].size = block_size; ++ } ++ if ((offsets[i].size & 0x80000000) == 0) { ++ j += offsets[i].size; ++ } ++ } ++ for (i = 0; i < n; i++) { + if (offsets[i].size & 0x80000000) { -+ unsigned long k = offsets[i].size & 0x7FFFFFFF; -+ offsets[i].offset = offsets[k].offset; -+ offsets[i].size = offsets[k].size; ++ offsets[i] = offsets[offsets[i].size & 0x7FFFFFFF]; + } -+ else j += offsets[i].size; + } -+ return (char *) "32BE v3.0"; ++ strcpy(v3, (char *) (v3_64) ? "64BE v3.0a" : "32BE v3.0a"); ++ v3[10] += global_flags; ++ return v3; + } +} + @@ -83,187 +224,884 @@ --- cloop.c +++ cloop.c -@@ -5,11 +5,18 @@ - * A cloop file looks like this: - * [32-bit uncompressed block size: network order] - * [32-bit number of blocks (n_blocks): network order] +@@ -1,26 +1,23 @@ +-/* +- * compressed_loop.c: Read-only compressed loop blockdevice +- * hacked up by Rusty in 1999, extended and maintained by Klaus Knopper +- * +- * A cloop file looks like this: +- * [32-bit uncompressed block size: network order] +- * [32-bit number of blocks (n_blocks): network order] - * [64-bit file offsets of start of blocks: network order] -+ * [for version < 3] -+ * [32-bit, 64-bit or 128-bit file offsets of start of blocks] - * ... - * (n_blocks + 1). - * n_blocks consisting of: - * [compressed block] -+ * ... -+ * [for version >= 3] -+ * [compressed list of 32-bit block sizes] -+ * [32-bit compressed index size: network order] -+ * [32-bit index size = 4: network order] -+ * [32-bit number of blocks (n_blocks): network order] - * - * Every version greatly inspired by code seen in loop.c - * by Theodore Ts'o, 3/29/93. -@@ -115,7 +122,7 @@ +- * ... +- * (n_blocks + 1). +- * n_blocks consisting of: +- * [compressed block] +- * +- * Every version greatly inspired by code seen in loop.c +- * by Theodore Ts'o, 3/29/93. +- * +- * Copyright 1999-2009 by Paul `Rusty' Russell & Klaus Knopper. +- * Redistribution of this file is permitted under the GNU Public License. +- * +- */ ++/************************************************************************\ ++* cloop.c: Read-only compressed loop blockdevice * ++* hacked up by Rusty in 1999, extended and maintained by Klaus Knopper * ++* * ++* For all supported cloop file formats, please check the file "cloop.h" * ++* New in Version 4: * ++* - Header can be first or last in cloop file, * ++* - Different compression algorithms supported (compression type * ++* encoded in first 4 bytes of block offset address) * ++* * ++* Every version greatly inspired by code seen in loop.c * ++* by Theodore Ts'o, 3/29/93. * ++* * ++* Copyright 1999-2009 by Paul `Rusty' Russell & Klaus Knopper. * ++* Redistribution of this file is permitted under the GNU Public License * ++* V2. * ++\************************************************************************/ + + #define CLOOP_NAME "cloop" +-#define CLOOP_VERSION "2.639" ++#define CLOOP_VERSION "4.12" + #define CLOOP_MAX 8 + + #ifndef KBUILD_MODNAME +@@ -47,8 +44,27 @@ + #include /* do_div() for 64bit division */ + #include + #include +-/* Use zlib_inflate from lib/zlib_inflate */ ++/* Check for ZLIB, LZO1X, LZ4 decompression algorithms in kernel. */ ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) + #include ++#endif ++#if (defined(CONFIG_LZO_DECOMPRESS) || defined(CONFIG_LZO_DECOMPRESS_MODULE)) ++#include ++#endif ++#if (defined(CONFIG_DECOMPRESS_LZ4) || defined(CONFIG_DECOMPRESS_LZ4_MODULE)) ++#include ++#endif ++#if (defined(CONFIG_DECOMPRESS_LZMA) || defined(CONFIG_DECOMPRESS_LZMA_MODULE)) ++#include ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++#include ++#endif ++ ++#if (!(defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE) || defined(CONFIG_LZO_DECOMPRESS) || defined(CONFIG_LZO_DECOMPRESS_MODULE) || defined(CONFIG_DECOMPRESS_LZ4) || defined(CONFIG_DECOMPRESS_LZ4_MODULE) || defined(CONFIG_DECOMPRESS_LZMA) || defined(CONFIG_DECOMPRESS_LZMA_MODULE) || defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE))) ++#error "No decompression library selected in kernel config!" ++#endif ++ + #include + #include + #include +@@ -92,47 +108,64 @@ + #define DEBUGP(format, x...) + #endif + ++/* Default size of buffer to keep some decompressed blocks in memory to speed up access */ ++#define BLOCK_BUFFER_MEM (16*65536) ++ + /* One file can be opened at module insertion time */ + /* insmod cloop file=/path/to/file */ + static char *file=NULL; + static unsigned int preload=0; + static unsigned int cloop_max=CLOOP_MAX; ++static unsigned int buffers=BLOCK_BUFFER_MEM; + module_param(file, charp, 0); + module_param(preload, uint, 0); + module_param(cloop_max, uint, 0); + MODULE_PARM_DESC(file, "Initial cloop image file (full path) for /dev/cloop"); + MODULE_PARM_DESC(preload, "Preload n blocks of cloop data into memory"); + MODULE_PARM_DESC(cloop_max, "Maximum number of cloop devices (default 8)"); ++MODULE_PARM_DESC(buffers, "Size of buffer to keep uncompressed blocks in memory in MiB (default 1)"); + + static struct file *initial_file=NULL; + static int cloop_major=MAJOR_NR; + +-/* Number of buffered decompressed blocks */ +-#define BUFFERED_BLOCKS 8 + struct cloop_device + { +- /* Copied straight from the file */ ++ /* Header filled from the file */ struct cloop_head head; ++ int header_first; ++ int file_format; - /* An array of offsets of compressed blocks within the file */ +- /* An array of offsets of compressed blocks within the file */ - loff_t *offsets; -+ struct block_info *offsets; ++ /* An or'd sum of all flags of each compressed block (v3) */ ++ u_int32_t allflags; ++ ++ /* An array of cloop_ptr flags/offset for compressed blocks within the file */ ++ struct block_info *block_ptrs; /* We buffer some uncompressed blocks for performance */ - int buffered_blocknum[BUFFERED_BLOCKS]; -@@ -256,11 +263,11 @@ - return i; - } +- int buffered_blocknum[BUFFERED_BLOCKS]; +- int current_bufnum; +- void *buffer[BUFFERED_BLOCKS]; +- void *compressed_buffer; +- size_t preload_array_size; /* Size of pointer array in blocks */ +- size_t preload_size; /* Number of successfully allocated blocks */ +- char **preload_cache; /* Pointers to preloaded blocks */ ++ size_t num_buffered_blocks; /* how many uncompressed blocks buffered for performance */ ++ int *buffered_blocknum; /* list of numbers of uncompressed blocks in buffer */ ++ int current_bufnum; /* which block is current */ ++ unsigned char **buffer; /* cache space for num_buffered_blocks uncompressed blocks */ ++ void *compressed_buffer; /* space for the largest compressed block */ ++ size_t preload_array_size; /* Size of pointer array in blocks */ ++ size_t preload_size; /* Number of successfully allocated blocks */ ++ char **preload_cache; /* Pointers to preloaded blocks */ ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) + z_stream zstream; ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++ struct xz_dec *xzdecoderstate; ++ struct xz_buf xz_buffer; ++#endif + + struct file *backing_file; /* associated file */ + struct inode *backing_inode; /* for bmap */ + ++ unsigned char *underlying_filename; + unsigned long largest_block; + unsigned int underlying_blksize; ++ loff_t underlying_total_size; + int clo_number; + int refcnt; + struct block_device *bdev; +@@ -147,7 +180,6 @@ + struct request_queue *clo_queue; + struct gendisk *clo_disk; + int suspended; +- char clo_file_name[LO_NAME_SIZE]; + }; + + /* Changed in 2.639: cloop_dev is now a an array of cloop_dev pointers, +@@ -156,52 +188,113 @@ + static const char *cloop_name=CLOOP_NAME; + static int cloop_count = 0; + +-#if (!(defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE))) /* Must be compiled into kernel. */ +-#error "Invalid Kernel configuration. CONFIG_ZLIB_INFLATE support is needed for cloop." +-#endif +- +-/* Use __get_free_pages instead of vmalloc, allows up to 32 pages, +- * 2MB in one piece */ + static void *cloop_malloc(size_t size) + { +- int order = get_order(size); +- if(order <= KMALLOC_MAX_ORDER) +- return (void *)kmalloc(size, GFP_KERNEL); +- else if(order < MAX_ORDER) +- return (void *)__get_free_pages(GFP_KERNEL, order); ++ /* kmalloc will fail after the system is running for a while, */ ++ /* when large orders can't return contiguous memory. */ ++ /* Let's just use vmalloc for now. :-/ */ ++ /* int order = get_order(size); */ ++ /* if(order <= KMALLOC_MAX_ORDER) */ ++ /* return (void *)kmalloc(size, GFP_KERNEL); */ ++ /* else if(order < MAX_ORDER) */ ++ /* return (void *)__get_free_pages(GFP_KERNEL, order); */ + return (void *)vmalloc(size); + } + + static void cloop_free(void *mem, size_t size) + { +- int order = get_order(size); +- if(order <= KMALLOC_MAX_ORDER) +- kfree(mem); +- else if(order < MAX_ORDER) +- free_pages((unsigned long)mem, order); +- else vfree(mem); ++ /* int order = get_order(size); */ ++ /* if(order <= KMALLOC_MAX_ORDER) */ ++ /* kfree(mem); */ ++ /* else if(order < MAX_ORDER) */ ++ /* free_pages((unsigned long)mem, order); */ ++ /* else */ ++ vfree(mem); + } + +-static int uncompress(struct cloop_device *clo, +- unsigned char *dest, unsigned long *destLen, +- unsigned char *source, unsigned long sourceLen) ++static int uncompress(struct cloop_device *clo, unsigned char *dest, unsigned long *destLen, unsigned char *source, unsigned long sourceLen, int flags) + { +- /* Most of this code can be found in fs/cramfs/uncompress.c */ +- int err; +- clo->zstream.next_in = source; +- clo->zstream.avail_in = sourceLen; +- clo->zstream.next_out = dest; +- clo->zstream.avail_out = *destLen; +- err = zlib_inflateReset(&clo->zstream); +- if (err != Z_OK) +- { +- printk(KERN_ERR "%s: zlib_inflateReset error %d\n", cloop_name, err); +- zlib_inflateEnd(&clo->zstream); zlib_inflateInit(&clo->zstream); +- } +- err = zlib_inflate(&clo->zstream, Z_FINISH); +- *destLen = clo->zstream.total_out; +- if (err != Z_STREAM_END) return err; +- return Z_OK; ++ int err = -1; ++ switch(flags) ++ { ++ case CLOOP_COMPRESSOR_NONE: ++ memcpy(dest, source, *destLen = sourceLen); ++ err = Z_OK; ++ break; ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) ++ case CLOOP_COMPRESSOR_ZLIB: ++ clo->zstream.next_in = source; ++ clo->zstream.avail_in = sourceLen; ++ clo->zstream.next_out = dest; ++ clo->zstream.avail_out = *destLen; ++ err = zlib_inflateReset(&clo->zstream); ++ if (err != Z_OK) ++ { ++ printk(KERN_ERR "%s: zlib_inflateReset error %d\n", cloop_name, err); ++ zlib_inflateEnd(&clo->zstream); zlib_inflateInit(&clo->zstream); ++ } ++ err = zlib_inflate(&clo->zstream, Z_FINISH); ++ *destLen = clo->zstream.total_out; ++ if (err == Z_STREAM_END) err = 0; ++ DEBUGP("cloop: zlib decompression done, ret =%d, size =%lu\n", err, *destLen); ++ break; ++#endif ++#if (defined(CONFIG_LZO_DECOMPRESS) || defined(CONFIG_LZO_DECOMPRESS_MODULE)) ++ case CLOOP_COMPRESSOR_LZO1X: ++ { ++ size_t tmp = (size_t) clo->head.block_size; ++ err = lzo1x_decompress_safe(source, sourceLen, ++ dest, &tmp); ++ if (err == LZO_E_OK) *destLen = (u_int32_t) tmp; ++ } ++ break; ++#endif ++#if (defined(CONFIG_DECOMPRESS_LZ4) || defined(CONFIG_DECOMPRESS_LZ4_MODULE)) ++ case CLOOP_COMPRESSOR_LZ4: ++ { ++ size_t outputSize = *destLen; ++ /* We should adjust outputSize here, in case the last block is smaller than block_size */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* field removed */ ++ err = lz4_decompress(source, (size_t *) &sourceLen, ++ dest, outputSize); ++#else ++ err = LZ4_decompress_safe(source, ++ dest, ++ sourceLen, outputSize); ++#endif ++ if (err >= 0) ++ { ++ err = 0; ++ *destLen = outputSize; ++ } ++ } ++ break; ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++ case CLOOP_COMPRESSOR_XZ: ++ clo->xz_buffer.in = source; ++ clo->xz_buffer.in_pos = 0; ++ clo->xz_buffer.in_size = sourceLen; ++ clo->xz_buffer.out = dest; ++ clo->xz_buffer.out_pos = 0; ++ clo->xz_buffer.out_size = *destLen; ++ xz_dec_reset(clo->xzdecoderstate); ++ err = xz_dec_run(clo->xzdecoderstate, &clo->xz_buffer); ++ if (err == XZ_STREAM_END || err == XZ_OK) ++ { ++ err = 0; ++ } ++ else ++ { ++ printk(KERN_ERR "%s: xz_dec_run error %d\n", cloop_name, err); ++ err = 1; ++ } ++ break; ++#endif ++ default: ++ printk(KERN_ERR "%s: compression method is not supported!\n", cloop_name); ++ } ++ return err; + } + + static ssize_t cloop_read_from_file(struct cloop_device *clo, struct file *f, char *buf, +@@ -220,7 +313,7 @@ + + if(size_read <= 0) + { +- printk(KERN_ERR "%s: Read error %d at pos %Lu in file %s, " ++ printk(KERN_ERR "%s: Read error %d at pos %llu in file %s, " + "%d bytes lost.\n", cloop_name, (int)size_read, pos, + file, (int)size); + memset(buf + buf_len - size, 0, size); +@@ -232,72 +325,84 @@ + } + + /* This looks more complicated than it is */ +-/* Returns number of block buffer to use for this request */ ++/* Returns number of cache block buffer to use for this request */ + static int cloop_load_buffer(struct cloop_device *clo, int blocknum) + { +- unsigned int buf_done = 0; +- unsigned long buflen; +- unsigned int buf_length; ++ loff_t compressed_block_offset; ++ long compressed_block_len; ++ long uncompressed_block_len=0; + int ret; + int i; +- if(blocknum > ntohl(clo->head.num_blocks) || blocknum < 0) +- { +- printk(KERN_WARNING "%s: Invalid block number %d requested.\n", +- cloop_name, blocknum); +- return -1; +- } ++ if(blocknum > clo->head.num_blocks || blocknum < 0) ++ { ++ printk(KERN_WARNING "%s: Invalid block number %d requested.\n", ++ cloop_name, blocknum); ++ return -1; ++ } + + /* Quick return if the block we seek is already in one of the buffers. */ + /* Return number of buffer */ +- for(i=0; inum_buffered_blocks; i++) + if (blocknum == clo->buffered_blocknum[i]) +- { +- DEBUGP(KERN_INFO "cloop_load_buffer: Found buffered block %d\n", i); +- return i; +- } +- - buf_length = be64_to_cpu(clo->offsets[blocknum+1]) - be64_to_cpu(clo->offsets[blocknum]); -+ buf_length = clo->offsets[blocknum].size; +- +-/* Load one compressed block from the file. */ +- cloop_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer, +- be64_to_cpu(clo->offsets[blocknum]), buf_length); ++ { ++ DEBUGP(KERN_INFO "cloop_load_buffer: Found buffered block %d\n", i); ++ return i; ++ } - /* Load one compressed block from the file. */ - cloop_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer, -- be64_to_cpu(clo->offsets[blocknum]), buf_length); -+ clo->offsets[blocknum].offset, buf_length); +- buflen = ntohl(clo->head.block_size); ++ compressed_block_offset = clo->block_ptrs[blocknum].offset; ++ compressed_block_len = (long) (clo->block_ptrs[blocknum].size) ; - buflen = ntohl(clo->head.block_size); +- /* Go to next position in the block ring buffer */ +- clo->current_bufnum++; +- if(clo->current_bufnum >= BUFFERED_BLOCKS) clo->current_bufnum = 0; ++ /* Load one compressed block from the file. */ ++ if(compressed_block_offset > 0 && compressed_block_len >= 0) /* sanity check */ ++ { ++ size_t n = cloop_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer, ++ compressed_block_offset, compressed_block_len); ++ if (n!= compressed_block_len) ++ { ++ printk(KERN_ERR "%s: error while reading %lu bytes @ %llu from file %s\n", ++ cloop_name, compressed_block_len, clo->block_ptrs[blocknum].offset, clo->underlying_filename); ++ /* return -1; */ ++ } ++ } else { ++ printk(KERN_ERR "%s: invalid data block len %ld bytes @ %lld from file %s\n", ++ cloop_name, compressed_block_len, clo->block_ptrs[blocknum].offset, clo->underlying_filename); ++ return -1; ++ } ++ ++ /* Go to next position in the cache block buffer (which is used as a cyclic buffer) */ ++ if(++clo->current_bufnum >= clo->num_buffered_blocks) clo->current_bufnum = 0; -@@ -275,9 +282,9 @@ + /* Do the uncompression */ +- ret = uncompress(clo, clo->buffer[clo->current_bufnum], &buflen, clo->compressed_buffer, +- buf_length); ++ uncompressed_block_len = clo->head.block_size; ++ ret = uncompress(clo, clo->buffer[clo->current_bufnum], &uncompressed_block_len, ++ clo->compressed_buffer, compressed_block_len, clo->block_ptrs[blocknum].flags); + /* DEBUGP("cloop: buflen after uncompress: %ld\n",buflen); */ if (ret != 0) +- { +- printk(KERN_ERR "%s: zlib decompression error %i uncompressing block %u %u/%lu/%u/%u " +- "%Lu-%Lu\n", cloop_name, ret, blocknum, +- ntohl(clo->head.block_size), buflen, buf_length, buf_done, +- be64_to_cpu(clo->offsets[blocknum]), be64_to_cpu(clo->offsets[blocknum+1])); +- clo->buffered_blocknum[clo->current_bufnum] = -1; +- return -1; +- } ++ { ++ printk(KERN_ERR "%s: decompression error %i uncompressing block %u %lu bytes @ %llu, flags %u\n", ++ cloop_name, ret, blocknum, ++ compressed_block_len, clo->block_ptrs[blocknum].offset, ++ clo->block_ptrs[blocknum].flags); ++ clo->buffered_blocknum[clo->current_bufnum] = -1; ++ return -1; ++ } + clo->buffered_blocknum[clo->current_bufnum] = blocknum; + return clo->current_bufnum; + } + + /* This function does all the real work. */ +-/* returns "uptodate" */ ++/* returns "uptodate" */ + static int cloop_handle_request(struct cloop_device *clo, struct request *req) + { + int buffered_blocknum = -1; + int preloaded = 0; + loff_t offset = (loff_t) blk_rq_pos(req)<<9; /* req->sector<<9 */ +- struct bio_vec *bvec; ++ struct bio_vec bvec; + struct req_iterator iter; + rq_for_each_segment(bvec, req, iter) { - printk(KERN_ERR "%s: zlib decompression error %i uncompressing block %u %u/%lu/%u/%u " -- "%Lu-%Lu\n", cloop_name, ret, blocknum, -+ "%Lu:%u\n", cloop_name, ret, blocknum, - ntohl(clo->head.block_size), buflen, buf_length, buf_done, -- be64_to_cpu(clo->offsets[blocknum]), be64_to_cpu(clo->offsets[blocknum+1])); -+ clo->offsets[blocknum].offset, clo->offsets[blocknum].size); - clo->buffered_blocknum[clo->current_bufnum] = -1; - return -1; +- unsigned long len = bvec->bv_len; +- char *to_ptr = kmap(bvec->bv_page) + bvec->bv_offset; ++ unsigned long len = bvec.bv_len; ++ char *to_ptr = kmap(bvec.bv_page) + bvec.bv_offset; + while(len > 0) + { + u_int32_t length_in_buffer; +@@ -308,7 +413,7 @@ + /* puts the result in the first argument, i.e. block_offset */ + /* becomes the blocknumber to load, and offset_in_buffer the */ + /* position in the buffer */ +- offset_in_buffer = do_div(block_offset, ntohl(clo->head.block_size)); ++ offset_in_buffer = do_div(block_offset, clo->head.block_size); + /* Lookup preload cache */ + if(block_offset < clo->preload_size && clo->preload_cache != NULL && + clo->preload_cache[block_offset] != NULL) +@@ -325,7 +430,7 @@ + from_ptr = clo->buffer[buffered_blocknum]; + } + /* Now, at least part of what we want will be in the buffer. */ +- length_in_buffer = ntohl(clo->head.block_size) - offset_in_buffer; ++ length_in_buffer = clo->head.block_size - offset_in_buffer; + if(length_in_buffer > len) + { + /* DEBUGP("Warning: length_in_buffer=%u > len=%u\n", +@@ -337,18 +442,19 @@ + len -= length_in_buffer; + offset += length_in_buffer; + } /* while inner loop */ +- kunmap(bvec->bv_page); ++ kunmap(bvec.bv_page); ++ cond_resched(); + } /* end rq_for_each_segment*/ + return ((buffered_blocknum!=-1) || preloaded); + } + + /* Adopted from loop.c, a kernel thread to handle physical reads and +- * decompression. */ ++ decompression. */ + static int cloop_thread(void *data) + { + struct cloop_device *clo = data; + current->flags |= PF_NOFREEZE; +- set_user_nice(current, -15); ++ set_user_nice(current, 10); + while (!kthread_should_stop()||!list_empty(&clo->clo_list)) + { + int err; +@@ -390,10 +496,18 @@ + int rw; + /* quick sanity checks */ + /* blk_fs_request() was removed in 2.6.36 */ +- if (unlikely(req == NULL || (req->cmd_type != REQ_TYPE_FS))) ++ if (unlikely(req == NULL ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* field removed */ ++ || (req->cmd_type != REQ_TYPE_FS) ++#endif ++ )) + goto error_continue; + rw = rq_data_dir(req); +- if (unlikely(rw != READ && rw != READA)) ++ if (unlikely(rw != READ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) ++ && rw != READA ++#endif ++ )) + { + DEBUGP("cloop_do_request: bad command\n"); + goto error_continue; +@@ -409,40 +523,51 @@ + continue; /* next request */ + error_continue: + DEBUGP(KERN_ERR "cloop_do_request: Discarding request %p.\n", req); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + req->errors++; ++#else ++ req->error_count++; ++#endif + __blk_end_request_all(req, -EIO); } -@@ -489,30 +496,73 @@ - cloop_name, ntohl(clo->head.block_size)); - error=-EBADF; goto error_release; - } + } + +-/* Read header and offsets from already opened file */ +-static int cloop_set_file(int cloop_num, struct file *file, char *filename) ++/* Read header, flags and offsets from already opened file */ ++static int cloop_set_file(int cloop_num, struct file *file) + { + struct cloop_device *clo = cloop_dev[cloop_num]; + struct inode *inode; + char *bbuf=NULL; +- unsigned int i, offsets_read, total_offsets; +- int isblkdev; +- int error = 0; ++ unsigned int bbuf_size = 0; ++ const unsigned int header_size = sizeof(struct cloop_head); ++ unsigned int i, total_offsets=0; ++ loff_t fs_read_position = 0, header_pos[2]; ++ int flags, isblkdev, bytes_read, error = 0; ++ if (clo->suspended) return error; ++ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + inode = file->f_dentry->d_inode; ++ clo->underlying_filename = kstrdup(file->f_dentry->d_name.name ? file->f_dentry->d_name.name : (const unsigned char *)"anonymous filename", GFP_KERNEL); ++ #else ++ inode = file->f_path.dentry->d_inode; ++ clo->underlying_filename = kstrdup(file->f_path.dentry->d_name.name ? file->f_path.dentry->d_name.name : (const unsigned char *)"anonymous filename", GFP_KERNEL); ++ #endif + isblkdev=S_ISBLK(inode->i_mode)?1:0; + if(!isblkdev&&!S_ISREG(inode->i_mode)) + { + printk(KERN_ERR "%s: %s not a regular file or block device\n", +- cloop_name, filename); ++ cloop_name, clo->underlying_filename); + error=-EBADF; goto error_release; + } + clo->backing_file = file; + clo->backing_inode= inode ; +- if(!isblkdev&&inode->i_sizeunderlying_total_size = (isblkdev) ? inode->i_bdev->bd_inode->i_size : inode->i_size; ++ if(clo->underlying_total_size < header_size) + { +- printk(KERN_ERR "%s: %lu bytes (must be >= %u bytes)\n", +- cloop_name, (unsigned long)inode->i_size, +- (unsigned)sizeof(struct cloop_head)); ++ printk(KERN_ERR "%s: %llu bytes (must be >= %u bytes)\n", ++ cloop_name, clo->underlying_total_size, ++ (unsigned int)header_size); + error=-EBADF; goto error_release; + } +- /* In suspended mode, we have done all checks necessary - FF */ +- if (clo->suspended) +- return error; + if(isblkdev) + { + struct request_queue *q = bdev_get_queue(inode->i_bdev); +@@ -451,104 +576,225 @@ + /* blk_queue_max_hw_segments(clo->clo_queue, queue_max_hw_segments(q)); */ /* Removed in 2.6.34 */ + blk_queue_max_segment_size(clo->clo_queue, queue_max_segment_size(q)); + blk_queue_segment_boundary(clo->clo_queue, queue_segment_boundary(q)); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) + blk_queue_merge_bvec(clo->clo_queue, q->merge_bvec_fn); ++#endif + clo->underlying_blksize = block_size(inode->i_bdev); + } + else + clo->underlying_blksize = PAGE_SIZE; +- DEBUGP("Underlying blocksize is %u\n", clo->underlying_blksize); +- bbuf = cloop_malloc(clo->underlying_blksize); ++ ++ DEBUGP(KERN_INFO "Underlying blocksize of %s is %u\n", clo->underlying_filename, clo->underlying_blksize); ++ DEBUGP(KERN_INFO "Underlying total size of %s is %llu\n", clo->underlying_filename, clo->underlying_total_size); ++ ++ /* clo->underlying_blksize should be larger than header_size, even if it's only PAGE_SIZE */ ++ bbuf_size = clo->underlying_blksize; ++ bbuf = cloop_malloc(bbuf_size); + if(!bbuf) + { +- printk(KERN_ERR "%s: out of kernel mem for block buffer (%lu bytes)\n", +- cloop_name, (unsigned long)clo->underlying_blksize); ++ printk(KERN_ERR "%s: out of kernel mem for buffer (%u bytes)\n", ++ cloop_name, (unsigned int) bbuf_size); ++ error=-ENOMEM; goto error_release; ++ } ++ ++ header_pos[0] = 0; /* header first */ ++ header_pos[1] = clo->underlying_total_size - sizeof(struct cloop_head); /* header last */ ++ for(i=0; i<2; i++) ++ { ++ /* Check for header */ ++ size_t bytes_readable = MIN(clo->underlying_blksize, clo->underlying_total_size - header_pos[i]); ++ size_t bytes_read = cloop_read_from_file(clo, file, bbuf, header_pos[i], bytes_readable); ++ if(bytes_read != bytes_readable) ++ { ++ printk(KERN_ERR "%s: Bad file %s, read() of %s %u bytes returned %d.\n", ++ cloop_name, clo->underlying_filename, (i==0)?"first":"last", ++ (unsigned int)header_size, (int)bytes_read); ++ error=-EBADF; ++ goto error_release; ++ } ++ memcpy(&clo->head, bbuf, header_size); ++ if (strncmp(bbuf+CLOOP_SIGNATURE_OFFSET, CLOOP_SIGNATURE, CLOOP_SIGNATURE_SIZE)==0) ++ { ++ clo->file_format++; ++ clo->head.block_size=ntohl(clo->head.block_size); ++ clo->head.num_blocks=ntohl(clo->head.num_blocks); ++ clo->header_first = (i==0) ? 1 : 0; ++ printk(KERN_INFO "%s: file %s, %d blocks of %d bytes, header %s.\n", cloop_name, clo->underlying_filename, clo->head.num_blocks, clo->head.block_size, (i==0)?"first":"last"); ++ break; ++ } ++ } ++ if (clo->file_format == 0) ++ { ++ printk(KERN_ERR "%s: Cannot detect %s format.\n", ++ cloop_name, cloop_name); ++ error=-EBADF; goto error_release; ++ } ++ if (clo->head.block_size % 512 != 0) ++ { ++ printk(KERN_ERR "%s: blocksize %u not multiple of 512\n", ++ cloop_name, clo->head.block_size); ++ error=-EBADF; goto error_release; ++ } ++ total_offsets=clo->head.num_blocks; ++ if (!isblkdev && (sizeof(struct cloop_head)+sizeof(struct block_info)* ++ total_offsets > inode->i_size)) ++ { ++ printk(KERN_ERR "%s: file %s too small for %u blocks\n", ++ cloop_name, clo->underlying_filename, clo->head.num_blocks); ++ error=-EBADF; goto error_release; ++ } ++ /* Allocate Memory for decompressors */ ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) ++ clo->zstream.workspace = cloop_malloc(zlib_inflate_workspacesize()); ++ if(!clo->zstream.workspace) ++ { ++ printk(KERN_ERR "%s: out of mem for zlib working area %u\n", ++ cloop_name, zlib_inflate_workspacesize()); + error=-ENOMEM; goto error_release; + } +- total_offsets = 1; /* Dummy total_offsets: will be filled in first time around */ +- for (i = 0, offsets_read = 0; offsets_read < total_offsets; i++) ++ zlib_inflateInit(&clo->zstream); ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++#if XZ_INTERNAL_CRC32 ++ /* This must be called before any other xz_* function to initialize the CRC32 lookup table. */ ++ xz_crc32_init(void); ++#endif ++ clo->xzdecoderstate = xz_dec_init(XZ_SINGLE, 0); ++#endif ++ if (total_offsets + 1 == 0) /* Version 3 */ + { +- unsigned int offset = 0, num_readable; +- size_t bytes_read = cloop_read_from_file(clo, file, bbuf, +- i*clo->underlying_blksize, +- clo->underlying_blksize); +- if(bytes_read != clo->underlying_blksize) ++ struct cloop_tail tail; ++ if (isblkdev) + { +- printk(KERN_ERR "%s: Bad file, read() of first %lu bytes returned %d.\n", +- cloop_name, (unsigned long)clo->underlying_blksize, (int)bytes_read); +- error=-EBADF; +- goto error_release; ++ /* No end of file: can't find index */ ++ printk(KERN_ERR "%s: no V3 support for block device\n", ++ cloop_name); ++ error=-EBADF; goto error_release; + } +- /* Header will be in block zero */ +- if(i==0) ++ bytes_read = cloop_read_from_file(clo, file, (void *) &tail, ++ inode->i_size - sizeof(struct cloop_tail), ++ sizeof(struct cloop_tail)); ++ if (bytes_read == sizeof(struct cloop_tail)) + { +- memcpy(&clo->head, bbuf, sizeof(struct cloop_head)); +- offset = sizeof(struct cloop_head); +- if (ntohl(clo->head.block_size) % 512 != 0) ++ unsigned long len, zlen; ++ int ret; ++ void *zbuf; ++ clo->head.num_blocks = ntohl(tail.num_blocks); ++ total_offsets = clo->head.num_blocks; ++ clo->block_ptrs = cloop_malloc(sizeof(struct block_info) * total_offsets); ++ zlen = ntohl(tail.table_size); ++ zbuf = cloop_malloc(zlen); ++ if (!clo->block_ptrs || !zbuf) + { +- printk(KERN_ERR "%s: blocksize %u not multiple of 512\n", +- cloop_name, ntohl(clo->head.block_size)); +- error=-EBADF; goto error_release; +- } - if (clo->head.preamble[0x0B]!='V'||clo->head.preamble[0x0C]<'1') - { - printk(KERN_ERR "%s: Cannot read old 32-bit (version 0.68) images, " - "please use an older version of %s for this file.\n", - cloop_name, cloop_name); - error=-EBADF; goto error_release; -- } ++ printk(KERN_ERR "%s: out of kernel mem for index\n", cloop_name); ++ error=-ENOMEM; goto error_release; + } - if (clo->head.preamble[0x0C]<'2') -- { ++ bytes_read = cloop_read_from_file(clo, file, zbuf, ++ inode->i_size - zlen - sizeof(struct cloop_tail), ++ zlen); ++ if (bytes_read != zlen) + { - printk(KERN_ERR "%s: Cannot read old architecture-dependent " - "(format <= 1.0) images, please use an older " - "version of %s for this file.\n", - cloop_name, cloop_name); -- error=-EBADF; goto error_release; -- } ++ printk(KERN_ERR "%s: can't read index\n", cloop_name); + error=-EBADF; goto error_release; + } - total_offsets=ntohl(clo->head.num_blocks)+1; - if (!isblkdev && (sizeof(struct cloop_head)+sizeof(loff_t)* -+ total_offsets=ntohl(clo->head.num_blocks); -+ if (!isblkdev && (sizeof(struct cloop_head)+sizeof(struct block_info)* - total_offsets > inode->i_size)) +- total_offsets > inode->i_size)) ++ len = CLOOP3_INDEX_SIZE(ntohl(tail.index_size)) * total_offsets; ++ flags = CLOOP3_BLOCKS_FLAGS(ntohl(tail.index_size)); ++// May 3 19:45:20 (none) user.info kernel: cloop: uncompress(clo=e0a78000, block_ptrs=e0c9c000, &len(1440)=ddc05e6c, zbuf=e0c9f000, zlen=43, flag=0) ++printk(KERN_INFO "%s: uncompress(clo=%p, block_ptrs=%p, &len(%ld)=%p, zbuf=%p, zlen=%ld, flag=%d)\n", cloop_name, ++ clo, clo->block_ptrs, len, &len, zbuf, zlen, flags); ++ ret = uncompress(clo, (void *) clo->block_ptrs, &len, zbuf, zlen, flags); ++// May 3 19:45:20 (none) user.alert kernel: BUG: unable to handle kernel NULL pointer dereference at (null) ++printk(KERN_INFO "%s: uncompressed !\n", cloop_name); ++ cloop_free(zbuf, zlen); ++ if (ret != 0) { - printk(KERN_ERR "%s: file too small for %u blocks\n", - cloop_name, ntohl(clo->head.num_blocks)); +- printk(KERN_ERR "%s: file too small for %u blocks\n", +- cloop_name, ntohl(clo->head.num_blocks)); ++ printk(KERN_ERR "%s: decompression error %i uncompressing index, flags %u\n", ++ cloop_name, ret, flags); error=-EBADF; goto error_release; } - clo->offsets = cloop_malloc(sizeof(loff_t) * total_offsets); -+ if (total_offsets + 1 == 0) /* Version >= 3.0 */ -+ { -+ struct cloop_tail tail; -+ if(isblkdev) -+ { -+ /* No end of file: can't find index */ -+ printk(KERN_ERR "%s: no V3 support for block device\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ bytes_read = cloop_read_from_file(clo, file, (void *) &tail, -+ inode->i_size - sizeof(struct cloop_tail), -+ sizeof(struct cloop_tail)); -+ if(bytes_read == sizeof(struct cloop_tail)) -+ { -+ unsigned long len, zlen; -+ void *zbuf; -+ clo->head.num_blocks = tail.num_blocks; -+ total_offsets = ntohl(clo->head.num_blocks); -+ clo->offsets = cloop_malloc(sizeof(struct block_info) * total_offsets); -+ if (!clo->offsets) -+ { -+ printk(KERN_ERR "%s: can't alloc index\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ zbuf = &clo->offsets[total_offsets/2]; -+ zlen = ntohl(tail.table_size); -+ len = ntohl(tail.index_size) * total_offsets; -+ bytes_read = cloop_read_from_file(clo, file, zbuf, -+ inode->i_size - zlen - sizeof(struct cloop_tail), -+ zlen); -+ if (bytes_read != zlen) -+ { -+ printk(KERN_ERR "%s: can't read index\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ clo->zstream.workspace = cloop_malloc(zlib_inflate_workspacesize()); -+ if(!clo->zstream.workspace) -+ { -+ printk(KERN_ERR "%s: can't alloc index workspace\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ zlib_inflateInit(&clo->zstream); -+ uncompress(clo, (void *) clo->offsets, &len, zbuf, zlen); -+ cloop_free(clo->zstream.workspace, zlib_inflate_workspacesize()); -+ clo->zstream.workspace = NULL; -+ break; -+ } -+ else -+ { -+ printk(KERN_ERR "%s: can't find index\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ } -+ clo->offsets = cloop_malloc(sizeof(struct block_info) * total_offsets); - if (!clo->offsets) - { - printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name); -@@ -521,19 +571,22 @@ +- if (!clo->offsets) +- { +- printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name); +- error=-ENOMEM; goto error_release; +- } } - num_readable = MIN(total_offsets - offsets_read, - (clo->underlying_blksize - offset) +- num_readable = MIN(total_offsets - offsets_read, +- (clo->underlying_blksize - offset) - / sizeof(loff_t)); - memcpy(&clo->offsets[offsets_read], bbuf+offset, num_readable * sizeof(loff_t)); -+ / sizeof(struct block_info)); -+ memcpy(&clo->offsets[offsets_read], bbuf+offset, num_readable * sizeof(struct block_info)); - offsets_read += num_readable; - } - { /* Search for largest block rather than estimate. KK. */ - int i; +- offsets_read += num_readable; +- } +- { /* Search for largest block rather than estimate. KK. */ +- int i; - for(i=0;ioffsets, ntohl(clo->head.num_blocks)); -+ for(i=0,clo->largest_block=0;iblock_ptrs = cloop_malloc(sizeof(struct block_info) * total_offsets); ++ if (!clo->block_ptrs) ++ { ++ printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name); ++ error=-ENOMEM; goto error_release; ++ } ++ /* Read them offsets! */ ++ if(clo->header_first) ++ { ++ total_bytes = total_offsets * sizeof(struct block_info); ++ fs_read_position = sizeof(struct cloop_head); ++ } ++ else { - loff_t d=be64_to_cpu(clo->offsets[i+1]) - be64_to_cpu(clo->offsets[i]); - clo->largest_block=MAX(clo->largest_block,d); -+ clo->largest_block=MAX(clo->largest_block,clo->offsets[i].size); ++ total_bytes = total_offsets * sizeof(loff_t); ++ fs_read_position = clo->underlying_total_size - sizeof(struct cloop_head) - total_bytes; ++ } ++ for(n=0;nunderlying_total_size - fs_read_position); ++ if(bytes_readable <= 0) break; /* Done */ ++ bytes_read = cloop_read_from_file(clo, file, bbuf, fs_read_position, bytes_readable); ++ if(bytes_read != bytes_readable) ++ { ++ printk(KERN_ERR "%s: Bad file %s, read() %lu bytes @ %llu returned %d.\n", ++ cloop_name, clo->underlying_filename, (unsigned long)clo->underlying_blksize, fs_read_position, (int)bytes_read); ++ error=-EBADF; ++ goto error_release; ++ } ++ memcpy(((char *)clo->block_ptrs) + n, bbuf, bytes_read); ++ /* remember where to read the next blk from file */ ++ fs_read_position += bytes_read; ++ n += bytes_read; } - printk(KERN_INFO "%s: %s: %u blocks, %u bytes/block, largest block is %lu bytes.\n", - cloop_name, filename, ntohl(clo->head.num_blocks), -+ i = ntohl(clo->head.block_size); -+ i += i/1000 + 12 + 4; /* max gzip block size */ -+ if (clo->largest_block > i) clo->largest_block = i; /* broken index ? */ -+ printk(KERN_INFO "%s: %s: %s, %u blocks, %u bytes/block, largest block is %lu bytes.\n", -+ cloop_name, filename, version, ntohl(clo->head.num_blocks), - ntohl(clo->head.block_size), clo->largest_block); +- ntohl(clo->head.block_size), clo->largest_block); } - /* Combo kmalloc used too large chunks (>130000). */ -@@ -565,16 +618,6 @@ - error=-ENOMEM; goto error_release_free_all; +-/* Combo kmalloc used too large chunks (>130000). */ + { + int i; +- for(i=0;ibuffer[i] = cloop_malloc(ntohl(clo->head.block_size)); +- if(!clo->buffer[i]) +- { +- printk(KERN_ERR "%s: out of memory for buffer %lu\n", +- cloop_name, (unsigned long) ntohl(clo->head.block_size)); +- error=-ENOMEM; goto error_release_free; +- } +- } ++ char *version = build_index(clo->block_ptrs, clo->head.num_blocks, clo->head.block_size, flags); ++ clo->largest_block = 0; ++ for (i = 0; i < clo->head.num_blocks; i++) ++ if (clo->block_ptrs[i].size > clo->largest_block) ++ clo->largest_block = clo->block_ptrs[i].size; ++ printk(KERN_INFO "%s: %s: %s: %u blocks, %u bytes/block, largest block is %lu bytes.\n", ++ cloop_name, clo->underlying_filename, version, clo->head.num_blocks, ++ clo->head.block_size, clo->largest_block); ++ } ++ { ++ int i; ++ clo->num_buffered_blocks = (buffers > 0 && clo->head.block_size >= 512) ? ++ (buffers / clo->head.block_size) : 1; ++ clo->buffered_blocknum = cloop_malloc(clo->num_buffered_blocks * sizeof (u_int32_t)); ++ clo->buffer = cloop_malloc(clo->num_buffered_blocks * sizeof (char*)); ++ if (!clo->buffered_blocknum || !clo->buffer) ++ { ++ printk(KERN_ERR "%s: out of memory for index of cache buffer (%lu bytes)\n", ++ cloop_name, (unsigned long)clo->num_buffered_blocks * sizeof (u_int32_t) + sizeof(char*) ); ++ error=-ENOMEM; goto error_release; ++ } ++ memset(clo->buffer, 0, clo->num_buffered_blocks * sizeof (char*)); ++ for(i=0;inum_buffered_blocks;i++) ++ { ++ clo->buffered_blocknum[i] = -1; ++ clo->buffer[i] = cloop_malloc(clo->head.block_size); ++ if(!clo->buffer[i]) ++ { ++ printk(KERN_ERR "%s: out of memory for cache buffer %lu\n", ++ cloop_name, (unsigned long) clo->head.block_size); ++ error=-ENOMEM; goto error_release_free; ++ } ++ } ++ clo->current_bufnum = 0; + } + clo->compressed_buffer = cloop_malloc(clo->largest_block); + if(!clo->compressed_buffer) +@@ -557,31 +803,7 @@ + cloop_name, clo->largest_block); + error=-ENOMEM; goto error_release_free_buffer; } - zlib_inflateInit(&clo->zstream); +- clo->zstream.workspace = cloop_malloc(zlib_inflate_workspacesize()); +- if(!clo->zstream.workspace) +- { +- printk(KERN_ERR "%s: out of mem for zlib working area %u\n", +- cloop_name, zlib_inflate_workspacesize()); +- error=-ENOMEM; goto error_release_free_all; +- } +- zlib_inflateInit(&clo->zstream); - if(!isblkdev && - be64_to_cpu(clo->offsets[ntohl(clo->head.num_blocks)]) != inode->i_size) - { @@ -274,15 +1112,264 @@ - cloop_free(clo->zstream.workspace, zlib_inflate_workspacesize()); clo->zstream.workspace=NULL; - goto error_release_free_all; - } +- { +- int i; +- for(i=0; ibuffered_blocknum[i] = -1; +- clo->current_bufnum=0; +- } +- set_capacity(clo->clo_disk, (sector_t)(ntohl(clo->head.num_blocks)* +- (ntohl(clo->head.block_size)>>9))); ++ set_capacity(clo->clo_disk, (sector_t)(clo->head.num_blocks*(clo->head.block_size>>9))); + clo->clo_thread = kthread_create(cloop_thread, clo, "cloop%d", cloop_num); + if(IS_ERR(clo->clo_thread)) + { +@@ -591,17 +813,17 @@ + } + if(preload > 0) + { +- clo->preload_array_size = ((preload<=ntohl(clo->head.num_blocks))?preload:ntohl(clo->head.num_blocks)); ++ clo->preload_array_size = ((preload<=clo->head.num_blocks)?preload:clo->head.num_blocks); + clo->preload_size = 0; + if((clo->preload_cache = cloop_malloc(clo->preload_array_size * sizeof(char *))) != NULL) + { + int i; + for(i=0; ipreload_array_size; i++) + { +- if((clo->preload_cache[i] = cloop_malloc(ntohl(clo->head.block_size))) == NULL) ++ if((clo->preload_cache[i] = cloop_malloc(clo->head.block_size)) == NULL) + { /* Out of memory */ + printk(KERN_WARNING "%s: cloop_malloc(%d) failed for preload_cache[%d] (ignored).\n", +- cloop_name, ntohl(clo->head.block_size), i); ++ cloop_name, clo->head.block_size, i); + break; + } + } +@@ -612,13 +834,13 @@ + if(buffered_blocknum >= 0) + { + memcpy(clo->preload_cache[i], clo->buffer[buffered_blocknum], +- ntohl(clo->head.block_size)); ++ clo->head.block_size); + } + else + { + printk(KERN_WARNING "%s: can't read block %d into preload cache, set to zero.\n", + cloop_name, i); +- memset(clo->preload_cache[i], 0, ntohl(clo->head.block_size)); ++ memset(clo->preload_cache[i], 0, clo->head.block_size); + } + } + printk(KERN_INFO "%s: preloaded %d blocks into cache.\n", cloop_name, +@@ -641,22 +863,19 @@ + cloop_free(clo->compressed_buffer, clo->largest_block); + clo->compressed_buffer=NULL; + error_release_free_buffer: ++ if(clo->buffer) { int i; - for(i=0; ibuffered_blocknum[i] = -1; -@@ -653,7 +696,7 @@ - } +- for(i=0; ibuffer[i]) +- { +- cloop_free(clo->buffer[i], ntohl(clo->head.block_size)); +- clo->buffer[i]=NULL; +- } +- } ++ for(i=0; inum_buffered_blocks; i++) { if(clo->buffer[i]) { cloop_free(clo->buffer[i], clo->head.block_size); clo->buffer[i]=NULL; }} ++ cloop_free(clo->buffer, clo->num_buffered_blocks*sizeof(char*)); clo->buffer=NULL; } ++ if (clo->buffered_blocknum) { cloop_free(clo->buffered_blocknum, sizeof(int)*clo->num_buffered_blocks); clo->buffered_blocknum=NULL; } error_release_free: - cloop_free(clo->offsets, sizeof(loff_t) * total_offsets); -+ cloop_free(clo->offsets, sizeof(struct block_info) * total_offsets); - clo->offsets=NULL; +- clo->offsets=NULL; ++ cloop_free(clo->block_ptrs, sizeof(struct block_info) * total_offsets); ++ clo->block_ptrs=NULL; error_release: if(bbuf) cloop_free(bbuf, clo->underlying_blksize); ++ if(clo->underlying_filename) { kfree(clo->underlying_filename); clo->underlying_filename=NULL; } + clo->backing_file=NULL; + return error; + } +@@ -673,7 +892,7 @@ + if(clo->backing_file) return -EBUSY; + file = fget(arg); /* get filp struct from ioctl arg fd */ + if(!file) return -EBADF; +- error=cloop_set_file(cloop_num,file,"losetup_file"); ++ error=cloop_set_file(cloop_num,file); + set_device_ro(bdev, 1); + if(error) fput(file); + return error; +@@ -684,29 +903,48 @@ + { + struct cloop_device *clo = cloop_dev[cloop_num]; + struct file *filp = clo->backing_file; +- int i; + if(clo->refcnt > 1) /* we needed one fd for the ioctl */ + return -EBUSY; + if(filp==NULL) return -EINVAL; + if(clo->clo_thread) { kthread_stop(clo->clo_thread); clo->clo_thread=NULL; } +- if(filp!=initial_file) fput(filp); +- else { filp_close(initial_file,0); initial_file=NULL; } ++ if(filp!=initial_file) ++ fput(filp); ++ else ++ { ++ filp_close(initial_file,0); ++ initial_file=NULL; ++ } + clo->backing_file = NULL; + clo->backing_inode = NULL; +- if(clo->offsets) { cloop_free(clo->offsets, clo->underlying_blksize); clo->offsets = NULL; } ++ if(clo->underlying_filename) { kfree(clo->underlying_filename); clo->underlying_filename=NULL; } ++ if(clo->block_ptrs) { cloop_free(clo->block_ptrs, clo->head.num_blocks); clo->block_ptrs = NULL; } + if(clo->preload_cache) +- { +- for(i=0; i < clo->preload_size; i++) +- cloop_free(clo->preload_cache[i], ntohl(clo->head.block_size)); +- cloop_free(clo->preload_cache, clo->preload_array_size * sizeof(char *)); +- clo->preload_cache = NULL; +- clo->preload_size = clo->preload_array_size = 0; +- } +- for(i=0; ibuffer[i]) { cloop_free(clo->buffer[i], ntohl(clo->head.block_size)); clo->buffer[i]=NULL; } ++ { ++ int i; ++ for(i=0; i < clo->preload_size; i++) ++ cloop_free(clo->preload_cache[i], clo->head.block_size); ++ cloop_free(clo->preload_cache, clo->preload_array_size * sizeof(char *)); ++ clo->preload_cache = NULL; ++ clo->preload_size = clo->preload_array_size = 0; ++ } ++ if (clo->buffered_blocknum) ++ { ++ cloop_free(clo->buffered_blocknum, sizeof(int) * clo->num_buffered_blocks); clo->buffered_blocknum = NULL; ++ } ++ if (clo->buffer) ++ { ++ int i; ++ for(i=0; inum_buffered_blocks; i++) { if(clo->buffer[i]) cloop_free(clo->buffer[i], clo->head.block_size); } ++ cloop_free(clo->buffer, sizeof(char*) * clo->num_buffered_blocks); clo->buffer = NULL; ++ } + if(clo->compressed_buffer) { cloop_free(clo->compressed_buffer, clo->largest_block); clo->compressed_buffer = NULL; } ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) + zlib_inflateEnd(&clo->zstream); + if(clo->zstream.workspace) { cloop_free(clo->zstream.workspace, zlib_inflate_workspacesize()); clo->zstream.workspace = NULL; } ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++ xz_dec_end(clo->xzdecoderstate); ++#endif + if(bdev) invalidate_bdev(bdev); + if(clo->clo_disk) set_capacity(clo->clo_disk, 0); + return 0; +@@ -731,8 +969,8 @@ + const struct loop_info64 *info) + { + if (!clo->backing_file) return -ENXIO; +- memcpy(clo->clo_file_name, info->lo_file_name, LO_NAME_SIZE); +- clo->clo_file_name[LO_NAME_SIZE-1] = 0; ++ if(clo->underlying_filename) kfree(clo->underlying_filename); ++ clo->underlying_filename = kstrdup(info->lo_file_name, GFP_KERNEL); + return 0; + } + +@@ -743,7 +981,11 @@ + struct kstat stat; + int err; + if (!file) return -ENXIO; +- err = vfs_getattr(file->f_path.mnt, file->f_path.dentry, &stat); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ++ err = vfs_getattr(&file->f_path, &stat); ++#else ++ err = vfs_getattr(&file->f_path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT); ++#endif + if (err) return err; + memset(info, 0, sizeof(*info)); + info->lo_number = clo->clo_number; +@@ -753,7 +995,8 @@ + info->lo_offset = 0; + info->lo_sizelimit = 0; + info->lo_flags = 0; +- memcpy(info->lo_file_name, clo->clo_file_name, LO_NAME_SIZE); ++ strncpy(info->lo_file_name, clo->underlying_filename, LO_NAME_SIZE); ++ info->lo_file_name[LO_NAME_SIZE-1]=0; + return 0; + } + +@@ -833,8 +1076,6 @@ + if (!err && copy_to_user(arg, &info64, sizeof(info64))) err = -EFAULT; + return err; + } +-/* EOF get/set_status */ +- + + static int cloop_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +@@ -914,21 +1155,20 @@ + /* losetup uses write-open and flags=0x8002 to set a new file */ + if(mode & FMODE_WRITE) + { +- printk(KERN_WARNING "%s: Can't open device read-write in mode 0x%x\n", cloop_name, mode); ++ printk(KERN_INFO "%s: Open in read-write mode 0x%x requested, ignored.\n", cloop_name, mode); + return -EROFS; + } + cloop_dev[cloop_num]->refcnt+=1; + return 0; + } + +-static int cloop_close(struct gendisk *disk, fmode_t mode) ++static void cloop_close(struct gendisk *disk, fmode_t mode) + { +- int cloop_num, err=0; +- if(!disk) return 0; ++ int cloop_num; ++ if(!disk) return; + cloop_num=((struct cloop_device *)disk->private_data)->clo_number; +- if(cloop_num < 0 || cloop_num > (cloop_count-1)) return 0; ++ if(cloop_num < 0 || cloop_num > (cloop_count-1)) return; + cloop_dev[cloop_num]->refcnt-=1; +- return err; + } + + static struct block_device_operations clo_fops = +@@ -973,6 +1213,10 @@ + goto error_out; + } + clo->clo_queue->queuedata = clo; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) ++ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, clo->clo_queue); ++ queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, clo->clo_queue); ++#endif + clo->clo_disk = alloc_disk(1); + if(!clo->clo_disk) + { +@@ -1004,6 +1248,11 @@ + cloop_dev[cloop_num] = NULL; + } + ++/* LZ4 Stuff */ ++#if (defined USE_LZ4_INTERNAL) ++#include "lz4_kmod.c" ++#endif ++ + static int __init cloop_init(void) + { + int error=0; +@@ -1044,7 +1293,7 @@ + initial_file=NULL; /* if IS_ERR, it's NOT open. */ + } + else +- error=cloop_set_file(0,initial_file,file); ++ error=cloop_set_file(0,initial_file); + if(error) + { + printk(KERN_ERR +@@ -1052,9 +1301,6 @@ + cloop_name, file, error); + goto init_out_dealloc; + } +- if(namelen >= LO_NAME_SIZE) namelen = LO_NAME_SIZE-1; +- memcpy(cloop_dev[0]->clo_file_name, file, namelen); +- cloop_dev[0]->clo_file_name[namelen] = 0; + } + return 0; + init_out_dealloc: diff -r d98ac734626d -r 36e9a3dcd5de linux64-cloop/receipt --- a/linux64-cloop/receipt Mon May 04 07:56:50 2020 +0100 +++ b/linux64-cloop/receipt Mon May 04 09:05:12 2020 +0000 @@ -2,13 +2,15 @@ PACKAGE="linux64-cloop" SOURCE="cloop" -VERSION="2.639-2" +_VERSION="2.639-2" +#VERSION="$(sed '/+#define CLOOP_VERSION/!d;s|.* "\(.*\)"|\1|' stuff/cloop.u)" +VERSION="4.12" CATEGORY="base-system" MAINTAINER="pascal.bellard@slitaz.org" LICENSE="GPL2" SHORT_DESC="The read-only compressed loop device kernel module." WEB_SITE="http://knoppix.net/wiki/Cloop" -TARBALL="${SOURCE}_${VERSION}.tar.gz" +TARBALL="${SOURCE}_${_VERSION}.tar.gz" WGET_URL="http://debian-knoppix.alioth.debian.org/packages/$SOURCE/$TARBALL" PROVIDE="linux-cloop:linux64" @@ -22,9 +24,7 @@ compile_rules() { - patch -p0 < $stuff/cloop.u # 3.2.98 - sed -i -e 's|file->f_path.mnt, file->f_path.dentry|\&file->f_path|' \ - -e 's|bvec->|bvec.|g;s|*bvec|bvec|' cloop.c + patch -p0 < $stuff/cloop.u make KERNEL_DIR="/usr/src/linux" cloop.ko && xz cloop.ko } diff -r d98ac734626d -r 36e9a3dcd5de linux64-cloop/stuff/cloop.u --- a/linux64-cloop/stuff/cloop.u Mon May 04 07:56:50 2020 +0100 +++ b/linux64-cloop/stuff/cloop.u Mon May 04 09:05:12 2020 +0000 @@ -1,80 +1,221 @@ --- cloop.h +++ cloop.h -@@ -20,6 +20,80 @@ +@@ -1,15 +1,50 @@ ++#define CLOOP_SIGNATURE "#!/bin/sh" /* @ offset 0 */ ++#define CLOOP_SIGNATURE_SIZE 9 ++#define CLOOP_SIGNATURE_OFFSET 0x0 ++ + #ifndef _COMPRESSED_LOOP_H + #define _COMPRESSED_LOOP_H + +-#define CLOOP_HEADROOM 128 ++/*************************************************************************\ ++* Starting with Format V4.0 (cloop version 4.x), cloop can now have two * ++* alternative structures: * ++* * ++* 1. Header first: "robust" format, handles missing blocks well * ++* 2. Footer (header last): "streaming" format, easier to create * ++* * ++* The cloop kernel module autodetects both formats, and can (currently) * ++* still handle the V2.0 format as well. * ++* * ++* 1. Header first: * ++* +---------------------------- FIXED SIZE ---------------------------+ * ++* |Signature (128 bytes) | * ++* |block_size (32bit number, network order) | * ++* |num_blocks (32bit number, network order) | * ++* +--------------------------- VARIABLE SIZE -------------------------+ * ++* |num_blocks * FlagsOffset (upper 4 bits flags, lower 64 bits offset)| * ++* |compressed data blocks of variable size ... | * ++* +-------------------------------------------------------------------+ * ++* * ++* 2. Footer (header last): * ++* +--------------------------- VARIABLE SIZE -------------------------+ * ++* |compressed data blocks of variable size ... | * ++* |num_blocks * FlagsOffset (upper 4 bits flags, lower 64 bits offset)| * ++* +---------------------------- FIXED SIZE ---------------------------+ * ++* |Signature (128 bytes) | * ++* |block_size (32bit number, network order) | * ++* |num_blocks (32bit number, network order) | * ++* +-------------------------------------------------------------------+ * ++* * ++* Offsets are always relative to beginning of file, in all formats. * ++* The block index contains num_blocks+1 offsets, followed (1) or * ++* preceded (2) by the compressed blocks. * ++\*************************************************************************/ + +-/* The cloop header usually looks like this: */ +-/* #!/bin/sh */ +-/* #V2.00 Format */ +-/* ...padding up to CLOOP_HEADROOM... */ +-/* block_size (32bit number, network order) */ +-/* num_blocks (32bit number, network order) */ ++#include /* u_int32_t */ ++ ++#define CLOOP_HEADROOM 128 + ++/* Header of fixed length, can be located at beginning or end of file */ + struct cloop_head + { + char preamble[CLOOP_HEADROOM]; +@@ -17,9 +52,163 @@ + u_int32_t num_blocks; + }; + ++/************************************************************************\ ++* CLOOP4 flags for each compressed block * ++* Value Meaning * ++* 0 GZIP/7ZIP compression (compatible with V2.0 Format) * ++* 1 no compression (incompressible data) * ++* 2 xz compression (currently best space saver) * ++* 3 lz4 compression * ++* 4 lzo compression (fastest) * ++\************************************************************************/ ++ ++typedef uint64_t cloop_block_ptr; ++ ++/* Get value of first 4 bits */ ++#define CLOOP_BLOCK_FLAGS(x) ((unsigned int)(((x) & 0xf000000000000000LLU) >> 60)) ++/* Get value of last 60 bits */ ++#define CLOOP_BLOCK_OFFSET(x) ((x) & 0x0fffffffffffffffLLU) ++ ++#define CLOOP_COMPRESSOR_ZLIB 0x0 ++#define CLOOP_COMPRESSOR_NONE 0x1 ++#define CLOOP_COMPRESSOR_XZ 0x2 ++#define CLOOP_COMPRESSOR_LZ4 0x3 ++#define CLOOP_COMPRESSOR_LZO1X 0x4 ++ ++#define CLOOP_COMPRESSOR_VALID(x) ((x) >= CLOOP_COMPRESSOR_ZLIB && (x) <= CLOOP_COMPRESSOR_LZO1X) ++ ++#define CLOOP_COMPRESSOR_LINK 0xF ++ ++ /* data_index (num_blocks 64bit pointers, network order)... */ /* compressed data (gzip block compressed format)... */ +struct cloop_tail +{ -+ u_int32_t table_size; -+ u_int32_t index_size; ++ u_int32_t table_size; ++ u_int32_t index_size; /* size:4 comp:3 ctrl-c:1 lastlen:24 */ ++#define CLOOP3_INDEX_SIZE(x) ((unsigned int)((x) & 0xF)) ++#define CLOOP3_BLOCKS_FLAGS(x) ((unsigned int)((x) & 0x70) >> 4) ++#define CLOOP3_TRUNCATED(x) ((unsigned int)((x) & 0x80) >> 7) ++#define CLOOP3_LASTLEN(x) (unsigned int)((x) >> 8) + u_int32_t num_blocks; +}; + ++#define GZIP_MAX_BUFFER(n) ((n) + (n)/1000 + 12) ++ +struct block_info +{ + loff_t offset; /* 64-bit offsets of compressed block */ + u_int32_t size; /* 32-bit compressed block size */ -+ u_int32_t optidx; /* 32-bit index number */ ++ u_int32_t flags; /* 32-bit compression flags */ +}; + -+static inline char *build_index(struct block_info *offsets, unsigned long n) ++static inline char *build_index(struct block_info *offsets, unsigned long n, ++ unsigned long block_size, unsigned global_flags) +{ + u_int32_t *ofs32 = (u_int32_t *) offsets; + loff_t *ofs64 = (loff_t *) offsets; -+ ++ ++ /* v3 64bits bug: v1 assumed */ ++ unsigned long v3_64 = (n+1)/2; ++ loff_t prev; ++ ++ if (ofs32[0] != 0 && ofs32[1] == 0) { ++ for (prev=__le64_to_cpu(ofs64[v3_64]); ++ v3_64 > 0 && __le64_to_cpu(ofs64[--v3_64]) < prev; ++ prev=__le64_to_cpu(ofs64[v3_64])); ++ } ++ + if (ofs32[0] == 0) { + if (ofs32[2]) { /* ACCELERATED KNOPPIX V1.0 */ + while (n--) { + offsets[n].offset = __be64_to_cpu(offsets[n].offset); + offsets[n].size = ntohl(offsets[n].size); ++ offsets[n].flags = 0; + } + return (char *) "128BE accelerated knoppix 1.0"; + } -+ else { /* V2.0 */ -+ loff_t last = __be64_to_cpu(ofs64[n - 1]); -+ while (n--) { ++ else { /* V2.0/V4.0 */ ++ loff_t last = CLOOP_BLOCK_OFFSET(__be64_to_cpu(ofs64[n])); ++ u_int32_t flags; ++ static char v4[11]; ++ unsigned long i = n; ++ ++ for (flags = 0; n-- ;) { ++ loff_t data = __be64_to_cpu(ofs64[n]); ++ + offsets[n].size = last - -+ (offsets[n].offset = __be64_to_cpu(ofs64[n])); ++ (offsets[n].offset = CLOOP_BLOCK_OFFSET(data)); + last = offsets[n].offset; ++ offsets[n].flags = CLOOP_BLOCK_FLAGS(data); ++ flags |= 1 << offsets[n].flags; + } -+ return (char *) "64BE v2.0"; ++ if (flags < 2) return (char *) "64BE v2.0"; ++ while (i--) { ++ if (offsets[i].flags == CLOOP_COMPRESSOR_LINK) { ++ offsets[i] = offsets[offsets[i].offset]; ++ } ++ } ++ strcpy(v4, (char *) "64BE v4.0a"); ++ v4[10] = 'a' + ((flags-1) & 0xF); // compressors used ++ if (flags > 0x10) { // with links ? ++ v4[10] += 'A' - 'a'; ++ } ++ return v4; + } + } -+ else if (ofs32[1] == 0) { /* V1.0 */ -+ loff_t last = __le64_to_cpu(ofs64[n - 1]); ++ else if (ofs32[1] == 0 && v3_64 == 0) { /* V1.0 */ ++ loff_t last = __le64_to_cpu(ofs64[n]); + while (n--) { + offsets[n].size = last - + (offsets[n].offset = __le64_to_cpu(ofs64[n])); + last = offsets[n].offset; ++ offsets[n].flags = 0; + } + return (char *) "64LE v1.0"; + } -+ else if (ntohl(ofs32[0]) == (4*n) + 0x8C) { /* V0.68 */ -+ loff_t last = ntohl(ofs32[n - 1]); -+ while (n--) { -+ offsets[n].size = last - -+ (offsets[n].offset = ntohl(ofs32[n])); -+ last = offsets[n].offset; -+ } -+ return (char *) "32BE v0.68"; -+ } -+ else { /* V3.0 */ ++ else { /* V3.0 or V0.68 */ + unsigned long i; + loff_t j; ++ static char v3[11]; + ++ for (i = 0; i < n && ntohl(ofs32[i]) < ntohl(ofs32[i+1]); i++); ++ if (i == n && ntohl(ofs32[0]) == (4*n) + 0x8C) { /* V0.68 */ ++ loff_t last = ntohl(ofs32[n]); ++ while (n--) { ++ offsets[n].size = last - ++ (offsets[n].offset = ntohl(ofs32[n])); ++ last = offsets[n].offset; ++ offsets[n].flags = 0; ++ } ++ return (char *) "32BE v0.68"; ++ } ++ ++ v3_64 = (ofs32[1] == 0); + for (i = n; i-- != 0; ) -+ offsets[i].size = ntohl(ofs32[i]); ++ offsets[i].size = ntohl(ofs32[i << v3_64]); + for (i = 0, j = sizeof(struct cloop_head); i < n; i++) { + offsets[i].offset = j; ++ offsets[i].flags = global_flags; ++ if (offsets[i].size == 0xFFFFFFFF) { ++ offsets[i].flags = CLOOP_COMPRESSOR_NONE; ++ offsets[i].size = block_size; ++ } ++ if ((offsets[i].size & 0x80000000) == 0) { ++ j += offsets[i].size; ++ } ++ } ++ for (i = 0; i < n; i++) { + if (offsets[i].size & 0x80000000) { -+ unsigned long k = offsets[i].size & 0x7FFFFFFF; -+ offsets[i].offset = offsets[k].offset; -+ offsets[i].size = offsets[k].size; ++ offsets[i] = offsets[offsets[i].size & 0x7FFFFFFF]; + } -+ else j += offsets[i].size; + } -+ return (char *) "32BE v3.0"; ++ strcpy(v3, (char *) (v3_64) ? "64BE v3.0a" : "32BE v3.0a"); ++ v3[10] += global_flags; ++ return v3; + } +} + @@ -83,187 +224,884 @@ --- cloop.c +++ cloop.c -@@ -5,11 +5,18 @@ - * A cloop file looks like this: - * [32-bit uncompressed block size: network order] - * [32-bit number of blocks (n_blocks): network order] +@@ -1,26 +1,23 @@ +-/* +- * compressed_loop.c: Read-only compressed loop blockdevice +- * hacked up by Rusty in 1999, extended and maintained by Klaus Knopper +- * +- * A cloop file looks like this: +- * [32-bit uncompressed block size: network order] +- * [32-bit number of blocks (n_blocks): network order] - * [64-bit file offsets of start of blocks: network order] -+ * [for version < 3] -+ * [32-bit, 64-bit or 128-bit file offsets of start of blocks] - * ... - * (n_blocks + 1). - * n_blocks consisting of: - * [compressed block] -+ * ... -+ * [for version >= 3] -+ * [compressed list of 32-bit block sizes] -+ * [32-bit compressed index size: network order] -+ * [32-bit index size = 4: network order] -+ * [32-bit number of blocks (n_blocks): network order] - * - * Every version greatly inspired by code seen in loop.c - * by Theodore Ts'o, 3/29/93. -@@ -115,7 +122,7 @@ +- * ... +- * (n_blocks + 1). +- * n_blocks consisting of: +- * [compressed block] +- * +- * Every version greatly inspired by code seen in loop.c +- * by Theodore Ts'o, 3/29/93. +- * +- * Copyright 1999-2009 by Paul `Rusty' Russell & Klaus Knopper. +- * Redistribution of this file is permitted under the GNU Public License. +- * +- */ ++/************************************************************************\ ++* cloop.c: Read-only compressed loop blockdevice * ++* hacked up by Rusty in 1999, extended and maintained by Klaus Knopper * ++* * ++* For all supported cloop file formats, please check the file "cloop.h" * ++* New in Version 4: * ++* - Header can be first or last in cloop file, * ++* - Different compression algorithms supported (compression type * ++* encoded in first 4 bytes of block offset address) * ++* * ++* Every version greatly inspired by code seen in loop.c * ++* by Theodore Ts'o, 3/29/93. * ++* * ++* Copyright 1999-2009 by Paul `Rusty' Russell & Klaus Knopper. * ++* Redistribution of this file is permitted under the GNU Public License * ++* V2. * ++\************************************************************************/ + + #define CLOOP_NAME "cloop" +-#define CLOOP_VERSION "2.639" ++#define CLOOP_VERSION "4.12" + #define CLOOP_MAX 8 + + #ifndef KBUILD_MODNAME +@@ -47,8 +44,27 @@ + #include /* do_div() for 64bit division */ + #include + #include +-/* Use zlib_inflate from lib/zlib_inflate */ ++/* Check for ZLIB, LZO1X, LZ4 decompression algorithms in kernel. */ ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) + #include ++#endif ++#if (defined(CONFIG_LZO_DECOMPRESS) || defined(CONFIG_LZO_DECOMPRESS_MODULE)) ++#include ++#endif ++#if (defined(CONFIG_DECOMPRESS_LZ4) || defined(CONFIG_DECOMPRESS_LZ4_MODULE)) ++#include ++#endif ++#if (defined(CONFIG_DECOMPRESS_LZMA) || defined(CONFIG_DECOMPRESS_LZMA_MODULE)) ++#include ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++#include ++#endif ++ ++#if (!(defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE) || defined(CONFIG_LZO_DECOMPRESS) || defined(CONFIG_LZO_DECOMPRESS_MODULE) || defined(CONFIG_DECOMPRESS_LZ4) || defined(CONFIG_DECOMPRESS_LZ4_MODULE) || defined(CONFIG_DECOMPRESS_LZMA) || defined(CONFIG_DECOMPRESS_LZMA_MODULE) || defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE))) ++#error "No decompression library selected in kernel config!" ++#endif ++ + #include + #include + #include +@@ -92,47 +108,64 @@ + #define DEBUGP(format, x...) + #endif + ++/* Default size of buffer to keep some decompressed blocks in memory to speed up access */ ++#define BLOCK_BUFFER_MEM (16*65536) ++ + /* One file can be opened at module insertion time */ + /* insmod cloop file=/path/to/file */ + static char *file=NULL; + static unsigned int preload=0; + static unsigned int cloop_max=CLOOP_MAX; ++static unsigned int buffers=BLOCK_BUFFER_MEM; + module_param(file, charp, 0); + module_param(preload, uint, 0); + module_param(cloop_max, uint, 0); + MODULE_PARM_DESC(file, "Initial cloop image file (full path) for /dev/cloop"); + MODULE_PARM_DESC(preload, "Preload n blocks of cloop data into memory"); + MODULE_PARM_DESC(cloop_max, "Maximum number of cloop devices (default 8)"); ++MODULE_PARM_DESC(buffers, "Size of buffer to keep uncompressed blocks in memory in MiB (default 1)"); + + static struct file *initial_file=NULL; + static int cloop_major=MAJOR_NR; + +-/* Number of buffered decompressed blocks */ +-#define BUFFERED_BLOCKS 8 + struct cloop_device + { +- /* Copied straight from the file */ ++ /* Header filled from the file */ struct cloop_head head; ++ int header_first; ++ int file_format; - /* An array of offsets of compressed blocks within the file */ +- /* An array of offsets of compressed blocks within the file */ - loff_t *offsets; -+ struct block_info *offsets; ++ /* An or'd sum of all flags of each compressed block (v3) */ ++ u_int32_t allflags; ++ ++ /* An array of cloop_ptr flags/offset for compressed blocks within the file */ ++ struct block_info *block_ptrs; /* We buffer some uncompressed blocks for performance */ - int buffered_blocknum[BUFFERED_BLOCKS]; -@@ -256,11 +263,11 @@ - return i; - } +- int buffered_blocknum[BUFFERED_BLOCKS]; +- int current_bufnum; +- void *buffer[BUFFERED_BLOCKS]; +- void *compressed_buffer; +- size_t preload_array_size; /* Size of pointer array in blocks */ +- size_t preload_size; /* Number of successfully allocated blocks */ +- char **preload_cache; /* Pointers to preloaded blocks */ ++ size_t num_buffered_blocks; /* how many uncompressed blocks buffered for performance */ ++ int *buffered_blocknum; /* list of numbers of uncompressed blocks in buffer */ ++ int current_bufnum; /* which block is current */ ++ unsigned char **buffer; /* cache space for num_buffered_blocks uncompressed blocks */ ++ void *compressed_buffer; /* space for the largest compressed block */ ++ size_t preload_array_size; /* Size of pointer array in blocks */ ++ size_t preload_size; /* Number of successfully allocated blocks */ ++ char **preload_cache; /* Pointers to preloaded blocks */ ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) + z_stream zstream; ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++ struct xz_dec *xzdecoderstate; ++ struct xz_buf xz_buffer; ++#endif + + struct file *backing_file; /* associated file */ + struct inode *backing_inode; /* for bmap */ + ++ unsigned char *underlying_filename; + unsigned long largest_block; + unsigned int underlying_blksize; ++ loff_t underlying_total_size; + int clo_number; + int refcnt; + struct block_device *bdev; +@@ -147,7 +180,6 @@ + struct request_queue *clo_queue; + struct gendisk *clo_disk; + int suspended; +- char clo_file_name[LO_NAME_SIZE]; + }; + + /* Changed in 2.639: cloop_dev is now a an array of cloop_dev pointers, +@@ -156,52 +188,113 @@ + static const char *cloop_name=CLOOP_NAME; + static int cloop_count = 0; + +-#if (!(defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE))) /* Must be compiled into kernel. */ +-#error "Invalid Kernel configuration. CONFIG_ZLIB_INFLATE support is needed for cloop." +-#endif +- +-/* Use __get_free_pages instead of vmalloc, allows up to 32 pages, +- * 2MB in one piece */ + static void *cloop_malloc(size_t size) + { +- int order = get_order(size); +- if(order <= KMALLOC_MAX_ORDER) +- return (void *)kmalloc(size, GFP_KERNEL); +- else if(order < MAX_ORDER) +- return (void *)__get_free_pages(GFP_KERNEL, order); ++ /* kmalloc will fail after the system is running for a while, */ ++ /* when large orders can't return contiguous memory. */ ++ /* Let's just use vmalloc for now. :-/ */ ++ /* int order = get_order(size); */ ++ /* if(order <= KMALLOC_MAX_ORDER) */ ++ /* return (void *)kmalloc(size, GFP_KERNEL); */ ++ /* else if(order < MAX_ORDER) */ ++ /* return (void *)__get_free_pages(GFP_KERNEL, order); */ + return (void *)vmalloc(size); + } + + static void cloop_free(void *mem, size_t size) + { +- int order = get_order(size); +- if(order <= KMALLOC_MAX_ORDER) +- kfree(mem); +- else if(order < MAX_ORDER) +- free_pages((unsigned long)mem, order); +- else vfree(mem); ++ /* int order = get_order(size); */ ++ /* if(order <= KMALLOC_MAX_ORDER) */ ++ /* kfree(mem); */ ++ /* else if(order < MAX_ORDER) */ ++ /* free_pages((unsigned long)mem, order); */ ++ /* else */ ++ vfree(mem); + } + +-static int uncompress(struct cloop_device *clo, +- unsigned char *dest, unsigned long *destLen, +- unsigned char *source, unsigned long sourceLen) ++static int uncompress(struct cloop_device *clo, unsigned char *dest, unsigned long *destLen, unsigned char *source, unsigned long sourceLen, int flags) + { +- /* Most of this code can be found in fs/cramfs/uncompress.c */ +- int err; +- clo->zstream.next_in = source; +- clo->zstream.avail_in = sourceLen; +- clo->zstream.next_out = dest; +- clo->zstream.avail_out = *destLen; +- err = zlib_inflateReset(&clo->zstream); +- if (err != Z_OK) +- { +- printk(KERN_ERR "%s: zlib_inflateReset error %d\n", cloop_name, err); +- zlib_inflateEnd(&clo->zstream); zlib_inflateInit(&clo->zstream); +- } +- err = zlib_inflate(&clo->zstream, Z_FINISH); +- *destLen = clo->zstream.total_out; +- if (err != Z_STREAM_END) return err; +- return Z_OK; ++ int err = -1; ++ switch(flags) ++ { ++ case CLOOP_COMPRESSOR_NONE: ++ memcpy(dest, source, *destLen = sourceLen); ++ err = Z_OK; ++ break; ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) ++ case CLOOP_COMPRESSOR_ZLIB: ++ clo->zstream.next_in = source; ++ clo->zstream.avail_in = sourceLen; ++ clo->zstream.next_out = dest; ++ clo->zstream.avail_out = *destLen; ++ err = zlib_inflateReset(&clo->zstream); ++ if (err != Z_OK) ++ { ++ printk(KERN_ERR "%s: zlib_inflateReset error %d\n", cloop_name, err); ++ zlib_inflateEnd(&clo->zstream); zlib_inflateInit(&clo->zstream); ++ } ++ err = zlib_inflate(&clo->zstream, Z_FINISH); ++ *destLen = clo->zstream.total_out; ++ if (err == Z_STREAM_END) err = 0; ++ DEBUGP("cloop: zlib decompression done, ret =%d, size =%lu\n", err, *destLen); ++ break; ++#endif ++#if (defined(CONFIG_LZO_DECOMPRESS) || defined(CONFIG_LZO_DECOMPRESS_MODULE)) ++ case CLOOP_COMPRESSOR_LZO1X: ++ { ++ size_t tmp = (size_t) clo->head.block_size; ++ err = lzo1x_decompress_safe(source, sourceLen, ++ dest, &tmp); ++ if (err == LZO_E_OK) *destLen = (u_int32_t) tmp; ++ } ++ break; ++#endif ++#if (defined(CONFIG_DECOMPRESS_LZ4) || defined(CONFIG_DECOMPRESS_LZ4_MODULE)) ++ case CLOOP_COMPRESSOR_LZ4: ++ { ++ size_t outputSize = *destLen; ++ /* We should adjust outputSize here, in case the last block is smaller than block_size */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* field removed */ ++ err = lz4_decompress(source, (size_t *) &sourceLen, ++ dest, outputSize); ++#else ++ err = LZ4_decompress_safe(source, ++ dest, ++ sourceLen, outputSize); ++#endif ++ if (err >= 0) ++ { ++ err = 0; ++ *destLen = outputSize; ++ } ++ } ++ break; ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++ case CLOOP_COMPRESSOR_XZ: ++ clo->xz_buffer.in = source; ++ clo->xz_buffer.in_pos = 0; ++ clo->xz_buffer.in_size = sourceLen; ++ clo->xz_buffer.out = dest; ++ clo->xz_buffer.out_pos = 0; ++ clo->xz_buffer.out_size = *destLen; ++ xz_dec_reset(clo->xzdecoderstate); ++ err = xz_dec_run(clo->xzdecoderstate, &clo->xz_buffer); ++ if (err == XZ_STREAM_END || err == XZ_OK) ++ { ++ err = 0; ++ } ++ else ++ { ++ printk(KERN_ERR "%s: xz_dec_run error %d\n", cloop_name, err); ++ err = 1; ++ } ++ break; ++#endif ++ default: ++ printk(KERN_ERR "%s: compression method is not supported!\n", cloop_name); ++ } ++ return err; + } + + static ssize_t cloop_read_from_file(struct cloop_device *clo, struct file *f, char *buf, +@@ -220,7 +313,7 @@ + + if(size_read <= 0) + { +- printk(KERN_ERR "%s: Read error %d at pos %Lu in file %s, " ++ printk(KERN_ERR "%s: Read error %d at pos %llu in file %s, " + "%d bytes lost.\n", cloop_name, (int)size_read, pos, + file, (int)size); + memset(buf + buf_len - size, 0, size); +@@ -232,72 +325,84 @@ + } + + /* This looks more complicated than it is */ +-/* Returns number of block buffer to use for this request */ ++/* Returns number of cache block buffer to use for this request */ + static int cloop_load_buffer(struct cloop_device *clo, int blocknum) + { +- unsigned int buf_done = 0; +- unsigned long buflen; +- unsigned int buf_length; ++ loff_t compressed_block_offset; ++ long compressed_block_len; ++ long uncompressed_block_len=0; + int ret; + int i; +- if(blocknum > ntohl(clo->head.num_blocks) || blocknum < 0) +- { +- printk(KERN_WARNING "%s: Invalid block number %d requested.\n", +- cloop_name, blocknum); +- return -1; +- } ++ if(blocknum > clo->head.num_blocks || blocknum < 0) ++ { ++ printk(KERN_WARNING "%s: Invalid block number %d requested.\n", ++ cloop_name, blocknum); ++ return -1; ++ } + + /* Quick return if the block we seek is already in one of the buffers. */ + /* Return number of buffer */ +- for(i=0; inum_buffered_blocks; i++) + if (blocknum == clo->buffered_blocknum[i]) +- { +- DEBUGP(KERN_INFO "cloop_load_buffer: Found buffered block %d\n", i); +- return i; +- } +- - buf_length = be64_to_cpu(clo->offsets[blocknum+1]) - be64_to_cpu(clo->offsets[blocknum]); -+ buf_length = clo->offsets[blocknum].size; +- +-/* Load one compressed block from the file. */ +- cloop_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer, +- be64_to_cpu(clo->offsets[blocknum]), buf_length); ++ { ++ DEBUGP(KERN_INFO "cloop_load_buffer: Found buffered block %d\n", i); ++ return i; ++ } - /* Load one compressed block from the file. */ - cloop_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer, -- be64_to_cpu(clo->offsets[blocknum]), buf_length); -+ clo->offsets[blocknum].offset, buf_length); +- buflen = ntohl(clo->head.block_size); ++ compressed_block_offset = clo->block_ptrs[blocknum].offset; ++ compressed_block_len = (long) (clo->block_ptrs[blocknum].size) ; - buflen = ntohl(clo->head.block_size); +- /* Go to next position in the block ring buffer */ +- clo->current_bufnum++; +- if(clo->current_bufnum >= BUFFERED_BLOCKS) clo->current_bufnum = 0; ++ /* Load one compressed block from the file. */ ++ if(compressed_block_offset > 0 && compressed_block_len >= 0) /* sanity check */ ++ { ++ size_t n = cloop_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer, ++ compressed_block_offset, compressed_block_len); ++ if (n!= compressed_block_len) ++ { ++ printk(KERN_ERR "%s: error while reading %lu bytes @ %llu from file %s\n", ++ cloop_name, compressed_block_len, clo->block_ptrs[blocknum].offset, clo->underlying_filename); ++ /* return -1; */ ++ } ++ } else { ++ printk(KERN_ERR "%s: invalid data block len %ld bytes @ %lld from file %s\n", ++ cloop_name, compressed_block_len, clo->block_ptrs[blocknum].offset, clo->underlying_filename); ++ return -1; ++ } ++ ++ /* Go to next position in the cache block buffer (which is used as a cyclic buffer) */ ++ if(++clo->current_bufnum >= clo->num_buffered_blocks) clo->current_bufnum = 0; -@@ -275,9 +282,9 @@ + /* Do the uncompression */ +- ret = uncompress(clo, clo->buffer[clo->current_bufnum], &buflen, clo->compressed_buffer, +- buf_length); ++ uncompressed_block_len = clo->head.block_size; ++ ret = uncompress(clo, clo->buffer[clo->current_bufnum], &uncompressed_block_len, ++ clo->compressed_buffer, compressed_block_len, clo->block_ptrs[blocknum].flags); + /* DEBUGP("cloop: buflen after uncompress: %ld\n",buflen); */ if (ret != 0) +- { +- printk(KERN_ERR "%s: zlib decompression error %i uncompressing block %u %u/%lu/%u/%u " +- "%Lu-%Lu\n", cloop_name, ret, blocknum, +- ntohl(clo->head.block_size), buflen, buf_length, buf_done, +- be64_to_cpu(clo->offsets[blocknum]), be64_to_cpu(clo->offsets[blocknum+1])); +- clo->buffered_blocknum[clo->current_bufnum] = -1; +- return -1; +- } ++ { ++ printk(KERN_ERR "%s: decompression error %i uncompressing block %u %lu bytes @ %llu, flags %u\n", ++ cloop_name, ret, blocknum, ++ compressed_block_len, clo->block_ptrs[blocknum].offset, ++ clo->block_ptrs[blocknum].flags); ++ clo->buffered_blocknum[clo->current_bufnum] = -1; ++ return -1; ++ } + clo->buffered_blocknum[clo->current_bufnum] = blocknum; + return clo->current_bufnum; + } + + /* This function does all the real work. */ +-/* returns "uptodate" */ ++/* returns "uptodate" */ + static int cloop_handle_request(struct cloop_device *clo, struct request *req) + { + int buffered_blocknum = -1; + int preloaded = 0; + loff_t offset = (loff_t) blk_rq_pos(req)<<9; /* req->sector<<9 */ +- struct bio_vec *bvec; ++ struct bio_vec bvec; + struct req_iterator iter; + rq_for_each_segment(bvec, req, iter) { - printk(KERN_ERR "%s: zlib decompression error %i uncompressing block %u %u/%lu/%u/%u " -- "%Lu-%Lu\n", cloop_name, ret, blocknum, -+ "%Lu:%u\n", cloop_name, ret, blocknum, - ntohl(clo->head.block_size), buflen, buf_length, buf_done, -- be64_to_cpu(clo->offsets[blocknum]), be64_to_cpu(clo->offsets[blocknum+1])); -+ clo->offsets[blocknum].offset, clo->offsets[blocknum].size); - clo->buffered_blocknum[clo->current_bufnum] = -1; - return -1; +- unsigned long len = bvec->bv_len; +- char *to_ptr = kmap(bvec->bv_page) + bvec->bv_offset; ++ unsigned long len = bvec.bv_len; ++ char *to_ptr = kmap(bvec.bv_page) + bvec.bv_offset; + while(len > 0) + { + u_int32_t length_in_buffer; +@@ -308,7 +413,7 @@ + /* puts the result in the first argument, i.e. block_offset */ + /* becomes the blocknumber to load, and offset_in_buffer the */ + /* position in the buffer */ +- offset_in_buffer = do_div(block_offset, ntohl(clo->head.block_size)); ++ offset_in_buffer = do_div(block_offset, clo->head.block_size); + /* Lookup preload cache */ + if(block_offset < clo->preload_size && clo->preload_cache != NULL && + clo->preload_cache[block_offset] != NULL) +@@ -325,7 +430,7 @@ + from_ptr = clo->buffer[buffered_blocknum]; + } + /* Now, at least part of what we want will be in the buffer. */ +- length_in_buffer = ntohl(clo->head.block_size) - offset_in_buffer; ++ length_in_buffer = clo->head.block_size - offset_in_buffer; + if(length_in_buffer > len) + { + /* DEBUGP("Warning: length_in_buffer=%u > len=%u\n", +@@ -337,18 +442,19 @@ + len -= length_in_buffer; + offset += length_in_buffer; + } /* while inner loop */ +- kunmap(bvec->bv_page); ++ kunmap(bvec.bv_page); ++ cond_resched(); + } /* end rq_for_each_segment*/ + return ((buffered_blocknum!=-1) || preloaded); + } + + /* Adopted from loop.c, a kernel thread to handle physical reads and +- * decompression. */ ++ decompression. */ + static int cloop_thread(void *data) + { + struct cloop_device *clo = data; + current->flags |= PF_NOFREEZE; +- set_user_nice(current, -15); ++ set_user_nice(current, 10); + while (!kthread_should_stop()||!list_empty(&clo->clo_list)) + { + int err; +@@ -390,10 +496,18 @@ + int rw; + /* quick sanity checks */ + /* blk_fs_request() was removed in 2.6.36 */ +- if (unlikely(req == NULL || (req->cmd_type != REQ_TYPE_FS))) ++ if (unlikely(req == NULL ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* field removed */ ++ || (req->cmd_type != REQ_TYPE_FS) ++#endif ++ )) + goto error_continue; + rw = rq_data_dir(req); +- if (unlikely(rw != READ && rw != READA)) ++ if (unlikely(rw != READ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) ++ && rw != READA ++#endif ++ )) + { + DEBUGP("cloop_do_request: bad command\n"); + goto error_continue; +@@ -409,40 +523,51 @@ + continue; /* next request */ + error_continue: + DEBUGP(KERN_ERR "cloop_do_request: Discarding request %p.\n", req); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + req->errors++; ++#else ++ req->error_count++; ++#endif + __blk_end_request_all(req, -EIO); } -@@ -489,30 +496,73 @@ - cloop_name, ntohl(clo->head.block_size)); - error=-EBADF; goto error_release; - } + } + +-/* Read header and offsets from already opened file */ +-static int cloop_set_file(int cloop_num, struct file *file, char *filename) ++/* Read header, flags and offsets from already opened file */ ++static int cloop_set_file(int cloop_num, struct file *file) + { + struct cloop_device *clo = cloop_dev[cloop_num]; + struct inode *inode; + char *bbuf=NULL; +- unsigned int i, offsets_read, total_offsets; +- int isblkdev; +- int error = 0; ++ unsigned int bbuf_size = 0; ++ const unsigned int header_size = sizeof(struct cloop_head); ++ unsigned int i, total_offsets=0; ++ loff_t fs_read_position = 0, header_pos[2]; ++ int flags, isblkdev, bytes_read, error = 0; ++ if (clo->suspended) return error; ++ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + inode = file->f_dentry->d_inode; ++ clo->underlying_filename = kstrdup(file->f_dentry->d_name.name ? file->f_dentry->d_name.name : (const unsigned char *)"anonymous filename", GFP_KERNEL); ++ #else ++ inode = file->f_path.dentry->d_inode; ++ clo->underlying_filename = kstrdup(file->f_path.dentry->d_name.name ? file->f_path.dentry->d_name.name : (const unsigned char *)"anonymous filename", GFP_KERNEL); ++ #endif + isblkdev=S_ISBLK(inode->i_mode)?1:0; + if(!isblkdev&&!S_ISREG(inode->i_mode)) + { + printk(KERN_ERR "%s: %s not a regular file or block device\n", +- cloop_name, filename); ++ cloop_name, clo->underlying_filename); + error=-EBADF; goto error_release; + } + clo->backing_file = file; + clo->backing_inode= inode ; +- if(!isblkdev&&inode->i_sizeunderlying_total_size = (isblkdev) ? inode->i_bdev->bd_inode->i_size : inode->i_size; ++ if(clo->underlying_total_size < header_size) + { +- printk(KERN_ERR "%s: %lu bytes (must be >= %u bytes)\n", +- cloop_name, (unsigned long)inode->i_size, +- (unsigned)sizeof(struct cloop_head)); ++ printk(KERN_ERR "%s: %llu bytes (must be >= %u bytes)\n", ++ cloop_name, clo->underlying_total_size, ++ (unsigned int)header_size); + error=-EBADF; goto error_release; + } +- /* In suspended mode, we have done all checks necessary - FF */ +- if (clo->suspended) +- return error; + if(isblkdev) + { + struct request_queue *q = bdev_get_queue(inode->i_bdev); +@@ -451,104 +576,225 @@ + /* blk_queue_max_hw_segments(clo->clo_queue, queue_max_hw_segments(q)); */ /* Removed in 2.6.34 */ + blk_queue_max_segment_size(clo->clo_queue, queue_max_segment_size(q)); + blk_queue_segment_boundary(clo->clo_queue, queue_segment_boundary(q)); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) + blk_queue_merge_bvec(clo->clo_queue, q->merge_bvec_fn); ++#endif + clo->underlying_blksize = block_size(inode->i_bdev); + } + else + clo->underlying_blksize = PAGE_SIZE; +- DEBUGP("Underlying blocksize is %u\n", clo->underlying_blksize); +- bbuf = cloop_malloc(clo->underlying_blksize); ++ ++ DEBUGP(KERN_INFO "Underlying blocksize of %s is %u\n", clo->underlying_filename, clo->underlying_blksize); ++ DEBUGP(KERN_INFO "Underlying total size of %s is %llu\n", clo->underlying_filename, clo->underlying_total_size); ++ ++ /* clo->underlying_blksize should be larger than header_size, even if it's only PAGE_SIZE */ ++ bbuf_size = clo->underlying_blksize; ++ bbuf = cloop_malloc(bbuf_size); + if(!bbuf) + { +- printk(KERN_ERR "%s: out of kernel mem for block buffer (%lu bytes)\n", +- cloop_name, (unsigned long)clo->underlying_blksize); ++ printk(KERN_ERR "%s: out of kernel mem for buffer (%u bytes)\n", ++ cloop_name, (unsigned int) bbuf_size); ++ error=-ENOMEM; goto error_release; ++ } ++ ++ header_pos[0] = 0; /* header first */ ++ header_pos[1] = clo->underlying_total_size - sizeof(struct cloop_head); /* header last */ ++ for(i=0; i<2; i++) ++ { ++ /* Check for header */ ++ size_t bytes_readable = MIN(clo->underlying_blksize, clo->underlying_total_size - header_pos[i]); ++ size_t bytes_read = cloop_read_from_file(clo, file, bbuf, header_pos[i], bytes_readable); ++ if(bytes_read != bytes_readable) ++ { ++ printk(KERN_ERR "%s: Bad file %s, read() of %s %u bytes returned %d.\n", ++ cloop_name, clo->underlying_filename, (i==0)?"first":"last", ++ (unsigned int)header_size, (int)bytes_read); ++ error=-EBADF; ++ goto error_release; ++ } ++ memcpy(&clo->head, bbuf, header_size); ++ if (strncmp(bbuf+CLOOP_SIGNATURE_OFFSET, CLOOP_SIGNATURE, CLOOP_SIGNATURE_SIZE)==0) ++ { ++ clo->file_format++; ++ clo->head.block_size=ntohl(clo->head.block_size); ++ clo->head.num_blocks=ntohl(clo->head.num_blocks); ++ clo->header_first = (i==0) ? 1 : 0; ++ printk(KERN_INFO "%s: file %s, %d blocks of %d bytes, header %s.\n", cloop_name, clo->underlying_filename, clo->head.num_blocks, clo->head.block_size, (i==0)?"first":"last"); ++ break; ++ } ++ } ++ if (clo->file_format == 0) ++ { ++ printk(KERN_ERR "%s: Cannot detect %s format.\n", ++ cloop_name, cloop_name); ++ error=-EBADF; goto error_release; ++ } ++ if (clo->head.block_size % 512 != 0) ++ { ++ printk(KERN_ERR "%s: blocksize %u not multiple of 512\n", ++ cloop_name, clo->head.block_size); ++ error=-EBADF; goto error_release; ++ } ++ total_offsets=clo->head.num_blocks; ++ if (!isblkdev && (sizeof(struct cloop_head)+sizeof(struct block_info)* ++ total_offsets > inode->i_size)) ++ { ++ printk(KERN_ERR "%s: file %s too small for %u blocks\n", ++ cloop_name, clo->underlying_filename, clo->head.num_blocks); ++ error=-EBADF; goto error_release; ++ } ++ /* Allocate Memory for decompressors */ ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) ++ clo->zstream.workspace = cloop_malloc(zlib_inflate_workspacesize()); ++ if(!clo->zstream.workspace) ++ { ++ printk(KERN_ERR "%s: out of mem for zlib working area %u\n", ++ cloop_name, zlib_inflate_workspacesize()); + error=-ENOMEM; goto error_release; + } +- total_offsets = 1; /* Dummy total_offsets: will be filled in first time around */ +- for (i = 0, offsets_read = 0; offsets_read < total_offsets; i++) ++ zlib_inflateInit(&clo->zstream); ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++#if XZ_INTERNAL_CRC32 ++ /* This must be called before any other xz_* function to initialize the CRC32 lookup table. */ ++ xz_crc32_init(void); ++#endif ++ clo->xzdecoderstate = xz_dec_init(XZ_SINGLE, 0); ++#endif ++ if (total_offsets + 1 == 0) /* Version 3 */ + { +- unsigned int offset = 0, num_readable; +- size_t bytes_read = cloop_read_from_file(clo, file, bbuf, +- i*clo->underlying_blksize, +- clo->underlying_blksize); +- if(bytes_read != clo->underlying_blksize) ++ struct cloop_tail tail; ++ if (isblkdev) + { +- printk(KERN_ERR "%s: Bad file, read() of first %lu bytes returned %d.\n", +- cloop_name, (unsigned long)clo->underlying_blksize, (int)bytes_read); +- error=-EBADF; +- goto error_release; ++ /* No end of file: can't find index */ ++ printk(KERN_ERR "%s: no V3 support for block device\n", ++ cloop_name); ++ error=-EBADF; goto error_release; + } +- /* Header will be in block zero */ +- if(i==0) ++ bytes_read = cloop_read_from_file(clo, file, (void *) &tail, ++ inode->i_size - sizeof(struct cloop_tail), ++ sizeof(struct cloop_tail)); ++ if (bytes_read == sizeof(struct cloop_tail)) + { +- memcpy(&clo->head, bbuf, sizeof(struct cloop_head)); +- offset = sizeof(struct cloop_head); +- if (ntohl(clo->head.block_size) % 512 != 0) ++ unsigned long len, zlen; ++ int ret; ++ void *zbuf; ++ clo->head.num_blocks = ntohl(tail.num_blocks); ++ total_offsets = clo->head.num_blocks; ++ clo->block_ptrs = cloop_malloc(sizeof(struct block_info) * total_offsets); ++ zlen = ntohl(tail.table_size); ++ zbuf = cloop_malloc(zlen); ++ if (!clo->block_ptrs || !zbuf) + { +- printk(KERN_ERR "%s: blocksize %u not multiple of 512\n", +- cloop_name, ntohl(clo->head.block_size)); +- error=-EBADF; goto error_release; +- } - if (clo->head.preamble[0x0B]!='V'||clo->head.preamble[0x0C]<'1') - { - printk(KERN_ERR "%s: Cannot read old 32-bit (version 0.68) images, " - "please use an older version of %s for this file.\n", - cloop_name, cloop_name); - error=-EBADF; goto error_release; -- } ++ printk(KERN_ERR "%s: out of kernel mem for index\n", cloop_name); ++ error=-ENOMEM; goto error_release; + } - if (clo->head.preamble[0x0C]<'2') -- { ++ bytes_read = cloop_read_from_file(clo, file, zbuf, ++ inode->i_size - zlen - sizeof(struct cloop_tail), ++ zlen); ++ if (bytes_read != zlen) + { - printk(KERN_ERR "%s: Cannot read old architecture-dependent " - "(format <= 1.0) images, please use an older " - "version of %s for this file.\n", - cloop_name, cloop_name); -- error=-EBADF; goto error_release; -- } ++ printk(KERN_ERR "%s: can't read index\n", cloop_name); + error=-EBADF; goto error_release; + } - total_offsets=ntohl(clo->head.num_blocks)+1; - if (!isblkdev && (sizeof(struct cloop_head)+sizeof(loff_t)* -+ total_offsets=ntohl(clo->head.num_blocks); -+ if (!isblkdev && (sizeof(struct cloop_head)+sizeof(struct block_info)* - total_offsets > inode->i_size)) +- total_offsets > inode->i_size)) ++ len = CLOOP3_INDEX_SIZE(ntohl(tail.index_size)) * total_offsets; ++ flags = CLOOP3_BLOCKS_FLAGS(ntohl(tail.index_size)); ++// May 3 19:45:20 (none) user.info kernel: cloop: uncompress(clo=e0a78000, block_ptrs=e0c9c000, &len(1440)=ddc05e6c, zbuf=e0c9f000, zlen=43, flag=0) ++printk(KERN_INFO "%s: uncompress(clo=%p, block_ptrs=%p, &len(%ld)=%p, zbuf=%p, zlen=%ld, flag=%d)\n", cloop_name, ++ clo, clo->block_ptrs, len, &len, zbuf, zlen, flags); ++ ret = uncompress(clo, (void *) clo->block_ptrs, &len, zbuf, zlen, flags); ++// May 3 19:45:20 (none) user.alert kernel: BUG: unable to handle kernel NULL pointer dereference at (null) ++printk(KERN_INFO "%s: uncompressed !\n", cloop_name); ++ cloop_free(zbuf, zlen); ++ if (ret != 0) { - printk(KERN_ERR "%s: file too small for %u blocks\n", - cloop_name, ntohl(clo->head.num_blocks)); +- printk(KERN_ERR "%s: file too small for %u blocks\n", +- cloop_name, ntohl(clo->head.num_blocks)); ++ printk(KERN_ERR "%s: decompression error %i uncompressing index, flags %u\n", ++ cloop_name, ret, flags); error=-EBADF; goto error_release; } - clo->offsets = cloop_malloc(sizeof(loff_t) * total_offsets); -+ if (total_offsets + 1 == 0) /* Version >= 3.0 */ -+ { -+ struct cloop_tail tail; -+ if(isblkdev) -+ { -+ /* No end of file: can't find index */ -+ printk(KERN_ERR "%s: no V3 support for block device\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ bytes_read = cloop_read_from_file(clo, file, (void *) &tail, -+ inode->i_size - sizeof(struct cloop_tail), -+ sizeof(struct cloop_tail)); -+ if(bytes_read == sizeof(struct cloop_tail)) -+ { -+ unsigned long len, zlen; -+ void *zbuf; -+ clo->head.num_blocks = tail.num_blocks; -+ total_offsets = ntohl(clo->head.num_blocks); -+ clo->offsets = cloop_malloc(sizeof(struct block_info) * total_offsets); -+ if (!clo->offsets) -+ { -+ printk(KERN_ERR "%s: can't alloc index\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ zbuf = &clo->offsets[total_offsets/2]; -+ zlen = ntohl(tail.table_size); -+ len = ntohl(tail.index_size) * total_offsets; -+ bytes_read = cloop_read_from_file(clo, file, zbuf, -+ inode->i_size - zlen - sizeof(struct cloop_tail), -+ zlen); -+ if (bytes_read != zlen) -+ { -+ printk(KERN_ERR "%s: can't read index\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ clo->zstream.workspace = cloop_malloc(zlib_inflate_workspacesize()); -+ if(!clo->zstream.workspace) -+ { -+ printk(KERN_ERR "%s: can't alloc index workspace\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ zlib_inflateInit(&clo->zstream); -+ uncompress(clo, (void *) clo->offsets, &len, zbuf, zlen); -+ cloop_free(clo->zstream.workspace, zlib_inflate_workspacesize()); -+ clo->zstream.workspace = NULL; -+ break; -+ } -+ else -+ { -+ printk(KERN_ERR "%s: can't find index\n", -+ cloop_name); -+ error=-EBADF; goto error_release; -+ } -+ } -+ clo->offsets = cloop_malloc(sizeof(struct block_info) * total_offsets); - if (!clo->offsets) - { - printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name); -@@ -521,19 +571,22 @@ +- if (!clo->offsets) +- { +- printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name); +- error=-ENOMEM; goto error_release; +- } } - num_readable = MIN(total_offsets - offsets_read, - (clo->underlying_blksize - offset) +- num_readable = MIN(total_offsets - offsets_read, +- (clo->underlying_blksize - offset) - / sizeof(loff_t)); - memcpy(&clo->offsets[offsets_read], bbuf+offset, num_readable * sizeof(loff_t)); -+ / sizeof(struct block_info)); -+ memcpy(&clo->offsets[offsets_read], bbuf+offset, num_readable * sizeof(struct block_info)); - offsets_read += num_readable; - } - { /* Search for largest block rather than estimate. KK. */ - int i; +- offsets_read += num_readable; +- } +- { /* Search for largest block rather than estimate. KK. */ +- int i; - for(i=0;ioffsets, ntohl(clo->head.num_blocks)); -+ for(i=0,clo->largest_block=0;iblock_ptrs = cloop_malloc(sizeof(struct block_info) * total_offsets); ++ if (!clo->block_ptrs) ++ { ++ printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name); ++ error=-ENOMEM; goto error_release; ++ } ++ /* Read them offsets! */ ++ if(clo->header_first) ++ { ++ total_bytes = total_offsets * sizeof(struct block_info); ++ fs_read_position = sizeof(struct cloop_head); ++ } ++ else { - loff_t d=be64_to_cpu(clo->offsets[i+1]) - be64_to_cpu(clo->offsets[i]); - clo->largest_block=MAX(clo->largest_block,d); -+ clo->largest_block=MAX(clo->largest_block,clo->offsets[i].size); ++ total_bytes = total_offsets * sizeof(loff_t); ++ fs_read_position = clo->underlying_total_size - sizeof(struct cloop_head) - total_bytes; ++ } ++ for(n=0;nunderlying_total_size - fs_read_position); ++ if(bytes_readable <= 0) break; /* Done */ ++ bytes_read = cloop_read_from_file(clo, file, bbuf, fs_read_position, bytes_readable); ++ if(bytes_read != bytes_readable) ++ { ++ printk(KERN_ERR "%s: Bad file %s, read() %lu bytes @ %llu returned %d.\n", ++ cloop_name, clo->underlying_filename, (unsigned long)clo->underlying_blksize, fs_read_position, (int)bytes_read); ++ error=-EBADF; ++ goto error_release; ++ } ++ memcpy(((char *)clo->block_ptrs) + n, bbuf, bytes_read); ++ /* remember where to read the next blk from file */ ++ fs_read_position += bytes_read; ++ n += bytes_read; } - printk(KERN_INFO "%s: %s: %u blocks, %u bytes/block, largest block is %lu bytes.\n", - cloop_name, filename, ntohl(clo->head.num_blocks), -+ i = ntohl(clo->head.block_size); -+ i += i/1000 + 12 + 4; /* max gzip block size */ -+ if (clo->largest_block > i) clo->largest_block = i; /* broken index ? */ -+ printk(KERN_INFO "%s: %s: %s, %u blocks, %u bytes/block, largest block is %lu bytes.\n", -+ cloop_name, filename, version, ntohl(clo->head.num_blocks), - ntohl(clo->head.block_size), clo->largest_block); +- ntohl(clo->head.block_size), clo->largest_block); } - /* Combo kmalloc used too large chunks (>130000). */ -@@ -565,16 +618,6 @@ - error=-ENOMEM; goto error_release_free_all; +-/* Combo kmalloc used too large chunks (>130000). */ + { + int i; +- for(i=0;ibuffer[i] = cloop_malloc(ntohl(clo->head.block_size)); +- if(!clo->buffer[i]) +- { +- printk(KERN_ERR "%s: out of memory for buffer %lu\n", +- cloop_name, (unsigned long) ntohl(clo->head.block_size)); +- error=-ENOMEM; goto error_release_free; +- } +- } ++ char *version = build_index(clo->block_ptrs, clo->head.num_blocks, clo->head.block_size, flags); ++ clo->largest_block = 0; ++ for (i = 0; i < clo->head.num_blocks; i++) ++ if (clo->block_ptrs[i].size > clo->largest_block) ++ clo->largest_block = clo->block_ptrs[i].size; ++ printk(KERN_INFO "%s: %s: %s: %u blocks, %u bytes/block, largest block is %lu bytes.\n", ++ cloop_name, clo->underlying_filename, version, clo->head.num_blocks, ++ clo->head.block_size, clo->largest_block); ++ } ++ { ++ int i; ++ clo->num_buffered_blocks = (buffers > 0 && clo->head.block_size >= 512) ? ++ (buffers / clo->head.block_size) : 1; ++ clo->buffered_blocknum = cloop_malloc(clo->num_buffered_blocks * sizeof (u_int32_t)); ++ clo->buffer = cloop_malloc(clo->num_buffered_blocks * sizeof (char*)); ++ if (!clo->buffered_blocknum || !clo->buffer) ++ { ++ printk(KERN_ERR "%s: out of memory for index of cache buffer (%lu bytes)\n", ++ cloop_name, (unsigned long)clo->num_buffered_blocks * sizeof (u_int32_t) + sizeof(char*) ); ++ error=-ENOMEM; goto error_release; ++ } ++ memset(clo->buffer, 0, clo->num_buffered_blocks * sizeof (char*)); ++ for(i=0;inum_buffered_blocks;i++) ++ { ++ clo->buffered_blocknum[i] = -1; ++ clo->buffer[i] = cloop_malloc(clo->head.block_size); ++ if(!clo->buffer[i]) ++ { ++ printk(KERN_ERR "%s: out of memory for cache buffer %lu\n", ++ cloop_name, (unsigned long) clo->head.block_size); ++ error=-ENOMEM; goto error_release_free; ++ } ++ } ++ clo->current_bufnum = 0; + } + clo->compressed_buffer = cloop_malloc(clo->largest_block); + if(!clo->compressed_buffer) +@@ -557,31 +803,7 @@ + cloop_name, clo->largest_block); + error=-ENOMEM; goto error_release_free_buffer; } - zlib_inflateInit(&clo->zstream); +- clo->zstream.workspace = cloop_malloc(zlib_inflate_workspacesize()); +- if(!clo->zstream.workspace) +- { +- printk(KERN_ERR "%s: out of mem for zlib working area %u\n", +- cloop_name, zlib_inflate_workspacesize()); +- error=-ENOMEM; goto error_release_free_all; +- } +- zlib_inflateInit(&clo->zstream); - if(!isblkdev && - be64_to_cpu(clo->offsets[ntohl(clo->head.num_blocks)]) != inode->i_size) - { @@ -274,15 +1112,264 @@ - cloop_free(clo->zstream.workspace, zlib_inflate_workspacesize()); clo->zstream.workspace=NULL; - goto error_release_free_all; - } +- { +- int i; +- for(i=0; ibuffered_blocknum[i] = -1; +- clo->current_bufnum=0; +- } +- set_capacity(clo->clo_disk, (sector_t)(ntohl(clo->head.num_blocks)* +- (ntohl(clo->head.block_size)>>9))); ++ set_capacity(clo->clo_disk, (sector_t)(clo->head.num_blocks*(clo->head.block_size>>9))); + clo->clo_thread = kthread_create(cloop_thread, clo, "cloop%d", cloop_num); + if(IS_ERR(clo->clo_thread)) + { +@@ -591,17 +813,17 @@ + } + if(preload > 0) + { +- clo->preload_array_size = ((preload<=ntohl(clo->head.num_blocks))?preload:ntohl(clo->head.num_blocks)); ++ clo->preload_array_size = ((preload<=clo->head.num_blocks)?preload:clo->head.num_blocks); + clo->preload_size = 0; + if((clo->preload_cache = cloop_malloc(clo->preload_array_size * sizeof(char *))) != NULL) + { + int i; + for(i=0; ipreload_array_size; i++) + { +- if((clo->preload_cache[i] = cloop_malloc(ntohl(clo->head.block_size))) == NULL) ++ if((clo->preload_cache[i] = cloop_malloc(clo->head.block_size)) == NULL) + { /* Out of memory */ + printk(KERN_WARNING "%s: cloop_malloc(%d) failed for preload_cache[%d] (ignored).\n", +- cloop_name, ntohl(clo->head.block_size), i); ++ cloop_name, clo->head.block_size, i); + break; + } + } +@@ -612,13 +834,13 @@ + if(buffered_blocknum >= 0) + { + memcpy(clo->preload_cache[i], clo->buffer[buffered_blocknum], +- ntohl(clo->head.block_size)); ++ clo->head.block_size); + } + else + { + printk(KERN_WARNING "%s: can't read block %d into preload cache, set to zero.\n", + cloop_name, i); +- memset(clo->preload_cache[i], 0, ntohl(clo->head.block_size)); ++ memset(clo->preload_cache[i], 0, clo->head.block_size); + } + } + printk(KERN_INFO "%s: preloaded %d blocks into cache.\n", cloop_name, +@@ -641,22 +863,19 @@ + cloop_free(clo->compressed_buffer, clo->largest_block); + clo->compressed_buffer=NULL; + error_release_free_buffer: ++ if(clo->buffer) { int i; - for(i=0; ibuffered_blocknum[i] = -1; -@@ -653,7 +696,7 @@ - } +- for(i=0; ibuffer[i]) +- { +- cloop_free(clo->buffer[i], ntohl(clo->head.block_size)); +- clo->buffer[i]=NULL; +- } +- } ++ for(i=0; inum_buffered_blocks; i++) { if(clo->buffer[i]) { cloop_free(clo->buffer[i], clo->head.block_size); clo->buffer[i]=NULL; }} ++ cloop_free(clo->buffer, clo->num_buffered_blocks*sizeof(char*)); clo->buffer=NULL; } ++ if (clo->buffered_blocknum) { cloop_free(clo->buffered_blocknum, sizeof(int)*clo->num_buffered_blocks); clo->buffered_blocknum=NULL; } error_release_free: - cloop_free(clo->offsets, sizeof(loff_t) * total_offsets); -+ cloop_free(clo->offsets, sizeof(struct block_info) * total_offsets); - clo->offsets=NULL; +- clo->offsets=NULL; ++ cloop_free(clo->block_ptrs, sizeof(struct block_info) * total_offsets); ++ clo->block_ptrs=NULL; error_release: if(bbuf) cloop_free(bbuf, clo->underlying_blksize); ++ if(clo->underlying_filename) { kfree(clo->underlying_filename); clo->underlying_filename=NULL; } + clo->backing_file=NULL; + return error; + } +@@ -673,7 +892,7 @@ + if(clo->backing_file) return -EBUSY; + file = fget(arg); /* get filp struct from ioctl arg fd */ + if(!file) return -EBADF; +- error=cloop_set_file(cloop_num,file,"losetup_file"); ++ error=cloop_set_file(cloop_num,file); + set_device_ro(bdev, 1); + if(error) fput(file); + return error; +@@ -684,29 +903,48 @@ + { + struct cloop_device *clo = cloop_dev[cloop_num]; + struct file *filp = clo->backing_file; +- int i; + if(clo->refcnt > 1) /* we needed one fd for the ioctl */ + return -EBUSY; + if(filp==NULL) return -EINVAL; + if(clo->clo_thread) { kthread_stop(clo->clo_thread); clo->clo_thread=NULL; } +- if(filp!=initial_file) fput(filp); +- else { filp_close(initial_file,0); initial_file=NULL; } ++ if(filp!=initial_file) ++ fput(filp); ++ else ++ { ++ filp_close(initial_file,0); ++ initial_file=NULL; ++ } + clo->backing_file = NULL; + clo->backing_inode = NULL; +- if(clo->offsets) { cloop_free(clo->offsets, clo->underlying_blksize); clo->offsets = NULL; } ++ if(clo->underlying_filename) { kfree(clo->underlying_filename); clo->underlying_filename=NULL; } ++ if(clo->block_ptrs) { cloop_free(clo->block_ptrs, clo->head.num_blocks); clo->block_ptrs = NULL; } + if(clo->preload_cache) +- { +- for(i=0; i < clo->preload_size; i++) +- cloop_free(clo->preload_cache[i], ntohl(clo->head.block_size)); +- cloop_free(clo->preload_cache, clo->preload_array_size * sizeof(char *)); +- clo->preload_cache = NULL; +- clo->preload_size = clo->preload_array_size = 0; +- } +- for(i=0; ibuffer[i]) { cloop_free(clo->buffer[i], ntohl(clo->head.block_size)); clo->buffer[i]=NULL; } ++ { ++ int i; ++ for(i=0; i < clo->preload_size; i++) ++ cloop_free(clo->preload_cache[i], clo->head.block_size); ++ cloop_free(clo->preload_cache, clo->preload_array_size * sizeof(char *)); ++ clo->preload_cache = NULL; ++ clo->preload_size = clo->preload_array_size = 0; ++ } ++ if (clo->buffered_blocknum) ++ { ++ cloop_free(clo->buffered_blocknum, sizeof(int) * clo->num_buffered_blocks); clo->buffered_blocknum = NULL; ++ } ++ if (clo->buffer) ++ { ++ int i; ++ for(i=0; inum_buffered_blocks; i++) { if(clo->buffer[i]) cloop_free(clo->buffer[i], clo->head.block_size); } ++ cloop_free(clo->buffer, sizeof(char*) * clo->num_buffered_blocks); clo->buffer = NULL; ++ } + if(clo->compressed_buffer) { cloop_free(clo->compressed_buffer, clo->largest_block); clo->compressed_buffer = NULL; } ++#if (defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE)) + zlib_inflateEnd(&clo->zstream); + if(clo->zstream.workspace) { cloop_free(clo->zstream.workspace, zlib_inflate_workspacesize()); clo->zstream.workspace = NULL; } ++#endif ++#if (defined(CONFIG_DECOMPRESS_XZ) || defined(CONFIG_DECOMPRESS_XZ_MODULE)) ++ xz_dec_end(clo->xzdecoderstate); ++#endif + if(bdev) invalidate_bdev(bdev); + if(clo->clo_disk) set_capacity(clo->clo_disk, 0); + return 0; +@@ -731,8 +969,8 @@ + const struct loop_info64 *info) + { + if (!clo->backing_file) return -ENXIO; +- memcpy(clo->clo_file_name, info->lo_file_name, LO_NAME_SIZE); +- clo->clo_file_name[LO_NAME_SIZE-1] = 0; ++ if(clo->underlying_filename) kfree(clo->underlying_filename); ++ clo->underlying_filename = kstrdup(info->lo_file_name, GFP_KERNEL); + return 0; + } + +@@ -743,7 +981,11 @@ + struct kstat stat; + int err; + if (!file) return -ENXIO; +- err = vfs_getattr(file->f_path.mnt, file->f_path.dentry, &stat); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ++ err = vfs_getattr(&file->f_path, &stat); ++#else ++ err = vfs_getattr(&file->f_path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT); ++#endif + if (err) return err; + memset(info, 0, sizeof(*info)); + info->lo_number = clo->clo_number; +@@ -753,7 +995,8 @@ + info->lo_offset = 0; + info->lo_sizelimit = 0; + info->lo_flags = 0; +- memcpy(info->lo_file_name, clo->clo_file_name, LO_NAME_SIZE); ++ strncpy(info->lo_file_name, clo->underlying_filename, LO_NAME_SIZE); ++ info->lo_file_name[LO_NAME_SIZE-1]=0; + return 0; + } + +@@ -833,8 +1076,6 @@ + if (!err && copy_to_user(arg, &info64, sizeof(info64))) err = -EFAULT; + return err; + } +-/* EOF get/set_status */ +- + + static int cloop_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +@@ -914,21 +1155,20 @@ + /* losetup uses write-open and flags=0x8002 to set a new file */ + if(mode & FMODE_WRITE) + { +- printk(KERN_WARNING "%s: Can't open device read-write in mode 0x%x\n", cloop_name, mode); ++ printk(KERN_INFO "%s: Open in read-write mode 0x%x requested, ignored.\n", cloop_name, mode); + return -EROFS; + } + cloop_dev[cloop_num]->refcnt+=1; + return 0; + } + +-static int cloop_close(struct gendisk *disk, fmode_t mode) ++static void cloop_close(struct gendisk *disk, fmode_t mode) + { +- int cloop_num, err=0; +- if(!disk) return 0; ++ int cloop_num; ++ if(!disk) return; + cloop_num=((struct cloop_device *)disk->private_data)->clo_number; +- if(cloop_num < 0 || cloop_num > (cloop_count-1)) return 0; ++ if(cloop_num < 0 || cloop_num > (cloop_count-1)) return; + cloop_dev[cloop_num]->refcnt-=1; +- return err; + } + + static struct block_device_operations clo_fops = +@@ -973,6 +1213,10 @@ + goto error_out; + } + clo->clo_queue->queuedata = clo; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) ++ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, clo->clo_queue); ++ queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, clo->clo_queue); ++#endif + clo->clo_disk = alloc_disk(1); + if(!clo->clo_disk) + { +@@ -1004,6 +1248,11 @@ + cloop_dev[cloop_num] = NULL; + } + ++/* LZ4 Stuff */ ++#if (defined USE_LZ4_INTERNAL) ++#include "lz4_kmod.c" ++#endif ++ + static int __init cloop_init(void) + { + int error=0; +@@ -1044,7 +1293,7 @@ + initial_file=NULL; /* if IS_ERR, it's NOT open. */ + } + else +- error=cloop_set_file(0,initial_file,file); ++ error=cloop_set_file(0,initial_file); + if(error) + { + printk(KERN_ERR +@@ -1052,9 +1301,6 @@ + cloop_name, file, error); + goto init_out_dealloc; + } +- if(namelen >= LO_NAME_SIZE) namelen = LO_NAME_SIZE-1; +- memcpy(cloop_dev[0]->clo_file_name, file, namelen); +- cloop_dev[0]->clo_file_name[namelen] = 0; + } + return 0; + init_out_dealloc: