Exemple #1
0
static void
np_conn_destroy(Npconn *conn)
{
    int n;

    NP_ASSERT(conn != NULL);
    NP_ASSERT(conn->refcount == 0);
    /* issue 83: remove from srv->conns before destroying fidpool
     */
    np_srv_remove_conn (conn->srv, conn);
    if (conn->fidpool) {
        if ((n = np_fidpool_destroy(conn->fidpool)) > 0) {
            np_logmsg (conn->srv, "%s: connection closed with "
                       "%d unclunked fids",
                       np_conn_get_client_id (conn), n);
        }
        conn->fidpool = NULL;
    }
    if (conn->trans) {
        np_trans_destroy (conn->trans);
        conn->trans = NULL;
    }
    pthread_mutex_destroy(&conn->lock);
    pthread_mutex_destroy(&conn->wlock);
    pthread_cond_destroy(&conn->refcond);

    free(conn);
}
Exemple #2
0
static Path
_path_alloc (Npsrv *srv, char *s, int len)
{
    PathPool pp = srv->srvaux;
    Path path;

    xpthread_mutex_lock (&pp->lock);
    path = hash_find (pp->hash, s);
    if (path) {
        path_incref (path);
        free (s);
    } else {
        NP_ASSERT (errno == 0);
        if (!(path = malloc (sizeof (*path)))) {
            free (s);
            goto error;
        }
        path->refcount = 1;
        pthread_mutex_init (&path->lock, NULL);
        path->s = s;
        path->len = len;
        path->ioctx = NULL;
        if (!hash_insert (pp->hash, path->s, path)) {
            NP_ASSERT (errno == ENOMEM);
            goto error;
        }
    }
    xpthread_mutex_unlock (&pp->lock);
    return path;
error:
    xpthread_mutex_unlock (&pp->lock);
    if (path)
        _path_free (path);
    return NULL;
}
Exemple #3
0
/* refcount--
 * Destroy when refcount reaches zero.
 */
void
np_fid_decref (Npfid **fp)
{
	Npfid *f = *fp;
	int refcount;

	NP_ASSERT(f != NULL);
	NP_ASSERT(f->magic == FID_MAGIC);

	xpthread_mutex_lock (&f->lock);
	refcount = --f->refcount;
	if (refcount == 0)
		*fp = NULL;
	xpthread_mutex_unlock (&f->lock);

	if (refcount == 0) {
		Npfidpool *pool = f->conn->fidpool;
		int hash = f->fid % pool->size;

		xpthread_mutex_lock (&pool->lock);
		_unlink_fid (&pool->htable[hash], f);
		xpthread_mutex_unlock (&pool->lock);

		(void) _destroy_fid (f);
	}
}
Exemple #4
0
/* refcount++
 */
