/* $Id: afsroot.C,v 1.35 2001/07/02 14:59:18 dm Exp $ */ /* * * Copyright (C) 1998-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 "sfscd.h" void afsroot::nfs_lookup (svccb *sbp, str name) { if (afsnode *e = lookup (name, sbp2aid (sbp))) lookup_reply (sbp, e); else userdir (sbp)->nfs_lookup (sbp, name); } bool afsroot::entryok (afsdirentry *de, sfs_aid aid) { afsdir *d = userdir (aid); if (d && d->entryok (de, aid)) return true; return afsdir::entryok (de, aid); } afsdirentry * afsroot::firstentry (sfs_aid aid) { afsdir *d = userdir (aid); afsdirentry *de = afsdir::firstentry (aid); if (!d || !d->lookup (de->name, aid)) return de; return nextentry (de, aid); } afsdirentry * afsroot::nextentry (afsdirentry *de, sfs_aid aid) { afsdir *d = userdir (aid); if (de->dir == this) { for (de = afsdir::nextentry (de, aid); de; de = afsdir::nextentry (de, aid)) if (!d || !d->lookup (de->name, aid)) return de; } else { assert (de); } if (!d) return NULL; if (!de) return d->firstentry (aid); else return d->nextentry (de, aid); } void afsroot::mkfattr3 (fattr3 *f, sfs_aid aid) { /* BSD needs the seconds (not just milliseconds/nanoseconds) of the * mtime to change on every lookup/getattr in order to defeat the * name cache. */ if (aid != lastaid) { lastaid = aid; bumpmtime (); } afsdir::mkfattr3 (f, aid); if (afsdir *d = userdir (aid)) { if (d != afs_naroot) f->mode |= 0222; f->nlink += d->getnlinks () - 2; } } void afsroot::nfs3_access (svccb *sbp) { const sfs_aid aid = sbp2aid (sbp); if (usrinfo *u = usrtab[aid]) u->root->nfs3_access (sbp); else { access3res res (NFS3_OK); mkpoattr (res.resok->obj_attributes, aid); res.resok->access = (ACCESS3_READ | ACCESS3_LOOKUP | ACCESS3_EXECUTE | ACCESS3_DELETE); res.resok->access &= sbp->template getarg ()->access; sbp->reply (&res); } } bool nameok (const str &name) { static rxx namerx ("[a-zA-Z0-9\\-]+(\\.[a-zA-Z0-9\\-]+)*"); return name.len () < NFS_MAXNAMLEN && namerx.match (name); } void afsroot::nfs_remove (svccb *sbp) { str name = sbp->vers () == 2 ? str (sbp->template getarg ()->name) : str (sbp->template getarg ()->name); if (srvinfo *si = srvinfo::lookup (name)) { si->unmount (0); nfs_error (sbp, nfsstat (EINPROGRESS)); } else if (afsdir *d = userdir (sbp)) d->nfs_remove (sbp); else nfs_error (sbp, NFSERR_ACCES); } void afsroot::nfs_symlink (svccb *sbp) { str name = sbp->vers () == 2 ? str (sbp->template getarg ()->from.name) : str (sbp->template getarg ()->where.name); if (entries[name]) nfs_error (sbp, NFSERR_EXIST); else if (usrinfo *u = usrtab[sbp2aid (sbp)]) u->root->nfs_symlink (sbp); else nfs_error (sbp, NFSERR_ACCES); } void afsroot::nfs_mkdir (svccb *sbp) { str name = sbp->vers () == 2 ? str (sbp->template getarg ()->where.name) : str (sbp->template getarg ()->where.name); if (entries[name]) nfs_error (sbp, nfsstat (NFSERR_EXIST)); else if (usrinfo *u = usrtab[sbp2aid (sbp)]) u->root->nfs_mkdir (sbp); else nfs_error (sbp, nfsstat (NFSERR_ACCES)); } bool afsusrdir::chkaid (svccb *sbp) { const sfs_aid rqaid = sbp2aid (sbp); if (rqaid != aid) { nfs_error (sbp, NFSERR_STALE); return false; } return true; } ptr afsusrdir::agentc () { if (!terminating) if (usrinfo *u = usrtab[aid]) return u->ch; return NULL; } void afsusrdir::bumpmtime () { if (root == parent) root->bumpmtime (); afsdir::bumpmtime (); } afsnode * afsusrdir::lookup (const str &name, sfs_aid rqaid) { if (afsnode *n = afsdir::lookup (name, aid)) return n; ptr ch; if (negcache[name] || !nameok (name) || !(ch = agentc ())) return NULL; ref dpt = delaypt::alloc (); link (dpt, name); sfs_filename lname = path ? str (path << "/" << name) : name; ref resp = New refcounted; ch->timedcall (agent_timeout, AGENTCB_LOOKUP, &lname, resp, wrap (mkref (this), &afsusrdir::lookup_cb, name, dpt, resp)); return dpt; } void afsusrdir::lookup_cb (str name, ref dpt, ref resp, clnt_stat err) { unlink (name); if (err || !resp->makelink || !resp->path->len ()) { mkulink (NULL, name); dpt->setres (NFSERR_NOENT); } else { mkulink (*resp->path, name); dpt->setres (name); } } void afsusrdir::mkfh (nfs_fh *fhp) { if (root == parent) // For lookup ("..") root->mkfh (fhp); else afsdir::mkfh (fhp); } void afsusrdir::mkfattr3 (fattr3 *f, sfs_aid rqaid) { if (root == parent) root->mkfattr3 (f, rqaid); else { afsdir::mkfattr3 (f, rqaid); f->mode = 0755; f->uid = aid; f->gid = aid >> 32; f->gid = f->gid ? f->gid - 1 + sfs_resvgid_start : sfs_gid; } } void afsusrdir::nfs3_access (svccb *sbp) { if (!chkaid (sbp)) return; access3res res (NFS3_OK); mkpoattr (res.resok->obj_attributes, sbp2aid (sbp)); res.resok->access = ACCESS3_READ | ACCESS3_LOOKUP | ACCESS3_EXECUTE | ACCESS3_DELETE | ACCESS3_EXTEND | ACCESS3_MODIFY; res.resok->access &= sbp->template getarg ()->access; sbp->reply (&res); } void afsusrdir::nfs_remove (svccb *sbp) { if (!chkaid (sbp)) return; str name = sbp->vers () == 2 ? str (sbp->template getarg ()->name) : str (sbp->template getarg ()->name); if (!entries[name]) nfs_error (sbp, NFSERR_NOENT); else if (!nameok (name) && !sfs_parsepath (name)) nfs_error (sbp, NFSERR_ACCES); else { clrulink (name); if (sbp->vers () == 2) sbp->replyref (NFS_OK); else sbp->replyref (wccstat3 (NFS3_OK)); } } void afsusrdir::nfs_mkdir (svccb *sbp) { str name = sbp->vers () == 2 ? str (sbp->template getarg ()->where.name) : str (sbp->template getarg ()->where.name); if (entries[name]) { nfs_error (sbp, NFSERR_EXIST); return; } if (!nameok (name)) { nfs_error (sbp, nfsstat (NFSERR_ACCES)); return; } clrulink (name); ptr e = mkdir (name); dirop_reply (sbp, e); } void afsusrdir::nfs_symlink (svccb *sbp) { if (!chkaid (sbp)) // XXX - redundant (handled by afsroot) return; str name, contents; if (sbp->vers () == 2) { symlinkargs *argp = sbp->template getarg (); name = argp->from.name; contents = argp->to; } else { symlink3args *argp = sbp->template getarg (); name = argp->where.name; contents = argp->symlink.symlink_data; } afsnode *e = afsdir::lookup (name, aid); if (e && e->type != NF3LNK) nfs_error (sbp, NFSERR_EXIST); if (mkulink (contents, name)) dirop_reply (sbp, afsdir::lookup (name, aid)); else nfs_error (sbp, NFSERR_ACCES); } bool afsusrdir::mkulink (const str &path, const str &name) { if (!nameok (name)) return false; unlink (name); if (path && path.len ()) { negcache.remove (name); if (nentries < maxulinks) symlink (path, name); else { // XXX - what to do? warn ("afsusrdir: maxulinks exceeded\n"); return false; } } else { if (negcache.size () >= maxulinks) // XXX - this is kind of low-tech negcache.clear (); negcache.insert (name); } return true; } void afsusrdir::clrulink (const str &name) { negcache.remove (name); unlink (name); bumpmtime (); } ptr afsusrdir::mkdir (const str &name) { ref d (alloc (root, aid, this, path ? str (path << "/" << name) : name)); if (!link (d, name)) return NULL; addlink (); return d; } afsnode * afsusrroot::lookup (const str &name, sfs_aid rqaid) { if (ptr r = revocation::lookup (name)) { afsnode *e = afsdir::lookup (name, rqaid); if (e != r) { if (e) unlink (name); link (r, name); } return r; } return super::lookup (name, rqaid); } void afsusrroot::nfs_lookup (svccb *sbp, str name) { afsnode *e = lookup (name, aid); if (e) { if (int err = srvinfo::geterr (name)) nfs_error (sbp, err); else lookup_reply (sbp, e); return; } else if ((!sfs_parsepath (name) && (!namedprotrx.match (name) /*|| !nptab[namedprotrx[1]]*/)) || terminating) { nfs_error (sbp, ENOENT); return; } ref dpt = delaypt::alloc (); ref ss = New refcounted (name, dpt); link (dpt, name); if (ptr ch = agentc ()) ch->timedcall (agent_timeout, AGENTCB_REVOKED, &name, &ss->revres, wrap (mkref (this), &afsusrroot::revcb, ss)); else { ss->revdone = true; finish (ss, NFS_OK); } lookup_reply (sbp, afsdir::lookup (name, aid)); } void afsusrroot::revcb (ref ss, clnt_stat err) { static ptr revokedlink; if (!revokedlink) revokedlink = afslink::alloc (":REVOKED:"); ss->revdone = true; if (err == RPC_PROCUNAVAIL) err = RPC_SUCCESS; if (err) { finish (ss, NFSERR_IO); return; } switch (ss->revres.type) { case REVOCATION_NONE: finish (ss, NFS_OK); break; default: /* case REVOCATION_BLOCK: */ unlink (ss->name); link (revokedlink, ss->name); finish (ss, NFSERR_NOENT); break; case REVOCATION_CERT: unlink (ss->name); if (ptr e = revocation::alloc (*ss->revres.cert)) link (e, ss->name); else link (revokedlink, ss->name); finish (ss, NFSERR_NOENT); break; } } static void afsdir_unlink (ref d, str name) { d->unlink (name); } void afsusrroot::finish (ref ss, int err) { if (!ss->dpt) return; if (!err) err = srvinfo::geterr (ss->name); if (err) { ss->dpt->setres (nfsstat (err)); ss->dpt = NULL; timecb (timenow + 5, wrap (afsdir_unlink, mkref (this), ss->name)); return; } if (!ss->revdone) return; afsnode *e = lookup (ss->name, NULL); if (!e || e == ss->dpt) e = afs_sfsroot->lookup (ss->name, NULL); if (e) { unlink (ss->name); link (e, ss->name); ss->dpt->setres (strbuf ("%s/%s", sfsroot, ss->name.cstr ())); } else srvinfo::alloc (ss->name, wrap (mkref (this), &afsusrroot::finish, ss)); } ref ctldiralloc (afsdir *p, sfs_aid aid); ref afsusrroot::alloc (afsroot *r, sfs_aid aid, afsdir *p, str pn) { ref root (New refcounted (r, aid, p ? p : r, pn)); if (aid != sfsaid_nobody) { root->link (afsaidfile::alloc (aid, strbuf () << aid << "\n"), ".aid"); // root->link (ctldiralloc (root, aid), ".ctl"); } return root; } void afsaidfile::nfs_getattr (svccb *sbp) { const sfs_aid aid = sbp2aid (sbp); if (aid != owner) nfs_error (sbp, NFSERR_STALE); else afsnode::nfs_getattr (sbp); } void afsaidfile::nfs3_access (svccb *sbp) { const sfs_aid aid = sbp2aid (sbp); if (aid != owner) nfs_error (sbp, NFSERR_STALE); else afsnode::nfs3_access (sbp); } void afsrootfile::mkfattr3 (fattr3 *f, sfs_aid aid) { afsreg::mkfattr3 (f, aid); f->mode = 0400; f->uid = 0; } void afsrootfile::nfs3_access (svccb *sbp) { access3res res (NFS3_OK); mkpoattr (res.resok->obj_attributes, sbp2aid (sbp)); const authunix_parms *aup = sbp->getaup (); if (!aup || aup->aup_uid) res.resok->access = 0; else res.resok->access = (ACCESS3_READ & sbp->template getarg ()->access); sbp->reply (&res); }