/* $Id: afsdir.C,v 1.2 2001/10/08 22:40:13 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 "afsnode.h" ihash cookietab; static void pentry (afsnode *, const char *); static u_int32_t gencookie () { static u_int32_t cctr; while (++cctr < 3 || cookietab[cctr]) ; return cctr; } afsdirentry::afsdirentry (afsdir *dir, const str &name, afsnode *node) : dir (dir), name (name), node (mkref (node)), cookie (gencookie ()) { node->addlink (); cookietab.insert (this); dir->entries.insert (this); } afsdirentry::~afsdirentry () { node->remlink (); dir->entries.remove (this); cookietab.remove (this); } static bool xdr_putentry (XDR *x, afsnode *n, filename name, u_int32_t cookie) { return // entry * (non-null): xdr_putint (x, 1) // u_int fileid: && xdr_putint (x, n->ino) // filename name: && xdr_filename (x, &name) // nfscookie cookie: && xdr_putint (x, cookie); } static bool xdr_putentry3 (XDR *x, afsnode *n, filename name, u_int32_t cookie) { return // entry * (non-null): xdr_putint (x, 1) // uint64 fileid: && xdr_puthyper (x, n->ino) // filename name: && xdr_filename (x, &name) // uint64 cookie: && xdr_puthyper (x, cookie); } /* For correctness, this must *NOT* return attributes (since we are * trying to defeat all kernel attribute and name caching). Note also * that the revocation/redirect mechanism also relies on readdirplus * not returning file handles or attributes. */ static bool xdr_putentryplus3 (XDR *x, afsnode *n, filename name, u_int32_t cookie) { return // entry * (non-null): xdr_putint (x, 1) // uint64 fileid: && xdr_puthyper (x, n->ino) // filename name: && xdr_filename (x, &name) // nfscookie cookie: && xdr_puthyper (x, cookie) // post_op_attr name_attributes; && xdr_putint (x, 0) // post_op_fh3 name_handle; && xdr_putint (x, 0); } BOOL afsdir::xdr (XDR *x, void *_sbp) { /* When encoding . and .. in an XDR, we want to make sure the string * memory doesn't get freed before the XDR uses it. */ static const filename dot ("."); static const filename dotdot (".."); svccb *sbp = static_cast (_sbp); const sfs_aid aid = sbp2aid (sbp); assert (x->x_op == XDR_ENCODE); const bool v2 = sbp->vers () == 2; afsdir *d; u_int32_t cookie; u_int32_t count; bool (*putentry) (XDR *, afsnode *, filename, u_int32_t); if (v2) { const readdirargs *arg = sbp->template getarg (); d = static_cast (afsnode::fh2node (&arg->dir)); cookie = getint (arg->cookie.base ()); count = arg->count; putentry = xdr_putentry; } else if (sbp->proc () == NFSPROC3_READDIR) { const readdir3args *arg = sbp->template getarg (); d = static_cast (afsnode::fh3node (&arg->dir)); cookie = arg->cookie; count = arg->count; putentry = xdr_putentry3; } else if (sbp->proc () == NFSPROC3_READDIRPLUS) { const readdirplus3args *arg = sbp->template getarg (); d = static_cast (afsnode::fh3node (&arg->dir)); cookie = arg->cookie; count = arg->dircount; putentry = xdr_putentryplus3; } else return xdr_putint (x, NFS3ERR_NOTSUPP); if (!d) return xdr_putint (x, NFSERR_STALE); afsdirentry *e = NULL; if (cookie >= 3 && (!(e = cookietab[cookie]) || !d->entryok (e, aid))) { warn ("afsdir::xdr: bad cookie 0x%x\n", cookie); return xdr_putint (x, v2 ? EINVAL : NFS3ERR_BAD_COOKIE); } if (!xdr_putint (x, NFS_OK)) return false; if (!v2) { post_op_attr poa; d->mkpoattr (poa, aid); if (!xdr_post_op_attr (x, &poa) || !xdr_puthyper (x, 0)) return false; } switch (cookie) { case 0: if (!putentry (x, d, dot, 1)) return false; case 1: if (!putentry (x, d->parent, dotdot, 2)) return false; case 2: e = d->firstentry (aid); break; default: e = d->nextentry (e, aid); break; } for (; e && XDR_GETPOS (x) + 24 + e->name.len () <= count; e = d->nextentry (e, aid)) if (!putentry (x, e->node, e->name, e->cookie)) return false; return xdr_putint (x, 0) // NULL entry * && xdr_putint (x, !e); // bool eof } afsdir::afsdir (afsdir *p) : afsnode (NF3DIR), parent (p ? p : this) { mtime.seconds = mtime.nseconds = 0; nlinks += 2; } afsdir::~afsdir () { entries.traverse (wrap (&afsdirentry::del)); } afsnode * afsdir::lookup (const str &name, sfs_aid) { if (name == ".") return this; if (name == "..") return parent; if (afsdirentry *e = entries[name]) return e->node; return NULL; } bool afsdir::entryok (afsdirentry *e, sfs_aid) { return e->dir == this; } afsdirentry * afsdir::firstentry (sfs_aid) { return entries.first (); } afsdirentry * afsdir::nextentry (afsdirentry *e, sfs_aid) { return entries.next (e); } bool afsdir::link (afsnode *node, const str &name) { if (entries[name]) return false; vNew afsdirentry (this, name, node); bumpmtime (); return true; } bool afsdir::unlink (const str &name) { afsdirentry *e = entries[name]; if (e) { bumpmtime (); delete e; } return e; } ptr afsdir::mkdir (const str &name) { ref d = allocsubdir (this); if (!link (d, name)) return NULL; addlink (); return d; } ptr afsdir::symlink (const str &contents, const str &name) { ref lnk = afslink::alloc (contents); if (!link (lnk, name)) return NULL; return lnk; } #if 0 /* Returns true for "." and ".." */ static bool isddot (const char *name) { if (name[0] != '.') return false; if (name[1] == '\0') return true; if (name[1] != '.') return false; if (name[2] != '\0') return false; return true; } #endif void afsdir::nfs_lookup (svccb *sbp, str name) { lookup_reply (sbp, lookup (name, sbp2aid (sbp))); } static char tc (ftype3 t) { switch (t) { case NF3REG: return '-'; case NF3DIR: return 'd'; case NF3LNK: return 'l'; default: return '?'; } } static char * mc (u_int mode, char s[10]) { char *p = s; for (int i = 0; i < 3; i++) { *p++ = mode & 4 ? 'r' : '-'; *p++ = mode & 2 ? 'w' : '-'; *p++ = mode & 1 ? 'x' : '-'; mode >>= 3; } *p = '\0'; return s; } static void pentry (afsnode *n, const char *name) { fattr f; n->mkfattr (&f, NULL); time_t mtime = time (NULL); int year = gmtime (&mtime)->tm_year; mtime = f.mtime.seconds; tm *tmp = gmtime (&mtime); char t[24]; if (!strftime (t, sizeof (t), year <= tmp->tm_year ? "%b %e %H:%M" : "%b %e %Y", tmp)) panic ("strftime overflow\n"); char m[10]; warnx ("%6d %c%s %2d %5d %5d %7d %s %s\n", (u_int32_t) n->ino, tc (n->type), mc (f.mode, m), n->getnlinks (), f.uid, f.gid, f.size, t, name); } void afsdir::ls () { warnx ("files in directory %d:\n", (u_int32_t) ino); pentry (this, "."); pentry (parent, ".."); for (afsdirentry *e = firstentry (NULL); e; e = nextentry (e, NULL)) pentry (e->node, e->name); }