Npfid *
np_fid_incref (Npfid *f)
{
	NP_ASSERT(f != NULL);
	NP_ASSERT(f->magic == FID_MAGIC);

	xpthread_mutex_lock (&f->lock);
	f->refcount++;
	xpthread_mutex_unlock (&f->lock);

	return f;
}
Exemple #5
0
Fichier : opt.c Projet : kainz/diod
int
opt_check_allowed_csv (Opt o, const char *csv)
{
    Opt allow;
    ListIterator itr;
    char *item, *cpy, *p;
    int ret = 0;

    NP_ASSERT (o->magic == OPT_MAGIC);

    allow = opt_create ();
    opt_addf (allow, "%s", csv);

    if (!(itr = list_iterator_create (o->list)))
        msg_exit ("out of memory");
    while ((item = list_next (itr))) {
        if (!(cpy = strdup (item)))
            msg_exit ("out of memory");
        if ((p = strchr (cpy, '=')))
            *p = '\0';
        if (!opt_find (allow, cpy)) {
            ret = 1;
            free (cpy);
            break;
        }
        free (cpy);
    }
    list_iterator_destroy (itr);

    opt_destroy (allow);
    return ret;
}
Exemple #6
0
Fichier : opt.c Projet : kainz/diod
char *
opt_csv (Opt o)
{
    ListIterator itr;
    char *item, *s;
    int strsize = 1;
    int n;

    NP_ASSERT (o->magic == OPT_MAGIC);
    if (!(itr = list_iterator_create (o->list)))
        msg_exit ("out of memory");
    while ((item = list_next (itr)))
        strsize += strlen (item) + 1;
    list_iterator_reset (itr);
    if (!(s = malloc (strsize + list_count (o->list) - 1)))
        msg_exit ("out of memory");
    n = 0;
    while ((item = list_next (itr))) {
        snprintf (s + n, strsize - n, "%s%s", n > 0 ? "," : "", item);
        n = strlen (s);
    }
    list_iterator_destroy (itr);

    return s;
}
Exemple #7
0
int
ioctx_open (Npfid *fid, u32 flags, u32 mode)
{
    Fid *f = fid->aux;
    IOCtx ip = NULL;

    NP_ASSERT (f->ioctx == NULL);

    xpthread_mutex_lock (&f->path->lock);
    if ((f->flags & DIOD_FID_FLAGS_SHAREFD) && (flags & 3) == O_RDONLY) {
        for (ip = f->path->ioctx; ip != NULL; ip = ip->next) {
            if (ip->qid.type != P9_QTFILE)
                continue;
            if (ip->open_flags != flags)
                continue;
            if (ip->user->uid != fid->user->uid)
                continue;
            /* NOTE: we could do a stat and check qid? */
            _ioctx_incref (ip);
            break;
        }
    }
    if (!ip) {
        if ((ip = _ioctx_create_open (fid->user, f->path, flags, mode)))
            _link_ioctx (&f->path->ioctx, ip);
    }
    xpthread_mutex_unlock (&f->path->lock);
    if (!ip)
        goto error;
    f->ioctx = ip;
    return 0;
error:
    return -1;
}
Exemple #8
0
/* Retrieve export flags for the given aname.
 * Don't set np_uerror() here, just return 1 on match, 0 otherwise.
 */
int diod_fetch_xflags (Npstr *aname, int *xfp)
{
    List exports = diod_conf_get_exports ();
    ListIterator itr = NULL;
    Export *x;
    char *path = NULL;
    int res = 0;

    if (!(path = np_strdup (aname)))
        goto done;
    NP_ASSERT (exports != NULL);
    if (strstr (path, "/..") != NULL)
        goto done;
    if (!(itr = list_iterator_create (exports)))
        goto done;
    while ((x = list_next (itr))) {
        if (_match_export_path (x, path)) {
            if (xfp)
                *xfp = x->oflags;
            res = 1;
            break;
        }
    }
done:
    if (itr)
        list_iterator_destroy (itr);
    if (path)
        free (path);
    return res;
}
Exemple #9
0
Fichier : opt.c Projet : kainz/diod
/* Returns number of deletions.
 */
