1 /* $NetBSD: psshfs.c,v 1.57 2010/01/07 21:23:10 pooka Exp $ */
4 * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * psshfs implements sshfs functionality on top of puffs making it
32 * possible to mount a filesystme through the sftp service.
34 * psshfs can execute multiple operations in "parallel" by using the
35 * puffs_cc framework for continuations.
37 * Concurrency control is handled currently by vnode locking (this
38 * will change in the future). Context switch locations are easy to
39 * find by grepping for puffs_framebuf_enqueue_cc().
42 #include <sys/cdefs.h>
44 __RCSID("$NetBSD: psshfs.c,v 1.57 2010/01/07 21:23:10 pooka Exp $");
47 #include <sys/types.h>
64 static int pssh_connect(struct puffs_usermount
*, int);
65 static void psshfs_loopfn(struct puffs_usermount
*);
66 static void usage(void);
67 static void add_ssharg(char ***, int *, const char *);
68 static void psshfs_notify(struct puffs_usermount
*, int, int);
70 #define SSH_PATH "/usr/bin/ssh"
72 unsigned int max_reads
;
76 add_ssharg(char ***sshargs
, int *nargs
, const char *arg
)
79 *sshargs
= realloc(*sshargs
, (*nargs
+ 2) * sizeof(char*));
82 (*sshargs
)[(*nargs
)++] = estrdup(arg
);
83 (*sshargs
)[*nargs
] = NULL
;
90 fprintf(stderr
, "usage: %s "
91 "[-ceprst] [-F configfile] [-O sshopt=value] [-o opts] "
92 "user@host:path mountpath\n",
105 main(int argc
, char *argv
[])
107 struct psshfs_ctx pctx
;
108 struct puffs_usermount
*pu
;
109 struct puffs_ops
*pops
;
110 struct psshfs_node
*root
= &pctx
.psn_root
;
111 struct puffs_node
*pn_root
;
112 puffs_framev_fdnotify_fn notfn
;
118 int mntflags
, pflags
, ch
;
120 int exportfs
, refreshival
, numconnections
;
123 setprogname(argv
[0]);
128 memset(&pctx
, 0, sizeof(pctx
));
129 mntflags
= pflags
= exportfs
= nargs
= 0;
132 refreshival
= DEFAULTREFRESH
;
133 notfn
= puffs_framev_unmountonclose
;
135 add_ssharg(&sshargs
, &nargs
, SSH_PATH
);
136 add_ssharg(&sshargs
, &nargs
, "-axs");
137 add_ssharg(&sshargs
, &nargs
, "-oClearAllForwardings=yes");
139 while ((ch
= getopt(argc
, argv
, "c:eF:g:o:O:pr:st:u:")) != -1) {
142 numconnections
= atoi(optarg
);
143 if (numconnections
< 1 || numconnections
> 2) {
144 fprintf(stderr
, "%s: only 1 or 2 connections "
145 "permitted currently\n", getprogname());
154 add_ssharg(&sshargs
, &nargs
, "-F");
155 add_ssharg(&sshargs
, &nargs
, optarg
);
158 pctx
.domanglegid
= 1;
159 pctx
.manglegid
= atoi(optarg
);
160 if (pctx
.manglegid
== (gid_t
)-1)
161 errx(1, "-1 not allowed for -g");
162 pctx
.mygid
= getegid();
165 add_ssharg(&sshargs
, &nargs
, "-o");
166 add_ssharg(&sshargs
, &nargs
, optarg
);
169 mp
= getmntopts(optarg
, puffsmopts
, &mntflags
, &pflags
);
171 err(1, "getmntopts");
175 notfn
= psshfs_notify
;
178 max_reads
= atoi(optarg
);
184 refreshival
= atoi(optarg
);
185 if (refreshival
< 0 && refreshival
!= -1)
186 errx(1, "invalid timeout %d", refreshival
);
189 pctx
.domangleuid
= 1;
190 pctx
.mangleuid
= atoi(optarg
);
191 if (pctx
.mangleuid
== (uid_t
)-1)
192 errx(1, "-1 not allowed for -u");
193 pctx
.myuid
= geteuid();
203 if (pflags
& PUFFS_FLAG_OPDUMP
)
205 pflags
|= PUFFS_FLAG_BUILDPATH
;
206 pflags
|= PUFFS_KFLAG_WTCACHE
| PUFFS_KFLAG_IAONDEMAND
;
213 PUFFSOP_SET(pops
, psshfs
, fs
, unmount
);
214 PUFFSOP_SETFSNOP(pops
, sync
); /* XXX */
215 PUFFSOP_SET(pops
, psshfs
, fs
, statvfs
);
216 PUFFSOP_SET(pops
, psshfs
, fs
, nodetofh
);
217 PUFFSOP_SET(pops
, psshfs
, fs
, fhtonode
);
219 PUFFSOP_SET(pops
, psshfs
, node
, lookup
);
220 PUFFSOP_SET(pops
, psshfs
, node
, create
);
221 PUFFSOP_SET(pops
, psshfs
, node
, open
);
222 PUFFSOP_SET(pops
, psshfs
, node
, inactive
);
223 PUFFSOP_SET(pops
, psshfs
, node
, readdir
);
224 PUFFSOP_SET(pops
, psshfs
, node
, getattr
);
225 PUFFSOP_SET(pops
, psshfs
, node
, setattr
);
226 PUFFSOP_SET(pops
, psshfs
, node
, mkdir
);
227 PUFFSOP_SET(pops
, psshfs
, node
, remove
);
228 PUFFSOP_SET(pops
, psshfs
, node
, readlink
);
229 PUFFSOP_SET(pops
, psshfs
, node
, rmdir
);
230 PUFFSOP_SET(pops
, psshfs
, node
, symlink
);
231 PUFFSOP_SET(pops
, psshfs
, node
, rename
);
232 PUFFSOP_SET(pops
, psshfs
, node
, read
);
233 PUFFSOP_SET(pops
, psshfs
, node
, write
);
234 PUFFSOP_SET(pops
, psshfs
, node
, reclaim
);
236 pu
= puffs_init(pops
, argv
[0], "psshfs", &pctx
, pflags
);
238 err(1, "puffs_init");
240 pctx
.mounttime
= time(NULL
);
241 pctx
.refreshival
= refreshival
;
242 pctx
.numconnections
= numconnections
;
245 hostpath
= strchr(userhost
, ':');
248 pctx
.mountpath
= hostpath
;
250 pctx
.mountpath
= ".";
252 add_ssharg(&sshargs
, &nargs
, argv
[0]);
253 add_ssharg(&sshargs
, &nargs
, "sftp");
254 pctx
.sshargs
= sshargs
;
257 memset(root
, 0, sizeof(struct psshfs_node
));
258 pn_root
= puffs_pn_new(pu
, root
);
261 puffs_setroot(pu
, pn_root
);
263 puffs_framev_init(pu
, psbuf_read
, psbuf_write
, psbuf_cmp
, NULL
, notfn
);
265 signal(SIGHUP
, takehup
);
266 puffs_ml_setloopfn(pu
, psshfs_loopfn
);
267 if (pssh_connect(pu
, PSSHFD_META
) == -1)
268 err(1, "can't connect meta");
269 if (puffs_framev_addfd(pu
, pctx
.sshfd
,
270 PUFFS_FBIO_READ
| PUFFS_FBIO_WRITE
) == -1)
271 err(1, "framebuf addfd meta");
272 if (numconnections
== 2) {
273 if (pssh_connect(pu
, PSSHFD_DATA
) == -1)
274 err(1, "can't connect data");
275 if (puffs_framev_addfd(pu
, pctx
.sshfd_data
,
276 PUFFS_FBIO_READ
| PUFFS_FBIO_WRITE
) == -1)
277 err(1, "framebuf addfd data");
279 pctx
.sshfd_data
= pctx
.sshfd
;
283 puffs_setfhsize(pu
, sizeof(struct psshfs_fid
),
284 PUFFS_FHFLAG_NFSV2
| PUFFS_FHFLAG_NFSV3
);
286 rva
= &pn_root
->pn_va
;
287 rva
->va_fileid
= pctx
.nextino
++;
288 rva
->va_nlink
= 101; /* XXX */
291 if (puffs_daemon(pu
, 1, 1) == -1)
292 err(1, "puffs_daemon");
294 if (puffs_mount(pu
, argv
[1], mntflags
, puffs_getroot(pu
)) == -1)
295 err(1, "puffs_mount");
296 if (puffs_setblockingmode(pu
, PUFFSDEV_NONBLOCK
) == -1)
297 err(1, "setblockingmode");
299 if (puffs_mainloop(pu
) == -1)
306 #define RETRY_MAX 100
309 psshfs_notify(struct puffs_usermount
*pu
, int fd
, int what
)
311 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
312 int nretry
, which
, newfd
, dummy
;
314 if (fd
== pctx
->sshfd
) {
317 assert(fd
== pctx
->sshfd_data
);
321 if (puffs_getstate(pu
) != PUFFS_STATE_RUNNING
)
324 if (what
!= (PUFFS_FBIO_READ
| PUFFS_FBIO_WRITE
)) {
325 puffs_framev_removefd(pu
, fd
, ECONNRESET
);
330 /* deal with zmobies, beware of half-eaten brain */
331 while (waitpid(-1, &dummy
, WNOHANG
) > 0)
334 for (nretry
= 0;;nretry
++) {
335 if ((newfd
= pssh_connect(pu
, which
)) == -1)
338 if (puffs_framev_addfd(pu
, newfd
,
339 PUFFS_FBIO_READ
| PUFFS_FBIO_WRITE
) == -1)
344 fprintf(stderr
, "reconnect failed... ");
347 if (nretry
< RETRY_MAX
) {
348 fprintf(stderr
, "retry (%d left)\n", RETRY_MAX
-nretry
);
351 fprintf(stderr
, "retry count exceeded, going south\n");
352 exit(1); /* XXXXXXX */
358 pssh_connect(struct puffs_usermount
*pu
, int which
)
360 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
361 char * const *sshargs
= pctx
->sshargs
;
368 if (which
== PSSHFD_META
) {
369 sshfd
= &pctx
->sshfd
;
370 sshpid
= &pctx
->sshpid
;
372 assert(which
== PSSHFD_DATA
);
373 sshfd
= &pctx
->sshfd_data
;
374 sshpid
= &pctx
->sshpid_data
;
377 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, fds
) == -1)
386 if (dup2(fds
[0], STDIN_FILENO
) == -1)
387 err(1, "child dup2");
388 if (dup2(fds
[0], STDOUT_FILENO
) == -1)
389 err(1, "child dup2");
393 dnfd
= open(_PATH_DEVNULL
, O_RDWR
);
395 dup2(dnfd
, STDERR_FILENO
);
397 execvp(sshargs
[0], sshargs
);
407 if (psshfs_handshake(pu
, *sshfd
) != 0)
408 errx(1, "psshfs_handshake %d", which
);
410 if (ioctl(*sshfd
, FIONBIO
, &x
) == -1)
411 err(1, "nonblocking descriptor %d", which
);
417 invalone(struct puffs_usermount
*pu
, struct puffs_node
*pn
, void *arg
)
419 struct psshfs_node
*psn
= pn
->pn_data
;
429 psshfs_loopfn(struct puffs_usermount
*pu
)
433 puffs_pn_nodewalk(pu
, invalone
, NULL
);