freebsd-skq/lib/libautofs/libautofs.c

466 lines
8.7 KiB
C
Raw Normal View History

2004-08-31 16:26:01 +00:00
/*
* 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.3 2004/08/31 08:49:56 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>
#include <autofs.h>
#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)
{
while (*ah != NULL) {
autoh_free(*ah);
ah++;
}
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;
}
/* 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)));
}