xmem_vnops.c revision d5dbd18d69de8954ab5ceb588e99d43fc9b21d46
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/pathname.h>
#ifndef lint
int, struct cred *);
#endif
/* ARGSUSED1 */
static int
{
/*
* swapon to a xmemfs file is not supported so access
* is denied on open if VISSWAP is set.
*/
return (EINVAL);
return (0);
}
/* ARGSUSED1 */
static int
{
return (0);
}
/*
* wrxmem does the real work of write requests for xmemfs.
*/
static int
{
int error = 0;
/*
* xp->xn_size is incremented before the uiomove
* is done on a write. If the move fails (bad user
* address) reset xp->xn_size.
* The better way would be to increment xp->xn_size
* only if the uiomove succeeds.
*/
long xn_size_changed = 0;
/*
* xmem_getattr ends up being called by chklock
*/
if (error != 0) {
return (error);
}
}
return (EINVAL);
mutex_enter(&p->p_lock);
p, RCA_UNSAFE_SIGINFO);
mutex_exit(&p->p_lock);
return (EFBIG);
}
return (0);
}
/*
* Get the highest blocknumber and allocate page array if needed.
* Note that if xm_bsize != PAGESIZE, each ppa[] is pointer to
* a page array rather than just a page.
*/
/* file size increase */
int ppasz;
/*
* check if sufficient blocks available for the given offset.
*/
return (ENOSPC);
/*
* to prevent reallocating every time the file grows by a
* single block, double the size of the array.
*/
else
}
/*
* fill in the 'hole' if write offset beyond file size. This
* helps in creating large files quickly; an application can
* lseek to a large offset and perform a single write
* operation to create the large file.
*/
if (blksinfile < blkwr) {
if (error) {
/* truncate file back to original size */
return (error);
}
/*
* if error on blkwr, this allows truncation of the
* filled hole.
*/
}
}
do {
/*
* A maximum of xm->xm_bsize bytes of data is transferred
* each pass through this loop
*/
goto out;
}
}
/* zero fill new pages - simplify partial updates */
if (error)
return (error);
}
/* grow the file to the new length */
xn_size_changed = 1;
}
#ifdef LOCKNEST
xmem_getpage();
#endif
/* xn_ppa[] is a page_t * if ppb == 1 */
else
/*
* subtract 1 in case (offset + bytes) is mod PAGESIZE
* so that pageend is the actual index of last page.
*/
/*
* Re-acquire contents lock.
*/
/*
* If the uiomove failed, fix up xn_size.
*/
if (error) {
if (xn_size_changed) {
/*
* The uiomove failed, and we
* allocated blocks,so get rid
* of them.
*/
}
} else {
!= 0) {
/*
* Clear Set-UID & Set-GID bits on
* successful write if not privileged
* and at least one of the execute bits
* is set. If we always clear Set-GID,
* mandatory file and record locking is
* unuseable.
*/
}
gethrestime(&now);
}
blkwr++;
out:
/*
* If we've already done a partial-write, terminate
* the write but return no error.
*/
error = 0;
return (error);
}
/*
* rdxmem does the real work of read requests for xmemfs.
*/
static int
struct caller_context *ct)
{
int error;
/*
* xmem_getattr ends up being called by chklock
*/
if (error != 0) {
XMEMPRINTF(1,
return (error);
}
}
return (0);
}
if (offset < 0)
return (EINVAL);
return (0);
}
do {
/*
* A maximum of xm->xm_bsize bytes of data is transferred
* each pass through this loop
*/
if (diff <= 0) {
error = 0;
goto out;
}
return (error);
}
/*
* We have to drop the contents lock to prevent the VM
* system from trying to reacquire it in xmem_getpage()
* should the uiomove cause a pagefault.
*/
#ifdef LOCKNEST
xmem_getpage();
#endif
/* 2/10 panic in hat_memload_array - len & MMU_OFFSET */
else {
}
/*
* Re-acquire contents lock.
*/
blocknumber++;
out:
/*
* If we've already done a partial read, terminate
* the read but return no error.
*/
error = 0;
return (error);
}
/* ARGSUSED2 */
static int
struct caller_context *ct)
{
int error;
/*
* We don't currently support reading non-regular files
*/
return (EINVAL);
/*
* xmem_rwlock should have already been called from layers above
*/
return (error);
}
static int
struct caller_context *ct)
{
int error;
/*
* We don't currently support writing to non-regular files
*/
return (EINVAL); /* XXX EISDIR? */
/*
* xmem_rwlock should have already been called from layers above
*/
/*
* In append mode start at end of file.
*/
}
return (error);
}
/* ARGSUSED */
static int
{
return (ENOTTY);
}
/* ARGSUSED2 */
static int
{
return (0);
}
/*ARGSUSED*/
static int
{
int error;
/*
* Cannot set these attributes
*/
return (EINVAL);
xmem_xaccess, xp);
if (error != 0)
goto out;
/*
* Change file access modes.
*/
/* prevent execute permission to be set for regular files */
}
goto out;
}
/* Don't support large files. */
goto out;
}
goto out;
goto out1;
}
out:
out1:
return (error);
}
/* ARGSUSED2 */
static int
{
int error;
return (error);
}
/* ARGSUSED3 */
static int
{
int error;
/*
* Null component name is a synonym for directory being searched.
*/
if (*nm == '\0') {
return (0);
}
if (error == 0) {
/*
* If vnode is a device return special vnode instead
*/
cred);
}
}
return (error);
}
/*ARGSUSED7*/
static int
int flag)
{
int error;
error = 0;
/* Must be privileged to set sticky bit */
if (secpolicy_vnode_stky_modify(cred) != 0)
return (EINVAL);
}
/*
* Null component name is a synonym for directory being searched.
*/
if (*nm == '\0') {
} else {
}
if (error == 0) { /* name found */
/*
* directory, allow it
*/
else {
}
if (error) {
return (error);
}
}
cred);
}
return (0);
}
return (error);
if (error) {
if (self)
/*
* This means that the file was created sometime
* after we checked and did not find it and when
* we went to create it.
* Since creat() is supposed to truncate a file
* that already exits go back to the begining
* of the function. This time we will find it
* and go down the xmem_trunc() path
*/
goto again;
}
return (error);
}
cred);
}
return (0);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
return (EPERM);
if (error == 0) {
return (EEXIST);
}
return (error);
return (error);
}
static int
char *onm, /* source name */
char *nnm, /* destination name */
{
struct xmemnode *fromparent;
int error;
int samedir = 0; /* set if odvp == ndvp */
/*
* Look up xmemnode of file we're supposed to rename.
*/
if (error) {
return (error);
}
/*
* Make sure we can delete the old (source) entry. This
* requires write permission on the containing directory. If
* that directory is "sticky" it further requires (except for
* for privileged users) that the user own the directory or
* the source entry, or else have permission to write the
* source entry.
*/
goto done;
/*
* Check for renaming to or from '.' or '..' or that
* fromxp == fromparent
*/
if ((onm[0] == '.' &&
(nnm[0] == '.' &&
(fromparent == fromxp)) {
goto done;
}
/*
* Make sure we can search and rename into the new
* (destination) directory.
*/
if (!samedir) {
if (error)
goto done;
}
/*
* Link source to new target
*/
if (error) {
/*
* ESAME isn't really an error; it indicates that the
* operation should not be done because the source and target
* are the same file, but that no error should be reported.
*/
error = 0;
goto done;
}
/*
* Unlink from source.
*/
/*
* The following handles the case where our source xmemnode was
* removed before we got to it.
*
* XXX We should also cleanup properly in the case where xdirdelete
* fails for some other reason. Currently this case shouldn't happen.
* (see 1184991).
*/
error = 0;
done:
return (error);
}
static int
{
int error;
/*
* Might be dangling directory. Catch it here,
* because a ENOENT return from xdirlookup() is
* an "o.k. return".
*/
return (ENOENT);
if (error == 0) {
return (EEXIST);
}
return (error);
if (error) {
if (self)
return (error);
}
return (0);
}
static int
{
int error = 0;
/*
* Return error when removing . and ..
*/
return (EINVAL);
return (EEXIST); /* Should be ENOTEMPTY */
if (error)
return (error);
goto done1;
}
goto done1;
}
goto done1;
}
if (vn_vfswlock(vp)) {
goto done1;
}
goto done;
}
/*
* Check for an empty directory
* i.e. only includes entries for "." and ".."
*/
/*
* Update atime because checking xn_dirents is logically
* equivalent to reading the directory
*/
goto done;
}
done:
return (error);
}
/* ARGSUSED2 */
static int
{
int error;
register ulong_t total_bytes_wanted;
register long outcount = 0;
register long bufsize;
int reclen;
if (eofp)
*eofp = 1;
return (0);
}
/*
* assuming system call has already called xmem_rwlock
*/
return (EINVAL);
return (ENOTDIR);
/*
* There's a window here where someone could have removed
* all the entries in the directory after we put a hold on the
* vnode but before we grabbed the rwlock. Just return unless
* there are still references to the current file in which case panic.
*/
return (0);
}
/*
* Get space for multiple directory entries
*/
offset = 0;
while (xdp) {
break;
/* use strncpy(9f) to zero out uninitialized bytes */
}
}
if (!error) {
/* If we reached the end of the list our offset */
/* should now be just past the end. */
if (!xdp) {
offset += 1;
if (eofp)
*eofp = 1;
} else if (eofp)
*eofp = 0;
}
return (error);
}
static int
{
int error;
if (error == 0) {
/*
* The entry already exists
*/
return (EEXIST); /* was 0 */
}
return (error);
}
if (error) {
if (self)
return (error);
}
return (ENOSPC);
}
return (error);
}
/* ARGSUSED2 */
static int
{
int error = 0;
return (EINVAL);
return (error);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static void
{
top:
/*
* If we don't have the last hold or the link count is non-zero,
* there's little to do -- just drop our hold.
*/
return;
}
/*
* We have the last hold *and* the link count is zero, so this
* xmemnode is dead from the filesystem's viewpoint. However,
* if the xmemnode has any pages associated with it (i.e. if it's
* a normal file with non-zero size), the xmemnode can still be
* discovered by pageout or fsflush via the page vnode pointers.
* In this case we must drop all our locks, truncate the xmemnode,
* and try the whole dance again.
*/
goto top;
}
}
else
}
static int
{
return (ENOSPC);
}
return (0);
}
/*
* Return all the pages from [off..off+len] in given file
*/
static int
{
int err = 0;
return (EFAULT);
}
else
gethrestime(&now);
return (err);
}
/*
* Called from pvn_getpages to get a particular page.
*/
/*ARGSUSED*/
static int
{
return (0);
}
/* ARGSUSED */
int
{
return (0);
}
#ifndef lint
/*
* Write out a single page.
* For xmemfs this means choose a physical swap slot and write the page
* out using VOP_PAGEIO. For performance, we attempt to kluster; i.e.,
* we try to find a bunch of other dirty pages adjacent in the file
* and a bunch of contiguous swap slots, and then write all the pages
* out in a single i/o.
*/
/*ARGSUSED*/
static int
{
return (1);
}
#endif
static int
{
struct segxmem_crargs xmem_a;
int error;
#ifdef lint
#endif
return (ENOSYS);
if (off < 0)
return (EINVAL);
/* offset, length and address has to all be block aligned */
return (EINVAL);
}
return (ENODEV);
if (flags & MAP_PRIVATE)
return (EINVAL); /* XXX need to be handled */
/*
* Don't allow mapping to locked file
*/
return (EAGAIN);
}
return (error);
}
/*
* User specified address - blow away any previous mappings
*/
/*
* Fast path. segxmem_remap will fail if this is the wrong
* segment or if the len is beyond end of seg. If it fails,
* we do the regular stuff thru as_* routines.
*/
return (0);
}
if (seg)
if (error != RANGE_OKAY ||
return (EINVAL);
}
} else {
}
return (ENOMEM);
}
return (error);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
{
return (0);
}
static int
{
register int i;
int error;
return (EINVAL);
return (0);
}
/*
* Check for any mandatory locks on the range
*/
long save_start;
/*
* "Truncate up" case: need to make sure there
* is no lock beyond current end-of-file. To
* do so, we need to set l_start to the size
* of the file temporarily.
*/
}
return (i ? i : EAGAIN);
}
}
return (error);
}
/* ARGSUSED */
static int
{
int error;
return (EINVAL);
return (EFBIG);
}
return (error);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED2 */
static int
{
if (write_lock) {
} else {
}
return (write_lock);
}
/* ARGSUSED1 */
static void
{
}
struct vnodeops *xmem_vnodeops;
const fs_operation_def_t xmem_vnodeops_template[] = {
};