int
opt_delete (Opt o, char *key)
{
    NP_ASSERT (o->magic == OPT_MAGIC);

    return list_delete_all (o->list, (ListFindF)_match_key, key);   
}
Exemple #10
0
Fichier : opt.c Projet : kainz/diod
int
opt_addf (Opt o, const char *fmt, ...)
{
    va_list ap;
    char *csv, *item, *cpy;
    char *saveptr = NULL;
    int error;

    NP_ASSERT (o->magic == OPT_MAGIC);
    va_start (ap, fmt);
    error = vasprintf (&csv, fmt, ap);
    va_end (ap);
    if (error < 0)
        msg_exit ("out of memory");

    item = strtok_r (csv, ",", &saveptr);
    while (item) {
        if (!(cpy = strdup (item)))
            msg_exit ("out of memory");
        (void)list_delete_all (o->list, (ListFindF)_match_key, cpy);   
        if (!list_append (o->list, cpy))
            msg_exit ("out of memory");
        item = strtok_r (NULL, ",", &saveptr);
    }
    free (csv);
    return 1;
}
Exemple #11
0
Fichier : opt.c Projet : kainz/diod
void
opt_destroy (Opt o)
{
    NP_ASSERT (o->magic == OPT_MAGIC);
    if (o->list)
        list_destroy (o->list);
    free (o);
}
Exemple #12
0
void
np_conn_decref(Npconn *conn)
{
    xpthread_mutex_lock(&conn->lock);
    NP_ASSERT(conn->refcount > 0);
    conn->refcount--;
    xpthread_mutex_unlock(&conn->lock);
    xpthread_cond_signal(&conn->refcond);
}
Exemple #13
0
static void
_path_free (Path path)
{
    NP_ASSERT (path->ioctx == NULL);
    if (path->s)
        free (path->s);
    pthread_mutex_destroy (&path->lock);
    free (path);
}
Exemple #14
0
static void
_dirent2qid (struct dirent *d, Npqid *qid)
{
    NP_ASSERT (d->d_type != DT_UNKNOWN);
    qid->path = d->d_ino;
    qid->version = 0;
    qid->type = 0;
    if (d->d_type == DT_DIR)
        qid->type |= P9_QTDIR;
    if (d->d_type == DT_LNK)
        qid->type |= P9_QTSYMLINK;
}
Exemple #15
0
char *
diod_get_exports (char *name, void *a)
{
    List exports = diod_conf_get_exports ();
    List seen = NULL;
    ListIterator itr = NULL;
    Export *x;
    int len = 0;
    char *s = NULL;
    char *ret = NULL;

    NP_ASSERT (exports != NULL);

    if (!(seen = list_create (NULL))) {
        np_uerror (ENOMEM);
        goto done;
    }
    if (!(itr = list_iterator_create (exports))) {
        np_uerror (ENOMEM);
        goto done;
    }
    while ((x = list_next (itr))) {
        if (list_find_first (seen, (ListFindF)_strmatch, x->path))
            continue;
        if (!list_append (seen, x->path)) {
            np_uerror (ENOMEM);
            goto done;
        }
        if (!(x->oflags & XFLAGS_SUPPRESS)) {
            if (aspf (&s, &len, "%s %s %s %s\n",
                      x->path,
                      x->opts ? x->opts : "-",
                      x->users ? x->users : "-",
                      x->hosts ? x->hosts : "-") < 0) { 
                np_uerror (ENOMEM);
                goto done;
            }
        }
    }
    if (diod_conf_get_exportall ())
        if (!_get_mounts (&s, &len, seen))
            goto done;
    ret = s;
done:
    if (itr)
        list_iterator_destroy (itr);
    if (seen)
        list_destroy (seen);
    return ret;
}
Exemple #16
0
Fichier : opt.c Projet : kainz/diod
char *
opt_find (Opt o, char *key)
{
    char *s;

    NP_ASSERT (o->magic == OPT_MAGIC);

    if (strchr (key, '='))
        s = list_find_first (o->list, (ListFindF)_match_keyval, key);
    else
        s = list_find_first (o->list, (ListFindF)_match_key, key);

    return s ? _optstr (s) : NULL;
}
Exemple #17
0
void
spf (char *s, int len, const char *fmt, ...)
{
        va_list ap;
        int n = strlen (s);

        len -= n;
        s += n;
        NP_ASSERT (len > 0);

        va_start (ap, fmt);
        vsnprintf (s, len, fmt, ap); /* ignore overflow */
        va_end (ap);
}
Exemple #18
0
void
np_usercache_destroy (Npsrv *srv)
{
    Npusercache *uc;
    Npuser *u;

    NP_ASSERT (srv->usercache != NULL);
    uc = srv->usercache;

    u = uc->users;
    while (u)
        u = _usercache_del (srv, NULL, u);
    free (uc);
    srv->usercache = NULL;
}
Exemple #19
0
static Npfid *
_destroy_fid (Npfid *f)
{
	Npsrv *srv;
	Npfid *next;

	NP_ASSERT(f != NULL);
	NP_ASSERT(f->magic == FID_MAGIC);

	srv = f->conn->srv;
	next = f->next;
	if (f->refcount > 0 && (srv->flags & SRV_FLAGS_DEBUG_FIDPOOL)) {
		np_logmsg (srv, "_destroy_fid: fid %d has %d refs",
			   f->fid, f->refcount);
	}
	if ((f->type & P9_QTAUTH)) {
		if (srv->auth && srv->auth->clunk)
			(*srv->auth->clunk)(f);
	} else if ((f->type & P9_QTTMP)) {
		np_ctl_fiddestroy (f);
	} else {
		if (srv->fiddestroy)
			(*srv->fiddestroy)(f);
	}	
	if (f->user)
		np_user_decref(f->user);
	if (f->tpool)
		np_tpool_decref(f->tpool);
	if (f->aname)
		free (f->aname);
	pthread_mutex_destroy (&f->lock);
	f->magic = FID_MAGIC_FREED;
	free(f);

	return next;
}
Exemple #20
0
void
np_tpool_select (Npreq *req)
{
	Npsrv *srv = req->conn->srv;
	Nptpool *tp = NULL;

	NP_ASSERT (srv->tpool != NULL);
	if (!req->fid || req->fid->tpool)
		return;

	xpthread_mutex_lock (&srv->lock);
	if ((srv->flags & SRV_FLAGS_TPOOL_SINGLE) || !req->fid->aname
						  || *req->fid->aname != '/') {
		tp = srv->tpool;
	}
	if (!tp) {
		for (tp = srv->tpool; tp != NULL; tp = tp->next) {
			if (!strcmp (req->fid->aname, tp->name))
				break;
		}
	}
	if (!tp) {
		tp = np_tpool_create(srv, req->fid->aname);
		if (tp) {
			NP_ASSERT (srv->tpool); /* default tpool */
			tp->next = srv->tpool->next;
			srv->tpool->next = tp;
		} else
			np_logerr (srv, "np_tpool_create %s", req->fid->aname);
	}
	if (tp) {
		np_tpool_incref (tp);
		req->fid->tpool = tp;
	}
	xpthread_mutex_unlock (&srv->lock);
}
Exemple #21
0
/* Clone newfid->aux from fid->aux.
 */
