/* $Id: nfsserv.C,v 1.6 2001/04/05 05:15:42 dm Exp $ */ /* * * Copyright (C) 2000 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "nfsserv.h" #include "sfsmisc.h" #include "nfstrans.h" #include "nfs3_nonnul.h" #include "crypt.h" static void stalereply (nfscall *nc) { nc->error (NFS3ERR_BADHANDLE); } const nfsserv::cb_t nfsserv::stalecb (wrap (stalereply)); /* A simulated close call. */ const rpcgen_table nfscall::closert = { "NFSPROC_CLOSE", &typeid (nfs_fh3), nfs_fh3_alloc, xdr_nfs_fh3, print_nfs_fh3, &typeid (nfsstat3), nfsstat3_alloc, xdr_nfsstat3, print_nfsstat3 }; nfscall::nfscall (const authunix_parms *au, u_int32_t p, void *a) : aup (au), procno (p), argp (a), resp (NULL), xdr_res (NULL), acstat (SUCCESS), austat (AUTH_OK), nocache (false), nofree (false), stopserv (NULL), curserv (NULL) { } void nfscall::seterr (nfsstat3 err) { /* After JUKEBOX errors, FreeBSD resends requests using the same xid. */ nocache = err == NFS3ERR_JUKEBOX; clearres (); #define mkerr(proc, arg, res) \ case proc: \ { \ resp = New res (err); \ break; \ } switch (proc ()) { NFS_PROGRAM_3_APPLY_NONULL (mkerr); case NFSPROC_CLOSE: resp = New nfsstat3 (err); break; default: panic ("nfscall::error: invalid proc %d\n", proc ()); } #undef mkerr } void nfscall::sendreply () { if (!curserv || curserv == stopserv) delete this; else { nfsserv *s = curserv; curserv = s->nextserv; s->getreply (this); } } void nfscall::setreply (void *r, xdrproc_t xdr, bool nc) { if (resp != r) { clearres (); resp = r; nofree = true; } if (xdr) xdr_res = xdr; if (nc) nocache = nc; } void nfscall::reply (void *r, xdrproc_t xdr, bool nc) { setreply (r, xdr, nc); sendreply (); } const rpcgen_table & nfscall::getrpct () const { u_int32_t pn = proc (); if (pn <= NFSPROC3_COMMIT) return nfs_program_3.tbl[pn]; else if (pn == NFSPROC_CLOSE) return closert; else panic ("Bad NFS proc number %d\n", pn); } void nfscall::pinres () { assert (!xdr_res); // see nfscall_cb::~nfscall_cb if (!nofree) return; if (proc () == NFSPROC3_NULL) { clearres (); return; } void *newres = NULL; #define mkcopy(proc, arg, res) \ case proc: \ { \ newres = New res (*static_cast (resp)); \ break; \ } switch (proc ()) { NFS_PROGRAM_3_APPLY_NONULL (mkcopy); case NFSPROC_CLOSE: resp = New nfsstat3 (*static_cast (resp)); break; default: panic ("nfscall::pinres: invalid proc %d\n", proc ()); } #undef mkcopy clearres (); resp = newres; } void nfscall::clearres () { if (nofree) nofree = false; else if (resp) xdr_delete (xdr_res ? xdr_res : getrpct ().xdr_res, resp); resp = NULL; nocache = false; xdr_res = NULL; } void * nfscall::getvoidres () { if (!resp) resp = getrpct ().alloc_res (); return resp; } nfscall_rpc::nfscall_rpc (svccb *s) : nfscall (s->getaup (), s->proc (), s->getvoidarg ()), sbp (s) { } nfscall_rpc::~nfscall_rpc () { if (sbp) { if (acstat != SUCCESS) sbp->reject (acstat); else if (austat != AUTH_OK) sbp->reject (austat); else { #if 0 if (proc () == NFSPROC3_LOOKUP) { lookup3res *lres = static_cast (resp); if (lres->status == NFS3_OK) assert (lres->resok->obj_attributes.present); } #endif sbp->reply (resp, xdr_res, nocache); } } } nfsserv::nfsserv (ptr n) : cb (stalecb), nextserv (n) { if (nextserv) nextserv->setcb (wrap (this, &nfsserv::getcall)); } bool nfsserv::encodefh (nfs_fh3 &fh) { return !nextserv || nextserv->encodefh (fh); } nfsserv_udp::nfsserv_udp () { fd = inetsocket (SOCK_DGRAM, 0, INADDR_LOOPBACK); if (fd < 0) fatal ("inetsocket: %m\n"); x = axprt_dgram::alloc (fd); s = asrv::alloc (x, nfs_program_3, wrap (this, &nfsserv_udp::getsbp)); } void nfsserv_udp::getsbp (svccb *sbp) { if (sbp) mkcb (New nfscall_rpc (sbp)); else warn << "nfsserv_udp::getsbp: NULL sbp\n"; } void nfsserv_fixup::getattr (nfscall *nc, nfs_fh3 *fhp, getattr3res *res) { delete fhp; if (res->status) nc->error (res->status); else { lookup3res *lres = static_cast (nc->resp); lres->resok->obj_attributes.set_present (true); *lres->resok->obj_attributes.attributes = *res->attributes; nc->sendreply (); } } void nfsserv_fixup::getreply (nfscall *nc) { /* After JUKEBOX errors, FreeBSD resends requests using the same xid. */ if (nc->proc () != NFSPROC3_NULL && *nc->template getres () == NFS3ERR_JUKEBOX) nc->nocache = true; /* Many NFS3 clients flip out if lookups replies don't have attributes */ lookup3res *lres = static_cast (nc->resp); if (nc->proc () == NFSPROC3_LOOKUP && lres->status == NFS3_OK && !lres->resok->obj_attributes.present) { nc->pinres (); nfs_fh3 *fhp = New nfs_fh3 (lres->resok->object); vNew nfscall_cb (nc->aup, fhp, wrap (this, &nfsserv_fixup::getattr, nc, fhp), this); return; } nc->sendreply (); } nfsdemux::nfsserv_cryptfh::nfsserv_cryptfh (const ref &dd, u_int32_t s) : nfsserv (dd->ns), d (dd), srvno (s) { d->srvnotab.insert (this); d->ns->setcb (wrap (d.get (), &nfsdemux::getcall)); // XXX - gross } nfsdemux::nfsserv_cryptfh::~nfsserv_cryptfh () { d->srvnotab.remove (this); } void nfsdemux::nfsserv_cryptfh::getreply (nfscall *nc) { if (!nc->xdr_res) { fh3trans fht (fh3trans::ENCODE, d->fhkey, srvno); if (!nfs3_transres (fht, nc->getvoidres (), nc->proc ())) { warn ("Cannot encrypt file handles (err %d)\n", fht.err); nc->seterr (nfsstat3 (fht.err)); } } nc->sendreply (); } bool nfsdemux::nfsserv_cryptfh::encodefh (nfs_fh3 &fh) { fh3trans fht (fh3trans::ENCODE, d->fhkey, srvno); if (!rpc_traverse (fht, fh)) { warn ("file handle too large\n"); return false; } return nextserv->encodefh (fh); } nfsdemux::nfsdemux (const ref &n) : ns (n), srvnoctr (0) { char fhkeydat[53]; rnd.getbytes (fhkeydat, sizeof (fhkeydat)); fhkey.setkey (fhkeydat, sizeof (fhkeydat)); bzero (fhkeydat, sizeof (fhkeydat)); ns->setcb (wrap (this, &nfsdemux::getcall)); } void nfsdemux::getcall (nfscall *nc) { if (nc->proc () == NFSPROC3_NULL) { nc->reply (NULL); return; } fh3trans fht (fh3trans::DECODE, fhkey); nfsserv_cryptfh *nsc; if (!nfs3_transarg (fht, nc->getvoidarg (), nc->proc ()) || !(nsc = srvnotab[fht.srvno]) || !nsc->cb) { nc->error (NFS3ERR_BADHANDLE); return; } nsc->getcall (nc); } ref nfsdemux::servalloc () { while (srvnotab[++srvnoctr]) ; return New refcounted (mkref (this), srvnoctr); }