Npfcall * np_getlock(Npreq *req, Npfcall *tc) { Npfid *fid = req->fid; Npfcall *rc = NULL; if (!fid) { np_uerror (EIO); goto done; } if (fid->type & P9_QTTMP) { np_uerror (EPERM); goto done; } else { if (np_setfsid (req, fid->user, -1) < 0) goto done; if (!req->conn->srv->getlock) { np_uerror (ENOSYS); goto done; } rc = (*req->conn->srv->getlock)(fid, tc->u.tgetlock.type, tc->u.tgetlock.start, tc->u.tgetlock.length, tc->u.tgetlock.proc_id, &tc->u.tgetlock.client_id); } done: return rc; }
/* Tclunk - close a file. */ Npfcall* diod_clunk (Npfid *fid) { Fid *f = fid->aux; Npfcall *ret; if (f->dir) { if (closedir(f->dir) < 0) { np_uerror (errno); goto error_quiet; } } else if (f->fd != -1) { if (close (f->fd) < 0) { np_uerror (errno); goto error_quiet; } } if (!(ret = np_create_rclunk ())) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_clunk %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: return NULL; }
/* Tstatfs - read file system information. */ Npfcall* diod_statfs (Npfid *fid) { Fid *f = fid->aux; struct statfs sb; Npfcall *ret; u64 fsid; if (statfs (f->path, &sb) < 0) { np_uerror (errno); goto error; } fsid = (u64)sb.f_fsid.__val[0] | ((u64)sb.f_fsid.__val[1] << 32); if (!(ret = np_create_rstatfs(sb.f_type, sb.f_bsize, sb.f_blocks, sb.f_bfree, sb.f_bavail, sb.f_files, sb.f_ffree, fsid, sb.f_namelen))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_statfs %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); return NULL; }
Npsrv* np_srv_create(int nwthread, int flags) { Npsrv *srv = NULL; np_uerror (0); if (!(srv = malloc(sizeof(*srv)))) { np_uerror (ENOMEM); goto error; } memset (srv, 0, sizeof (*srv)); pthread_mutex_init(&srv->lock, NULL); pthread_cond_init(&srv->conncountcond, NULL); srv->msize = 8216; srv->flags = flags; srv->netroot = np_net_make_root(); if (srv->netroot == 0) goto error; if (np_usercache_create (srv) < 0) goto error; srv->nwthread = nwthread; if (!(srv->tpool = np_tpool_create (srv, "default"))) goto error; np_tpool_incref (srv->tpool); np_assert_srv = srv; return srv; error: if (srv) np_srv_destroy (srv); return NULL; }
/* Tread - read from a file or directory. */ Npfcall* diod_read (Npfid *fid, u64 offset, u32 count, Npreq *req) { Fid *f = fid->aux; Npfcall *ret = NULL; ssize_t n; if (!(ret = np_alloc_rread (count))) { np_uerror (ENOMEM); goto error; } if ((n = pread (f->fd, ret->u.rread.data, count, offset)) < 0) { np_uerror (errno); goto error_quiet; } np_set_rread_count (ret, n); return ret; error: errn (np_rerror (), "diod_read %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); return NULL; }
int xattr_close (Npfid *fid) { Fid *f = fid->aux; int rc = 0; if (f->xattr) { if ((f->xattr->flags & XATTR_FLAGS_SET)) { if (f->xattr->len > 0) { if (lsetxattr (path_s (f->path), f->xattr->name, f->xattr->buf, f->xattr->len, f->xattr->setflags) < 0) { np_uerror (errno); rc = -1; } } else if (f->xattr->len == 0) { if (lremovexattr (path_s (f->path), f->xattr->name) < 0) { np_uerror (errno); rc = -1; } } } _xattr_destroy (&f->xattr); } return rc; }
static int np_wthread_create(Nptpool *tp) { int err; Npwthread *wt; /* assert srv->lock held */ if (!(wt = malloc(sizeof(*wt)))) { np_uerror (ENOMEM); goto error; } memset (wt, 0, sizeof (*wt)); wt->tpool = tp; wt->shutdown = 0; wt->fsuid = geteuid (); wt->fsgid = getegid (); wt->privcap = (wt->fsuid == 0 ? 1 : 0); if ((err = pthread_create(&wt->thread, NULL, np_wthread_proc, wt))) { np_uerror (err); goto error; } wt->next = tp->wthreads; tp->wthreads = wt; return 0; error: return -1; }
/* This needs to be called with the usercache lock held. * I don't think it's thread safe. -jg */ static int _getgrouplist (Npsrv *srv, Npuser *u) { int i, ret = -1; gid_t *sgcpy; u->nsg = sysconf(_SC_NGROUPS_MAX); if (u->nsg < 65536) u->nsg = 65536; if (!(u->sg = malloc (u->nsg * sizeof (gid_t)))) { np_uerror (ENOMEM); np_logerr (srv, "_alloc_user: %s", u->uname); goto done; } if (getgrouplist(u->uname, u->gid, u->sg, &u->nsg) == -1) { np_logerr (srv, "_alloc_user: %s: getgrouplist", u->uname); if (np_rerror () == 0) np_uerror (EPERM); goto done; } if ((sgcpy = malloc (u->nsg * sizeof (gid_t)))) { for (i = 0; i < u->nsg; i++) sgcpy[i] = u->sg[i]; free (u->sg); u->sg = sgcpy; } ret = 0; done: return ret; }
static Npuser * _alloc_user (Npsrv *srv, struct passwd *pwd) { Npuser *u; if (!(u = malloc (sizeof (*u)))) { np_uerror (ENOMEM); np_logerr (srv, "_alloc_user: %s", pwd->pw_name); goto error; } u->sg = NULL; u->nsg = 0; if (!(u->uname = strdup (pwd->pw_name))) { np_uerror (ENOMEM); np_logerr (srv, "_alloc_user: %s", pwd->pw_name); goto error; } u->uid = pwd->pw_uid; u->gid = pwd->pw_gid; if (u->uid != 0 && _getgrouplist(srv, u) < 0) goto error; pthread_mutex_init (&u->lock, NULL); u->refcount = 0; u->t = time (NULL); u->next = NULL; if (srv->flags & SRV_FLAGS_DEBUG_USER) np_logmsg (srv, "user lookup: %d", u->uid); return u; error: if (u) _free_user (u); return NULL; }
/* Tread - read from a file or directory. */ Npfcall* diod_read (Npfid *fid, u64 offset, u32 count, Npreq *req) { Fid *f = fid->aux; Npfcall *ret = NULL; ssize_t n; if (!f->ioctx) { msg ("diod_read: fid is not open"); np_uerror (EBADF); goto error; } if (!(ret = np_alloc_rread (count))) { np_uerror (ENOMEM); goto error; } n = ioctx_pread (f->ioctx, ret->u.rread.data, count, offset); if (n < 0) { np_uerror (errno); goto error_quiet; } np_set_rread_count (ret, n); return ret; error: errn (np_rerror (), "diod_read %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), path_s (f->path)); error_quiet: if (ret) free (ret); return NULL; }
/* Twrite - write to a file. */ Npfcall* diod_write (Npfid *fid, u64 offset, u32 count, u8 *data, Npreq *req) { Fid *f = fid->aux; Npfcall *ret; ssize_t n; if (!f->ioctx) { msg ("diod_write: fid is not open"); np_uerror (EBADF); goto error; } if ((f->flags & DIOD_FID_FLAGS_ROFS)) { np_uerror (EROFS); goto error_quiet; } if ((n = ioctx_pwrite (f->ioctx, data, count, offset)) < 0) { np_uerror (errno); goto error_quiet; } if (!(ret = np_create_rwrite (n))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_write %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), path_s (f->path)); error_quiet: return NULL; }
Npfcall* diod_fsync (Npfid *fid) { Fid *f = fid->aux; Npfcall *ret; if ((f->flags & DIOD_FID_FLAGS_ROFS)) { np_uerror (EROFS); goto error_quiet; } if (!f->ioctx) { msg ("diod_fsync: fid is not open"); np_uerror (EBADF); goto error; } if (ioctx_fsync (f->ioctx) < 0) { np_uerror (errno); goto error_quiet; } if (!((ret = np_create_rfsync ()))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_fsync %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), path_s (f->path)); error_quiet: return NULL; }
Npfcall* diod_readdir(Npfid *fid, u64 offset, u32 count, Npreq *req) { int n; Fid *f = fid->aux; Npfcall *ret; if (!f->ioctx) { msg ("diod_readdir: fid is not open"); np_uerror (EBADF); goto error; } if (!(ret = np_create_rreaddir (count))) { np_uerror (ENOMEM); goto error; } n = _read_dir_linux (f, ret->u.rreaddir.data, offset, count); if (np_rerror ()) { free (ret); ret = NULL; } else np_finalize_rreaddir (ret, n); return ret; error: errn (np_rerror (), "diod_readdir %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), path_s (f->path)); return NULL; }
Npfcall* diod_lcreate(Npfid *fid, Npstr *name, u32 flags, u32 mode, u32 gid) { Fid *f = fid->aux; Npfcall *ret = NULL; char *npath = NULL; Npqid qid; int fd = -1; struct stat sb; mode_t saved_umask; int created = 0; u32 iounit = 0; /* client will use msize-P9_IOHDRSZ */ if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if (!(flags & O_CREAT)) /* can't happen? */ flags |= O_CREAT; if (!(npath = _mkpath(f->path, name))) { np_uerror (ENOMEM); goto error; } saved_umask = umask(0); if ((fd = open (npath, flags, mode)) < 0) { np_uerror (errno); goto error_quiet; } created = 1; umask(saved_umask); if (fstat (fd, &sb) < 0) { np_uerror (errno); goto error; /* shouldn't happen? */ } _ustat2qid (&sb, &qid); //iounit = sb.st_blksize; if (!((ret = np_create_rlcreate (&qid, iounit)))) { np_uerror (ENOMEM); goto error; } free (f->path); f->path = npath; f->fd = fd; return ret; error: errn (np_rerror (), "diod_lcreate %s@%s:%s/%.*s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path, name->len, name->str); error_quiet: if (fd >= 0) { (void)close (fd); } if (created && npath) (void)unlink (npath); if (npath) free (npath); if (ret) free (ret); return NULL; }
Npcfid* npc_auth (Npcfsys *fs, char *aname, u32 uid, AuthFun auth) { Npcfid *afid = NULL; Npfcall *tc = NULL, *rc = NULL; if (!(afid = npc_fid_alloc (fs))) goto done; if (!(tc = np_create_tauth (afid->fid, NULL, aname, uid))) { np_uerror (ENOMEM); npc_fid_free (afid); afid = NULL; goto done; } if (afid->fsys->rpc (afid->fsys, tc, &rc) < 0) { npc_fid_free (afid); afid = NULL; goto done; } if (auth && auth (afid, uid) < 0) { int saved_err = np_rerror (); (void)npc_clunk (afid); afid = NULL; np_uerror (saved_err); goto done; } done: if (tc) free(tc); if (rc) free(rc); return afid; }
static int np_wthread_create(Nptpool *tp) { int err; Npwthread *wt; if (!(wt = malloc(sizeof(*wt)))) { np_uerror (ENOMEM); goto error; } memset (wt, 0, sizeof (*wt)); wt->tpool = tp; wt->shutdown = 0; wt->state = WT_START; wt->fsuid = geteuid (); wt->sguid = P9_NONUNAME; wt->fsgid = getegid (); if ((err = pthread_create(&wt->thread, NULL, np_wthread_proc, wt))) { np_uerror (err); goto error; } xpthread_mutex_lock(&tp->lock); wt->next = tp->wthreads; tp->wthreads = wt; xpthread_mutex_unlock(&tp->lock); return 0; error: return -1; }
static int _lgetxattr (Xattr x, const char *path) { ssize_t len; if (x->name) len = lgetxattr (path, x->name, NULL, 0); else len = llistxattr (path, NULL, 0); if (len < 0) { np_uerror (errno); return -1; } assert (x->buf == NULL); x->buf = malloc (len); if (!x->buf) { np_uerror (ENOMEM); return -1; } if (x->name) x->len = lgetxattr (path, x->name, x->buf, len); else x->len = llistxattr (path, x->buf, len); if (x->len < 0) { np_uerror (errno); return -1; } return 0; }
Npfcall* diod_fsync (Npfid *fid) { Fid *f = fid->aux; Npfcall *ret = NULL; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if (fsync(f->dir ? dirfd (f->dir) : f->fd) < 0) { np_uerror (errno); goto error_quiet; } if (!((ret = np_create_rfsync ()))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_fsync %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); return NULL; }
static int _mkdir_p (Npcfid *root, char *path, mode_t mode) { struct stat sb; char *cpy; int res = 0; if (npc_stat (root, path, &sb) == 0) { if (!S_ISDIR (sb.st_mode)) { np_uerror (ENOTDIR); return -1; } return 0; } if (!(cpy = strdup (path))) { np_uerror (ENOMEM); return -1; } res = _mkdir_p (root, dirname (cpy), mode); free (cpy); if (res == 0) res = npc_mkdir_bypath (root, path, mode); return res; }
/* Locking note: * Implement POSIX locks in terms of BSD flock locks. * This at least gets distributed whole-file locking to work. * Strategies for distributed record locking will deadlock. */ Npfcall* diod_lock (Npfid *fid, u8 type, u32 flags, u64 start, u64 length, u32 proc_id, Npstr *client_id) { Fid *f = fid->aux; Npfcall *ret = NULL; u8 status = P9_LOCK_ERROR; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if (flags & ~P9_LOCK_FLAGS_BLOCK) { /* only one valid flag for now */ np_uerror (EINVAL); /* (which we ignore) */ goto error; } if (_fidstat(f) < 0) goto error_quiet; switch (type) { case F_UNLCK: if (flock (f->fd, LOCK_UN) >= 0) { status = P9_LOCK_SUCCESS; f->lock_type = LOCK_UN; } else status = P9_LOCK_ERROR; break; case F_RDLCK: if (flock (f->fd, LOCK_SH | LOCK_NB) >= 0) { status = P9_LOCK_SUCCESS; f->lock_type = LOCK_SH; } else if (errno == EWOULDBLOCK) { status = P9_LOCK_BLOCKED; } else status = P9_LOCK_ERROR; break; case F_WRLCK: if (flock (f->fd, LOCK_EX | LOCK_NB) >= 0) { status = P9_LOCK_SUCCESS; f->lock_type = LOCK_EX; } else if (errno == EWOULDBLOCK) { status = P9_LOCK_BLOCKED; } break; default: np_uerror (EINVAL); goto error; } if (!((ret = np_create_rlock (status)))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_lock %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); return NULL; }
static Nptpool * np_tpool_create(Npsrv *srv, char *name) { Nptpool *tp; /* assert srv->lock held */ if (!(tp = malloc (sizeof (*tp)))) { np_uerror (ENOMEM); goto error; } memset (tp, 0, sizeof (*tp)); if (!(tp->name = strdup (name))) { np_uerror (ENOMEM); goto error; } tp->srv = srv; tp->refcount = 0; pthread_mutex_init(&tp->lock, NULL); pthread_cond_init(&tp->reqcond, NULL); for(tp->nwthread = 0; tp->nwthread < srv->nwthread; tp->nwthread++) { if (np_wthread_create(tp) < 0) goto error; } return tp; error: if (tp) np_tpool_destroy (tp); return NULL; }
Npfcall* diod_readlink(Npfid *fid) { Fid *f = fid->aux; Npfcall *ret = NULL; char target[PATH_MAX + 1]; int n; if ((n = readlink (f->path, target, sizeof(target))) < 0) { np_uerror (errno); goto error_quiet; } target[n] = '\0'; if (!(ret = np_create_rreadlink(target))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_readlink %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); return NULL; }
Npcpool * npc_create_pool(u32 maxid) { Npcpool *p; p = malloc(sizeof(*p)); if (!p) { np_uerror(ENOMEM); return NULL; } p->maxid = maxid; pthread_mutex_init(&p->lock, NULL); pthread_cond_init(&p->cond, NULL); p->msize = 32; /* 256 ids */ p->map = malloc(p->msize); if (!p->map) { np_uerror(ENOMEM); free(p); return NULL; } memset(p->map, 0, p->msize); return p; }
static int writeafid(Npfid *afid, u64 offset, u32 count, u8 *data) { da_t da; int ret = -1; if (!afid || !afid->aux || !data || count == 0) { np_uerror (EIO); err ("writeafid: invalid arguments"); goto done; } da = afid->aux; assert (da->magic == DIOD_AUTH_MAGIC); if (offset == 0 && !da->datastr) { da->datastr = malloc (count + 1); } else if (da->datastr && offset == strlen (da->datastr)) { da->datastr = realloc (da->datastr, offset + count + 1); } else { np_uerror (EIO); err ("writeafid: write at unexpected offset"); goto done; } if (!da->datastr) { np_uerror (ENOMEM); err ("writeafid"); goto done; } memcpy (da->datastr + offset, data, count); da->datastr[offset + count] = '\0'; ret = count; done: return ret; }
/* Twrite - write to a file. */ Npfcall* diod_write (Npfid *fid, u64 offset, u32 count, u8 *data, Npreq *req) { Fid *f = fid->aux; Npfcall *ret; ssize_t n; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if ((n = pwrite (f->fd, data, count, offset)) < 0) { np_uerror (errno); goto error_quiet; } if (!(ret = np_create_rwrite (n))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_write %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: return NULL; }
/* This is called from libnpclient user space. * It drives the client end of authentication. */ int diod_auth (Npcfid *afid, u32 uid) { int ret = -1; #if HAVE_LIBMUNGE char *cred = NULL; munge_ctx_t ctx = NULL; if (!(ctx = munge_ctx_create ())) { np_uerror (ENOMEM); goto done; } if (munge_encode (&cred, ctx, NULL, 0) != EMUNGE_SUCCESS) { np_uerror (EPERM); goto done; } if (npc_puts (afid, cred) < 0) { goto done; } ret = 0; done: if (ctx) munge_ctx_destroy (ctx); #endif return ret; }
/* Tremove - remove a file or directory. */ Npfcall* diod_remove (Npfid *fid) { Fid *f = fid->aux; Npfcall *ret; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if (remove (f->path) < 0) { np_uerror (errno); goto error_quiet; } if (!(ret = np_create_rremove ())) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_remove %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: return NULL; }
static int _match_mounts (char *path, int *xfp) { List exports = diod_conf_get_mounts (); ListIterator itr = NULL; Export *x; int res = 0; /* DENIED */ if (!exports) { np_uerror (ENOMEM); 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)) { *xfp = x->oflags; res = 1; } } if (res == 0) np_uerror (EPERM); done: if (itr) list_iterator_destroy (itr); if (exports) list_destroy (exports); return res; }
Npcfsys* npc_start (int rfd, int wfd, int msize, int flags) { Npcfsys *fs; Npfcall *tc = NULL, *rc = NULL; if ((flags & NPC_MULTI_RPC)) fs = npc_create_mtfsys (rfd, wfd, msize, flags); else fs = npc_create_fsys (rfd, wfd, msize, flags); if (!fs) goto done; if (!(tc = np_create_tversion (msize, "9P2000.L"))) { np_uerror (ENOMEM); goto done; } if (fs->rpc (fs, tc, &rc) < 0) goto done; if (rc->u.rversion.msize < msize) fs->msize = rc->u.rversion.msize; if (np_strcmp (&rc->u.rversion.version, "9P2000.L") != 0) { np_uerror(EIO); goto done; } done: if (tc) free (tc); if (rc) free (rc); if (np_rerror () && fs) { npc_finish (fs); fs = NULL; } return fs; }
Npfcall * np_fsync(Npreq *req, Npfcall *tc) { Npfid *fid = req->fid; Npfcall *rc = NULL; if (!fid) { np_uerror (EIO); goto done; } if (fid->type & P9_QTTMP) { np_uerror (EPERM); goto done; } else { if (np_setfsid (req, fid->user, -1) < 0) goto done; if (!req->conn->srv->fsync) { np_uerror (ENOSYS); goto done; } rc = (*req->conn->srv->fsync)(fid); } done: return rc; }