Fid *
diod_fidclone (Npfid *newfid, Npfid *fid)
{
    Fid *f = fid->aux;
    Fid *nf = malloc (sizeof (*f));

    NP_ASSERT (newfid->aux == NULL);
    if (nf) {
        nf->flags = f->flags;
        nf->ioctx = NULL;
        nf->path = path_incref (f->path);
    }
    newfid->aux = nf;
  
    return nf;
}
Exemple #22
0
int
np_fidpool_count(Npfidpool *pool)
{
	int i;
	Npfid *f;
	int count = 0;

	xpthread_mutex_lock(&pool->lock);
	for(i = 0; i < pool->size; i++) {
		for (f = pool->htable[i]; f != NULL; f = f->next) {
			NP_ASSERT(f->magic == FID_MAGIC);
			count++;
		}
	}
	xpthread_mutex_unlock(&pool->lock);

	return count;
}
Exemple #23
0
/* Allocate local fid struct and attach to fid->aux.
 */
Fid *
diod_fidalloc (Npfid *fid, Npstr *ns)
{
    Fid *f = malloc (sizeof (*f));

    NP_ASSERT (fid->aux == NULL);
    if (f) {
        f->flags = 0;
        f->ioctx = NULL;
        f->path = path_create (fid->conn->srv, ns);
        if (!f->path) {
            free (f);
            f = NULL;
        }
    }
    fid->aux = f;
  
    return f;
}
Exemple #24
0
int
ioctx_close (Npfid *fid, int seterrno)
{
    Fid *f = fid->aux;
    int n;
    int rc = 0;

    NP_ASSERT (f->ioctx != NULL);

    xpthread_mutex_lock (&f->path->lock);
    n = _ioctx_decref (f->ioctx);
    if (n == 0)
        _unlink_ioctx (&f->path->ioctx, f->ioctx);
    xpthread_mutex_unlock (&f->path->lock);
    if (n == 0)
        rc = _ioctx_close_destroy (f->ioctx, seterrno);
    f->ioctx = NULL;

    return rc;
}
Exemple #25
0
/* Called from attach to determine if aname is valid for user/conn.
 * (Now via fcall.c::np_attach, not through diod_attach)
 */
