cachefs_ioctl.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/pathname.h>
#include <sys/sysmacros.h>
void cachefs_addhash(struct cnode *);
/*
* Local functions
*/
static void sync_metadata(cnode_t *);
static void drop_backvp(cnode_t *);
enum cachefs_rl_type type);
#if (defined(_SYSCALL32_IMPL) || defined(_LP64))
if (!error) { \
}
(vattr_t *)(out_vattrp))
#else /* not _SYSCALL32_IMPL || _LP64 */
#endif /* _SYSCALL32_IMPL || _LP64 */
/*
* Conjure up a credential from the partial credential stored in
* a file. This is bogus and cachefs should really be fixed, but
* this maintains maximum compatibility.
* dl_cred *cr points to a basic credential followed directly by a buffer that
* takes a number of groups.
*/
static cred_t *
{
return (newcr);
}
/*
* Pack a file in the cache
* dvp is the directory the file resides in.
* name is the name of the file.
* Returns 0 or an error if could not perform the operation.
*/
int
{
int error = 0;
int connected = 0;
/*
* Return if NFSv4 is the backfs (no caching).
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
for (;;) {
/* get access to the file system */
if (error)
break;
/* lookup the file name */
cr);
if (error == 0) {
}
connected = 0;
continue;
} else {
connected = 1;
continue;
}
}
break;
}
out:
return (error);
}
/*
* Packs the file belonging to the passed in vnode.
*/
int
{
int error = 0;
int buflen;
/* done if cannot write to cache */
goto out;
}
/* done if not usable */
goto out;
}
/* make sure up to date */
if (error)
goto out;
/* make it cachable */
/* get a metadata slot if we do not have one yet */
}
if (error)
goto out;
}
/* cache the ACL if necessary */
(cachefs_vtype_aclok(vp)) &&
if (error != 0)
goto out;
}
/* directory */
goto out;
goto out;
}
/* regular file */
goto out;
if (error)
goto out;
}
if (error)
goto out;
}
/* populate the file */
while (off < cnode_size) {
if (popsize != 0) {
if (error)
goto out;
else
}
}
}
}
/* symbolic link */
goto out;
/* get the sym link contents from the back fs */
if (error)
goto out;
/* try to cache the sym link */
}
/* assume that all other types fit in the attributes */
out:
/* get the rl slot if needed */
if (error == 0)
}
/* mark the file as packed */
if (error == 0) {
/* modified takes precedence over packed */
}
}
return (error);
}
/*
* Unpack a file from the cache
* dvp is the directory the file resides in.
* name is the name of the file.
* Returns 0 or an error if could not perform the operation.
*/
int
{
int error = 0;
int connected = 0;
/* Return error if NFSv4 is the backfs (no caching) */
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
for (;;) {
/* get access to the file system */
if (error)
break;
/* lookup the file name */
cr);
if (error == 0) {
}
connected = 0;
continue;
} else {
connected = 1;
continue;
}
}
break;
}
out:
return (error);
}
/*
* Unpacks the file belonging to the passed in vnode.
*/
static int
{
int error = 0;
/* nothing to do if not packed */
goto out;
/* nothing to do if cannot modify cache */
goto out;
}
/* mark file as no longer packed */
/* done if file has been modified */
goto out;
/* if there is no front file */
/* nuke front file resources */
}
/* else move the front file to the active list */
else {
}
out:
return (error);
}
/*
* Returns packing information on a file.
* dvp is the directory the file resides in.
* name is the name of the file.
* *statusp is set to the status of the file
* Returns 0 or an error if could not perform the operation.
*/
int
{
int error;
int connected = 0;
*statusp = 0;
/*
* Return if NFSv4 is the backfs (no caching).
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
for (;;) {
/* get access to the file system */
if (error)
break;
/* lookup the file name */
cr);
connected = 0;
continue;
} else {
connected = 1;
continue;
}
}
if (error)
break;
break;
}
out:
return (error);
}
/*
* Finds all packed files in the cache and unpacks them.
*/
int
{
int error;
/*
* Return if NFSv4 is the backfs (no caching).
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
goto out;
}
if (error)
goto out;
out:
return (error);
}
/*
* Finds all packed files on the specified list and unpacks them.
*/
static int
{
int error = 0;
for (;;) {
/* get the next entry on the specified resource list */
if (error) {
error = 0;
break;
}
/* if the fscp we have does not match */
if (fscp) {
}
/* get the file system cache object for this fsid */
if (error) {
"cachefs: cache error, run fsck\n");
break;
}
}
/* get access to the file system */
if (error) {
break;
}
}
/* get the cnode for the file */
if (error) {
#ifdef CFSDEBUG
printf("cachefs: cul: could not find %llu\n",
#endif
continue;
}
/* unpack the file */
}
/* free up allocated resources */
if (fscp) {
}
return (error);
}
/*
* Identifies this process as the cachefsd.
* Stays this way until close is done.
*/
int
/*ARGSUSED*/
{
int error = 0;
/* can only do this on the root of the file system */
/* else if there already is a daemon running */
else if (fscp->fs_cddaemonid)
/* else use the pid to identify the daemon */
else {
}
if (error == 0) {
/* the daemon that takes care of root is special */
}
}
return (error);
}
/*
* Returns the current state in doutp
*/
int
/*ARGSUSED*/
{
int state;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
switch (fscp->fs_cdconnected) {
case CFS_CD_CONNECTED:
break;
case CFS_CD_DISCONNECTED:
break;
case CFS_CD_RECONNECTING:
break;
default:
ASSERT(0);
break;
}
return (0);
}
/*
* Sets the state of the file system.
*/
int
/*ARGSUSED*/
{
int error = 0;
int nosig = 1;
/*
* State should not be changeable and always be connected if
* NFSv4 is in use.
*/
/* wait until the file system is quiet */
/* if someone is already changing the state */
return (0);
}
}
if (!nosig) {
fscp->fs_cdtransition = 0;
return (EINTR);
}
switch (state) {
case CFS_FS_CONNECTED:
/* done if already in this state */
break;
/* fix up modified files */
#if 0
printf("\ncachefs:server - %s",
fscp->fs_hostname);
printf("\ncachefs:mount point - %s",
printf("\ncachefs:back filesystem - %s",
printf("\nok\n");
#else
printf("cachefs: %s:%s ok\n",
else
printf("cachefs: server ok\n");
#endif
/* allow deletion of renamed open files to proceed */
break;
case CFS_FS_DISCONNECTED:
/* done if already in this state */
break;
/* drop all back vps */
#if 0
printf("\ncachefs:server - %s",
fscp->fs_hostname);
printf("\ncachefs:mount point - %s",
printf("\ncachefs:back filesystem - %s",
printf("\nnot responding still trying\n");
#else
printf("cachefs: %s:%s not responding still trying\n",
else
printf("cachefs: server not responding still trying\n");
#endif
break;
case CFS_FS_RECONNECTING:
/* done if already in this state */
break;
/*
* Before we enter disconnected state we sync all metadata,
* this allows us to read metadata directly in subsequent
* calls so we don't need to allocate cnodes when
* we just need metadata information.
*/
/* XXX bob: need to eliminate this */
/* no longer need dlog active */
break;
default:
break;
}
fscp->fs_cdtransition = 0;
return (error);
}
/*
* Blocks until the file system switches
* out of the connected state.
*/
int
/*ARGSUSED*/
{
int nosig = 1;
/*
* Only called in support of disconnectable operation, so assert
* that this is not used when NFSv4 is the backfilesytem.
*/
while (nosig &&
}
if (!nosig)
return (EINTR);
return (0);
}
/*
* Returns some statistics about the cache.
*/
int
/*ARGSUSED*/
{
fsblkcnt64_t avail = 0;
int error;
/* determine number of blocks available to the cache */
if (error == 0) {
blocks = (fsblkcnt64_t)0;
}
return (0);
}
/*
* This looks to see if the specified file exists in the cache.
* 0 is returned if it exists
* ENOENT is returned if it doesn't exist.
*/
int
/*ARGSUSED*/
{
int error;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* find the cnode of the file */
if (error)
return (ENOENT);
return (error);
}
/*
* Moves the specified file to the lost+found directory for the
* cached file system.
* Invalidates cached data and attributes.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* find the cnode of the file */
if (error) {
goto out;
}
/* must be regular file and modified */
goto out;
}
/* move to lost+found */
if (error == 0)
out:
if (cp)
return (error);
}
/*
* Given a cid, returns info about the file in the cache.
*/
int
{
u_offset_t blockoff = 0;
int offset = 0;
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* find the cnode of the file */
if (error) {
goto out;
}
goto out;
/* try to get the cnode of the parent dir */
if (error) {
error = 0;
goto out;
}
/* make sure a directory and populated */
error = 0;
goto out;
}
/* get the front file */
if (error) {
error = 0;
goto out;
}
/* make sure frontvp is still populated */
error = 0;
goto out;
}
}
/* Get the length of the directory */
if (error) {
error = 0;
goto out;
}
/* XXX bob: change this to use cachfs_dir_read */
/* We have found the parent, now we open the dir and look for file */
offset = 0;
if (error)
goto out;
offset);
sizeof (cfs_cid_t)) == 0)) {
/* found the name */
goto out;
}
}
}
out:
if (cp)
if (dcp)
return (error);
}
/*
* Given a file number, this functions returns the fid
* for the back file system.
* Returns ENOENT if file does not exist.
* Returns ENOMSG if fid is not valid, ie: local file.
*/
int
{
int error;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* get the cnode for the file */
if (error)
goto out;
/* if local file, fid is a local fid and is not valid */
goto out;
}
/* copy out the fid */
out:
if (cp)
return (error);
}
/*
* This performs a getattr on the back file system given
* a fid that is passed in.
*
* The backfid is in gafid->cg_backfid, the creds to use for
* this operation are in gafid->cg_cred. The attributes are
* returned in gafid->cg_attr
*
* the error returned is 0 if successful, nozero if not
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get a vnode for the back file */
if (error)
return (error);
/* VFS_VGET performs a VN_HOLD on the vp */
return (error);
}
/*
* This performs a getattr on the back file system. Instead of
* passing the fid to perform the gettr on we are given the
* parent directory fid and a name.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get a vnode for the parent directory */
if (error)
return (error);
/* lookup the file name */
if (error) {
return (error);
}
if (!error) {
}
return (error);
}
/*
* This will return the fid of the root of this mount point.
*/
int
/*ARGSUSED*/
{
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
return (0);
}
/*
* Pushes the data associated with a file back to the file server.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* get the backvp to push to */
if (error) {
goto out;
}
/* Get the cnode for the file we are to push back */
if (error) {
goto out;
}
/* must be a regular file */
goto out;
}
/* get the front file */
if (error) {
goto out;
}
}
/* better be populated */
goto out;
}
/* do open so NFS gets correct creds on writes */
if (error) {
goto out;
}
/* Read the data from the cache and write it to the server */
/* XXX why not use segmapio? */
off = 0;
else
/* read a block of data from the front file */
if (error) {
goto out;
}
/* write the block of data to the back file */
if (error) {
goto out;
}
}
if (error == 0)
if (error) {
goto out;
}
/*
* if we have successfully stored the data, we need the
* new ctime and mtimes.
*/
if (error)
goto out;
out:
if (buffer)
if (cp)
if (backvp)
if (cr)
return (error);
}
/*
* Create a file on the back file system.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* get a vnode for the parent directory */
if (error)
goto out;
/* do the create */
if (error)
goto out;
/* get the fid of the file */
if (error)
goto out;
/* get attributes for the file */
if (error)
goto out;
if (error)
goto out;
/* update the cnode for this file with the new info */
if (error) {
error = 0;
goto out;
}
out:
if (cr)
if (dvp)
if (cvp)
if (cp)
return (error);
}
/*
* Remove a file on the back file system.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get a vnode for the directory */
if (error) {
goto out;
}
/* if the caller wants the ctime after the remove */
if (ctimep) {
if (error == 0) {
}
if (error)
goto out;
}
/* do the remove */
if (error)
goto out;
/* get the new ctime if requested */
if (ctimep) {
if (error == 0) {
if (error == 0) {
}
}
}
out:
if (cr)
if (dvp)
return (error);
}
/*
* Perform a link on the back file system.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get a vnode parent directory */
if (error) {
goto out;
}
/* Get a vnode file to link to */
if (error) {
goto out;
}
/* do the link */
if (error)
goto out;
/* get the ctime */
if (error)
goto out;
if (error)
goto out;
out:
if (cr)
if (dvp)
if (lvp)
return (error);
}
/*
* Rename the file on the back file system.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get vnode of old parent directory */
if (error) {
goto out;
}
/* Get vnode of new parent directory */
if (error) {
goto out;
}
/* if the caller wants the ctime of the target after deletion */
if (rnp->rn_del_getctime) {
if (error) {
goto out;
}
if (error)
goto out;
}
/* do the rename */
NULL, 0);
if (error)
goto out;
/* get the new ctime on the renamed file */
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
/* get the new ctime if requested of the deleted target */
if (rnp->rn_del_getctime) {
if (error) {
goto out;
}
if (error)
goto out;
error);
if (error)
goto out;
}
out:
if (cr)
if (cvp)
if (odvp)
if (ndvp)
return (error);
}
/*
* Make a directory on the backfs.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get vnode of parent directory */
if (error) {
goto out;
}
/* make the directory */
if (error) {
goto out;
/* if the directory already exists, then use it */
if (error) {
goto out;
}
goto out;
}
}
/* get the fid of the directory */
if (error)
goto out;
/* get attributes of the directory */
if (error)
goto out;
/* update the cnode for this dir with the new fid */
if (error) {
error = 0;
goto out;
}
out:
if (cr)
if (dvp)
if (cvp)
if (cp)
return (error);
}
/*
* Perform a rmdir on the back file system.
* Returns 0 or an error if could not perform operation.
*/
int
/*ARGSUSED*/
{
int error;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* Get a vnode for the back file */
if (error) {
return (error);
}
return (error);
}
/*
* create a symlink on the back file system
* Returns 0 or an error if could not perform operation.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* get a vnode for the back directory */
if (error) {
goto out;
}
/* create the symlink */
if (error)
goto out;
/* get the vnode for the symlink */
if (error)
goto out;
/* get the attributes of the symlink */
if (error)
goto out;
if (error)
goto out;
/* get the fid */
if (error)
goto out;
/* update the cnode for this file with the new info */
if (error) {
error = 0;
goto out;
}
out:
if (cr)
if (dvp)
if (svp)
if (cp)
return (error);
}
/*
* Perform setattr on the back file system.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* get a vnode for the back directory */
if (error) {
goto out;
}
/* perform the setattr */
if (error)
goto out;
/* get the new ctime and mtime */
if (error)
goto out;
if (error)
goto out;
out:
if (cr)
if (cvp)
return (error);
}
int
{
int error = 0;
/*
* Only called in support of disconnectable operation, so assert
* that this is not called when NFSv4 is the backfilesytem.
*/
/* get vnode of back file to do VOP_SETSECATTR to */
if (error != 0) {
goto out;
}
/* get the creds */
/* form the vsecattr_t */
/* set the ACL */
if (error != 0)
goto out;
/* get the new ctime and mtime */
if (error)
goto out;
if (error)
goto out;
out:
return (error);
}
static void
{
return;
(void) cachefs_sync_metadata(cp);
}
static void
{
/* dump any pages, may be a dirty one */
}
}
}
static void
{
}
}
}
static void
{
int error = 0;
enum cachefs_rl_type type;
int timedout = 0;
/* XXX just return if fs is in error ro mode */
/* lock out other users of the MF list */
/* move the modified entries for this file system to the MF list */
for (;;) {
/* get the next entry on the MF list */
if (error) {
error = 0;
break;
}
/* get the cnode for the file */
if (error) {
#ifdef CFSDEBUG
printf("cachefs: mf: could not find %llu\n",
#endif
/* XXX this will loop forever, maybe put fs in */
/* ro mode */
continue;
}
/* if a regular file that has not been pushed */
MD_PUTPAGE))) {
/* move the file to lost+found */
if (error) {
/* XXX put fs in ro mode */
/* XXX need to drain MF list */
}
continue;
}
/* if a local file */
/* if the file was not created */
/* do not allow cnode to be used */
continue;
}
/* save the local fileno for later getattrs */
/* register the mapping from old to new fileno */
/* move to new location in the cache */
}
/* else if a modified file that needs to have its mode fixed */
(void) cachefs_getfrontfile(cp);
/* mark file as no longer modified */
if (error) {
"Cannot change ff mode.\n");
}
}
}
/* if there is a rl entry, put it on the correct list */
else
} else {
}
}
/* if a directory, populate it */
/* XXX hack for now */
}
if (!timedout) {
timedout = 1;
else if ((error == 0) &&
}
}
}
}
void
{
int i;
/*
* first, see if an empty slot exists.
*/
for (i = 0; i < fscp->fs_inum_size; i++)
break;
/*
* if there are no empty slots, try to grow the table.
*/
if (i >= fscp->fs_inum_size) {
/*
* try to fetch a new table size that's bigger than
* our current size
*/
for (i = 0; cachefs_hash_sizes[i] != 0; i++)
newsize = cachefs_hash_sizes[i];
break;
}
/*
* if we're out of larger twin-primes, give up. thus,
* the inode numbers in some directory entries might
* change at reconnect, and disagree with what stat()
* says. this isn't worth panicing over, but it does
* merit a warning message.
*/
if (newsize == 0) {
/* only print hash table warning once */
"cachefs: inode hash table full\n");
}
return;
}
/* set up this fscp with a new hash table */
KM_SLEEP);
/*
* re-insert all of the old values. this will never
* go more than one level into recursion-land.
*/
for (i = 0; i < oldsize; i++) {
} else {
ASSERT(0);
}
}
if (oldsize > 0)
sizeof (cachefs_inum_trans_t));
}
/*
* compute values for the hash table. see ken rosen's
* `elementary number theory and its applications' for one
* description of double hashing.
*/
/*
* since we know the hash table isn't full when we get here,
* this loop shouldn't terminate except via the `break'.
*/
for (i = 0; i < fscp->fs_inum_size; i++) {
break;
}
}
}
/*
* given an inode number, map it to the inode number that should be
* put in a directory entry before its copied out.
*
* don't call this function unless there is a fscp->fs_inum_trans
* table that has real entries in it!
*/
{
int i;
for (i = 0; i < fscp->fs_inum_size; i++) {
break;
break;
}
}
return (rc);
}
/*
* Passed a cid, finds the cnode and sets the MD_NEEDATTRS bit
* in the metadata.
*/
static void
{
int error;
if (error)
return;
}