root/morphix/trunk/cloop/compressed_loop.c

Revision 2, 22.0 kB (checked in by nextime, 2 years ago)

Initial import, branching from morphix svn

Line 
1 /*
2  *  compressed_loop.c: Read-only compressed loop blockdevice
3  *  hacked up by Rusty in 1999, extended and maintained by Klaus Knopper
4  *
5  *  cloop file looks like:
6  *  [32-bit uncompressed block size: network order]
7  *  [32-bit number of blocks (n_blocks): network order]
8  *  [64-bit file offsets of start of blocks: network order]
9  *    * (n_blocks + 1).
10  * n_blocks of:
11  *   [compressed block]
12  *
13  *  Inspired by loop.c by Theodore Ts'o, 3/29/93.
14  *
15  * Copyright 1999-2003 by Paul `Rusty' Russell & Klaus Knopper.
16  * Redistribution of this file is permitted under the GNU Public License.
17  *
18  * CHANGES: (see CHANGELOG file)
19  *
20  * Hacked to load on 2.6 kernels, patch from Olivier Evalet (ntohl adding)
21  */
22
23 #define CLOOP_NAME "cloop"
24 #define CLOOP_VERSION "2.01"
25 #define CLOOP_MAX 64
26
27 /* Define this if you are using Greenshoe Linux */
28 /* #define REDHAT_KERNEL */
29
30 #include <linux/version.h>
31 #include <linux/module.h>
32 #include <linux/init.h>
33 #include <linux/sched.h>
34 #include <linux/fs.h>
35 #include <linux/file.h>
36 #include <linux/stat.h>
37 #include <linux/errno.h>
38 #include <linux/major.h>
39 #include <linux/vmalloc.h>
40 #include <linux/slab.h>
41 #include <linux/devfs_fs_kernel.h>
42 #include <asm/semaphore.h>
43 #include <asm/div64.h> /* do_div() for 64bit division */
44 #include <asm/uaccess.h>
45 /* Use zlib_inflate from lib/zlib_inflate */
46 #include <linux/zutil.h>
47 #include <linux/loop.h>
48 // #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
49 // #include <linux/buffer_head.h>
50 // #endif
51 #include "compressed_loop.h"
52
53 /**
54  * I had this error message so I have hadded the ntohl function
55  * ntohl: Unknown symbol in module
56  *
57  * patch from Olivier Evalet
58  */
59 #undef ntohl
60 uint32_t ntohl(x)
61 uint32_t x;
62 {
63 #if BYTE_ORDER == LITTLE_ENDIAN
64         u_char *s = (u_char *)&x;
65         return (uint32_t)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
66 #else
67         return x;
68 #endif
69 }
70
71 /* end patch */
72
73 /* New License scheme */
74 #ifdef MODULE_LICENSE
75 MODULE_LICENSE("GPL");
76 #endif
77 #ifdef MODULE_AUTHOR
78 MODULE_AUTHOR("Klaus Knopper (Kernel 2.4 and up, Knoppix version), Paul Russel (initial version)");
79 #endif
80 #ifdef MODULE_DESCRIPTION
81 MODULE_DESCRIPTION("Transparently decompressing loopback block device");
82 #endif
83
84 #ifndef MIN
85 #define MIN(x,y) ((x) < (y) ? (x) : (y))
86 #endif
87
88 #ifndef MAX
89 #define MAX(x,y) ((x) > (y) ? (x) : (y))
90 #endif
91
92 /* Use experimental major for now */
93 #define MAJOR_NR 240
94
95 #define DEVICE_NAME CLOOP_NAME
96 #define DEVICE_NR(device) (MINOR(device))
97 #define DEVICE_ON(device)
98 #define DEVICE_OFF(device)
99 #define DEVICE_NO_RANDOM
100 #define TIMEOUT_VALUE (6 * HZ)
101
102 #include <linux/blkdev.h>
103 #include <linux/buffer_head.h>
104
105 #if 0
106 #define DEBUGP printk
107 #else
108 #define DEBUGP(format, x...)
109 #endif
110
111 /* One file can be opened at module insertion time */
112 /* insmod cloop file=/path/to/file */
113 static char *file=NULL;
114 MODULE_PARM(file, "s");
115 MODULE_PARM_DESC(file, "Initial cloop image file (full path) for /dev/cloop");
116 static struct file *initial_file=NULL;
117
118 struct cloop_device
119 {
120  /* Copied straight from the file */
121  struct cloop_head head;
122
123  /* An array of offsets of compressed blocks within the file */
124  loff_t *offsets;
125
126  /* We buffer one uncompressed `block' */
127  int buffered_blocknum;
128  void *buffer;
129  void *compressed_buffer;
130
131  z_stream zstream;
132
133  struct file   *backing_file;  /* associated file */
134  struct inode  *backing_inode; /* for bmap */
135
136  unsigned int underlying_blksize;
137  int refcnt;
138  struct block_device *bdev;
139  int isblkdev;
140  struct semaphore clo_lock;
141  struct gendisk *disk;
142  request_queue_t *clo_queue;
143 };
144
145 static struct cloop_device cloop_dev[CLOOP_MAX];
146 static char *cloop_name=CLOOP_NAME;
147 static const int max_cloop = CLOOP_MAX;
148
149 #if (!(defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE))) /* Must be compiled into kernel. */
150 #error  "Invalid Kernel configuration. CONFIG_ZLIB_INFLATE support is needed for cloop."
151 #endif
152
153 static int uncompress(struct cloop_device *clo, char *dest, unsigned long *destLen,
154                       char *source, unsigned long sourceLen)
155 {
156  /* Most of this code can be found in fs/cramfs/uncompress.c */
157  int err;
158  clo->zstream.next_in = source;
159  clo->zstream.avail_in = sourceLen;
160  clo->zstream.next_out = dest;
161  clo->zstream.avail_out = *destLen;
162  err = zlib_inflateReset(&clo->zstream);
163  if (err != Z_OK)
164   {
165    printk(KERN_ERR "%s: zlib_inflateReset error %d\n", cloop_name, err);
166    zlib_inflateEnd(&clo->zstream); zlib_inflateInit(&clo->zstream);
167   }
168  err = zlib_inflate(&clo->zstream, Z_FINISH);
169  *destLen = clo->zstream.total_out;
170  if (err != Z_STREAM_END) return err;
171  return Z_OK;
172 }
173
174 /* This is more complicated than it looks. */
175 struct clo_read_data
176 {
177  struct cloop_device *clo;
178  char *data; /* We need to keep track of where we are in the buffer */
179  int bsize;
180 };
181
182 /* We need this for do_generic_file_read() because the default function */
183 /* wants to read into user-space for an unknown reason. :-/ See loop.c. */
184 static int clo_read_actor(read_descriptor_t * desc, struct page *page,
185                           unsigned long offset, unsigned long size)
186 {
187  char *kaddr;
188  struct clo_read_data *p = (struct clo_read_data*)desc->arg.buf;
189  unsigned long count = desc->count;
190  if (size > count) size = count;
191  kaddr = kmap(page);
192  memcpy(p->data, kaddr + offset, size);
193  kunmap(page);
194  desc->count = count - size;
195  desc->written += size;
196  p->data += size;
197  return size;
198 }
199
200 static size_t clo_read_from_file(struct cloop_device *clo, struct file *f, char *buf,
201   loff_t pos, size_t buf_len)
202 {
203  size_t buf_done=0;
204  while (buf_done < buf_len)
205   {
206    size_t size = buf_len - buf_done;
207    struct clo_read_data cd={ /* do_generic_file_read() needs this. */
208            clo,              /* struct cloop_device *clo */
209            (char *)(buf + buf_done), /* char *data */
210            size};            /* Actual data size */
211    read_descriptor_t desc;
212    desc.written = 0;
213    desc.count   = size;
214    desc.arg.buf     = (char*)&cd;
215    desc.error   = 0;
216 #ifdef REDHAT_KERNEL /* Greenshoe Linux */
217    do_generic_file_read(f, &pos, &desc, clo_read_actor, 0);
218 #else /* Normal Kernel */
219    do_generic_file_read(f, &pos, &desc, clo_read_actor);
220 #endif
221    if(desc.error||desc.written<=0)
222     {
223      int left = size - desc.written;
224      if(left<0) left = 0; /* better safe than sorry */
225      printk(KERN_ERR "%s: Read error at pos %Lu in file %s, %d bytes lost.\n",
226             cloop_name, pos, file, left);
227      memset(buf + buf_len - left, 0, left);
228      break;
229     }
230    buf_done+=desc.written;
231   }
232  return buf_done;
233 }
234
235 /* This looks more complicated than it is */
236 static int load_buffer(struct cloop_device *clo, int blocknum)
237 {
238  unsigned int buf_done = 0;
239  unsigned long buflen;
240  unsigned int buf_length;
241  int ret;
242
243  if(blocknum > ntohl(clo->head.num_blocks) || blocknum < 0)
244   {
245    printk(KERN_WARNING "%s: Invalid block number %d requested.\n",
246                        cloop_name, blocknum);
247    clo->buffered_blocknum = -1;
248    return 0;
249   }
250
251  if (blocknum == clo->buffered_blocknum) return 1;
252
253  /* Is there a ntohl for 64-bit values? */
254  buf_length = be64_to_cpu(clo->offsets[blocknum+1]) - be64_to_cpu(clo->offsets[blocknum]);
255
256 /* Load one compressed block from the file. */
257  clo_read_from_file(clo, clo->backing_file, (char *)clo->compressed_buffer,
258                     be64_to_cpu(clo->offsets[blocknum]), buf_length);
259
260  /* Do decompression into real buffer. */
261  buflen = ntohl(clo->head.block_size);
262
263  /* Do the uncompression */
264  ret = uncompress(clo, clo->buffer, &buflen, clo->compressed_buffer,
265                   buf_length);
266  /* DEBUGP("cloop: buflen after uncompress: %ld\n",buflen); */
267  if (ret != 0)
268   {
269    printk(KERN_ERR "%s: error %i uncompressing block %u %u/%lu/%u/%u "
270           "%Lu-%Lu\n", cloop_name, ret, blocknum,
271           ntohl(clo->head.block_size), buflen, buf_length, buf_done,
272           be64_to_cpu(clo->offsets[blocknum]), be64_to_cpu(clo->offsets[blocknum+1]));
273    clo->buffered_blocknum = -1;
274    return 0;
275   }
276  clo->buffered_blocknum = blocknum;
277  return 1;
278 }
279
280 static int make_clo_request(request_queue_t *q, struct bio *bio)
281 {
282  struct cloop_device *cloop;
283  int status = 0;
284  unsigned int len;
285  loff_t offset;
286  char *dest;
287
288  int rw = bio_rw(bio);
289  unsigned int vecnr;
290  cloop = q->queuedata;
291
292  /* quick sanity checks */
293  if (rw != READ && rw != READA)
294   {
295    DEBUGP("do_clo_request: bad command\n");
296    goto out;
297   }
298
299  if (!cloop->backing_file)
300   {
301    DEBUGP("do_clo_request: not connected to a file\n");
302    goto out;
303   }
304
305  down(&cloop->clo_lock);
306  offset     = (loff_t)bio->bi_sector << 9;
307  for(vecnr=0; vecnr < bio->bi_vcnt; vecnr++)
308   {
309    struct bio_vec *bvec=&bio->bi_io_vec[vecnr];
310    len = bvec->bv_len;
311    dest= kmap(bvec->bv_page) + bvec->bv_offset;
312  
313  while(len > 0)
314   {
315    u_int32_t length_in_buffer;
316    loff_t block_offset=offset;
317  
318    /* do_div (div64.h) returns the 64bit division remainder and  */
319    /* puts the result in the first argument, i.e. block_offset   */
320    /* becomes the blocknumber to load, and offset_in_buffer the  */
321    /* position in the buffer */
322    u_int32_t offset_in_buffer;
323    offset_in_buffer = do_div(block_offset, ntohl(cloop->head.block_size));
324  
325    status=load_buffer(cloop,block_offset);
326    if(!status) break; /* invalid data, leave inner loop, goto next request */
327  
328    /* Now, at least part of what we want will be in the buffer. */
329    length_in_buffer = ntohl(cloop->head.block_size) - offset_in_buffer;
330  
331    if(length_in_buffer > len)
332     {
333 /*     DEBUGP("Warning: length_in_buffer=%u > len=%u\n",
334                         length_in_buffer,len); */
335      length_in_buffer = len;
336     }
337
338    memcpy(dest, cloop->buffer + offset_in_buffer, length_in_buffer);
339
340    dest   += length_in_buffer;
341    len    -= length_in_buffer;
342    offset += length_in_buffer;
343   } /* while inner loop */
344
345    kunmap(bvec->bv_page);
346   } /* end for vecnr*/
347
348  up(&cloop->clo_lock);
349
350 out:
351  bio_endio(bio, bio->bi_size,status==0);
352  return 0;
353 }
354
355 /* Read header and offsets from already opened file */
356 static int clo_set_file(int cloop_num, struct file *file, char *filename)
357 {
358  struct cloop_device *clo=&cloop_dev[cloop_num];
359  struct inode *inode;
360  char *bbuf=NULL;
361  unsigned int i, offsets_read, total_offsets;
362  unsigned long largest_block=0;
363  int isblkdev;
364
365  int error = 0;
366
367  inode = file->f_dentry->d_inode;
368  isblkdev=S_ISBLK(inode->i_mode)?1:0;
369  if(!isblkdev&&!S_ISREG(inode->i_mode))
370   {
371    printk(KERN_ERR "%s: %s not a regular file or block device\n",
372                    cloop_name, filename);
373    error=-EBADF; goto error_release;
374   }
375
376  clo->backing_file = file;
377  clo->backing_inode= inode ;
378
379  if(!isblkdev&&inode->i_size<sizeof(struct cloop_head))
380   {
381    printk(KERN_ERR "%s: %lu bytes (must be >= %u bytes)\n",
382                    cloop_name, (unsigned long)inode->i_size,
383                    (unsigned)sizeof(struct cloop_head));
384    error=-EBADF; goto error_release;
385   }
386 /* Somehow blksize_size doesn't exist anymore, so we ignore this bit...
387  if(isblkdev)
388   {
389    request_queue_t *q = bdev_get_queue(inode->i_bdev);
390    blk_queue_max_sectors(clo->clo_queue, q->max_sectors);
391    blk_queue_max_phys_segments(clo->clo_queue,q->max_phys_segments);
392    blk_queue_max_hw_segments(clo->clo_queue, q->max_hw_segments);
393    blk_queue_max_segment_size(clo->clo_queue, q->max_segment_size);
394    blk_queue_segment_boundary(clo->clo_queue, q->seg_boundary_mask);
395    blk_queue_merge_bvec(clo->clo_queue, q->merge_bvec_fn);
396    clo->underlying_blksize = blksize_size(inode->i_bdev);
397  }
398  else */
399    clo->underlying_blksize = inode->i_blksize;
400
401  DEBUGP("Underlying blocksize is %u\n", clo->underlying_blksize);
402
403  bbuf = vmalloc(clo->underlying_blksize);
404  if(!bbuf)
405   {
406    printk(KERN_ERR "%s: out of kernel mem for block buffer (%lu bytes)\n",
407                    cloop_name, (unsigned long)clo->underlying_blksize);
408    error=-ENOMEM; goto error_release;
409   }
410  total_offsets = 1; /* Dummy total_offsets: will be filled in first time around */
411  for (i = 0, offsets_read = 0; offsets_read < total_offsets; i++)
412   {
413    unsigned int offset = 0, num_readable;
414
415    /* Kernel 2.4 version */
416    size_t bytes_read = clo_read_from_file(clo, file, bbuf,
417                                           i*clo->underlying_blksize,
418                                           clo->underlying_blksize);
419    if(bytes_read != clo->underlying_blksize) { error=-EBADF; goto error_release; }
420
421    /* Header will be in block zero */
422    if(i==0)
423     {
424      memcpy(&clo->head, bbuf, sizeof(struct cloop_head));
425      offset = sizeof(struct cloop_head);
426      if (ntohl(clo->head.block_size) % 512 != 0)
427       {
428        printk(KERN_ERR "%s: blocksize %u not multiple of 512\n",
429               cloop_name, ntohl(clo->head.block_size));
430        error=-EBADF; goto error_release;
431       }
432
433      if (clo->head.preamble[0x0B]!='V'||clo->head.preamble[0x0C]<'1')
434       {
435        printk(KERN_ERR "%s: Cannot read old 32-bit (version 0.68) images, "
436                        "please use an older version of %s for this file.\n",
437                        cloop_name, cloop_name);
438        error=-EBADF; goto error_release;
439       }
440
441      if (clo->head.preamble[0x0C]<'2')
442       {
443        printk(KERN_ERR "%s: Cannot read old architecture-dependent "
444                        "(format <= 1.0) images, please use an older "
445                        "version of %s for this file.\n",
446                        cloop_name, cloop_name);
447        error=-EBADF; goto error_release;
448       }
449
450      total_offsets=ntohl(clo->head.num_blocks)+1;
451
452      if (!isblkdev && (sizeof(struct cloop_head)+sizeof(loff_t)*
453                        total_offsets > inode->i_size))
454       {
455        printk(KERN_ERR "%s: file too small for %u blocks\n",
456               cloop_name, ntohl(clo->head.num_blocks));
457        error=-EBADF; goto error_release;
458       }
459
460      clo->offsets = vmalloc(sizeof(loff_t) * total_offsets);
461      if (!clo->offsets)
462       {
463        printk(KERN_ERR "%s: out of kernel mem for offsets\n", cloop_name);
464        error=-ENOMEM; goto error_release;
465       }
466     }
467
468    num_readable = MIN(total_offsets - offsets_read,
469                       (clo->underlying_blksize - offset)
470                       / sizeof(loff_t));
471    memcpy(&clo->offsets[offsets_read], bbuf+offset, num_readable * sizeof(loff_t));
472    offsets_read += num_readable;
473   }
474
475   { /* Search for largest block rather than estimate. KK. */
476    int i;
477    for(i=0;i<total_offsets-1;i++)
478     {
479      loff_t d=be64_to_cpu(clo->offsets[i+1]) - be64_to_cpu(clo->offsets[i]);
480      largest_block=MAX(largest_block,d);
481     }
482    printk("%s: %s: %u blocks, %u bytes/block, largest block is %lu bytes.\n",
483           cloop_name, filename, ntohl(clo->head.num_blocks),
484           ntohl(clo->head.block_size), largest_block);
485   }
486
487 /* Combo kmalloc used too large chunks (>130000). */
488  clo->buffer = vmalloc(ntohl(clo->head.block_size));
489  if(!clo->buffer)
490   {
491    printk(KERN_ERR "%s: out of memory for buffer %lu\n",
492           cloop_name, (unsigned long) ntohl(clo->head.block_size));
493    error=-ENOMEM; goto error_release_free;
494   }
495
496  clo->compressed_buffer = vmalloc(largest_block);
497
498  if(!clo->compressed_buffer)
499   {
500    printk(KERN_ERR "%s: out of memory for compressed buffer %lu\n",
501           cloop_name, largest_block);
502    error=-ENOMEM; goto error_release_free_buffer;
503   }
504  clo->zstream.workspace = vmalloc(zlib_inflate_workspacesize());
505  if(!clo->zstream.workspace)
506   {
507    printk(KERN_ERR "%s: out of mem for zlib working area %u\n",
508           cloop_name, zlib_inflate_workspacesize());
509    error=-ENOMEM; goto error_release_free_all;
510   }
511  zlib_inflateInit(&clo->zstream);
512
513  if(!isblkdev &&
514     be64_to_cpu(clo->offsets[ntohl(clo->head.num_blocks)]) != inode->i_size)
515   {
516    printk(KERN_ERR "%s: final offset wrong (%Lu not %Lu)\n",
517           cloop_name,
518           be64_to_cpu(clo->offsets[ntohl(clo->head.num_blocks)]),
519           inode->i_size);
520    vfree(clo->zstream.workspace); clo->zstream.workspace=NULL;
521    goto error_release_free_all;
522   }
523
524  clo->buffered_blocknum = -1;
525  set_capacity(clo->disk, (sector_t)(ntohl(clo->head.num_blocks)*(ntohl(clo->head.block_size)>>9)));
526  return error;
527
528 error_release_free_all:
529  vfree(clo->compressed_buffer);
530  clo->compressed_buffer=NULL;
531 error_release_free_buffer:
532  vfree(clo->buffer);
533  clo->buffer=NULL;
534 error_release_free:
535  vfree(clo->offsets);
536  clo->offsets=NULL;
537 error_release:
538  if(bbuf) vfree(bbuf);
539  clo->backing_file=NULL;
540  return error;
541 }
542
543 /* Code adapted from Theodore Ts'o's linux/drivers/block/loop.c */ 
544 /* Get file from ioctl arg (losetup) */
545 static int clo_set_fd(int cloop_num, struct file *clo_file,