int
diod_match_exports (char *path, Npconn *conn, Npuser *user, int *xfp)
{
    List exports = diod_conf_get_exports ();
    ListIterator itr = NULL;
    Export *x;
    int res = 0; /* DENIED */

    NP_ASSERT (exports != NULL);
    if (strstr (path, "/..") != NULL) {
        np_uerror (EPERM);
        goto done;
    }
    if (!(itr = list_iterator_create (exports))) {
        np_uerror (ENOMEM);
        goto done;
    }
    while (res == 0 && (x = list_next (itr))) {
        if (!_match_export_path (x, path))
            continue;
        if ((x->oflags & XFLAGS_SUPPRESS))
            goto done;
        if (!_match_export_hosts (x, conn))
            goto done;
        if (!_match_export_users (x, user))
            goto done;
        if (xfp)
            *xfp = x->oflags;
        res = 1;
    }
    if (res == 0 && diod_conf_get_exportall ())
        res = _match_mounts (path, xfp);
    if (res == 0 && np_rerror () == 0)
        np_uerror (EPERM);
done:
    if (itr)
        list_iterator_destroy (itr);
    return res;
}
Exemple #26
0
int
np_usercache_create (Npsrv *srv)
{
    Npusercache *uc;

    NP_ASSERT (srv->usercache == NULL);
    if (!(uc = malloc (sizeof (*uc)))) {
        np_uerror (ENOMEM);
        return -1;
    }
    uc->users = NULL;
    pthread_mutex_init (&uc->lock, NULL);
    uc->ttl	= 60;
    srv->usercache = uc;

    //if (!np_ctl_addfile (srv->ctlroot, "usercache", _get_usercache,srv,0)) {
    //	free (srv->usercache);
    //	return -1;
    //}

    return 0;
}
Exemple #27
0
int
npc_readdir (Npcfid *fid, u64 offset, char *data, u32 count)
{
	Npfcall *tc = NULL, *rc = NULL;
	int ret = -1;

	if (!(tc = np_create_treaddir(fid->fid, offset, count))) {
		np_uerror (ENOMEM);
		goto done;
	}
	if (fid->fsys->rpc(fid->fsys, tc, &rc) < 0)
		goto done;
	NP_ASSERT(rc->u.rreaddir.count <= count);
	memcpy (data, rc->u.rreaddir.data, rc->u.rreaddir.count);
	ret = rc->u.rreaddir.count;
done:
	if (tc)
		free(tc);
	if (rc)
		free(rc);	
	return ret;
}
Exemple #28
0
/* Set up listen ports based on list of strings, which can be either
 * host:port or /path/to/unix_domain_socket format.
 * Return the number of file descriptors opened (can return 0).
 */
