/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#include <sys/sysmacros.h>
#include <sys/vfs_opreg.h>
#include <vm/seg_kmem.h>
/*
* dcfs - A filesystem for automatic decompressing of fiocompressed files
*
* This filesystem is a layered filesystem that sits on top of a normal
* persistent filesystem and provides automatic decompression of files
* that have been previously compressed and stored on the host file system.
* This is a pseudo filesystem in that it does not persist data, rather it
* intercepts file lookup requests on the host filesystem and provides
* transparent decompression of those files. Currently the only supported
* host filesystem is ufs.
*
* A file is compressed via a userland utility (currently cmd/boot/fiocompress)
* and marked by fiocompress as a compressed file via a flag in the on-disk
* inode (set via a ufs ioctl() - see `ufs_vnops.c`ufs_ioctl()`_FIO_COMPRESSED
* ufs_lookup checks for this flag and if set, passes control to decompvp
* a function defined in this (dcfs) filesystem. decomvp uncompresses the file
* and returns a dcfs vnode to the VFS layer.
*
* dcfs is layered on top of ufs and passes requests involving persistence
* to the underlying ufs filesystem. The compressed files currently cannot be
* written to.
*/
/*
* Define data structures within this file.
*/
#else
#endif
static int dclru_len;
static int dcinit(int, char *);
static struct dcnode *dcnode_alloc(void);
static void dcnode_free(struct dcnode *);
static void dcnode_recycle(struct dcnode *);
/*
* This is the loadable module wrapper.
*/
"dcfs",
};
/*
* Module linkage information for the kernel.
*/
};
};
int
_init()
{
return (mod_install(&modlinkage));
}
int
{
}
struct cred *, caller_context_t *);
struct caller_context *);
struct cred *, caller_context_t *);
struct caller_context *);
struct cred *, caller_context_t *);
struct cred *, caller_context_t *);
struct cred *, caller_context_t *);
};
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static int
struct caller_context *ct)
{
int error;
/*
* Loop through file with segmap, decompression will occur
* in dc_getapage
*/
do {
size_t n;
/*
* read to end of block or file
*/
if (n == 0)
return (0); /* at EOF */
S_READ);
if (!error) {
flags = SM_DONTNEED;
else
flags = 0;
} else
return (error);
}
static int
{
int error;
/* substitute uncompressed size */
return (error);
}
static int
{
}
static int
{
}
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static void
{
/*
* Somebody accessed the dcnode before we got a chance to
* remove it. They will remove it when they do a vn_rele.
*/
return;
}
}
static int
{
}
static int
{
}
static int
{
int error;
/*
* If file is being mapped, disallow frlock.
*/
return (error);
return (EAGAIN);
}
/*ARGSUSED*/
static int
{
long xlen;
/*
* Get destination pages and make them addressable
*/
/*
* read compressed data from subordinate vnode
*/
if (error)
goto cleanup;
/*
* Uncompress
*/
goto cleanup;
}
/*
* Handle EOF
*/
/*
* Clean up
*/
return (error);
}
static int
{
int rdblk;
/*
* pvn_read_kluster() doesn't quite do what we want, since it
* thinks sub block reads are ok. Here we always decompress
* a full block.
*/
/*
* Check page cache
*/
rdblk = 0;
rdblk = 1;
break;
}
}
if (!rdblk) {
return (0); /* all pages in cache */
}
/*
* Undo any locks so getblock_miss has an open field
*/
}
static int
{
return (0);
}
/*ARGSUSED10*/
static int
{
/* does not support write */
panic("write attempt on compressed file");
/*NOTREACHED*/
}
if (protp)
/*
* We don't support asynchronous operation at the moment, so
* just pretend we did it. If the pages are ever actually
* needed, they'll get brought in then.
*/
return (0);
/*
* Calc block start and end offsets
*/
while (nblks--) {
}
if (!error)
else
return (error);
}
/*
* This function should never be called. We need to have it to pass
* it as an argument to other functions.
*/
/*ARGSUSED*/
static int
{
/* should never happen */
/*NOTREACHED*/
return (0);
}
/*
* The only flags we support are B_INVAL, B_FREE and B_DONTNEED.
* B_INVAL is set by:
*
* 1) the MC_SYNC command of memcntl(2) to support the MS_INVALIDATE flag.
* 2) the MC_ADVISE command of memcntl(2) with the MADV_DONTNEED advice
* which translates to an MC_SYNC with the MS_INVALIDATE flag.
*
* The B_FREE (as well as the B_DONTNEED) flag is set when the
* MADV_SEQUENTIAL advice has been used. VOP_PUTPAGE is invoked
* from SEGVN to release pages behind a pagefault.
*/
/*ARGSUSED5*/
static int
{
int error = 0;
panic("dcfs_putpage: bad v_count");
/*NOTREACHED*/
}
return (ENOSYS);
return (0);
if (len == 0) /* from 'off' to EOF */
else {
/*
* We insist on getting the page only if we are
* about to invalidate, free or write it and
* the B_ASYNC flag is not set.
*/
else
continue;
/*
* Normally pvn_getdirty() should return 0, which
* impies that it has done the job for us.
* The shouldn't-happen scenario is when it returns 1.
* This means that the page has been modified and
* needs to be put back.
* Since we can't write to a dcfs compressed file,
* we fake a failed I/O and force pvn_write_done()
* to destroy the page.
*/
}
}
}
return (error);
}
static int
{
int error;
return (ENOSYS);
return (ENXIO);
/*
* If file is being locked, disallow mapping.
*/
return (error);
return (EAGAIN);
return (ENOMEM);
}
} else {
/*
* User specified address - blow away any previous mappings
*/
}
return (error);
}
/*ARGSUSED*/
static int
{
return (ENOSYS);
return (0);
}
/*ARGSUSED*/
static int
{
return (ENOSYS);
return (0);
}
/*
* Constructor/destructor routines for dcnodes
*/
/*ARGSUSED1*/
static int
{
return (-1);
}
return (0);
}
/*ARGSUSED*/
static void
{
vn_invalid(vp);
}
static struct dcnode *
dcnode_alloc(void)
{
/*
* If the free list is above DCLRUSIZE
* re-use one from it
*/
} else {
}
return (dp);
}
static void
{
/*
* If no cached pages, no need to put it on lru
*/
if (!vn_has_cached_data(vp)) {
return;
}
/*
* Add to lru, if it's over the limit, free from head
*/
}
}
static void
{
}
static int
{
};
int error;
if (error) {
return (error);
}
dev = 0;
if (error != 0) {
(void) vfs_freevfsops_by_type(fstype);
return (error);
}
return (0);
}
/*
* Return shadow vnode with the given vp as its subordinate
*/
struct vnode *
{
int error;
/*
* See if we have an existing shadow
* If none, we have to manufacture one
*/
/*
* Make sure it's a valid compressed file
*/
return (NULL);
/* get underlying file size */
return (NULL);
/*
* Re-read entire header
*/
if (error) {
return (NULL);
}
/*
* add extra blkmap entry to make dc_getblock()'s
* life easier
*/
ndp = dcnode_alloc();
/*
* Allocate kmem cache if none there already
*/
/*
* Recheck table in case someone else created shadow
* while we were blocked above.
*/
}
}
/*
* dcnode lookup table
* These routines maintain a table of dcnodes hashed by their
* subordinate vnode so that they can be found if they already
* exist in the vnode cache
*/
/*
* Put a dcnode in the table.
*/
static void
{
}
/*
* Remove a dcnode from the hash table.
*/
void
{
else {
break;
}
}
}
}
/*
* Find a shadow vnode in the dctable hash list.
*/
static struct dcnode *
{
if (dp->dc_lrunext)
return (dp);
}
return (NULL);
}
#ifdef DEBUG
static int
dclru_count(void)
{
int i = 0;
return (0);
i++;
return (i + 1);
}
#endif
static void
{
/*
* Add to dclru as double-link chain
*/
} else {
}
dclru_len++;
}
static void
{
dclru_len--;
}