Exemplo n.º 1
0
int
main(int argc, char *argv[])
{
	struct puffs_usermount *pu;
	struct puffs_ops *pops;
	mntoptparse_t mp;
	int mntflags, pflags;
	int detach;
	int ch;

	setprogname(argv[0]);

	if (argc < 2)
		usage();

	mntflags = pflags = 0;
	detach = 1;
	while ((ch = getopt(argc, argv, "o:rs")) != -1) {
		switch (ch) {
		case 'o':
			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
			if (mp == NULL)
				err(EXIT_FAILURE, "getmntopts");
			freemntopts(mp);
			break;
		case 'r':
			rflag = 1;
			break;
		case 's':
			detach = 0;
			break;
		}
	}
	argv += optind;
	argc -= optind;
	pflags |= PUFFS_FLAG_BUILDPATH | PUFFS_KFLAG_NOCACHE;

	if (pflags & PUFFS_FLAG_OPDUMP)
		detach = 0;

	if (argc != 2)
		usage();

	PUFFSOP_INIT(pops);

	PUFFSOP_SETFSNOP(pops, unmount);
	PUFFSOP_SETFSNOP(pops, sync);
	PUFFSOP_SETFSNOP(pops, statvfs);
	PUFFSOP_SET(pops, sysctlfs, fs, nodetofh);
	PUFFSOP_SET(pops, sysctlfs, fs, fhtonode);

	PUFFSOP_SET(pops, sysctlfs, node, lookup);
	PUFFSOP_SET(pops, sysctlfs, node, getattr);
	PUFFSOP_SET(pops, sysctlfs, node, setattr);
	PUFFSOP_SET(pops, sysctlfs, node, readdir);
	PUFFSOP_SET(pops, sysctlfs, node, read);
	PUFFSOP_SET(pops, sysctlfs, node, write);
	PUFFSOP_SET(pops, puffs_genfs, node, reclaim);

	pu = puffs_init(pops, _PATH_PUFFS, "sysctlfs", NULL, pflags);
	if (pu == NULL)
		err(EXIT_FAILURE, "puffs_init");

	puffs_set_pathbuild(pu, sysctlfs_pathbuild);
	puffs_set_pathtransform(pu, sysctlfs_pathtransform);
	puffs_set_pathcmp(pu, sysctlfs_pathcmp);
	puffs_set_pathfree(pu, sysctlfs_pathfree);

	puffs_setfhsize(pu, sizeof(struct sfsfid), PUFFS_FHFLAG_NFSV3);

	if (sysctlfs_domount(pu) != 0)
		errx(EXIT_FAILURE, "domount");

	if (detach)
		if (puffs_daemon(pu, 1, 1) == -1)
			err(EXIT_FAILURE, "puffs_daemon");

#ifdef RUMP_ACTION
	{
		extern int puffs_fakecc;
		puffs_fakecc = 1;
		rump_init();
	}
#endif

	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
		err(EXIT_FAILURE, "puffs_mount");
	if (puffs_mainloop(pu) == -1)
		err(EXIT_FAILURE, "mainloop");

	return 0;
}
Exemplo n.º 2
0
int
main(int argc, char *argv[])
{
	struct psshfs_ctx pctx;
	struct puffs_usermount *pu;
	struct puffs_ops *pops;
	struct psshfs_node *root = &pctx.psn_root;
	struct puffs_node *pn_root;
	puffs_framev_fdnotify_fn notfn;
	struct vattr *rva;
	mntoptparse_t mp;
	char **sshargs;
	char *user;
	char *host;
	char *path;
	int mntflags, pflags, ch;
	int detach;
	int exportfs, refreshival, numconnections;
	int nargs;

	setprogname(argv[0]);
	puffs_unmountonsignal(SIGINT, true);
	puffs_unmountonsignal(SIGTERM, true);

	if (argc < 3)
		usage();

	memset(&pctx, 0, sizeof(pctx));
	mntflags = pflags = exportfs = nargs = 0;
	numconnections = 1;
	detach = 1;
	refreshival = DEFAULTREFRESH;
	notfn = puffs_framev_unmountonclose;
	sshargs = NULL;
	add_ssharg(&sshargs, &nargs, SSH_PATH);
	add_ssharg(&sshargs, &nargs, "-axs");
	add_ssharg(&sshargs, &nargs, "-oClearAllForwardings=yes");

	while ((ch = getopt(argc, argv, "c:eF:g:o:O:pr:st:u:")) != -1) {
		switch (ch) {
		case 'c':
			numconnections = atoi(optarg);
			if (numconnections < 1 || numconnections > 2) {
				fprintf(stderr, "%s: only 1 or 2 connections "
				    "permitted currently\n", getprogname());
				usage();
				/*NOTREACHED*/
			}
			break;
		case 'e':
			exportfs = 1;
			break;
		case 'F':
			add_ssharg(&sshargs, &nargs, "-F");
			add_ssharg(&sshargs, &nargs, optarg);
			break;
		case 'g':
			pctx.domanglegid = 1;
			pctx.manglegid = atoi(optarg);
			if (pctx.manglegid == (gid_t)-1)
				errx(1, "-1 not allowed for -g");
			pctx.mygid = getegid();
			break;
		case 'O':
			add_ssharg(&sshargs, &nargs, "-o");
			add_ssharg(&sshargs, &nargs, optarg);
			break;
		case 'o':
			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
			if (mp == NULL)
				err(1, "getmntopts");
			freemntopts(mp);
			break;
		case 'p':
			notfn = psshfs_notify;
			break;
		case 'r':
			max_reads = atoi(optarg);
			break;
		case 's':
			detach = 0;
			break;
		case 't':
			refreshival = atoi(optarg);
			if (refreshival < 0 && refreshival != -1)
				errx(1, "invalid timeout %d", refreshival);
			break;
		case 'u':
			pctx.domangleuid = 1;
			pctx.mangleuid = atoi(optarg);
			if (pctx.mangleuid == (uid_t)-1)
				errx(1, "-1 not allowed for -u");
			pctx.myuid = geteuid();
			break;
		default:
			usage();
			/*NOTREACHED*/
		}
	}
	argc -= optind;
	argv += optind;

	if (pflags & PUFFS_FLAG_OPDUMP)
		detach = 0;
	pflags |= PUFFS_FLAG_BUILDPATH;
	pflags |= PUFFS_KFLAG_WTCACHE | PUFFS_KFLAG_IAONDEMAND;

	if (argc != 2)
		usage();

	PUFFSOP_INIT(pops);

	PUFFSOP_SET(pops, psshfs, fs, unmount);
	PUFFSOP_SETFSNOP(pops, sync); /* XXX */
	PUFFSOP_SET(pops, psshfs, fs, statvfs);
	PUFFSOP_SET(pops, psshfs, fs, nodetofh);
	PUFFSOP_SET(pops, psshfs, fs, fhtonode);

	PUFFSOP_SET(pops, psshfs, node, lookup);
	PUFFSOP_SET(pops, psshfs, node, create);
	PUFFSOP_SET(pops, psshfs, node, open);
	PUFFSOP_SET(pops, psshfs, node, inactive);
	PUFFSOP_SET(pops, psshfs, node, readdir);
	PUFFSOP_SET(pops, psshfs, node, getattr);
	PUFFSOP_SET(pops, psshfs, node, setattr);
	PUFFSOP_SET(pops, psshfs, node, mkdir);
	PUFFSOP_SET(pops, psshfs, node, remove);
	PUFFSOP_SET(pops, psshfs, node, readlink);
	PUFFSOP_SET(pops, psshfs, node, rmdir);
	PUFFSOP_SET(pops, psshfs, node, symlink);
	PUFFSOP_SET(pops, psshfs, node, rename);
	PUFFSOP_SET(pops, psshfs, node, read);
	PUFFSOP_SET(pops, psshfs, node, write);
	PUFFSOP_SET(pops, psshfs, node, reclaim);

	pu = puffs_init(pops, argv[0], "psshfs", &pctx, pflags);
	if (pu == NULL)
		err(1, "puffs_init");

	pctx.mounttime = time(NULL);
	pctx.refreshival = refreshival;
	pctx.numconnections = numconnections;

	user = strdup(argv[0]);
	if ((host = strrchr(user, '@')) == NULL) {
		host = user;
	} else {
		*host++ = '\0';		/* break at the '@' */
		if (user[0] == '\0') {
			fprintf(stderr, "Missing username\n");
			usage();
		}
		add_ssharg(&sshargs, &nargs, "-l");
		add_ssharg(&sshargs, &nargs, user);
	}

	if ((path = colon(host)) != NULL) {
		*path++ = '\0';		/* break at the ':' */
		pctx.mountpath = path;
	} else {
		pctx.mountpath = ".";
	}

	host = cleanhostname(host);
	if (host[0] == '\0') {
		fprintf(stderr, "Missing hostname\n");
		usage();
	}

	add_ssharg(&sshargs, &nargs, host);
	add_ssharg(&sshargs, &nargs, "sftp");
	pctx.sshargs = sshargs;

	pctx.nextino = 2;
	memset(root, 0, sizeof(struct psshfs_node));
	TAILQ_INIT(&root->pw);
	pn_root = puffs_pn_new(pu, root);
	if (pn_root == NULL)
		return errno;
	puffs_setroot(pu, pn_root);

	puffs_framev_init(pu, psbuf_read, psbuf_write, psbuf_cmp, NULL, notfn);

	signal(SIGHUP, takehup);
	puffs_ml_setloopfn(pu, psshfs_loopfn);
	if (pssh_connect(pu, PSSHFD_META) == -1)
		err(1, "can't connect meta");
	if (puffs_framev_addfd(pu, pctx.sshfd,
	    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
		err(1, "framebuf addfd meta");
	if (numconnections == 2) {
		if (pssh_connect(pu, PSSHFD_DATA) == -1)
			err(1, "can't connect data");
		if (puffs_framev_addfd(pu, pctx.sshfd_data,
		    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
			err(1, "framebuf addfd data");
	} else {
		pctx.sshfd_data = pctx.sshfd;
	}

	if (exportfs)
		puffs_setfhsize(pu, sizeof(struct psshfs_fid),
		    PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3);

	rva = &pn_root->pn_va;
	rva->va_fileid = pctx.nextino++;

	/*
	 * For root link count, just guess something ridiculously high.
	 * Guessing too high has no known adverse effects, but fts(3)
	 * doesn't like too low values.  This guess will be replaced
	 * with the real value when readdir is first called for
	 * the root directory.
	 */
	rva->va_nlink = 8811;

	if (detach)
		if (puffs_daemon(pu, 1, 1) == -1)
			err(1, "puffs_daemon");

	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
		err(1, "puffs_mount");
	if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1)
		err(1, "setblockingmode");

	if (puffs_mainloop(pu) == -1)
		err(1, "mainloop");
	puffs_exit(pu, 1);

	return 0;
}
Exemplo n.º 3
0
int
main(int argc, char *argv[])
{
	struct puffs_usermount *pu;
	struct puffs_ops *pops;
	struct puffs_pathobj *po_root;
	struct puffs_node *pn_root;
	struct stat sb;
	int mntflags, pflags;
	int ch;
	int detach;

	setprogname(argv[0]);

	if (argc < 3)
		usage();

	pflags = mntflags = 0;
	detach = 1;
	while ((ch = getopt(argc, argv, "o:s")) != -1) {
		switch (ch) {
		case 'o':
			getmntopts(optarg, puffsmopts, &mntflags, &pflags);
			break;
		case 's':
			detach = 0;
			break;
		}
	}
	pflags |= PUFFS_FLAG_BUILDPATH;
	argv += optind;
	argc -= optind;

	if (pflags & PUFFS_FLAG_OPDUMP)
		detach = 0;

	if (argc != 2)
		usage();

	if (lstat(argv[0], &sb) == -1)
		err(1, "stat %s", argv[0]);
	if ((sb.st_mode & S_IFDIR) == 0)
		errx(1, "%s is not a directory", argv[0]);

	PUFFSOP_INIT(pops);
	puffs_null_setops(pops);

	if ((pu = puffs_init(pops, argv[0], "pnullfs", NULL, pflags)) == NULL)
		err(1, "init");

	pn_root = puffs_pn_new(pu, NULL);
	if (pn_root == NULL)
		err(1, "puffs_pn_new");
	puffs_setroot(pu, pn_root);
	puffs_setfhsize(pu, 0, PUFFS_FHFLAG_PASSTHROUGH);

	po_root = puffs_getrootpathobj(pu);
	if (po_root == NULL)
		err(1, "getrootpathobj");
	po_root->po_path = argv[0];
	po_root->po_len = strlen(argv[0]);
	puffs_stat2vattr(&pn_root->pn_va, &sb);

	if (detach)
		if (puffs_daemon(pu, 1, 1) == -1)
			err(1, "puffs_daemon");

	if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1)
		err(1, "puffs_mount");
	if (puffs_mainloop(pu) == -1)
		err(1, "mainloop");

	return 0;
}
Exemplo n.º 4
0
int
main(int argc, char *argv[])
{
	extern char *optarg;
	extern int optind;
	struct puffs_usermount *pu;
	struct puffs_pathobj *po_root;
	struct puffs_ops *pops;
	struct timespec ts;
	const char *typename;
	char *rtstr;
	mntoptparse_t mp;
	int pflags, detach, mntflags;
	int ch;
	int khashbuckets;
	int maxreqsize;

	setprogname(argv[0]);

	rtstr = NULL;
	detach = 1;
	mntflags = 0;
	khashbuckets = 256;
	pflags = PUFFS_KFLAG_IAONDEMAND;
	typename = FSNAME;
	maxreqsize = MAXREQMAGIC;
	gdtm.dtm_allowprot = VM_PROT_ALL;
	while ((ch = getopt(argc, argv, "bc:dfilm:n:o:p:r:st")) != -1) {
		switch (ch) {
		case 'b': /* build paths, for debugging the feature */
			pflags |= PUFFS_FLAG_BUILDPATH;
			break;
		case 'c':
			khashbuckets = atoi(optarg);
			break;
		case 'd':
			dynamicfh = 1;
			break;
		case 'f':
			pflags |= PUFFS_KFLAG_LOOKUP_FULLPNBUF;
			break;
		case 'i':
			pflags &= ~PUFFS_KFLAG_IAONDEMAND;
			break;
		case 'l':
			straightflush = 1;
			break;
		case 'm':
			maxreqsize = atoi(optarg);
			break;
		case 'n':
			typename = optarg;
			break;
		case 'o':
			mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
			if (mp == NULL)
				err(1, "getmntopts");
			freemntopts(mp);
			break;
		case 'p':
			gdtm.dtm_allowprot = atoi(optarg);
			if ((gdtm.dtm_allowprot | VM_PROT_ALL) != VM_PROT_ALL)
				usage();
			break;
		case 'r':
			rtstr = optarg;
			break;
		case 's': /* stay on top */
			detach = 0;
			break;
		case 't':
			pflags |= PUFFS_KFLAG_WTCACHE;
			break;
		default:
			usage();
			/*NOTREACHED*/
		}
	}
	if (pflags & PUFFS_FLAG_OPDUMP)
		detach = 0;
	argc -= optind;
	argv += optind;

	if (argc != 2)
		usage();

	PUFFSOP_INIT(pops);

	PUFFSOP_SET(pops, dtfs, fs, statvfs);
	PUFFSOP_SET(pops, dtfs, fs, unmount);
	PUFFSOP_SETFSNOP(pops, sync);
	PUFFSOP_SET(pops, dtfs, fs, fhtonode);
	PUFFSOP_SET(pops, dtfs, fs, nodetofh);

	PUFFSOP_SET(pops, dtfs, node, lookup);
	PUFFSOP_SET(pops, dtfs, node, access);
	PUFFSOP_SET(pops, puffs_genfs, node, getattr);
	PUFFSOP_SET(pops, dtfs, node, setattr);
	PUFFSOP_SET(pops, dtfs, node, create);
	PUFFSOP_SET(pops, dtfs, node, remove);
	PUFFSOP_SET(pops, dtfs, node, readdir);
	PUFFSOP_SET(pops, dtfs, node, poll);
	PUFFSOP_SET(pops, dtfs, node, mmap);
	PUFFSOP_SET(pops, dtfs, node, mkdir);
	PUFFSOP_SET(pops, dtfs, node, rmdir);
	PUFFSOP_SET(pops, dtfs, node, rename);
	PUFFSOP_SET(pops, dtfs, node, read);
	PUFFSOP_SET(pops, dtfs, node, write);
	PUFFSOP_SET(pops, dtfs, node, link);
	PUFFSOP_SET(pops, dtfs, node, symlink);
	PUFFSOP_SET(pops, dtfs, node, readlink);
	PUFFSOP_SET(pops, dtfs, node, mknod);
	PUFFSOP_SET(pops, dtfs, node, inactive);
	PUFFSOP_SET(pops, dtfs, node, pathconf);
	PUFFSOP_SET(pops, dtfs, node, reclaim);

	srandom(time(NULL)); /* for random generation numbers */

	pu = puffs_init(pops, _PATH_PUFFS, typename, &gdtm, pflags);
	if (pu == NULL)
		err(1, "init");
	gpu = pu;

	puffs_setfhsize(pu, sizeof(struct dtfs_fid),
	    PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3
	    | (dynamicfh ? PUFFS_FHFLAG_DYNAMIC : 0));
	puffs_setncookiehash(pu, khashbuckets);

	if (signal(SIGALRM, wipe_the_sleep_out_of_my_eyes) == SIG_ERR)
		warn("cannot set alarm sighandler");

	/* init */
	if (dtfs_domount(pu, rtstr) != 0)
		errx(1, "dtfs_domount failed");

	po_root = puffs_getrootpathobj(pu);
	po_root->po_path = argv[0];
	po_root->po_len = strlen(argv[0]);

	/* often enough for testing poll */
	ts.tv_sec = 1;
	ts.tv_nsec = 0;
	puffs_ml_setloopfn(pu, loopfun);
	puffs_ml_settimeout(pu, &ts);

	if (maxreqsize != MAXREQMAGIC)
		puffs_setmaxreqlen(pu, maxreqsize);

	puffs_set_errnotify(pu, puffs_kernerr_abort);
	if (detach)
		if (puffs_daemon(pu, 1, 1) == -1)
			err(1, "puffs_daemon");

	if (puffs_mount(pu,  argv[1], mntflags, puffs_getroot(pu)) == -1)
		err(1, "mount");
	if (puffs_mainloop(pu) == -1)
		err(1, "mainloop");

	return 0;
}
Exemplo n.º 5
0
Arquivo: psshfs.c Projeto: glk/puffs
int
main(int argc, char *argv[])
{
	struct psshfs_ctx pctx;
	struct puffs_usermount *pu;
	struct puffs_ops *pops;
	struct psshfs_node *root = &pctx.psn_root;
	struct puffs_node *pn_root;
	puffs_framev_fdnotify_fn notfn;
	struct vattr *rva;
	char **sshargs;
	char *userhost;
	char *hostpath;
	int mntflags, pflags, ch;
	int detach;
	int exportfs, refreshival, numconnections;
	int nargs;

	setprogname(argv[0]);

	if (argc < 3)
		usage();

	mntflags = pflags = exportfs = nargs = 0;
	numconnections = 1;
	detach = 1;
	refreshival = DEFAULTREFRESH;
	notfn = puffs_framev_unmountonclose;
	sshargs = NULL;
	add_ssharg(&sshargs, &nargs, SSH_PATH);
	add_ssharg(&sshargs, &nargs, "-axs");
	add_ssharg(&sshargs, &nargs, "-oClearAllForwardings=yes");

	while ((ch = getopt(argc, argv, "c:eF:o:O:pr:st:")) != -1) {
		switch (ch) {
		case 'c':
			numconnections = atoi(optarg);
			if (numconnections < 1 || numconnections > 2) {
				fprintf(stderr, "%s: only 1 or 2 connections "
				    "permitted currently\n", getprogname());
				usage();
				/*NOTREACHED*/
			}
			break;
		case 'e':
			exportfs = 1;
			break;
		case 'F':
			add_ssharg(&sshargs, &nargs, "-F");
			add_ssharg(&sshargs, &nargs, optarg);
			break;
		case 'O':
			add_ssharg(&sshargs, &nargs, "-o");
			add_ssharg(&sshargs, &nargs, optarg);
			break;
		case 'o':
			getmntopts(optarg, puffsmopts, &mntflags, &pflags);
			break;
		case 'p':
			notfn = psshfs_notify;
			break;
		case 'r':
			max_reads = atoi(optarg);
			break;
		case 's':
			detach = 0;
			break;
		case 't':
			refreshival = atoi(optarg);
			if (refreshival < 0 && refreshival != -1)
				errx(1, "invalid timeout %d", refreshival);
			break;
		default:
			usage();
			/*NOTREACHED*/
		}
	}
	argc -= optind;
	argv += optind;

	if (pflags & PUFFS_FLAG_OPDUMP)
		detach = 0;
	pflags |= PUFFS_FLAG_BUILDPATH;
	pflags |= PUFFS_KFLAG_WTCACHE | PUFFS_KFLAG_IAONDEMAND;

	if (argc != 2)
		usage();

	PUFFSOP_INIT(pops);

	PUFFSOP_SET(pops, psshfs, fs, unmount);
	PUFFSOP_SETFSNOP(pops, sync); /* XXX */
	PUFFSOP_SET(pops, psshfs, fs, statvfs);
	PUFFSOP_SET(pops, psshfs, fs, nodetofh);
	PUFFSOP_SET(pops, psshfs, fs, fhtonode);

	PUFFSOP_SET(pops, psshfs, node, lookup);
	PUFFSOP_SET(pops, psshfs, node, create);
	PUFFSOP_SET(pops, psshfs, node, open);
	PUFFSOP_SET(pops, psshfs, node, inactive);
	PUFFSOP_SET(pops, psshfs, node, readdir);
	PUFFSOP_SET(pops, psshfs, node, getattr);
	PUFFSOP_SET(pops, psshfs, node, setattr);
	PUFFSOP_SET(pops, psshfs, node, mkdir);
	PUFFSOP_SET(pops, psshfs, node, remove);
	PUFFSOP_SET(pops, psshfs, node, readlink);
	PUFFSOP_SET(pops, psshfs, node, rmdir);
	PUFFSOP_SET(pops, psshfs, node, symlink);
	PUFFSOP_SET(pops, psshfs, node, rename);
	PUFFSOP_SET(pops, psshfs, node, read);
	PUFFSOP_SET(pops, psshfs, node, write);
	PUFFSOP_SET(pops, psshfs, node, reclaim);

	pu = puffs_init(pops, argv[0], "psshfs", &pctx, pflags);
	if (pu == NULL)
		err(1, "puffs_init");

	memset(&pctx, 0, sizeof(pctx));
	pctx.mounttime = time(NULL);
	pctx.refreshival = refreshival;
	pctx.numconnections = numconnections;

	userhost = argv[0];
	hostpath = strchr(userhost, ':');
	if (hostpath) {
		*hostpath++ = '\0';
		pctx.mountpath = hostpath;
	} else
		pctx.mountpath = ".";

	add_ssharg(&sshargs, &nargs, argv[0]);
	add_ssharg(&sshargs, &nargs, "sftp");
	pctx.sshargs = sshargs;

	pctx.nextino = 2;
	memset(root, 0, sizeof(struct psshfs_node));
	pn_root = puffs_pn_new(pu, root);
	if (pn_root == NULL)
		return errno;
	puffs_setroot(pu, pn_root);

	puffs_framev_init(pu, psbuf_read, psbuf_write, psbuf_cmp, NULL, notfn);

	signal(SIGHUP, takehup);
	puffs_ml_setloopfn(pu, psshfs_loopfn);
	if (pssh_connect(pu, PSSHFD_META) == -1)
		err(1, "can't connect meta");
	if (puffs_framev_addfd(pu, pctx.sshfd,
	    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
		err(1, "framebuf addfd meta");
	if (numconnections == 2) {
		if (pssh_connect(pu, PSSHFD_DATA) == -1)
			err(1, "can't connect data");
		if (puffs_framev_addfd(pu, pctx.sshfd_data,
		    PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1)
			err(1, "framebuf addfd data");
	} else {
		pctx.sshfd_data = pctx.sshfd;
	}

	if (exportfs)
		puffs_setfhsize(pu, sizeof(struct psshfs_fid),
		    PUFFS_FHFLAG_NFSV2 | PUFFS_FHFLAG_NFSV3);

	rva = &pn_root->pn_va;
	rva->va_fileid = pctx.nextino++;
	rva->va_nlink = 101; /* XXX */

	if (detach)
		if (puffs_daemon(pu, 1, 1) == -1)
			err(1, "puffs_daemon");

	if (puffs_mount(pu, argv[1], mntflags, puffs_getroot(pu)) == -1)
		err(1, "puffs_mount");
	if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1)
		err(1, "setblockingmode");

	if (puffs_mainloop(pu) == -1)
		err(1, "mainloop");
	puffs_exit(pu, 1);

	return 0;
}