int
diod_sock_listen (List l, struct pollfd **fdsp, int *nfdsp)
{
    ListIterator itr;
    char *s, *host, *port;
    int n, ret = 0;

    if (!(itr = list_iterator_create(l))) {
        msg ("out of memory");
        goto done;
    }
    while ((s = list_next(itr))) {
        if (s[0] == '/') {
            if ((n = _setup_one_unix (s, fdsp, nfdsp)) == 0)
                goto done;
            ret += n; 
        } else {
            if (!(host = strdup (s))) {
                msg ("out of memory");
                goto done;
            }
            port = strchr (host, ':');
            NP_ASSERT (port != NULL);
            *port++ = '\0';
            if ((n = _setup_one_inet (host, port, fdsp, nfdsp)) == 0) {
                free (host);
                goto done;
            }
            ret += n;
            free (host);
        }
    }
    ret = _listen_fds (*fdsp, *nfdsp);
done:
    if (itr)
        list_iterator_destroy(itr);
    return ret;
}
Exemple #29
0
Fichier : opt.c Projet : kainz/diod
int
opt_vscanf (Opt o, const char *fmt, va_list ap)
{
    ListIterator itr;
    char *item;
    int ret = 0;

    NP_ASSERT (o->magic == OPT_MAGIC);

    if (!(itr = list_iterator_create (o->list)))
        msg_exit ("out of memory");
    while ((item = list_next (itr))) {
        va_list vacpy;

        va_copy (vacpy, ap);
        ret = vsscanf (item, fmt, vacpy);
        va_end (vacpy);

        if (ret > 0)
            break;
    }
    list_iterator_destroy (itr);
    return ret;
}
Exemple #30
0
int
main (int argc, char *argv[])
{
    char *dir = NULL;
    char *spec, *host;
    char *nspec = NULL;
    int c, i;
    int nopt = 0;
    int vopt = 0;
    int fopt = 0;
    int aopt = 0;
    int dopt = 0;
    int rfd = -1, wfd = -1;
    Opt o; 

    diod_log_init (argv[0]);

    o = opt_create ();

    opterr = 0;
    while ((c = GETOPT (argc, argv, OPTIONS, longopts)) != -1) {
        switch (c) {
            case 'f':   /* --fake-mount */
                fopt = 1;
                break;
            case 'n':   /* --no-mtab */
                nopt = 1;
                break;
            case 'v':   /* --verbose */
                vopt++;
                break;
            case 'o':   /* --options OPT[,OPT]... */
                opt_addf (o, "%s", optarg);
                break;
            case 'a':   /* --9nbd-attach */
                aopt++;
                break;
            case 'd':   /* --9nbd-detach */
                dopt++;
                break;
            default:
                usage ();
        }
    }

    /* Take care of 9nbd operations and exit.
     */
    if (aopt) {
        _nbd_attach (o, argc - optind, argv + optind, nopt, vopt);
        exit (0);
    }
    if (dopt) {
        _nbd_detach (o, argc - optind, argv + optind, nopt, vopt);
        exit (0);
    }

    if (optind != argc - 2)
        usage ();

    if (geteuid () != 0)
        msg_exit ("you must be root");

    spec = argv[optind++];
    dir = argv[optind++];
    host = _parse_spec (spec, o);

    _verify_mountpoint (dir);

    /* Remount - only pass mount flags into the VFS for an existing mount.
     * Take care of it here and exit.
     */
    if (opt_find (o, "remount")) {
        if (opt_check_allowed_csv (o, "ro,rw,aname,remount"))
            msg_exit ("-oremount can only be used with ro,rw");
        _diod_remount (o, spec, dir, vopt, fopt);
        goto done;
    }

    /* Ensure uname and access are set, and to diod-compatible values.
     * The uname user becomes the euid which will be used by munge auth.
     */
    _parse_uname_access (o);
     if (seteuid (_uname2uid (opt_find (o, "uname"))) < 0)
        err_exit ("seteuid");

    /* We require -otrans=fd because auth occurs in user space, then live fd
     * is passed to the kernel via -orfdno,wfdno.
     */
    if (!opt_find (o, "trans"))
        opt_addf (o, "trans=%s", "fd");
    else if (!opt_find (o, "trans=fd"))
        msg_exit ("only -otrans=fd transport is supported");

    /* Set msize if not already set.  Validate it later.
     */
    if (!opt_find (o, "msize"))
        opt_addf (o, "msize=%d", DIOD_DEFAULT_MSIZE);

    /* Only .L version is supported.
     */
    if (!opt_find (o, "version"))
        opt_addf (o, "version=%s", "9p2000.L");
    else if (!opt_find (o, "version=9p2000.L"))
        msg_exit ("only -oversion=9p2000.L is supported (little p, big L)");

    /* Set debug level.
     */
    if (!opt_find (o, "debug"))
        opt_addf (o, "debug=%d", 0x1); /* send errors to dmesg */

    /* Set rwdepth (number of concurrent reads with buffer > msize).
     * N.B. this option is not upstream yet but unknown options are ignored.
     */
    if (!opt_find (o, "rwdepth"))
        opt_addf (o, "rwdepth=%d", 1);

    /* Server is on an inherited file descriptor.
     * For testing, we start server on a socketpair duped to fd 0.
     */
    if (opt_find (o, "rfdno") || opt_find (o, "wfdno")) {
        if (!opt_scanf (o, "rfdno=%d", &rfd) || !opt_scanf (o, "wfdno=%d",&wfd))
            msg_exit ("-orfdno,wfdno must be used together");
        nopt = 1; /* force no mtab */

    /* Connect to server on UNIX domain socket
     */
    } else if (host[0] == '/') {
        if (opt_find (o, "port"))
            msg_exit ("-oport won't work with UNIX domain socket");
        if ((rfd = diod_sock_connect_unix (host, 0)) < 0)
            exit (1);
        wfd = rfd;

        opt_addf (o, "rfdno=%d", rfd);
        opt_addf (o, "wfdno=%d", wfd);

    /* Connect to server on IANA port (or user-specified) and host.
     */
    } else {
        char *port = opt_find (o, "port");
        hostlist_iterator_t hi;
        hostlist_t hl; 
        char *h;

        if (!port)
            port = "564";
        if (!(hl = hostlist_create (host)))
            msg_exit ("error parsing host string: %s", host);
        if (!(hi = hostlist_iterator_create (hl)))
            msg_exit ("out of memory");
        while ((h = hostlist_next (hi))) {
            if (vopt)
                msg ("trying to connect to %s:%s", h, port);
            if ((rfd = diod_sock_connect_inet (h, port, DIOD_SOCK_QUIET)) >= 0)
                break;
        }
        if (h) { /* create new 'spec' string identifying successful host */
            char *p = strchr (spec , ':');
            int len = strlen (h) + (p ? strlen (p) : 0) + 1;

            if (!(nspec = malloc (len)))
                msg_exit ("out of memory");
            snprintf (nspec, len, "%s%s", h, p ? p : "");
        }
        hostlist_destroy (hl);
        if (rfd < 0)
            msg_exit ("could not connect to server(s), giving up");
        wfd = rfd;
        
        opt_delete (o, "port");
        opt_addf (o, "rfdno=%d", rfd);
        opt_addf (o, "wfdno=%d", wfd);
    }

    NP_ASSERT (opt_find (o, "trans=fd"));
    NP_ASSERT (opt_scanf (o, "msize=%d", &i));
    NP_ASSERT (opt_find (o, "version=9p2000.L"));
    NP_ASSERT (opt_scanf (o, "debug=%d", &i) || opt_scanf (o, "debug=%x", &i));
    NP_ASSERT (opt_scanf (o, "wfdno=%d", &i) && opt_scanf (o, "rfdno=%d", &i));
    NP_ASSERT ((opt_find (o, "access=user") && opt_find(o, "uname=root"))
         || (opt_scanf (o, "access=%d", &i) && opt_find(o, "uname")));

    NP_ASSERT (!opt_find (o, "port"));

    _diod_mount (o, rfd, wfd, nspec ? nspec : spec, dir, vopt, fopt, nopt);

done:
    opt_destroy (o);
    exit (0);
}