Print this page
PSARC 2007/569 lofi(7D) compression support
6618343 lofi compression support
6603856 Lofi(7D) can thrash the page cache

@@ -97,10 +97,11 @@
  *      Encryption. tpm would like this. Apparently Windows 2000 has it, and
  *      so does Linux.
  */
 
 #include <sys/types.h>
+#include <netinet/in.h>
 #include <sys/sysmacros.h>
 #include <sys/cmn_err.h>
 #include <sys/uio.h>
 #include <sys/kmem.h>
 #include <sys/cred.h>

@@ -121,12 +122,12 @@
 #include <sys/open.h>
 #include <sys/disp.h>
 #include <vm/seg_map.h>
 #include <sys/ddi.h>
 #include <sys/sunddi.h>
+#include <sys/zmod.h>
 
-/* seems safer than having to get the string right many times */
 #define NBLOCKS_PROP_NAME       "Nblocks"
 #define SIZE_PROP_NAME  "Size"
 
 static dev_info_t *lofi_dip;
 static void     *lofi_statep;

@@ -147,10 +148,19 @@
 static int lofi_taskq_maxalloc = 104857600 / DEV_BSIZE;
 static int lofi_taskq_nthreads = 4;     /* # of taskq threads per device */
 
 uint32_t lofi_max_files = LOFI_MAX_FILES;
 
