/* $Id: ctlnode.C,v 1.6 2001/08/20 04:43:24 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 "sfscd.h" enum { maxctlfile = 0x8000 }; enum { closetimeout = 2 }; typedef rpc_bytes fhbytes; class ctlnode; class msgnode : public afsnode { const inum_t fhextra; ctlnode *const ctl; char *buf; u_int size; u_int maxsize; timespec closetime; bool setsize (u_int s); void touch (); void close (); public: tailq_entry tlink; static void tmosched (bool expired = false); msgnode (ctlnode *c, const fhbytes &fh); ~msgnode (); void mkfattr3 (fattr3 *f, sfs_aid a); void nfs_setattr (svccb *sbp); void nfs_read (svccb *sbp); void nfs_write (svccb *sbp); }; class ctlnode : public afsreg { friend class msgnode; sfs_aid aid; str name; mutable inum_t lastino; msgnode *getmsgnode (svccb *sbp); public: qhash > ntab; void mkfh (nfs_fh *); void mkfattr3 (fattr3 *f, sfs_aid a); void nfs_setattr (svccb *sbp) { getmsgnode (sbp)->nfs_setattr (sbp); } void nfs_write (svccb *sbp) { getmsgnode (sbp)->nfs_write (sbp); } void nfs_read (svccb *sbp); void nfs3_access (svccb *sbp); ctlnode (sfs_aid aid, str name); }; class ctldir : public afsdir { protected: const sfs_aid aid; ctldir (afsdir *parent, sfs_aid a) : afsdir (parent), aid (a) {} public: void mkfattr3 (fattr3 *f, sfs_aid a); void nfs_create (svccb *sbp); void nfs_remove (svccb *sbp); void nfs3_access (svccb *sbp); }; static tailq msgnode_closeq; static timecb_t *msgnode_timecb; void fh2bytes (fhbytes *data, const svccb *sbp) { if (sbp->vers () == 2) *data = sbp->template getarg ()->data; else *data = sbp->template getarg ()->data; } inline afsnode::inum_t fhb2extra (const fhbytes &data) { assert (data.size () == 2 * sizeof (afsnode::inum_t) + sizeof (afsnode::fhsecret)); return gethyper (&data[sizeof (afsnode::inum_t) + sizeof (afsnode::fhsecret)]); } void getsattr3 (sattr3 *s, svccb *sbp) { assert (sbp->prog () == NFS_PROGRAM); switch (sbp->vers ()) { case 2: { sattr *sp = &sbp->template getarg ()->attributes; const u_int32_t nochange ((u_int32_t) -1); if (sp->mode != nochange) { s->mode.set_set (true); *s->mode.val = sp->mode; } if (sp->uid != nochange) { s->uid.set_set (true); *s->uid.val = sp->uid; } if (sp->gid != nochange) { s->gid.set_set (true); *s->gid.val = sp->gid; } if (sp->size != nochange) { s->size.set_set (true); *s->size.val = sp->size; } if (sp->atime.seconds != nochange) { s->atime.set_set (SET_TO_CLIENT_TIME); s->atime.time->seconds = sp->atime.seconds; s->atime.time->nseconds = 1000 * sp->atime.useconds; } if (sp->mtime.seconds != nochange) { s->mtime.set_set (SET_TO_CLIENT_TIME); s->mtime.time->seconds = sp->mtime.seconds; s->mtime.time->nseconds = 1000 * sp->mtime.useconds; } } case 3: *s = sbp->template getarg ()->new_attributes; break; default: panic ("getsattr3: bad NFS version %d\n", sbp->vers ()); break; } } void msgnode::tmosched (bool expired) { if (expired) msgnode_timecb = NULL; msgnode *mp, *nmp; for (mp = msgnode_closeq.first; mp && mp->closetime <= tsnow; mp = nmp) { nmp = msgnode_closeq.next (mp); mp->close (); } if (mp && !msgnode_timecb) msgnode_timecb = timecb (mp->closetime, wrap (&tmosched, true)); } msgnode::msgnode (ctlnode *c, const fhbytes &fh) : afsnode (NF3REG, geninum (fhb2extra (fh))), fhextra (fhb2extra (fh)), ctl (c) { size = maxsize = ctl->read ().len (); if (size) buf = static_cast (xmalloc (size)); else buf = NULL; memcpy (buf, ctl->read ().cstr (), size); closetime.tv_sec = tsnow.tv_sec + 2; closetime.tv_nsec = tsnow.tv_nsec; msgnode_closeq.insert_tail (this); tmosched (); } msgnode::~msgnode () { msgnode_closeq.remove (this); } bool msgnode::setsize (u_int s) { if (s <= size) size = s; else if (s > maxctlfile) return false; else { if (s > maxsize) { maxsize = s; buf = static_cast (xrealloc (buf, maxsize)); } bzero (buf + size, s - size); size = s; } return true; } void msgnode::touch () { closetime.tv_sec = tsnow.tv_sec + 2; closetime.tv_nsec = tsnow.tv_nsec; msgnode_closeq.remove (this); msgnode_closeq.insert_tail (this); } void msgnode::close () { bool mytime = (mtime.seconds > ctl->mtime.seconds || (mtime.seconds == ctl->mtime.seconds && mtime.nseconds > ctl->mtime.nseconds)); ctl->setcontents (str (buf, size)); if (mytime) ctl->mtime = mtime; warn ("msgnode::close inode %" U64F "d\n", ino); ctl->ntab.remove (fhextra); } void msgnode::mkfattr3 (fattr3 *f, sfs_aid a) { ctl->mkfattr3 (f, a); f->fileid = ino; f->size = size; f->used = (size + 0x1fff) & ~0x1fff; } void msgnode::nfs_setattr (svccb *sbp) { touch (); sattr3 arg; getsattr3 (&arg, sbp); /* XXX - do something about guard? */ if (arg.mode.set || arg.uid.set || arg.gid.set) { nfs_error (sbp, NFSERR_PERM); return; } if (arg.size.set && !setsize (*arg.size.val)) nfs_error (sbp, NFSERR_DQUOT); switch (arg.mtime.set) { case SET_TO_CLIENT_TIME: mtime = *arg.mtime.time; break; case SET_TO_SERVER_TIME: getnfstime (&mtime); break; default: break; } /* Ignore atime changes */ if (sbp->vers () == 2) nfs_getattr (sbp); else { wccstat3 res (NFS3_OK); mkpoattr (res.wcc->after, sbp2aid (sbp)); sbp->reply (&res); } } void msgnode::nfs_write (svccb *sbp) { touch (); size_t off, len; const char *data; if (sbp->vers () == 2) { writeargs *argp = sbp->template getarg (); off = argp->offset; len = argp->data.size (); data = argp->data.base (); } else { write3args *argp = sbp->template getarg (); off = argp->offset; len = argp->data.size (); data = argp->data.base (); } if (off > maxctlfile || len > maxctlfile || (off + len > maxsize && !setsize (off + len))) { nfs_error (sbp, NFSERR_DQUOT); return; } getnfstime (&mtime); if (size < off + len) size = off + len; memcpy (buf + off, data, len); if (sbp->vers () == 2) nfs_getattr (sbp); else { write3res res (NFS3_OK); res.resok->count = len; // res.resok->committed = sbp->template getarg ()->stable; res.resok->committed = FILE_SYNC; sbp->reply (&res); } } void msgnode::nfs_read (svccb *sbp) { touch (); size_t off, len; if (sbp->vers () == 2) { readargs *argp = sbp->template getarg (); off = argp->offset; len = argp->count; } else { read3args *argp = sbp->template getarg (); off = argp->offset; len = argp->count; } bool eof = true; if (off >= size || len >= size) off = len = 0; else if (off + len >= size) len = size - off; else eof = false; if (sbp->vers () == 2) { readres res (NFS_OK); mkfattr (&res.reply->attributes, sbp2aid (sbp)); if (len > NFS_MAXDATA) len = NFS_MAXDATA; res.reply->data.set (buf + off, len); sbp->reply (&res); } else { read3res res (NFS3_OK); mkpoattr (res.resok->file_attributes, sbp2aid (sbp)); res.resok->count = len; res.resok->eof = eof; res.resok->data.set (buf + off, len); sbp->reply (&res); } } ctlnode::ctlnode (sfs_aid a, str n) : afsreg (""), aid (a), name (n) { ctime.seconds = ctime.nseconds = 0; } msgnode * ctlnode::getmsgnode (svccb *sbp) { fhbytes fh; fh2bytes (&fh, sbp); inum_t fhextra = fhextra = fhb2extra (fh); ptr n = ntab[fhextra]; if (!n) { n = New refcounted (this, fh); ntab.insert (fhextra, n); } return n; } void ctlnode::mkfh (nfs_fh *fhp) { afsnode::mkfh (fhp); lastino = geninum (); puthyper (&fhp->data[sizeof (inum_t) + sizeof (fhsecret)], lastino); } void ctlnode::mkfattr3 (fattr3 *f, sfs_aid a) { ctime.seconds++; afsreg::mkfattr3 (f, a); f->mode = 0644; f->uid = aid; f->gid = aid >> 32; f->gid = f->gid ? f->gid - 1 + sfs_resvgid_start : sfs_gid; f->fileid = lastino; } void ctlnode::nfs_read (svccb *sbp) { fhbytes fh; fh2bytes (&fh, sbp); inum_t fhextra = fhb2extra (fh); ptr n = ntab[fhextra]; if (n) n->nfs_read (sbp); else afsreg::nfs_read (sbp); } void ctlnode::nfs3_access (svccb *sbp) { access3res res (NFS3_OK); mkpoattr (res.resok->obj_attributes, sbp2aid (sbp)); if (sbp2aid (sbp) == aid) res.resok->access = ((ACCESS3_READ | ACCESS3_LOOKUP | ACCESS3_MODIFY | ACCESS3_EXTEND) & sbp->template getarg ()->access); else res.resok->access = 0; sbp->reply (&res); } ref ctlnodealloc (sfs_aid aid) { ref cn = New refcounted (aid, "test"); cn->setcontents (strbuf ("hello %" U64F "d\n", aid)); return cn; } void ctldir::mkfattr3 (fattr3 *f, sfs_aid rqaid) { 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 ctldir::nfs_remove (svccb *sbp) { str name = sbp->vers () == 2 ? str (sbp->template getarg ()->name) : str (sbp->template getarg ()->name); if (!unlink (name)) nfs_error (sbp, NFSERR_NOENT); else if (sbp->vers () == 2) sbp->replyref (NFS_OK); else sbp->replyref (wccstat3 (NFS3_OK)); } void ctldir::nfs_create (svccb *sbp) { str name; if (sbp->vers () == 2) { createargs *ca = sbp->template getarg (); name = ca->where.name; } else { create3args *ca = sbp->template getarg (); name = ca->where.name; if (ca->how.mode == GUARDED && lookup (name, sbp2aid (sbp))) { nfs3_err (sbp, NFS3ERR_EXIST); return; } if (ca->how.mode == EXCLUSIVE) { nfs3_err (sbp, NFS3ERR_NOTSUPP); return; } } if (!nameok (name)) { nfs_error (sbp, NFSERR_ACCES); return; } afsnode *e = lookup (name, sbp2aid (sbp)); if (!e) { ref er = ctlnodealloc (sbp2aid (sbp)); link (er, name); e = er; } dirop_reply (sbp, e); } void ctldir::nfs3_access (svccb *sbp) { access3res res (NFS3_OK); mkpoattr (res.resok->obj_attributes, sbp2aid (sbp)); res.resok->access = ACCESS3_READ | ACCESS3_LOOKUP | ACCESS3_EXECUTE; if (sbp2aid (sbp) == aid) res.resok->access |= ACCESS3_DELETE | ACCESS3_EXTEND | ACCESS3_MODIFY; res.resok->access &= sbp->template getarg ()->access; sbp->reply (&res); } ref ctldiralloc (afsdir *p, sfs_aid aid) { return New refcounted (p, aid); }