auto_vnops.c revision 84d17760f67f1fb6b9f93821deb22c90c87bea7f
/*
* 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
*/
/*
*/
#include <sys/vfs_opreg.h>
#include <sys/pathname.h>
#include <sys/sysmacros.h>
#include <rpcsvc/autofs_prot.h>
/*
* Vnode ops for autofs
*/
caller_context_t *);
caller_context_t *);
caller_context_t *);
pathname_t *);
caller_context_t *, int);
caller_context_t *, int);
caller_context_t *, int, vsecattr_t *);
caller_context_t *, int);
caller_context_t *, int);
caller_context_t *, int);
caller_context_t *);
const fs_operation_def_t auto_vnodeops_template[] = {
};
/* ARGSUSED */
static int
{
int error;
if (error)
goto done;
/*
* Node is now mounted on.
*/
if (!error)
}
done:
error));
return (error);
}
/* ARGSUSED */
static int
int flag,
int count,
{
return (0);
}
static int
int flags,
{
int error;
if (flags & ATTR_TRIGGER) {
/*
* Pre-trigger the mount
*/
if (error)
return (error);
goto defattr;
return (error);
}
} else {
/*
* Recursive auto_getattr/mount; go to the vfsp == NULL
* case.
*/
if (vn_vfswlock_held(vp))
goto defattr;
return (error);
}
/*
* Node is mounted on.
*/
if (error)
return (error);
/*
* Recursive auto_getattr(); just release newvp and drop
* into the vfsp == NULL case.
*/
} else {
}
}
return (error);
}
} else {
}
return (0);
}
/*ARGSUSED4*/
static int
int flags,
{
int error;
goto done;
/*
* Node is mounted on.
*/
if (vn_is_readonly(newvp))
else
} else
done:
return (error);
}
/* ARGSUSED */
static int
int mode,
int flags,
{
int error;
goto done;
/*
* Node is mounted on.
*/
} else {
int shift = 0;
/*
* really interested in the autofs node, check the
* access on it
*/
shift += 3;
shift += 3;
}
}
done:
return (error);
}
static int
char *nm,
int flags,
int *direntflags,
{
int error = 0;
char *searchnm;
int operation; /* either AUTOFS_LOOKUP or AUTOFS_MOUNT */
if (nm[0] == 0) {
return (0);
}
return (error);
return (0);
}
/*
* Since it is legitimate to have the VROOT flag set for the
* subdirectories of the indirect map in autofs filesystem,
* rootfnnodep is checked against fnnode of dvp instead of
* just checking whether VROOT flag is set in dvp
*/
return (EIO);
}
return (error);
} else {
return (0);
}
}
top:
operation = 0;
(void *)dfnp));
/*
* If a lookup or mount of this node is in progress, wait for it
* to finish, and return whatever result it got.
*/
if (error == AUTOFS_SHUTDOWN)
goto top;
if (error)
return (error);
} else
if (error)
return (error);
if (!error) {
}
return (error);
}
if (error) {
/*
* direct map.
*/
if (dfnp->fn_dirents) {
/*
* Mount previously triggered.
* 'nm' not found
*/
} else {
/*
* I need to contact the daemon to trigger
* the mount. 'dfnp' will be the mountpoint.
*/
error = 0;
}
/*
* 'dfnp' is the root of the indirect AUTOFS.
*/
/*
* Could not acquire writer lock, release
* reader, and wait until available. We
* need to search for 'nm' again, since we
* had to release the lock before reacquiring
* it.
*/
}
if (error) {
/*
* create node being looked-up and request
* mount on it.
*/
if (!error)
}
/*
* dfnp is the actual 'mountpoint' of indirect map,
* it is the equivalent of a direct mount,
* ie, /home/'user1'
*/
error = 0;
}
}
goto top;
}
if (error) {
return (error);
}
/*
* We now have the actual fnnode we're interested in.
* The 'MF_LOOKUP' indicates another thread is currently
* performing a daemon lookup of this node, therefore we
* wait for its completion.
* The 'MF_INPROG' indicates another thread is currently
* performing a daemon mount of this node, we wait for it
* to be done if we are performing a MOUNT. We don't
* wait for it if we are performing a LOOKUP.
* the mutex, since the state of the lock can only change by
* first acquiring the mutex.
*/
if (error == AUTOFS_SHUTDOWN)
return (error);
goto top;
}
if (operation == 0) {
/*
* got the fnnode, check for any errors
* on the previous operation on that node.
*/
/*
* previous operation on this node was
* not completed, do a lookup now.
*/
} else {
/*
* previous operation completed. Return
* a pointer to the node only if there was
* no error.
*/
if (!error)
else
return (error);
}
}
/*
* Since I got to this point, it means I'm the one
*/
switch (operation) {
case AUTOFS_LOOKUP:
if (!error) {
/*
* Return this vnode
*/
} else {
/*
* release our reference to this vnode
* and return error
*/
}
break;
case AUTOFS_MOUNT:
/*
* auto_new_mount_thread fires up a new thread which
* calls automountd finishing up the work
*/
/*
* At this point, we are simply another thread
* waiting for the mount to complete
*/
if (error == AUTOFS_SHUTDOWN)
/*
* now release our reference to this vnode
*/
if (!error)
goto top;
break;
default:
"auto_lookup: unknown operation %d",
}
return (error);
}
static int
char *nm,
int mode,
int flag,
{
int error;
goto done;
/*
* Node is now mounted on.
*/
if (vn_is_readonly(newvp))
else
} else
done:
return (error);
}
static int
char *nm,
int flags)
{
int error;
goto done;
/*
* Node is now mounted on.
*/
if (vn_is_readonly(newvp))
else
} else
done:
return (error);
}
static int
char *nm,
int flags)
{
int error;
goto done;
/*
* an autonode can not be a link to another node
*/
goto done;
}
if (vn_is_readonly(newvp)) {
goto done;
}
/*
* source vp can't be an autonode
*/
goto done;
}
done:
return (error);
}
static int
char *onm,
char *nnm,
int flags)
{
int error;
/*
* we know odvp is an autonode, otherwise this function
* could not have ever been called.
*/
goto done;
/*
* can't rename an autonode
*/
goto done;
}
/*
* directory is AUTOFS, need to trigger the
* mount of the real filesystem.
*/
goto done;
}
/*
* target can't be an autonode
*/
goto done;
}
} else {
/*
* destination directory mount had been
* triggered prior to the call to this function.
*/
}
if (vn_is_readonly(n_newvp)) {
goto done;
}
done:
return (error);
}
static int
char *nm,
int flags,
{
int error;
goto done;
/*
* Node is now mounted on.
*/
if (vn_is_readonly(newvp))
else
} else
done:
return (error);
}
static int
char *nm,
int flags)
{
int error;
goto done;
/*
* Node is now mounted on.
*/
if (vn_is_readonly(newvp))
else
} else
done:
return (error);
}
static int autofs_nobrowse = 0;
#ifdef nextdp
#endif
/* ARGSUSED */
static int
int *eofp,
int flags)
{
struct autofs_rddirargs rda;
dirent64_t *dp;
int error = 0;
int reached_max = 0;
int myeof = 0;
int this_reclen;
*eofp = 0;
return (EINVAL);
/*
* Held when getdents calls VOP_RWLOCK....
*/
/*
* Do readdir of daemon contents only
* Drop readers lock and reacquire after reply.
*/
count = 0;
&rda,
(void *)&rd,
sizeof (autofs_rddirres),
TRUE);
/*
* reacquire previously dropped lock
*/
if (!error) {
}
if (error) {
if (error == AUTOFS_SHUTDOWN) {
/*
* treat as empty directory
*/
error = 0;
myeof = 1;
if (eofp)
*eofp = 1;
}
goto done;
}
/*
* Check for duplicates here
*/
do {
/*
* entry not found in kernel list,
* include it in readdir output.
*
* If we are skipping entries. then
* we need to copy this entry to the
* correct position in the buffer
* to be copied out.
*/
outcount += this_reclen;
} else {
/*
* Entry was found in the kernel
* list. If it is the first entry
* in this buffer, then just skip it
*/
}
}
count += this_reclen;
((char *)cdp + this_reclen);
if (outcount)
} else {
/*
* alloc_count not large enough for one
* directory entry
*/
}
}
myeof = 1;
if (eofp)
*eofp = 1;
}
/*
* call daemon with new cookie, all previous
* elements happened to be duplicates
*/
goto again;
}
goto done;
}
if (uiop->uio_offset == 0) {
/*
* first time: so fudge the . and ..
*/
if (alloc_count < this_reclen) {
goto done;
}
/* use strncpy(9f) to zero out uninitialized bytes */
goto done;
}
/* use strncpy(9f) to zero out uninitialized bytes */
}
offset = 2;
int reclen;
/*
* include node only if its offset is greater or
* equal to the one required and it is not in
* transient state (not being looked-up)
*/
reached_max = 1;
break;
}
/*
* get the offset of the next element
*/
} else {
/*
* This is the last element, make
* offset one plus the current
*/
}
/* use strncpy(9f) to zero out uninitialized bytes */
}
}
if (outcount)
if (!error) {
if (reached_max) {
/*
* This entry did not get added to the buffer on this,
* call. We need to add it on the next call therefore
* set uio_offset to this entry's offset. If there
* wasn't enough space for one dirent, return EINVAL.
*/
if (outcount == 0)
} else if (autofs_nobrowse ||
/*
* done reading directory entries
*/
if (eofp)
*eofp = 1;
} else {
/*
* Need to get the rest of the entries from the daemon.
*/
}
}
done:
return (error);
}
static int
char *lnknm, /* new entry */
char *tnm, /* existing entry */
int flags)
{
int error;
goto done;
/*
* Node is mounted on.
*/
if (vn_is_readonly(newvp))
else
} else
done:
return (error);
}
/* ARGSUSED */
static int
{
int error;
gethrestime(&now);
else {
}
return (error);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static void
{
int count;
/*
* The rwlock should not be already held by this thread.
* The assert relies on the fact that the owner field is cleared
* when the lock is released.
*/
if (count == 0) {
/*
* Free only if node has no subdirectories.
*/
(void *)vp));
return;
}
}
}
/* ARGSUSED2 */
static int
{
if (write_lock)
else
return (write_lock);
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static int
{
/*
* Return 0 unconditionally, since we expect
* a VDIR all the time
*/
return (0);
}
/*
* Triggers the mount if needed. If the mount has been triggered by
* another thread, it will wait for its return status, and return it.
* Whether the mount is triggered by this thread, another thread, or
* if the vnode was already covered, '*newvp' is a
* VN_HELD vnode pointing to the root of the filesystem covering 'vp'.
* If the node is not mounted on, and should not be mounted on, '*newvp'
* will be NULL.
* The calling routine may use '*newvp' to do the filesystem jump.
*/
static int
{
int delayed_ind;
char name[AUTOFS_MAXPATHLEN];
int error;
/*
* Cross-zone mount triggering is disallowed.
*/
return (EPERM); /* Not owner of mount */
error = 0;
delayed_ind = 0;
/*
* Mount or lookup in progress,
* wait for it before proceeding.
*/
if (error == AUTOFS_SHUTDOWN) {
error = 0;
goto done;
}
goto done;
error = 0;
}
/*
* If the vfslock can't be acquired for the first time.
* drop the fn_lock and retry next time in blocking mode.
*/
if (vn_vfswlock(vp)) {
/*
* Lock held by another thread.
* Perform blocking by dropping the
* fn_lock.
*/
if (error)
goto done;
/*
* Because fn_lock wasn't held, the state
* of the trigger node might have changed.
* Need to run through the checks on trigger
* node again.
*/
goto retry;
}
goto done;
} else {
/*
* The filesystem that used to sit here has been
* forcibly unmounted. Do our best to recover.
* Try to unmount autofs subtree below this node
* and retry the action.
*/
goto done;
}
goto retry;
}
}
/*
* If the parent of this node is the root of an indirect
* AUTOFS filesystem, this node is remountable.
*/
delayed_ind = 1;
}
if (delayed_ind ||
/*
* Trigger mount since:
* direct mountpoint with no subdirs or
* delayed indirect.
*/
if (delayed_ind)
else
/*
* At this point we're simply another thread waiting
* for the mount to finish.
*/
goto retry;
if (error == AUTOFS_SHUTDOWN) {
error = 0;
goto done;
}
if (error == 0) {
goto done;
/* Reacquire after dropping locks */
} else {
goto retry;
}
}
} else
done:
return (error);
}