+static int gzip_decompress(void *src, size_t srclen, void *dst,
+        size_t *destlen, int level);
+
+lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
+        {gzip_decompress,       NULL,   6,      "gzip"}, /* default */
+        {gzip_decompress,       NULL,   6,      "gzip-6"},
+        {gzip_decompress,       NULL,   9,      "gzip-9"}
+};
+
 static int
 lofi_busy(void)
 {
         minor_t minor;
 

@@ -221,12 +231,11 @@
 {
         dev_t   newdev;
         char    namebuf[50];
 
         if (lsp->ls_vp) {
-                (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag,
-                    1, 0, credp, NULL);
+                (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, credp);
                 VN_RELE(lsp->ls_vp);
                 lsp->ls_vp = NULL;
         }
 
         newdev = makedevice(getmajor(dev), minor);

@@ -322,68 +331,40 @@
                 lofi_free_handle(dev, minor, lsp, credp);
         mutex_exit(&lofi_lock);
         return (0);
 }
 
-/*
- * This is basically what strategy used to be before we found we
- * needed task queues.
- */
-static void
-lofi_strategy_task(void *arg)
+static int
+lofi_mapped_rdwr(caddr_t bufaddr, offset_t offset, struct buf *bp,
+        struct lofi_state *lsp)
 {
-        struct buf *bp = (struct buf *)arg;
         int error;
-        struct lofi_state *lsp;
-        offset_t        offset, alignedoffset;
-        offset_t        mapoffset;
-        caddr_t bufaddr;
-        caddr_t mapaddr;
+        offset_t alignedoffset, mapoffset;
         size_t  xfersize;
-        size_t  len;
         int     isread;
         int     smflags;
+        caddr_t mapaddr;
+        size_t  len;
         enum seg_rw srw;
 
-        lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev));
-        if (lsp->ls_kstat) {
-                mutex_enter(lsp->ls_kstat->ks_lock);
-                kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat));
-                mutex_exit(lsp->ls_kstat->ks_lock);
-        }
-        bp_mapin(bp);
-        bufaddr = bp->b_un.b_addr;
-        offset = bp->b_lblkno * DEV_BSIZE;      /* offset within file */
-
         /*
-         * We used to always use vn_rdwr here, but we cannot do that because
-         * we might decide to read or write from the the underlying
-         * file during this call, which would be a deadlock because
-         * we have the rw_lock. So instead we page, unless it's not
-         * mapable or it's a character device.
-         */
-        if (lsp->ls_vp == NULL || lsp->ls_vp_closereq) {
-                error = EIO;
-        } else if (((lsp->ls_vp->v_flag & VNOMAP) == 0) &&
-            (lsp->ls_vp->v_type != VCHR)) {
-                /*
                  * segmap always gives us an 8K (MAXBSIZE) chunk, aligned on
                  * an 8K boundary, but the buf transfer address may not be
-                 * aligned on more than a 512-byte boundary (we don't
-                 * enforce that, though we could). This matters since the
-                 * initial part of the transfer may not start at offset 0
-                 * within the segmap'd chunk. So we have to compensate for
-                 * that with 'mapoffset'. Subsequent chunks always start
-                 * off at the beginning, and the last is capped by b_resid.
+         * aligned on more than a 512-byte boundary (we don't enforce
+         * that even though we could). This matters since the initial
+         * part of the transfer may not start at offset 0 within the
+         * segmap'd chunk. So we have to compensate for that with
+         * 'mapoffset'. Subsequent chunks always start off at the
+         * beginning, and the last is capped by b_resid
                  */
                 mapoffset = offset & MAXBOFFSET;
-                alignedoffset = offset - mapoffset;     /* now map-aligned */
+        alignedoffset = offset - mapoffset;
                 bp->b_resid = bp->b_bcount;
                 isread = bp->b_flags & B_READ;
                 srw = isread ? S_READ : S_WRITE;
                 do {
-                        xfersize = MIN(lsp->ls_vp_size - offset,
+                xfersize = MIN(lsp->ls_vp_comp_size - offset,
                             MIN(MAXBSIZE - mapoffset, bp->b_resid));
                         len = roundup(mapoffset + xfersize, PAGESIZE);
                         mapaddr = segmap_getmapflt(segkmap, lsp->ls_vp,
                             alignedoffset, MAXBSIZE, 1, srw);
                         /*

@@ -404,10 +385,19 @@
                                         error = EIO;
                                 break;
                         }
                         smflags = 0;
                         if (isread) {
+                        smflags |= SM_FREE;
+                        /*
+                         * If we're reading an entire page starting
+                         * at a page boundary, there's a good chance
+                         * we won't need it again. Put it on the
+                         * head of the freelist.
+                         */
+                        if (mapoffset == 0 && xfersize == PAGESIZE)
+                                smflags |= SM_DONTNEED;
                                 bcopy(mapaddr + mapoffset, bufaddr, xfersize);
                         } else {
                                 smflags |= SM_WRITE;
                                 bcopy(bufaddr, mapaddr + mapoffset, xfersize);
                         }

@@ -419,12 +409,222 @@
                         error = segmap_release(segkmap, mapaddr, smflags);
                         /* only the first map may start partial */
                         mapoffset = 0;
                         alignedoffset += MAXBSIZE;
                 } while ((error == 0) && (bp->b_resid > 0) &&
-                    (offset < lsp->ls_vp_size));
+            (offset < lsp->ls_vp_comp_size));
+
+        return (error);
+}
+
+/*ARGSUSED*/
+static int gzip_decompress(void *src, size_t srclen, void *dst,
+    size_t *dstlen, int level)
+{
+        ASSERT(*dstlen >= srclen);
+
+        if (z_uncompress(dst, dstlen, src, srclen) != Z_OK)
+                return (-1);
+        return (0);
+}
+
+/*
+ * This is basically what strategy used to be before we found we
+ * needed task queues.
+ */
+static void
+lofi_strategy_task(void *arg)
+{
+        struct buf *bp = (struct buf *)arg;
+        int error;
+        struct lofi_state *lsp;
+        uint64_t sblkno, eblkno, cmpbytes;
+        offset_t offset, sblkoff, eblkoff;
+        offset_t salign, ealign;
+        offset_t sdiff;
+        uint32_t comp_data_sz;
+        caddr_t bufaddr;
+        unsigned char *compressed_seg = NULL, *cmpbuf;
+        unsigned char *uncompressed_seg = NULL;
+        lofi_compress_info_t *li;
+        size_t oblkcount, xfersize;
+        unsigned long seglen;
+
+        lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev));
+        if (lsp->ls_kstat) {
+                mutex_enter(lsp->ls_kstat->ks_lock);
+                kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat));
+                mutex_exit(lsp->ls_kstat->ks_lock);
+        }
+        bp_mapin(bp);
+        bufaddr = bp->b_un.b_addr;
+        offset = bp->b_lblkno * DEV_BSIZE;      /* offset within file */
+
+        /*
+         * We used to always use vn_rdwr here, but we cannot do that because
+         * we might decide to read or write from the the underlying
+         * file during this call, which would be a deadlock because
+         * we have the rw_lock. So instead we page, unless it's not
+         * mapable or it's a character device.
+         */
+        if (lsp->ls_vp == NULL || lsp->ls_vp_closereq) {
+                error = EIO;
+        } else if (((lsp->ls_vp->v_flag & VNOMAP) == 0) &&
+            (lsp->ls_vp->v_type != VCHR)) {
+                uint64_t i;
+
+                /*
+                 * Handle uncompressed files with a regular read
+                 */
+                if (lsp->ls_uncomp_seg_sz == 0) {
+                        error = lofi_mapped_rdwr(bufaddr, offset, bp, lsp);
+                        goto done;
+                }
+
+                /*
+                 * From here on we're dealing primarily with compressed files
+                 */
+
+                /*
+                 * Compressed files can only be read from and
+                 * not written to
+                 */
+                if (!(bp->b_flags & B_READ)) {
+                        bp->b_resid = bp->b_bcount;
+                        error = EROFS;
+                        goto done;
+                }
+
+                ASSERT(lsp->ls_comp_algorithm_index >= 0);
+                li = &lofi_compress_table[lsp->ls_comp_algorithm_index];
+                /*
+                 * Compute starting and ending compressed segment numbers
+                 * We use only bitwise operations avoiding division and
+                 * modulus because we enforce the compression segment size
+                 * to a power of 2
+                 */
+                sblkno = offset >> lsp->ls_comp_seg_shift;
+                sblkoff = offset & (lsp->ls_uncomp_seg_sz - 1);
+                eblkno = (offset + bp->b_bcount) >> lsp->ls_comp_seg_shift;
+                eblkoff = (offset + bp->b_bcount) & (lsp->ls_uncomp_seg_sz - 1);
+
+                /*
+                 * Align start offset to block boundary for segmap
+                 */
+                salign = lsp->ls_comp_seg_index[sblkno];
+                sdiff = salign & (DEV_BSIZE - 1);
+                salign -= sdiff;
+                if (eblkno >= (lsp->ls_comp_index_sz - 1)) {
+                        /*
+                         * We're dealing with the last segment of
+                         * the compressed file -- the size of this
+                         * segment *may not* be the same as the
+                         * segment size for the file
+                         */
+                        eblkoff = (offset + bp->b_bcount) &
+                            (lsp->ls_uncomp_last_seg_sz - 1);
+                        ealign = lsp->ls_vp_comp_size;
         } else {
+                        ealign = lsp->ls_comp_seg_index[eblkno + 1];
+                }
+
+                /*
+                 * Preserve original request paramaters
+                 */
+                oblkcount = bp->b_bcount;
+
+                /*
+                 * Assign the calculated parameters
+                 */
+                comp_data_sz = ealign - salign;
+                bp->b_bcount = comp_data_sz;
+
+                /*
+                 * Allocate fixed size memory blocks to hold one
+                 * compressed and uncompressed segment since we
+                 * uncompress segments one at a time
+                 */
+                compressed_seg = kmem_alloc(bp->b_bcount, KM_SLEEP);
+                uncompressed_seg = kmem_alloc(lsp->ls_uncomp_seg_sz, KM_SLEEP);
+                /*
+                 * Map in the calculated number of blocks
+                 */
+                error = lofi_mapped_rdwr((caddr_t)compressed_seg, salign,
+                    bp, lsp);
+
+                bp->b_bcount = oblkcount;
+                bp->b_resid = oblkcount;
+                if (error != 0)
+                        goto done;
+
+                /*
+                 * We have the compressed blocks, now uncompress them
+                 */
+                cmpbuf = compressed_seg + sdiff;
+                for (i = sblkno; i < (eblkno + 1) && i < lsp->ls_comp_index_sz;
+                    i++) {
+                        /*
+                         * Each of the segment index entries contains
+                         * the starting block number for that segment.
+                         * The number of compressed bytes in a segment
+                         * is thus the difference between the starting
+                         * block number of this segment and the starting
+                         * block number of the next segment.
+                         */
+                        if ((i == eblkno) &&
+                            (i == lsp->ls_comp_index_sz - 1)) {
+                                cmpbytes = lsp->ls_vp_comp_size -
+                                    lsp->ls_comp_seg_index[i];
+                        } else {
+                                cmpbytes = lsp->ls_comp_seg_index[i + 1] -
+                                    lsp->ls_comp_seg_index[i];
+                        }
+
+                        /*
+                         * The first byte in a compressed segment is a flag
+                         * that indicates whether is this segment is
+                         * compressed at all
+                         */
+                        if (*cmpbuf == UNCOMPRESSED) {
+                                bcopy((cmpbuf + SEGHDR), uncompressed_seg,
+                                    (cmpbytes - SEGHDR));
+                        } else {
+                                seglen = lsp->ls_uncomp_seg_sz;
+
+                                if (li->l_decompress((cmpbuf + SEGHDR),
+                                    (cmpbytes - SEGHDR), uncompressed_seg,
+                                    &seglen, li->l_level) != 0) {
+                                        error = EIO;
+                                        goto done;
+                                }
+                        }
+
+                        /*
+                         * Determine how much uncompressed data we
+                         * have to copy and copy it
+                         */
+                        xfersize = lsp->ls_uncomp_seg_sz - sblkoff;
+                        if (i == eblkno) {
+                                if (i == (lsp->ls_comp_index_sz - 1))
+                                        xfersize -= (lsp->ls_uncomp_last_seg_sz
+                                            - eblkoff);
+                                else
+                                        xfersize -=
+                                            (lsp->ls_uncomp_seg_sz - eblkoff);
+                        }
+
+                        bcopy((uncompressed_seg + sblkoff), bufaddr, xfersize);
+
+                        cmpbuf += cmpbytes;
+                        bufaddr += xfersize;
+                        bp->b_resid -= xfersize;
+                        sblkoff = 0;
+
+                        if (bp->b_resid == 0)
+                                break;
+                }
+        } else {
                 ssize_t resid;
                 enum uio_rw rw;
 
                 if (bp->b_flags & B_READ)
                         rw = UIO_READ;

@@ -433,10 +633,16 @@
                 error = vn_rdwr(rw, lsp->ls_vp, bufaddr, bp->b_bcount,
                     offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid);
                 bp->b_resid = resid;
         }
 
