Home | History | Annotate | Line # | Download | only in mount_psshfs
psshfs.c revision 1.5
      1  1.5  pooka /*	$NetBSD: psshfs.c,v 1.5 2007/01/20 13:52:35 pooka Exp $	*/
      2  1.1  pooka 
      3  1.1  pooka /*
      4  1.1  pooka  * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
      5  1.1  pooka  *
      6  1.1  pooka  * Redistribution and use in source and binary forms, with or without
      7  1.1  pooka  * modification, are permitted provided that the following conditions
      8  1.1  pooka  * are met:
      9  1.1  pooka  * 1. Redistributions of source code must retain the above copyright
     10  1.1  pooka  *    notice, this list of conditions and the following disclaimer.
     11  1.1  pooka  * 2. Redistributions in binary form must reproduce the above copyright
     12  1.1  pooka  *    notice, this list of conditions and the following disclaimer in the
     13  1.1  pooka  *    documentation and/or other materials provided with the distribution.
     14  1.1  pooka  * 3. The name of the company nor the name of the author may be used to
     15  1.1  pooka  *    endorse or promote products derived from this software without specific
     16  1.1  pooka  *    prior written permission.
     17  1.1  pooka  *
     18  1.1  pooka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     19  1.1  pooka  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  1.1  pooka  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  1.1  pooka  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22  1.1  pooka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  1.1  pooka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     24  1.1  pooka  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  1.1  pooka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  1.1  pooka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  1.1  pooka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  1.1  pooka  * SUCH DAMAGE.
     29  1.1  pooka  */
     30  1.1  pooka 
     31  1.1  pooka /*
     32  1.1  pooka  * psshfs: puffs sshfs
     33  1.1  pooka  *
     34  1.1  pooka  * psshfs implements sshfs functionality on top of puffs making it
     35  1.1  pooka  * possible to mount a filesystme through the sftp service.
     36  1.1  pooka  *
     37  1.1  pooka  * psshfs can execute multiple operations in "parallel" by using the
     38  1.1  pooka  * puffs_cc framework for continuations.
     39  1.1  pooka  *
     40  1.1  pooka  * Concurrency control is handled currently by vnode locking (this
     41  1.1  pooka  * will change in the future).  Context switch locations are easy to
     42  1.1  pooka  * find by grepping for puffs_cc_yield().
     43  1.1  pooka  *
     44  1.1  pooka  * The operation revolves around an event loop.  Incoming requests from
     45  1.1  pooka  * the kernel are dispatched off to the libpuffs event handler, which
     46  1.1  pooka  * eventually calls the callbacks.  The callbacks do whatever they need
     47  1.1  pooka  * to do, queue output and call puffs_cc_yield() to put them to sleep.
     48  1.1  pooka  * Meanwhile execution continues.  When an answer to the callback's
     49  1.1  pooka  * query arrives, the blocker is located by the protocol's request
     50  1.1  pooka  * id and awakened by calling puffs_docc().  All changes made to the
     51  1.1  pooka  * buffer (psbuf->buf) will be seen by the callback.  Here the buffer
     52  1.1  pooka  * contains the response of the sftp server.  The callback will then
     53  1.1  pooka  * proceed to parse the buffer.
     54  1.1  pooka  *
     55  1.1  pooka  * XXX: struct psbuf is a mess.  it will be fixed sooner or later
     56  1.1  pooka  */
     57  1.1  pooka 
     58  1.1  pooka #include <sys/cdefs.h>
     59  1.1  pooka #ifndef lint
     60  1.5  pooka __RCSID("$NetBSD: psshfs.c,v 1.5 2007/01/20 13:52:35 pooka Exp $");
     61  1.1  pooka #endif /* !lint */
     62  1.1  pooka 
     63  1.1  pooka #include <sys/types.h>
     64  1.1  pooka 
     65  1.1  pooka #include <assert.h>
     66  1.1  pooka #include <err.h>
     67  1.1  pooka #include <errno.h>
     68  1.2  pooka #include <mntopts.h>
     69  1.1  pooka #include <poll.h>
     70  1.1  pooka #include <puffs.h>
     71  1.1  pooka #include <signal.h>
     72  1.1  pooka #include <stdlib.h>
     73  1.1  pooka #include <util.h>
     74  1.1  pooka #include <unistd.h>
     75  1.1  pooka 
     76  1.1  pooka #include "psshfs.h"
     77  1.1  pooka 
     78  1.1  pooka static void	psshfs_eventloop(struct puffs_usermount *, struct psshfs_ctx *);
     79  1.1  pooka static void	pssh_connect(struct psshfs_ctx *, char **);
     80  1.2  pooka static void	usage(void);
     81  1.1  pooka 
     82  1.1  pooka #define SSH_PATH "/usr/bin/ssh"
     83  1.1  pooka 
     84  1.2  pooka static void
     85  1.2  pooka usage()
     86  1.2  pooka {
     87  1.2  pooka 
     88  1.4  pooka 	errx(1, "usage: %s [-s][-o opts]user@host:path mountpath",
     89  1.4  pooka 	    getprogname());
     90  1.2  pooka }
     91  1.2  pooka 
     92  1.1  pooka int
     93  1.1  pooka main(int argc, char *argv[])
     94  1.1  pooka {
     95  1.1  pooka 	struct psshfs_ctx pctx;
     96  1.1  pooka 	struct puffs_usermount *pu;
     97  1.1  pooka 	struct puffs_ops *pops;
     98  1.2  pooka 	mntoptparse_t mp;
     99  1.1  pooka 	char *sshargs[16];
    100  1.1  pooka 	char *userhost;
    101  1.1  pooka 	char *hostpath;
    102  1.2  pooka 	int mntflags, pflags, ch;
    103  1.4  pooka 	int detach;
    104  1.1  pooka 
    105  1.1  pooka 	setprogname(argv[0]);
    106  1.1  pooka 
    107  1.2  pooka 	if (argc < 3)
    108  1.2  pooka 		usage();
    109  1.2  pooka 
    110  1.3  pooka 	mntflags = pflags = 0;
    111  1.4  pooka 	detach = 1;
    112  1.4  pooka 	while ((ch = getopt(argc, argv, "o:s")) != -1) {
    113  1.2  pooka 		switch (ch) {
    114  1.2  pooka 		case 'o':
    115  1.2  pooka 			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
    116  1.2  pooka 			if (mp == NULL)
    117  1.2  pooka 				err(1, "getmntopts");
    118  1.2  pooka 			freemntopts(mp);
    119  1.2  pooka 			break;
    120  1.4  pooka 		case 's':
    121  1.4  pooka 			detach = 0;
    122  1.4  pooka 			break;
    123  1.2  pooka 		default:
    124  1.2  pooka 			usage();
    125  1.2  pooka 			/*NOTREACHED*/
    126  1.2  pooka 		}
    127  1.2  pooka 	}
    128  1.2  pooka 	argc -= optind;
    129  1.2  pooka 	argv += optind;
    130  1.2  pooka 
    131  1.4  pooka 	if (pflags & PUFFS_FLAG_OPDUMP)
    132  1.4  pooka 		detach = 0;
    133  1.4  pooka 
    134  1.2  pooka 	if (argc != 2)
    135  1.2  pooka 		usage();
    136  1.1  pooka 
    137  1.1  pooka 	PUFFSOP_INIT(pops);
    138  1.1  pooka 
    139  1.1  pooka 	PUFFSOP_SET(pops, psshfs, fs, unmount);
    140  1.1  pooka 	PUFFSOP_SETFSNOP(pops, sync); /* XXX */
    141  1.1  pooka 	PUFFSOP_SETFSNOP(pops, statvfs);
    142  1.1  pooka 
    143  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, lookup);
    144  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, create);
    145  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, readdir);
    146  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, getattr);
    147  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, setattr);
    148  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, mkdir);
    149  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, remove);
    150  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, readlink);
    151  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, rmdir);
    152  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, symlink);
    153  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, rename);
    154  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, read);
    155  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, write);
    156  1.1  pooka #if 0
    157  1.1  pooka 	/* must support this some day */
    158  1.1  pooka 	PUFFSOP_SET(pops, psshfs, node, reclaim);
    159  1.1  pooka #endif
    160  1.1  pooka 
    161  1.1  pooka 	memset(&pctx, 0, sizeof(pctx));
    162  1.1  pooka 	TAILQ_INIT(&pctx.outbufq);
    163  1.1  pooka 	TAILQ_INIT(&pctx.req_queue);
    164  1.1  pooka 
    165  1.2  pooka 	userhost = argv[0];
    166  1.1  pooka 	hostpath = strchr(userhost, ':');
    167  1.1  pooka 	if (hostpath) {
    168  1.1  pooka 		*hostpath++ = '\0';
    169  1.1  pooka 		pctx.mountpath = hostpath;
    170  1.1  pooka 	} else
    171  1.1  pooka 		pctx.mountpath = ".";
    172  1.1  pooka 
    173  1.1  pooka 	/* xblah */
    174  1.1  pooka 	sshargs[0] = SSH_PATH;
    175  1.2  pooka 	sshargs[1] = argv[0];
    176  1.1  pooka 	sshargs[2] = "-oClearAllForwardings=yes";
    177  1.1  pooka 	sshargs[3] = "-a";
    178  1.1  pooka 	sshargs[4] = "-x";
    179  1.1  pooka 	sshargs[5] = "-s";
    180  1.1  pooka 	sshargs[6] = "sftp";
    181  1.1  pooka 	sshargs[7] = 0;
    182  1.1  pooka 
    183  1.2  pooka 	if ((pu = puffs_mount(pops, argv[1], mntflags, "psshfs", &pctx,
    184  1.2  pooka 	    PUFFS_FLAG_BUILDPATH | pflags, 0))==NULL)
    185  1.1  pooka 		err(1, "puffs_mount");
    186  1.1  pooka 
    187  1.4  pooka 	pssh_connect(&pctx, sshargs);
    188  1.4  pooka 
    189  1.1  pooka 	if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1)
    190  1.1  pooka 		err(1, "setblockingmode");
    191  1.2  pooka 	if (psshfs_domount(pu) != 0)
    192  1.2  pooka 		errx(1, "domount");
    193  1.1  pooka 
    194  1.4  pooka 	if (detach)
    195  1.4  pooka 		daemon(1, 0);
    196  1.1  pooka 
    197  1.1  pooka 	psshfs_eventloop(pu, &pctx);
    198  1.1  pooka 	return 0;
    199  1.1  pooka }
    200  1.1  pooka 
    201  1.1  pooka void
    202  1.1  pooka pssh_outbuf_enqueue(struct psshfs_ctx *pctx, struct psbuf *pb,
    203  1.1  pooka 	struct puffs_cc *pcc, uint32_t reqid)
    204  1.1  pooka {
    205  1.1  pooka 
    206  1.1  pooka 	pb->psr.reqid = reqid;
    207  1.1  pooka 	pb->psr.pcc = pcc;
    208  1.1  pooka 	TAILQ_INSERT_TAIL(&pctx->outbufq, pb, psr.entries);
    209  1.1  pooka }
    210  1.1  pooka 
    211  1.1  pooka void
    212  1.1  pooka psshreq_put(struct psshfs_ctx *pctx, struct psbuf *pb)
    213  1.1  pooka {
    214  1.1  pooka 
    215  1.1  pooka 	TAILQ_INSERT_TAIL(&pctx->req_queue, pb, psr.entries);
    216  1.1  pooka }
    217  1.1  pooka 
    218  1.1  pooka struct psbuf *
    219  1.1  pooka psshreq_get(struct psshfs_ctx *pctx, uint32_t reqid)
    220  1.1  pooka {
    221  1.1  pooka 	struct psbuf *pb;
    222  1.1  pooka 
    223  1.1  pooka 	TAILQ_FOREACH(pb, &pctx->req_queue, psr.entries)
    224  1.1  pooka 		if (pb->psr.reqid == reqid)
    225  1.1  pooka 			break;
    226  1.1  pooka 
    227  1.1  pooka 	if (!pb)
    228  1.1  pooka 		return NULL;
    229  1.1  pooka 
    230  1.1  pooka 	TAILQ_REMOVE(&pctx->req_queue, pb, psr.entries);
    231  1.1  pooka 
    232  1.1  pooka 	return pb;
    233  1.1  pooka }
    234  1.1  pooka 
    235  1.1  pooka static void
    236  1.1  pooka handlebuf(struct psshfs_ctx *pctx, struct psbuf *datapb,
    237  1.1  pooka 	struct puffs_putreq *ppr)
    238  1.1  pooka {
    239  1.1  pooka 	struct psreq psrtmp;
    240  1.1  pooka 	struct psbuf *pb;
    241  1.1  pooka 
    242  1.1  pooka 	/* is this something we are expecting? */
    243  1.1  pooka 	pb = psshreq_get(pctx, datapb->reqid);
    244  1.1  pooka 
    245  1.1  pooka 	if (pb) {
    246  1.1  pooka 		/* keep psreq clean, xxx uknow */
    247  1.1  pooka 		psrtmp = pb->psr;
    248  1.1  pooka 		*pb = *datapb;
    249  1.1  pooka 		pb->psr = psrtmp;
    250  1.1  pooka 		free(datapb);
    251  1.1  pooka 
    252  1.1  pooka 		puffs_docc(ppr, pb->psr.pcc);
    253  1.1  pooka 		return;
    254  1.1  pooka 	}
    255  1.1  pooka 
    256  1.1  pooka 	printf("invalid server request response %d\n", datapb->reqid);
    257  1.1  pooka 	psbuf_destroy(datapb);
    258  1.1  pooka }
    259  1.1  pooka 
    260  1.1  pooka static int
    261  1.1  pooka psshinput(struct psshfs_ctx *pctx, struct puffs_putreq *ppr)
    262  1.1  pooka {
    263  1.1  pooka 	struct psbuf *cb;
    264  1.1  pooka 	int rv;
    265  1.1  pooka 
    266  1.1  pooka 	for (;;) {
    267  1.1  pooka 		if ((cb = pctx->curpb) == NULL) {
    268  1.1  pooka 			cb = psbuf_make(PSB_IN);
    269  1.1  pooka 			if (cb == NULL)
    270  1.1  pooka 				return -1;
    271  1.1  pooka 			pctx->curpb = cb;
    272  1.1  pooka 		}
    273  1.1  pooka 
    274  1.1  pooka 		rv = psbuf_read(pctx, cb);
    275  1.1  pooka 		if (rv == -1)
    276  1.1  pooka 			err(1, "psbuf read");
    277  1.1  pooka 		if (rv == 0)
    278  1.1  pooka 			break;
    279  1.1  pooka 
    280  1.1  pooka 		handlebuf(pctx, cb, ppr);
    281  1.1  pooka 		pctx->curpb = NULL;
    282  1.1  pooka 	}
    283  1.1  pooka 
    284  1.1  pooka 	return rv;
    285  1.1  pooka }
    286  1.1  pooka 
    287  1.1  pooka static int
    288  1.1  pooka psshoutput(struct psshfs_ctx *pctx)
    289  1.1  pooka {
    290  1.1  pooka 	struct psbuf *pb;
    291  1.1  pooka 	int rv;
    292  1.1  pooka 
    293  1.1  pooka 	TAILQ_FOREACH(pb, &pctx->outbufq, psr.entries) {
    294  1.1  pooka 		rv = psbuf_write(pctx, pb);
    295  1.1  pooka 		if (rv == -1)
    296  1.1  pooka 			return -1;
    297  1.1  pooka 		if (rv == 0)
    298  1.1  pooka 			return 0;
    299  1.1  pooka 
    300  1.1  pooka 		/* sent everything, move to cookiequeue */
    301  1.1  pooka 		TAILQ_REMOVE(&pctx->outbufq, pb, psr.entries);
    302  1.1  pooka 		free(pb->buf);
    303  1.1  pooka 		TAILQ_INSERT_TAIL(&pctx->req_queue, pb, psr.entries);
    304  1.1  pooka 	}
    305  1.1  pooka 
    306  1.1  pooka 	return 1;
    307  1.1  pooka }
    308  1.1  pooka 
    309  1.1  pooka volatile int timetodie;
    310  1.1  pooka 
    311  1.1  pooka #define PFD_SSH 0
    312  1.1  pooka #define PFD_PUFFS 1
    313  1.1  pooka static void
    314  1.1  pooka psshfs_eventloop(struct puffs_usermount *pu, struct psshfs_ctx *pctx)
    315  1.1  pooka {
    316  1.1  pooka 	struct puffs_getreq *pgr;
    317  1.1  pooka 	struct puffs_putreq *ppr;
    318  1.1  pooka 	struct pollfd pfds[2];
    319  1.1  pooka 	int x;
    320  1.1  pooka 
    321  1.5  pooka 	pgr = puffs_req_makeget(pu, pu->pu_maxreqlen, 0);
    322  1.1  pooka 	if (!pgr)
    323  1.1  pooka 		err(1, "makegetreq");
    324  1.5  pooka 	ppr = puffs_req_makeput(pu);
    325  1.1  pooka 	if (!ppr)
    326  1.1  pooka 		err(1, "makeputreq");
    327  1.1  pooka 
    328  1.1  pooka 	x = 1;
    329  1.1  pooka 	if (ioctl(pctx->sshfd, FIONBIO, &x) == -1)
    330  1.1  pooka 		err(1, "nonblocking descriptor");
    331  1.1  pooka 
    332  1.1  pooka 	while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
    333  1.1  pooka 		if (timetodie) {
    334  1.1  pooka 			kill(pctx->sshpid, SIGTERM);
    335  1.1  pooka 			break;
    336  1.1  pooka 		}
    337  1.1  pooka 
    338  1.1  pooka 		memset(pfds, 0, sizeof(pfds));
    339  1.1  pooka 		pfds[PFD_SSH].events = POLLIN;
    340  1.1  pooka 		if (!TAILQ_EMPTY(&pctx->outbufq))
    341  1.1  pooka 			pfds[PFD_SSH].events |= POLLOUT;
    342  1.1  pooka 		pfds[PFD_SSH].fd = pctx->sshfd;
    343  1.1  pooka 		pfds[PFD_PUFFS].fd = puffs_getselectable(pu);
    344  1.1  pooka 		pfds[PFD_PUFFS].events = POLLIN;
    345  1.1  pooka 
    346  1.1  pooka 		if (poll(pfds, 2, INFTIM) == -1)
    347  1.1  pooka 			err(1, "poll");
    348  1.1  pooka 
    349  1.1  pooka 		if (pfds[PFD_SSH].revents & POLLOUT)
    350  1.1  pooka 			if (psshoutput(pctx) == -1)
    351  1.1  pooka 				err(1, "psshoutput");
    352  1.1  pooka 
    353  1.1  pooka 		/* get & possibly dispatch events from kernel */
    354  1.1  pooka 		if (pfds[PFD_PUFFS].revents & POLLIN)
    355  1.5  pooka 			if (puffs_req_handle(pu, pgr, ppr, 0) == -1)
    356  1.1  pooka 				err(1, "puffs_handlereqs");
    357  1.1  pooka 
    358  1.1  pooka 		/* get input from sftpd, possibly build more responses */
    359  1.1  pooka 		if (pfds[PFD_SSH].revents & POLLIN)
    360  1.1  pooka 			if (psshinput(pctx, ppr) == -1)
    361  1.1  pooka 				errx(1, "psshinput");
    362  1.1  pooka 
    363  1.1  pooka 		/* it's likely we got outputtables, poke the ice with a stick */
    364  1.1  pooka 		if (psshoutput(pctx) == -1)
    365  1.1  pooka 			err(1, "psshoutput");
    366  1.1  pooka 
    367  1.1  pooka 		/* stuff all replies from both of the above into kernel */
    368  1.5  pooka 		if (puffs_req_putput(ppr) == -1)
    369  1.1  pooka 			err(1, "putputreq");
    370  1.5  pooka 		puffs_req_resetput(ppr);
    371  1.1  pooka 	}
    372  1.1  pooka 
    373  1.5  pooka 	puffs_req_destroyget(pgr);
    374  1.1  pooka }
    375  1.1  pooka 
    376  1.1  pooka static void
    377  1.1  pooka pssh_connect(struct psshfs_ctx *pctx, char **sshargs)
    378  1.1  pooka {
    379  1.1  pooka 	int fds[2];
    380  1.1  pooka 	pid_t pid;
    381  1.1  pooka 
    382  1.1  pooka 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1)
    383  1.1  pooka 		err(1, "socketpair");
    384  1.1  pooka 
    385  1.1  pooka 	pid = fork();
    386  1.1  pooka 	switch (pid) {
    387  1.1  pooka 	case -1:
    388  1.1  pooka 		err(1, "fork");
    389  1.1  pooka 		/*NOTREACHED*/
    390  1.1  pooka 	case 0: /* child */
    391  1.1  pooka 		if (dup2(fds[0], STDIN_FILENO) == -1)
    392  1.1  pooka 			err(1, "child dup2");
    393  1.1  pooka 		if (dup2(fds[0], STDOUT_FILENO) == -1)
    394  1.1  pooka 			err(1, "child dup2");
    395  1.1  pooka 		close(fds[0]);
    396  1.1  pooka 		close(fds[1]);
    397  1.1  pooka 		execvp(sshargs[0], sshargs);
    398  1.1  pooka 		break;
    399  1.1  pooka 	default:
    400  1.1  pooka 		pctx->sshpid = pid;
    401  1.1  pooka 		pctx->sshfd = fds[1];
    402  1.1  pooka 		close(fds[0]);
    403  1.1  pooka 		break;
    404  1.1  pooka 	}
    405  1.1  pooka }
    406