freebsd-skq/lib/libautofs/libautofs.c
alfred 1d00793bf6 Add/document autoreq_getxid(3), which gets the autofs request
transaction id from the request, this is useful for debugging.

Fix the autoh_freeall(3) function to properly free the array of
auto handles.  Before it was freeing individual members of the list
OK, however it was then advancing the pointer and freeing the wrong
data for the whole list.
2004-09-09 01:23:27 +00:00

480 lines
8.9 KiB
C

/*
* Copyright (c) 2004 Alfred Perlstein <alfred@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
* $Id: libautofs.c,v 1.5 2004/09/08 08:44:12 bright Exp $
*/
#include <err.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#ifdef AUTOFSSTANDALONE
#include "../autofs/autofs.h"
#else
#include <fs/autofs/autofs.h>
#endif
#include "libautofs.h"
struct auto_handle {
char ah_mp[MNAMELEN];
fsid_t ah_fsid;
int ah_fd;
};
static int autofs_sysctl(int, fsid_t *, void *, size_t *, void *, size_t);
static void safe_free(void *ptr);
static int getmntlst(struct statfs **sfsp, int *cntp);
static void
safe_free(void *ptr)
{
int saved_errno;
saved_errno = errno;
free(ptr);
errno = saved_errno;
}
int
getmntlst(struct statfs **sfsp, int *cntp)
{
int cnt;
long bufsize;
*sfsp = NULL;
cnt = getfsstat(NULL, 0, MNT_NOWAIT);
bufsize = cnt * sizeof(**sfsp);
/*fprintf(stderr, "getmntlst bufsize %ld, cnt %d\n", bufsize, cnt);*/
*sfsp = malloc(bufsize);
if (sfsp == NULL)
goto err;
cnt = getfsstat(*sfsp, bufsize, MNT_NOWAIT);
if (cnt == -1)
goto err;
*cntp = cnt;
/*fprintf(stderr, "getmntlst ok, cnt %d\n", cnt);*/
return (0);
err:
safe_free(sfsp);
*sfsp = NULL;
/*fprintf(stderr, "getmntlst bad\n");*/
return (-1);
}
/* get a handle based on a path. */
int
autoh_get(const char *path, autoh_t *ahp)
{
struct statfs *sfsp, *sp;
int cnt, fd, i;
autoh_t ret;
ret = NULL;
/*
* We use getfsstat to prevent avoid the lookups on the mountpoints
* that statfs(2) would do.
*/
if (getmntlst(&sfsp, &cnt))
goto err;
for (i = 0; i < cnt; i++) {
if (strcmp(sfsp[i].f_mntonname, path) == 0)
break;
}
if (i == cnt) {
/*fprintf(stderr, "autoh_get bad %d %d\n", i, cnt);*/
errno = ENOENT;
goto err;
}
sp = &sfsp[i];
if (strcmp(sp->f_fstypename, "autofs")) {
errno = ENOTTY;
goto err;
}
fd = open(sp->f_mntonname, O_RDONLY);
if (fd == -1)
goto err;
ret = malloc(sizeof(*ret));
if (ret == NULL)
goto err;
ret->ah_fsid = sp->f_fsid;
ret->ah_fd = fd;
strlcpy(ret->ah_mp, sp->f_mntonname, sizeof(ret->ah_mp));
safe_free(sfsp);
*ahp = ret;
return (0);
err:
safe_free(ret);
safe_free(sfsp);
return (-1);
}
/* release. */
void
autoh_free(autoh_t ah)
{
int saved_errno;
saved_errno = errno;
close(ah->ah_fd);
free(ah);
errno = saved_errno;
}
/*
* Get an array of pointers to all the currently mounted autofs
* instances.
*/
int
autoh_getall(autoh_t **arrayp, int *cntp)
{
struct statfs *sfsp;
int cnt, i, pos;
autoh_t *array;
array = NULL;
/*
* We use getfsstat to prevent avoid the lookups on the mountpoints
* that statfs(2) would do.
*/
if (getmntlst(&sfsp, &cnt))
goto err;
array = *arrayp = calloc(cnt + 1, sizeof(**arrayp));
if (array == NULL)
goto err;
for (i = 0, pos = 0; i < cnt; i++) {
if (autoh_get(sfsp[i].f_mntonname, &array[pos]) == -1) {
/* not an autofs entry, that's ok, otherwise bail */
if (errno == ENOTTY)
continue;
goto err;
}
pos++;
}
if (pos == 0) {
errno = ENOENT;
goto err;
}
*arrayp = array;
*cntp = pos;
safe_free(sfsp);
return (0);
err:
safe_free(sfsp);
if (array)
autoh_freeall(array);
return (-1);
}
/* release. */
void
autoh_freeall(autoh_t *ah)
{
autoh_t *ahp;
ahp = ah;
while (*ahp != NULL) {
autoh_free(*ahp);
ahp++;
}
safe_free(ah);
}
/* return fd to select on. */
int
autoh_fd(autoh_t ah)
{
return (ah->ah_fd);
}
const char *
autoh_mp(autoh_t ah)
{
return (ah->ah_mp);
}
static int do_autoreq_get(autoh_t ah, autoreq_t *reqp, int *cntp);
/* get an array of pending requests */
int
autoreq_get(autoh_t ah, autoreq_t **reqpp, int *cntp)
{
int cnt, i;
autoreq_t req, *reqp;
if (do_autoreq_get(ah, &req, &cnt))
return (-1);
reqp = calloc(cnt + 1, sizeof(*reqp));
if (reqp == NULL) {
safe_free(req);
return (-1);
}
for (i = 0; i < cnt; i++)
reqp[i] = &req[i];
*reqpp = reqp;
*cntp = cnt;
return (0);
}
int
do_autoreq_get(autoh_t ah, autoreq_t *reqp, int *cntp)
{
size_t olen;
struct autofs_userreq *reqs;
int cnt, error;
int vers;
vers = AUTOFS_PROTOVERS;
error = 0;
reqs = NULL;
olen = 0;
cnt = 0;
error = autofs_sysctl(AUTOFS_CTL_GETREQS, &ah->ah_fsid, NULL, &olen,
&vers, sizeof(vers));
if (error == -1)
goto out;
if (olen == 0)
goto out;
reqs = malloc(olen);
if (reqs == NULL)
goto out;
error = autofs_sysctl(AUTOFS_CTL_GETREQS, &ah->ah_fsid, reqs, &olen,
&vers, sizeof(vers));
if (error == -1)
goto out;
out:
if (error) {
safe_free(reqs);
return (-1);
}
cnt = olen / sizeof(*reqs);
*cntp = cnt;
*reqp = reqs;
return (0);
}
/* free an array of requests */
void
autoreq_free(autoh_t ah __unused, autoreq_t *req)
{
free(*req);
free(req);
}
/* serve a request */
int
autoreq_serv(autoh_t ah, autoreq_t req)
{
int error;
error = autofs_sysctl(AUTOFS_CTL_SERVREQ, &ah->ah_fsid, NULL, NULL,
req, sizeof(*req));
return (error);
}
enum autoreq_op
autoreq_getop(autoreq_t req)
{
switch (req->au_op) {
case AREQ_LOOKUP:
return (AUTOREQ_OP_LOOKUP);
case AREQ_STAT:
return (AUTOREQ_OP_STAT);
case AREQ_READDIR:
return (AUTOREQ_OP_READDIR);
default:
return (AUTOREQ_OP_UNKNOWN);
}
}
/* get a request's file name. */
const char *
autoreq_getpath(autoreq_t req)
{
return (req->au_name);
}
/* get a request's inode. a indirect mount may return AUTO_INODE_NONE. */
autoino_t
autoreq_getino(autoreq_t req)
{
return (req->au_ino);
}
void
autoreq_setino(autoreq_t req, autoino_t ino)
{
req->au_ino = ino;
}
/* get a request's directory inode. */
autoino_t
autoreq_getdirino(autoreq_t req)
{
return (req->au_dino);
}
void
autoreq_seterrno(autoreq_t req, int error)
{
req->au_errno = error;
}
void
autoreq_setaux(autoreq_t req, void *auxdata, size_t auxlen)
{
req->au_auxdata = auxdata;
req->au_auxlen = auxlen;
}
void
autoreq_getaux(autoreq_t req, void **auxdatap, size_t *auxlenp)
{
*auxdatap = req->au_auxdata;
*auxlenp = req->au_auxlen;
}
void
autoreq_seteof(autoreq_t req, int eof)
{
req->au_eofflag = eof;
}
void
autoreq_getoffset(autoreq_t req, off_t *offp)
{
*offp = req->au_offset - AUTOFS_USEROFF;
}
void
autoreq_getxid(autoreq_t req, int *xid)
{
*xid = req->au_xid;
}
/* toggle by path. args = handle, AUTO_?, pid (-1 to disable), path. */
int
autoh_togglepath(autoh_t ah, int op, pid_t pid, const char *path)
{
int fd, ret;
fd = open(path, O_RDONLY);
if (fd == -1)
return (-1);
ret = autoh_togglefd(ah, op, pid, fd);
close(fd);
return (ret);
}
/* toggle by fd. args = handle, AUTO_?, pid (-1 to disable), fd. */
int
autoh_togglefd(autoh_t ah, int op, pid_t pid, int fd)
{
struct stat sb;
struct autofs_mounterreq mr;
int error, realop;
switch (op) {
case AUTO_DIRECT:
realop = AUTOFS_CTL_TRIGGER;
break;
case AUTO_INDIRECT:
realop = AUTOFS_CTL_SUBTRIGGER;
break;
case AUTO_MOUNTER:
realop = AUTOFS_CTL_MOUNTER;
break;
case AUTO_BROWSE:
realop = AUTOFS_CTL_BROWSE;
break;
default:
errno = ENOTTY;
return (-1);
}
if (fstat(fd, &sb))
return (-1);
bzero(&mr, sizeof(mr));
mr.amu_ino = sb.st_ino;
mr.amu_pid = pid;
error = autofs_sysctl(realop, &ah->ah_fsid, NULL, NULL,
&mr, sizeof(mr));
return (error);
}
int
autofs_sysctl(op, fsid, oldp, oldlenp, newp, newlen)
int op;
fsid_t *fsid;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
{
struct vfsidctl vc;
bzero(&vc, sizeof(vc));
vc.vc_op = op;
strcpy(vc.vc_fstypename, "*");
vc.vc_vers = VFS_CTL_VERS1;
vc.vc_fsid = *fsid;
vc.vc_ptr = newp;
vc.vc_len = newlen;
return (sysctlbyname("vfs.autofs.ctl", oldp, oldlenp, &vc, sizeof(vc)));
}