+done:
+        if (compressed_seg != NULL)
+                kmem_free(compressed_seg, comp_data_sz);
+        if (uncompressed_seg != NULL)
+                kmem_free(uncompressed_seg, lsp->ls_uncomp_seg_sz);
+
         if (lsp->ls_kstat) {
                 size_t n_done = bp->b_bcount - bp->b_resid;
                 kstat_io_t *kioptr;
 
                 mutex_enter(lsp->ls_kstat->ks_lock);

@@ -629,11 +835,11 @@
                 kmem_free(klip, sizeof (struct lofi_ioctl));
                 return (NULL);
         }
 
         /* make sure filename is always null-terminated */
-        klip->li_filename[MAXPATHLEN] = '\0';
+        klip->li_filename[MAXPATHLEN - 1] = '\0';
 
         /* validate minor number */
         if (klip->li_minor > lofi_max_files) {
                 kmem_free(klip, sizeof (struct lofi_ioctl));
                 return (NULL);

@@ -751,11 +957,20 @@
         lsp->ls_vtoc.v_version = V_VERSION;
         bcopy(LOFI_DRIVER_NAME, lsp->ls_vtoc.v_volume, 7);
         lsp->ls_vtoc.v_sectorsz = DEV_BSIZE;
         lsp->ls_vtoc.v_nparts = 1;
         lsp->ls_vtoc.v_part[0].p_tag = V_UNASSIGNED;
+
+        /*
+         * A compressed file is read-only, other files can
+         * be read-write
+         */
+        if (lsp->ls_uncomp_seg_sz > 0) {
+                lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT | V_RONLY;
+        } else {
         lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT;
+        }
         lsp->ls_vtoc.v_part[0].p_start = (daddr_t)0;
         /*
          * The partition size cannot just be the number of sectors, because
          * that might not end on a cylinder boundary. And if that's the case,
          * newfs/mkfs will print a scary warning. So just figure the size

@@ -786,10 +1001,131 @@
          */
         lsp->ls_ci.dki_maxtransfer = 16;
 }
 
 /*
+ * map in a compressed file
+ *
+ * Read in the header and the index that follows.
+ *
+ * The header is as follows -
+ *
+ * Signature (name of the compression algorithm)
+ * Compression segment size (a multiple of 512)
+ * Number of index entries
+ * Size of the last block
+ * The array containing the index entries
+ *
+ * The header information is always stored in
+ * network byte order on disk.
+ */
+static int
+lofi_map_compressed_file(struct lofi_state *lsp, char *buf)
+{
+        uint32_t index_sz, header_len, i;
+        ssize_t resid;
+        enum uio_rw rw;
+        char *tbuf = buf;
+        int error;
+
+        /* The signature has already been read */
+        tbuf += lsp->ls_comp_algorithm_len;
+        bcopy(tbuf, &(lsp->ls_uncomp_seg_sz), sizeof (lsp->ls_uncomp_seg_sz));
+        lsp->ls_uncomp_seg_sz = ntohl(lsp->ls_uncomp_seg_sz);
+
+        /*
+         * The compressed segment size must be a power of 2
+         */
+        if (lsp->ls_uncomp_seg_sz % 2)
+                return (EINVAL);
+
+        for (i = 0; !((lsp->ls_uncomp_seg_sz >> i) & 1); i++)
+                ;
+
+        lsp->ls_comp_seg_shift = i;
+
+        tbuf += sizeof (lsp->ls_uncomp_seg_sz);
+        bcopy(tbuf, &(lsp->ls_comp_index_sz), sizeof (lsp->ls_comp_index_sz));
+        lsp->ls_comp_index_sz = ntohl(lsp->ls_comp_index_sz);
+
+        tbuf += sizeof (lsp->ls_comp_index_sz);
+        bcopy(tbuf, &(lsp->ls_uncomp_last_seg_sz),
+            sizeof (lsp->ls_uncomp_last_seg_sz));
+        lsp->ls_uncomp_last_seg_sz = ntohl(lsp->ls_uncomp_last_seg_sz);
+
+        /*
+         * Compute the total size of the uncompressed data
+         * for use in fake_disk_geometry and other calculations.
+         * Disk geometry has to be faked with respect to the
+         * actual uncompressed data size rather than the
+         * compressed file size.
+         */
+        /* XXX '2' shouldn't subtracted here - should be '1' */
+        lsp->ls_vp_size = (lsp->ls_comp_index_sz - 2) * lsp->ls_uncomp_seg_sz
+            + lsp->ls_uncomp_last_seg_sz;
+
+        /*
+         * Index size is rounded up to a 512 byte boundary for ease
+         * of segmapping
+         */
+        index_sz = sizeof (lsp->ls_comp_seg_index) * lsp->ls_comp_index_sz;
+        header_len = lsp->ls_comp_algorithm_len +
+            sizeof (lsp->ls_uncomp_seg_sz) +
+            sizeof (lsp->ls_comp_index_sz) +
+            sizeof (lsp->ls_uncomp_last_seg_sz);
+        lsp->ls_comp_offbase = header_len + index_sz;
+
+        index_sz += header_len;
+        index_sz = roundup(index_sz, DEV_BSIZE);
+
+        lsp->ls_comp_index_data = kmem_alloc(index_sz, KM_SLEEP);
+        lsp->ls_comp_index_data_sz = index_sz;
+
+        /*
+         * Read in the index -- this has a side-effect
+         * of reading in the header as well
+         */
+        rw = UIO_READ;
+        error = vn_rdwr(rw, lsp->ls_vp, lsp->ls_comp_index_data, index_sz,
+            0, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid);
+
+        if (error != 0)
+                return (error);
+
+        /* Skip the header, this is where the index really begins */
+        lsp->ls_comp_seg_index =
+            /*LINTED*/
+            (uint64_t *)(lsp->ls_comp_index_data + header_len);
+
+        /* Now map the index into memory */
+        for (i = 0; i < lsp->ls_comp_index_sz; i++)
+                lsp->ls_comp_seg_index[i] = lsp->ls_comp_offbase +
+                    lsp->ls_comp_seg_index[i];
+
+        return (error);
+}
+
+/*
+ * Check to see if the passed in signature is a valid
+ * one. If it is valid, return the index into
+ * lofi_compress_table.
+ *
+ * Return -1 if it is invalid
+ */
+static int lofi_compress_select(char *signature)
+{
+        int i;
+
+        for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
+                if (strcmp(lofi_compress_table[i].l_name, signature) == 0)
+                        return (i);
+        }
+
+        return (-1);
+}
+
+/*
  * map a file to a minor number. Return the minor number.
  */
 static int
 lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
     int *rvalp, struct cred *credp, int ioctl_flag)

