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;
}
}