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); }
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; }
/* 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); } }
/* 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; }
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; }
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; }
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; }
/* 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; }
/* 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); }
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; }
void opt_destroy (Opt o) { NP_ASSERT (o->magic == OPT_MAGIC); if (o->list) list_destroy (o->list); free (o); }
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); }
static void _path_free (Path path) { NP_ASSERT (path->ioctx == NULL); if (path->s) free (path->s); pthread_mutex_destroy (&path->lock); free (path); }
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; }
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; }
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; }
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); }
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; }
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; }
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); }
/* 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; }
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; }
/* 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; }
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; }
/* 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; }
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; }
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; }
/* 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; }
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; }
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); }