@@ -799,16 +1135,21 @@
         struct lofi_ioctl *klip;
         int     error;
         struct vnode *vp;
         int64_t Nblocks_prop_val;
         int64_t Size_prop_val;
+        int     compress_index;
         vattr_t vattr;
         int     flag;
         enum vtype v_type;
         int zalloced = 0;
         dev_t   newdev;
         char    namebuf[50];
+        char    buf[DEV_BSIZE];
+        char    *tbuf;
+        ssize_t resid;
+        enum uio_rw rw;
 
         klip = copy_in_lofi_ioctl(ulip, ioctl_flag);
         if (klip == NULL)
                 return (EFAULT);
 

@@ -863,11 +1204,11 @@
                 if (error) {
                         goto out;
                 }
         }
         vattr.va_mask = AT_SIZE;
-        error = VOP_GETATTR(vp, &vattr, 0, credp, NULL);
+        error = VOP_GETATTR(vp, &vattr, 0, credp);
         if (error) {
                 goto closeout;
         }
         /* the file needs to be a multiple of the block size */
         if ((vattr.va_size % DEV_BSIZE) != 0) {

@@ -935,11 +1276,11 @@
 
         /*
          * Try to handle stacked lofs vnodes.
          */
         if (vp->v_type == VREG) {
-                if (VOP_REALVP(vp, &lsp->ls_vp, NULL) != 0) {
+                if (VOP_REALVP(vp, &lsp->ls_vp) != 0) {
                         lsp->ls_vp = vp;
                 } else {
                         /*
                          * Even though vp was obtained via vn_open(), we
                          * can't call vn_close() on it, since lofs will

@@ -960,10 +1301,52 @@
         (void) strcpy(lsp->ls_filename, klip->li_filename);
         if (rvalp)
                 *rvalp = (int)newminor;
         klip->li_minor = newminor;
 
+        /*
+         * Read the file signature to check if it is compressed.
+         * 'rw' is set to read since only reads are allowed to
+         * a compressed file.
+         */
+        rw = UIO_READ;
+        error = vn_rdwr(rw, lsp->ls_vp, buf, DEV_BSIZE, 0, UIO_SYSSPACE,
+            0, RLIM64_INFINITY, kcred, &resid);
+
+        if (error != 0)
+                goto propout;
+
+        tbuf = buf;
+        lsp->ls_uncomp_seg_sz = 0;
+        lsp->ls_vp_comp_size = lsp->ls_vp_size;
+        lsp->ls_comp_algorithm_len = 0;
+
+        compress_index = lofi_compress_select(tbuf);
+        if (compress_index != -1) {
+                lsp->ls_comp_algorithm_index = compress_index;
+                lsp->ls_comp_algorithm_len =
+                    strlen(lofi_compress_table[compress_index].l_name);
+                error = lofi_map_compressed_file(lsp, buf);
+                if (error != 0)
+                        goto propout;
+
+                /* update DDI properties */
+                Size_prop_val = lsp->ls_vp_size;
+                if ((ddi_prop_update_int64(newdev, lofi_dip, SIZE_PROP_NAME,
+                    Size_prop_val)) != DDI_PROP_SUCCESS) {
+                        error = EINVAL;
+                        goto propout;
+                }
+
+                Nblocks_prop_val = lsp->ls_vp_size / DEV_BSIZE;
+                if ((ddi_prop_update_int64(newdev, lofi_dip, NBLOCKS_PROP_NAME,
+                    Nblocks_prop_val)) != DDI_PROP_SUCCESS) {
+                        error = EINVAL;
+                        goto propout;
+                }
+        }
+
         fake_disk_geometry(lsp);
         mutex_exit(&lofi_lock);
         (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
         free_lofi_ioctl(klip);
         return (0);

@@ -970,11 +1353,11 @@
 
 propout:
         (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME);
         (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME);
 closeout:
-        (void) VOP_CLOSE(vp, flag, 1, 0, credp, NULL);
+        (void) VOP_CLOSE(vp, flag, 1, 0, credp);
         VN_RELE(vp);
 out:
         if (zalloced)
                 ddi_soft_state_free(lofi_statep, newminor);
         mutex_exit(&lofi_lock);

@@ -1034,11 +1417,11 @@
                         mutex_enter(&lsp->ls_vp_lock);
                         lsp->ls_vp_closereq = B_TRUE;
                         while (lsp->ls_vp_iocount > 0)
                                 cv_wait(&lsp->ls_vp_cv, &lsp->ls_vp_lock);
                         (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0,
-                            credp, NULL);
+                            credp);
                         VN_RELE(lsp->ls_vp);
                         lsp->ls_vp = NULL;
                         cv_broadcast(&lsp->ls_vp_cv);
                         mutex_exit(&lsp->ls_vp_lock);
                         mutex_exit(&lofi_lock);

@@ -1050,10 +1433,15 @@
                 mutex_exit(&lofi_lock);
                 free_lofi_ioctl(klip);
                 return (EBUSY);
         }
 
+        if (lsp->ls_uncomp_seg_sz > 0) {
+                kmem_free(lsp->ls_comp_index_data, lsp->ls_comp_index_data_sz);
+                lsp->ls_uncomp_seg_sz = 0;
+        }
+
         lofi_free_handle(dev, minor, lsp, credp);
 
         klip->li_minor = minor;
         mutex_exit(&lofi_lock);
         (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag);

@@ -1093,10 +1481,16 @@
                         mutex_exit(&lofi_lock);
                         free_lofi_ioctl(klip);
                         return (ENXIO);
                 }
                 (void) strcpy(klip->li_filename, lsp->ls_filename);
+                if (lsp->ls_comp_algorithm_len == 0)
+                        klip->li_algorithm[0] = '\0';
+                else
+                        (void) strlcpy(klip->li_algorithm, lofi_compress_table[
+                            lsp->ls_comp_algorithm_index].l_name,
+                            lsp->ls_comp_algorithm_len + 1);
                 mutex_exit(&lofi_lock);
                 error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
                 free_lofi_ioctl(klip);
                 return (error);
         case LOFI_GET_MINOR:

@@ -1108,10 +1502,38 @@
                         return (ENOENT);
                 }
                 error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
                 free_lofi_ioctl(klip);
                 return (error);
+        case LOFI_CHECK_COMPRESSED:
+                mutex_enter(&lofi_lock);
+                klip->li_minor = file_to_minor(klip->li_filename);
+                mutex_exit(&lofi_lock);
+                if (klip->li_minor == 0) {
+                        free_lofi_ioctl(klip);
+                        return (ENOENT);
+                }
+                mutex_enter(&lofi_lock);
+                lsp = ddi_get_soft_state(lofi_statep, klip->li_minor);
+                if (lsp == NULL) {
+                        mutex_exit(&lofi_lock);
+                        free_lofi_ioctl(klip);
+                        return (ENXIO);
+                }
+                ASSERT(strcmp(klip->li_filename, lsp->ls_filename) == 0);
+
+                if (lsp->ls_comp_algorithm_len == 0)
+                        klip->li_algorithm[0] = '\0';
+                else
+                        (void) strlcpy(klip->li_algorithm, lofi_compress_table[
+                            lsp->ls_comp_algorithm_index].l_name,
+                            lsp->ls_comp_algorithm_len + 1);
+
+                mutex_exit(&lofi_lock);
+                error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
+                free_lofi_ioctl(klip);
+                return (error);
         default:
                 free_lofi_ioctl(klip);
                 return (EINVAL);
         }
 

@@ -1167,10 +1589,13 @@
                         error = ddi_copyout(&lofi_max_files, &lip->li_minor,
                             sizeof (lofi_max_files), flag);
                         if (error)
                                 return (EFAULT);
                         return (0);
+                case LOFI_CHECK_COMPRESSED:
+                        return (lofi_get_info(dev, lip, LOFI_CHECK_COMPRESSED,
+                            credp, flag));
                 default:
                         break;
                 }
         }