lx_autofs.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/sysmacros.h>
#include <sys/vfs_opreg.h>
#include <sys/lx_autofs_impl.h>
/*
* External functions
*/
/*
* Globals
*/
static vfsops_t *lx_autofs_vfsops;
static int lx_autofs_fstype;
static major_t lx_autofs_major;
static minor_t lx_autofs_minor = 0;
/*
* Support functions
*/
static void
{
}
static char *
{
return (ptr);
}
static int
{
long res;
return (-1);
return (-1);
return (0);
}
static void
{
}
static void
{
}
static void
{
}
static int
{
return (-1);
return (0);
}
static vnode_t *
{
}
static vnode_t *
{
/* Allocate a new vnode structure in case we need it. */
/*
* Take a hold on the vfs structure. This is how unmount will
* determine if there are any active vnodes in the file system.
*/
/*
* Check if we already have a vnode allocated for this underlying
* vnode_t.
*/
/*
* Didn't find an existing node.
* Add this node to the hash and return.
*/
(mod_hash_val_t)vp) == 0);
return (vp);
}
/* Get a hold on the existing vnode and free up the one we allocated. */
/* Free up the new vnode we allocated. */
vn_invalid(vp);
return (vp_old);
}
static void
{
/* We're about to free this vnode so take it out of the hash. */
/*
* No one else can lookup this vnode any more so there's no need
* to hold locks.
*/
/* Release the underlying vnode. */
vn_invalid(vp);
}
static lx_autofs_lookup_req_t *
{
/* Pre-allocate a new automounter request before grabbing locks. */
/* Assign a unique id for this request. */
/*
* The token expected by the linux automount is the name of
* the directory entry to look up. (And not the entire
* path that is being accessed.)
*/
"invalid autofs lookup: \"%s\"", nm);
return (NULL);
}
/* Check for an outstanding request for this path. */
/*
* There's already an outstanding request for this
* path so we don't need a new one.
*/
/* Bump the ref count on the old request. */
*dup_request = 1;
} else {
/* Add it to the hashes. */
(mod_hash_val_t)lalr) == 0);
(mod_hash_val_t)lalr) == 0);
*dup_request = 0;
}
return (lalr);
}
static lx_autofs_lookup_req_t *
{
/* Check for an outstanding request for this id. */
(mod_hash_val_t *)&lalr) != 0) {
return (NULL);
}
return (lalr);
}
static void
{
/* Remove this request from the hashes so no one can look it up. */
/* Mark this requst as complete and wakeup anyone waiting on it. */
}
static void
{
return;
}
static void
{
/*
* This is a little tricky. We're aborting the wait for this
* request. So if anyone else is waiting for this request we
* can't free it, but if no one else is waiting for the request
* we should free it.
*/
return;
}
/* Remove this request from the hashes so no one can look it up. */
/* It's ok to free this now because the ref count was zero. */
}
static int
{
int i;
/*
* sprlock() is zone aware, so assuming this mount call was
* initiated by a process in a zone, if it tries to specify
* a pgrp outside of it's zone this call will fail.
*
* Also, we want to grab hold of the main automounter process
* and its going to be the group leader for pgrp, so its
* pid will be equal to pgrp.
*/
return (-1);
/* Now we want to access the processes open file descriptors. */
/* Sanity check fifo write fd. */
return (-1);
}
/* Get a pointer to the write fifo. */
/* Invalid fifo fd. */
return (-1);
}
/*
* Now we need to find the read end of the fifo (for reasons
* explained below.) We assume that the read end of the fifo
* is in the same process as the write end.
*/
break;
}
/* Didn't find it. */
return (-1);
}
/*
* We need to drop fi_lock before we can try to acquire f_tlock
* the good news is that the file pointers are protected because
* we're still holding uf_lock.
*/
/*
* Here we bump the open counts on the fifos. The reason
* that we do this is because when we go to write to the
* fifo we want to ensure that they are actually open (and
* not in the process of being closed) without having to
* stop the automounter. (If the write end of the fifo
* were closed and we tried to write to it we would panic.
* If the read end of the fifo was closed and we tried to
* write to the other end, the process that invoked the
* lookup operation would get an unexpected SIGPIPE.)
*/
/* Release all our locks. */
/* Return the file pointers. */
return (0);
}
static uint_t
/*ARGSUSED*/
{
/* Return the key and terminate the walk. */
return (MH_WALK_TERMINATE);
}
static void
{
/*
* Close the fifo to prevent any future requests from
* getting sent to the automounter.
*/
}
}
/*
* Wakeup any threads currently waiting for the automounter
* note that it's possible for multiple threads to have entered
* this function and to be doing the work below simultaneously.
*/
for (;;) {
int id;
/* Lookup the first entry in the hash. */
id = -1;
i_fifo_close_cb, &id);
if (id == -1) {
/* No more id's in the hash. */
break;
}
/* Someone else beat us to it. */
continue;
}
/* Mark the request as compleate and release it. */
}
}
static int
{
int i;
/* Check if we've already been shut down. */
return (-1);
}
/*
* sprlock() is zone aware, so assuming this mount call was
* initiated by a process in a zone, if it tries to specify
* a pgrp outside of it's zone this call will fail.
*
* Also, we want to grab hold of the main automounter process
* and its going to be the group leader for pgrp, so its
* pid will be equal to pgrp.
*/
return (-1);
/* Now we want to access the processes open file descriptors. */
/*
* Now we need to find the read end of the fifo (for reasons
* explained below.) We assume that the read end of the fifo
* is in the same process as the write end.
*/
break;
}
/* Didn't find it. */
return (-1);
}
/*
* Seems the automounter still has the read end of the fifo
* open, we're done here. Release all our locks and exit.
*/
return (0);
}
static int
{
int error;
/*
* The catch here is we need to make sure _we_ don't close
* the the fifo while writing to it. (Another thread could come
* along and realize the automounter process is gone and close
* the fifo. To do this we bump the open count before we
* write to the fifo.
*/
return (ENOENT);
}
/* Bump the open count on the write fifo. */
/* Bump the open count on the read fifo. */
uio.uio_loffset = 0;
uio.uio_llimit = 0;
/*
* After every write we verify that the automounter still has
* these files open.
*/
if (i_fifo_verify_rd(data) != 0) {
/*
* Something happened to the automounter.
* Close down the communication pipe we setup.
*/
if (error != 0)
return (error);
return (ENOENT);
}
return (error);
}
static int
{
char *nm;
uio.uio_loffset = 0;
eof = 0;
error = 0;
return (-1);
}
/* We're done. */
break;
}
continue;
return (-1);
}
} else {
}
} else {
if (file_stack != NULL) {
} else {
}
}
}
}
return (0);
}
static void
{
int ret;
/* A directory entry with this name doesn't actually exist. */
return;
}
/* Easy, the directory entry is a file so delete it. */
return;
}
/*
* The directory entry is a subdirectory, now we have a bit more
* work to do. (We'll have to recurse into the sub directory.)
* It would have been much easier to do this recursively but kernel
* stacks are notoriously small.
*/
/* Save our newfound subdirectory into a list. */
/* Do a recursive depth first search into the subdirectories. */
while (i_stack_pop(&search_stack,
/* Get a list of the subdirectories in this directory. */
goto exit;
/* Save the current directory a separate stack. */
}
/*
* Now dir_stack contains a list of directories, the deepest paths
* are at the top of the list. So let's go through and process them.
*/
while (i_stack_pop(&dir_stack,
/* Get a list of the files in this directory. */
goto exit;
}
/* Delete all the files in this directory. */
while (i_stack_pop(&file_stack,
if (ret != 0) {
goto exit;
}
}
/* Delete this directory. */
if (ret != 0)
goto exit;
}
exit:
while (
}
}
static vnode_t *
{
/*
* After looking at the mkdir syscall path it seems we don't need
* to initialize all of the vattr_t structure.
*/
return (NULL);
return (vp);
}
static int
{
int error, dup_request;
/* Get a pointer to the vfs mount data. */
/* The automounter only support queries in the root directory. */
return (ENOENT);
/*
* Check if the current process is in the automounters process
* group. (If it is, the current process is either the autmounter
* itself or one of it's forked child processes.) If so, don't
* redirect this lookup back into the automounter because we'll
* hang.
*/
return (ENOENT);
}
/* Verify that the automount process pipe still exists. */
return (ENOENT);
}
/* Allocate an automounter request structure. */
return (ENOENT);
/*
* If we were the first one to allocate this request then we
* need to send it to the automounter.
*/
if ((!dup_request) &&
/*
* Unable to send the request to the automounter.
* Unblock any other threads waiting on the request
* and release the request.
*/
return (error);
}
/* Wait for someone to signal us that this request has compleated. */
while (!lalr->lalr_complete) {
/* We got a signal, abort this lookup. */
return (EINTR);
}
}
return (0);
}
static int
{
/*
* Be strict.
* We only accept ioctls from the automounter process group.
*/
return (ENOENT);
}
/*
* We don't actually care if the request failed or succeeded.
* We do the same thing either way.
*/
return (ENXIO);
/* Mark the request as compleate and release it. */
return (0);
}
if (cmd == LX_AUTOFS_IOC_CATATONIC) {
/* The automounter is shutting down. */
return (0);
}
return (ENOTSUP);
}
static int
{
/* Require all options to be present. */
return (EINVAL);
/* Get the values for each parameter. */
return (EINVAL);
/*
* We support v2 of the linux kernel automounter protocol.
* Make sure the mount request we got indicates support
* for this version of the protocol.
*/
return (EINVAL);
/*
* Now we need to lookup the fifos we'll be using
* to talk to the userland automounter process.
*/
return (EINVAL);
/* Save the mount options and fifo pointers. */
return (0);
}
/*
* VFS entry points
*/
static int
{
char name[40];
int error;
return (EPERM);
return (ENOTDIR);
return (EBUSY);
/* We don't support mountes in the global zone. */
if (getzoneid() == GLOBAL_ZONEID)
return (EPERM);
/* We don't support mounting on top of ourselves. */
return (EPERM);
/* Allocate a vfs struct. */
/* Parse mount options. */
return (error);
}
/* Initialize the backing store. */
return (EBUSY);
}
/* We have to hold the underlying vnode we're mounted on. */
/* Initialize vfs fields */
/* Invent a dev_t (sigh) */
do {
} while (vfs_devismounted(dev));
/* Create an id space arena for automounter requests. */
/* Create hashes to keep track of automounter requests. */
/* Create a hash to keep track of vnodes. */
sizeof (vnode_t));
/* Create root vnode */
return (0);
}
static int
{
return (EPERM);
/* We do not currently support forced unmounts. */
return (ENOTSUP);
/*
* We should never have a reference count of less than 2: one for the
* caller, one for the root vnode.
*/
/* If there are any outstanding vnodes, we can't unmount. */
return (EBUSY);
/* Check for any remaining holds on the root vnode. */
return (EBUSY);
/* Close the fifo to the automount process. */
/*
* We have to release our hold on our root vnode before we can
* delete the backing store. (Since the root vnode is linked
* to the backing store.)
*/
/* Cleanup the backing store. */
/* Cleanup out remaining data structures. */
return (0);
}
static int
{
return (0);
}
static int
{
int error;
return (error);
/* Update some of values before returning. */
sizeof (sp->f_basetype));
return (0);
}
static const fs_operation_def_t lx_autofs_vfstops[] = {
};
/*
* VOP entry points - simple passthrough
*
* For most VOP entry points we can simply pass the request on to
* the underlying filesystem we're mounted on.
*/
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static void
{
}
/*ARGSUSED*/
static int
{
/*
* cdir is the calling processes current directory.
* If cdir is lx_autofs vnode then get its real underlying
* vnode ptr. (It seems like the only thing cdir is
* ever used for is to make sure the user doesn't delete
* their current directory.)
*/
}
}
/*
* VOP entry points - special passthrough
*
* For some VOP entry points we will first pass the request on to
* the underlying filesystem we're mounted on. If there's an error
* then we immediately return the error, but if the request succeeds
* we have to do some extra work before returning.
*/
static int
{
int error;
return (error);
/* Check for clone opens. */
return (0);
/* Deal with clone opens by returning a new vnode. */
return (0);
}
static int
{
int error;
return (error);
/* Update the attributes with our filesystem id. */
return (0);
}
static int
{
int error;
return (error);
/* Update the attributes with our filesystem id. */
/* Allocate a new vnode. */
return (0);
}
/*
* VOP entry points - custom
*/
/*ARGSUSED*/
static void
{
/*
* We need to hold the vfs lock because if we're going to free
* this vnode we have to prevent anyone from looking it up
* in the vnode hash.
*/
panic("lx_autofs_inactive: bad v_count");
/*NOTREACHED*/
}
/* Drop the temporary hold by vn_rele now. */
return;
}
/*
* No one should have been blocked on this lock because we're
* about to free this vnode.
*/
}
static int
{
int error;
/* First try to lookup if this path component already exitst. */
direntflags, realpnp)) == 0) {
return (0);
}
/* Only query the automounter if the path does not exist. */
return (error);
/* Refer the lookup to the automounter. */
return (error);
/* Retry the lookup operation. */
direntflags, realpnp)) == 0) {
return (0);
}
return (error);
}
/*ARGSUSED*/
static int
{
/* Intercept certain ioctls. */
case LX_AUTOFS_IOC_READY:
case LX_AUTOFS_IOC_FAIL:
case LX_AUTOFS_IOC_CATATONIC:
case LX_AUTOFS_IOC_EXPIRE:
case LX_AUTOFS_IOC_PROTOVER:
case LX_AUTOFS_IOC_SETTIMEOUT:
}
/* Pass any remaining ioctl on. */
}
/*
* VOP entry points definitions
*/
static const fs_operation_def_t lx_autofs_tops_root[] = {
{ NULL }
};
/*
* lx_autofs_init() gets invoked via the mod_install() call in
* this modules _init() routine. Therefor, the code that cleans
* up the structures we allocate below is actually found in
* our _fini() routine.
*/
/* ARGSUSED */
static int
{
int error;
if ((lx_autofs_major =
"can't get unique device number");
return (EAGAIN);
}
(uintptr_t)lx_autofs_major) != 0) {
"can't save unique device number");
return (EAGAIN);
}
}
if ((error = vfs_setfsops(
return (error);
}
lx_autofs_tops_root, &lx_autofs_vn_ops)) != 0) {
return (error);
}
return (0);
}
/*
* Module linkage
*/
static mntopt_t lx_autofs_mntopt[] = {
};
static mntopts_t lx_autofs_mntopts = {
sizeof (lx_autofs_mntopt) / sizeof (mntopt_t),
};
};
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
{
}
int
_fini(void)
{
int error;
return (error);
if (lx_autofs_vn_ops != NULL) {
}
/*
* In our init routine, if we get an error after calling
* vfs_setfsops() we cleanup by calling vfs_freevfsops_by_type().
* But we don't need to call vfs_freevfsops_by_type() here
* because the fs framework did this for us as part of the
* mod_remove() call above.
*/
return (0);
}