tSmall tweaks Lots of new code imported. - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 2277c5d7bbe1f9595fad512d8f790708473a9bf1
 (DIR) parent a770daa795754cb600ad3fab2fdd2961147006c4
 (HTM) Author: rsc <devnull@localhost>
       Date:   Sun, 21 Mar 2004 04:33:13 +0000
       
       Small tweaks
       Lots of new code imported.
       
       Diffstat:
         A include/9p.h                        |     244 +++++++++++++++++++++++++++++++
         A include/auth.h                      |     159 +++++++++++++++++++++++++++++++
         A include/authsrv.h                   |     177 +++++++++++++++++++++++++++++++
         A include/mp.h                        |     153 +++++++++++++++++++++++++++++++
         A include/nfs3.h                      |     801 ++++++++++++++++++++++++++++++
         A include/sunrpc.h                    |     400 +++++++++++++++++++++++++++++++
         M src/cmd/acme/acme.c                 |       9 ++++++---
         M src/cmd/acme/dat.h                  |       2 --
         M src/cmd/acme/elog.c                 |       2 +-
         M src/cmd/acme/exec.c                 |       1 +
         M src/cmd/acme/fns.h                  |       1 +
         M src/cmd/acme/look.c                 |      39 +++++++++++++++++++++++++++++--
         A src/cmd/factotum/BUGS               |       1 +
         A src/cmd/factotum/apop.c             |     348 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/attr.c             |     228 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/chap.c             |     424 ++++++++++++++++++++++++++++++
         A src/cmd/factotum/confirm.c          |     139 ++++++++++++++++++++++++++++++
         A src/cmd/factotum/conv.c             |     254 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/cpu.c              |    1117 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/ctl.c              |     158 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/dat.h              |     224 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/fs.acid            |    1686 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/fs.c               |     524 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/guide              |       3 +++
         A src/cmd/factotum/guide2             |       6 ++++++
         A src/cmd/factotum/key.c              |     190 ++++++++++++++++++++++++++++++
         A src/cmd/factotum/log.c              |     121 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/main.c             |     163 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/mkfile             |      34 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/p9any.c            |     266 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/p9cr.c             |     545 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/p9sk1.c            |     352 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/pass.c             |     100 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/plan9.c            |     189 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/privattr           |       0 
         A src/cmd/factotum/proto.c            |      22 ++++++++++++++++++++++
         A src/cmd/factotum/rpc.c              |     315 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/ssh.c              |     135 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/sshrsa.c           |     172 ++++++++++++++++++++++++++++++
         A src/cmd/factotum/std.h              |      10 ++++++++++
         A src/cmd/factotum/test.c             |     121 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/testsetup          |      11 +++++++++++
         A src/cmd/factotum/util.c             |      52 +++++++++++++++++++++++++++++++
         A src/cmd/factotum/x.c                |      15 +++++++++++++++
         A src/cmd/factotum/xio.c              |     165 +++++++++++++++++++++++++++++++
         M src/cmd/mkfile                      |       2 +-
         M src/cmd/plumb/plumber.c             |       2 ++
         M src/cmd/rc/plan9ish.c               |       2 +-
         M src/cmd/rc/rc.h                     |       3 +++
         M src/cmd/samterm/plan9.c             |      36 +++++++++++++++----------------
         M src/cmd/vac/vac.c                   |      28 +++++++++++++++++++++-------
         M src/lib9/getenv.c                   |       1 -
         M src/lib9/mkfile                     |       3 +++
         M src/lib9/pipe.c                     |       5 +++++
         A src/lib9p/_post.c                   |      77 +++++++++++++++++++++++++++++++
         A src/lib9p/dirread.c                 |      40 +++++++++++++++++++++++++++++++
         A src/lib9p/fid.c                     |      81 ++++++++++++++++++++++++++++++
         A src/lib9p/file.c                    |     372 +++++++++++++++++++++++++++++++
         A src/lib9p/ftest.c                   |      29 +++++++++++++++++++++++++++++
         A src/lib9p/intmap.c                  |     166 +++++++++++++++++++++++++++++++
         A src/lib9p/mem.c                     |      49 +++++++++++++++++++++++++++++++
         A src/lib9p/mkfile                    |      20 ++++++++++++++++++++
         A src/lib9p/parse.c                   |     115 +++++++++++++++++++++++++++++++
         A src/lib9p/post.c                    |      24 ++++++++++++++++++++++++
         A src/lib9p/post.h                    |      13 +++++++++++++
         A src/lib9p/ramfs.c                   |     163 +++++++++++++++++++++++++++++++
         A src/lib9p/req.c                     |     112 +++++++++++++++++++++++++++++++
         A src/lib9p/srv.c                     |     837 +++++++++++++++++++++++++++++++
         A src/lib9p/tpost.c                   |      18 ++++++++++++++++++
         A src/lib9p/uid.c                     |      34 +++++++++++++++++++++++++++++++
         A src/lib9p/util.c                    |      25 +++++++++++++++++++++++++
         M src/libfs/fs.c                      |       2 +-
         M src/libfs/read.c                    |      18 ++++++++++++++++++
         M src/libhttpd/gethead.c              |       7 ++++++-
         M src/libhttpd/hio.c                  |      13 +++++++++----
         M src/libip/mkfile                    |      13 +++++++------
         M src/libplumb/mesg.c                 |      62 ++++++++++++++++++++++++++-----
         M src/libplumb/thread.c               |       1 +
         M src/libsec/port/aes.c               |       4 +---
         M src/libsec/port/genrandom.c         |       1 +
         M src/libsec/port/mkfile              |      41 +++++++++++++++++++++++++++++++
         M src/libsec/port/sha1.c              |      13 +------------
         M src/libsec/port/sha1block.c         |      14 +-------------
         M src/libthread/main.c                |       1 +
         M src/libventi/server.c               |       2 ++
         M src/mkfile                          |       2 +-
       
       86 files changed, 12441 insertions(+), 88 deletions(-)
       ---
 (DIR) diff --git a/include/9p.h b/include/9p.h
       t@@ -0,0 +1,244 @@
       +#ifndef __9P_H__
       +#define __9P_H__ 1
       +
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +
       +/*
       +#pragma src "/sys/src/lib9p"
       +#pragma lib "lib9p.a"
       +*/
       +
       +/*
       + * Maps from ulongs to void*s.
       + */
       +typedef struct Intmap        Intmap;
       +
       +Intmap*        allocmap(void (*inc)(void*));
       +void                freemap(Intmap*, void (*destroy)(void*));
       +void*        lookupkey(Intmap*, ulong);
       +void*        insertkey(Intmap*, ulong, void*);
       +int                caninsertkey(Intmap*, ulong, void*);
       +void*        deletekey(Intmap*, ulong);
       +
       +/*
       + * Fid and Request structures.
       + */
       +typedef struct Fid                Fid;
       +typedef struct Req                Req;
       +typedef struct Fidpool        Fidpool;
       +typedef struct Reqpool        Reqpool;
       +typedef struct File                File;
       +typedef struct Filelist        Filelist;
       +typedef struct Tree                Tree;
       +typedef struct Readdir        Readdir;
       +typedef struct Srv Srv;
       +
       +struct Fid
       +{
       +        ulong        fid;
       +        char                omode;        /* -1 = not open */
       +        File*                file;
       +        char*        uid;
       +        Qid                qid;
       +        void*        aux;
       +
       +/* below is implementation-specific; don't use */
       +        Readdir*        rdir;
       +        Ref                ref;
       +        Fidpool*        pool;
       +        vlong        diroffset;
       +        long                dirindex;
       +};
       +
       +struct Req
       +{
       +        ulong        tag;
       +        void*        aux;
       +        Fcall                ifcall;
       +        Fcall                ofcall;
       +        Dir                d;
       +        Req*                oldreq;
       +        Fid*                fid;
       +        Fid*                afid;
       +        Fid*                newfid;
       +        Srv*                srv;
       +
       +/* below is implementation-specific; don't use */
       +        QLock        lk;
       +        Ref                ref;
       +        Reqpool*        pool;
       +        uchar*        buf;
       +        uchar        type;
       +        uchar        responded;
       +        char*        error;
       +        void*        rbuf;
       +        Req**        flush;
       +        int                nflush;
       +};
       +
       +/*
       + * Pools to maintain Fid <-> fid and Req <-> tag maps.
       + */
       +
       +struct Fidpool {
       +        Intmap        *map;
       +        void                (*destroy)(Fid*);
       +        Srv                *srv;
       +};
       +
       +struct Reqpool {
       +        Intmap        *map;
       +        void                (*destroy)(Req*);
       +        Srv                *srv;
       +};
       +
       +Fidpool*        allocfidpool(void (*destroy)(Fid*));
       +void                freefidpool(Fidpool*);
       +Fid*                allocfid(Fidpool*, ulong);
       +Fid*                lookupfid(Fidpool*, ulong);
       +void                closefid(Fid*);
       +Fid*                removefid(Fidpool*, ulong);
       +
       +Reqpool*        allocreqpool(void (*destroy)(Req*));
       +void                freereqpool(Reqpool*);
       +Req*                allocreq(Reqpool*, ulong);
       +Req*                lookupreq(Reqpool*, ulong);
       +void                closereq(Req*);
       +Req*                removereq(Reqpool*, ulong);
       +
       +typedef        int        Dirgen(int, Dir*, void*);
       +void                dirread9p(Req*, Dirgen*, void*);
       +
       +/*
       + * File trees.
       + */
       +struct File {
       +        Ref ref;
       +        Dir dir;
       +        File *parent;
       +        void *aux;
       +
       +/* below is implementation-specific; don't use */
       +        RWLock rwlock;
       +        Filelist *filelist;
       +        Tree *tree;
       +        int nchild;
       +        int allocd;
       +};
       +
       +struct Tree {
       +        File *root;
       +        void        (*destroy)(File *file);
       +
       +/* below is implementation-specific; don't use */
       +        Lock genlock;
       +        ulong qidgen;
       +        ulong dirqidgen;
       +};
       +
       +Tree*        alloctree(char*, char*, ulong, void(*destroy)(File*));
       +void                freetree(Tree*);
       +File*                createfile(File*, char*, char*, ulong, void*);
       +int                removefile(File*);
       +void                closefile(File*);
       +File*                walkfile(File*, char*);
       +Readdir*        opendirfile(File*);
       +long                readdirfile(Readdir*, uchar*, long);
       +void                closedirfile(Readdir*);
       +
       +/*
       + * Kernel-style command parser
       + */
       +typedef struct Cmdbuf Cmdbuf;
       +typedef struct Cmdtab Cmdtab;
       +Cmdbuf*                parsecmd(char *a, int n);
       +void                respondcmderror(Req*, Cmdbuf*, char*, ...);
       +Cmdtab*        lookupcmd(Cmdbuf*, Cmdtab*, int);
       +/*
       +#pragma varargck argpos respondcmderr 3
       +*/
       +struct Cmdbuf
       +{
       +        char        *buf;
       +        char        **f;
       +        int        nf;
       +};
       +
       +struct Cmdtab
       +{
       +        int        index;        /* used by client to switch on result */
       +        char        *cmd;        /* command name */
       +        int        narg;        /* expected #args; 0 ==> variadic */
       +};
       +
       +/*
       + * File service loop.
       + */
       +struct Srv {
       +        Tree*        tree;
       +        void                (*destroyfid)(Fid*);
       +        void                (*destroyreq)(Req*);
       +        void                (*end)(Srv*);
       +        void*        aux;
       +
       +        void                (*attach)(Req*);
       +        void                (*auth)(Req*);
       +        void                (*open)(Req*);
       +        void                (*create)(Req*);
       +        void                (*read)(Req*);
       +        void                (*write)(Req*);
       +        void                (*remove)(Req*);
       +        void                (*flush)(Req*);
       +        void                (*stat)(Req*);
       +        void                (*wstat)(Req*);
       +        void                (*walk)(Req*);
       +        char*        (*clone)(Fid*, Fid*);
       +        char*        (*walk1)(Fid*, char*, Qid*);
       +
       +        int                infd;
       +        int                outfd;
       +        int                nopipe;
       +        int                srvfd;
       +        int                leavefdsopen;        /* magic for acme win */
       +
       +/* below is implementation-specific; don't use */
       +        Fidpool*        fpool;
       +        Reqpool*        rpool;
       +        uint                msize;
       +
       +        uchar*        rbuf;
       +        QLock        rlock;
       +        uchar*        wbuf;
       +        QLock        wlock;
       +};
       +
       +void                srv(Srv*);
       +void                postmountsrv(Srv*, char*, char*, int);
       +int                 postfd(char*, int);
       +int                chatty9p;
       +void                respond(Req*, char*);
       +void                threadpostmountsrv(Srv*, char*, char*, int);
       +
       +/*
       + * Helper.  Assumes user is same as group.
       + */
       +int                hasperm(File*, char*, int);
       +
       +void*        emalloc9p(ulong);
       +void*        erealloc9p(void*, ulong);
       +char*        estrdup9p(char*);
       +
       +enum {
       +        OMASK = 3
       +};
       +
       +void readstr(Req*, char*);
       +void readbuf(Req*, void*, long);
       +void        walkandclone(Req*, char*(*walk1)(Fid*,char*,void*), char*(*clone)(Fid*,Fid*,void*), void*);
       +
       +#ifdef __cplusplus
       +}
       +#endif
       +#endif
 (DIR) diff --git a/include/auth.h b/include/auth.h
       t@@ -0,0 +1,159 @@
       +#ifndef __AUTH_H__
       +#define __AUTH_H__ 1
       +
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +/*
       +#pragma        src        "/sys/src/libauth"
       +#pragma        lib        "libauth.a"
       +*/
       +
       +/*
       + * Interface for typical callers.
       + */
       +
       +typedef struct        AuthInfo        AuthInfo;
       +typedef struct        Chalstate        Chalstate;
       +typedef struct        Chapreply        Chapreply;
       +typedef struct        MSchapreply        MSchapreply;
       +typedef struct        UserPasswd        UserPasswd;
       +typedef struct        AuthRpc                AuthRpc;
       +
       +enum
       +{
       +        MAXCHLEN=        256,                /* max challenge length        */
       +        MAXNAMELEN=        256,                /* maximum name length */
       +        MD5LEN=                16,
       +
       +        ARok = 0,                        /* rpc return values */
       +        ARdone,
       +        ARerror,
       +        ARneedkey,
       +        ARbadkey,
       +        ARwritenext,
       +        ARtoosmall,
       +        ARtoobig,
       +        ARrpcfailure,
       +        ARphase,
       +
       +        AuthRpcMax = 4096,
       +};
       +
       +struct AuthRpc
       +{
       +        int afd;
       +        char ibuf[AuthRpcMax];
       +        char obuf[AuthRpcMax];
       +        char *arg;
       +        uint narg;
       +};
       +
       +struct AuthInfo
       +{
       +        char        *cuid;                /* caller id */
       +        char        *suid;                /* server id */
       +        char        *cap;                /* capability (only valid on server side) */
       +        int        nsecret;        /* length of secret */
       +        uchar        *secret;        /* secret */
       +};
       +
       +struct Chalstate
       +{
       +        char        *user;
       +        char        chal[MAXCHLEN];
       +        int        nchal;
       +        void        *resp;
       +        int        nresp;
       +
       +/* for implementation only */
       +        int        afd;                        /* to factotum */
       +        AuthRpc        *rpc;                        /* to factotum */
       +        char        userbuf[MAXNAMELEN];        /* temp space if needed */
       +        int        userinchal;                /* user was sent to obtain challenge */
       +};
       +
       +struct        Chapreply                /* for protocol "chap" */
       +{
       +        uchar        id;
       +        char        resp[MD5LEN];
       +};
       +
       +struct        MSchapreply        /* for protocol "mschap" */
       +{
       +        char        LMresp[24];                /* Lan Manager response */
       +        char        NTresp[24];                /* NT response */
       +};
       +
       +struct        UserPasswd
       +{
       +        char        *user;
       +        char        *passwd;
       +};
       +
       +extern        int        newns(char*, char*);
       +extern        int        addns(char*, char*);
       +
       +extern        int        noworld(char*);
       +extern        int        amount(int, char*, int, char*);
       +
       +/* these two may get generalized away -rsc */
       +extern        int        login(char*, char*, char*);
       +extern        int        httpauth(char*, char*);
       +
       +typedef struct Attr Attr;
       +enum {
       +        AttrNameval,                /* name=val -- when matching, must have name=val */
       +        AttrQuery,                /* name? -- when matching, must be present */
       +        AttrDefault,                /* name:=val -- when matching, if present must match INTERNAL */
       +};
       +struct Attr
       +{
       +        int type;
       +        Attr *next;
       +        char *name;
       +        char *val;
       +};
       +
       +typedef int AuthGetkey(char*);
       +
       +int        _attrfmt(Fmt*);
       +Attr        *_copyattr(Attr*);
       +Attr        *_delattr(Attr*, char*);
       +Attr        *_findattr(Attr*, char*);
       +void        _freeattr(Attr*);
       +Attr        *_mkattr(int, char*, char*, Attr*);
       +Attr        *_parseattr(char*);
       +char        *_strfindattr(Attr*, char*);
       +/*
       +#pragma varargck type "A" Attr*
       +*/
       +
       +extern AuthInfo*        fauth_proxy(int, AuthRpc *rpc, AuthGetkey *getkey, char *params);
       +extern AuthInfo*        auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...);
       +extern int                auth_getkey(char*);
       +extern int                (*amount_getkey)(char*);
       +extern void                auth_freeAI(AuthInfo *ai);
       +extern int                auth_chuid(AuthInfo *ai, char *ns);
       +extern Chalstate        *auth_challenge(char*, ...);
       +extern AuthInfo*        auth_response(Chalstate*);
       +extern int                auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...);
       +extern void                auth_freechal(Chalstate*);
       +extern AuthInfo*        auth_userpasswd(char *user, char *passwd);
       +extern UserPasswd*        auth_getuserpasswd(AuthGetkey *getkey, char*, ...);
       +extern AuthInfo*        auth_getinfo(AuthRpc *rpc);
       +extern AuthRpc*                auth_allocrpc(int afd);
       +extern Attr*                auth_attr(AuthRpc *rpc);
       +extern void                auth_freerpc(AuthRpc *rpc);
       +extern uint                auth_rpc(AuthRpc *rpc, char *verb, void *a, int n);
       +extern int                auth_wep(char*, char*, ...);
       +/*
       +#pragma varargck argpos auth_proxy 3
       +#pragma varargck argpos auth_challenge 1
       +#pragma varargck argpos auth_respond 3
       +#pragma varargck argpos auth_getuserpasswd 2
       +*/
       +#ifdef __cplusplus
       +}
       +#endif
       +#endif
 (DIR) diff --git a/include/authsrv.h b/include/authsrv.h
       t@@ -0,0 +1,177 @@
       +#ifndef __AUTHSRV_H__
       +#define __AUTHSRV_H__ 1
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +/*
       +#pragma        src        "/sys/src/libauthsrv"
       +#pragma        lib        "libauthsrv.a"
       +*/
       +
       +/*
       + * Interface for talking to authentication server.
       + */
       +typedef struct        Ticket                Ticket;
       +typedef struct        Ticketreq        Ticketreq;
       +typedef struct        Authenticator        Authenticator;
       +typedef struct        Nvrsafe                Nvrsafe;
       +typedef struct        Passwordreq        Passwordreq;
       +typedef struct        OChapreply        OChapreply;
       +typedef struct        OMSchapreply        OMSchapreply;
       +
       +enum
       +{
       +        ANAMELEN=        28,                /* maximum size of name in previous proto */
       +        AERRLEN=        64,                /* maximum size of errstr in previous proto */
       +        DOMLEN=                48,                /* length of an authentication domain name */
       +        DESKEYLEN=        7,                /* length of a des key for encrypt/decrypt */
       +        CHALLEN=        8,                /* length of a plan9 sk1 challenge */
       +        NETCHLEN=        16,                /* max network challenge length (used in AS protocol) */
       +        CONFIGLEN=        14,
       +        SECRETLEN=        32,                /* max length of a secret */
       +
       +        KEYDBOFF=        8,                /* length of random data at the start of key file */
       +        OKEYDBLEN=        ANAMELEN+DESKEYLEN+4+2,        /* length of an entry in old key file */
       +        KEYDBLEN=        OKEYDBLEN+SECRETLEN,        /* length of an entry in key file */
       +        OMD5LEN=        16,
       +};
       +
       +/* encryption numberings (anti-replay) */
       +enum
       +{
       +        AuthTreq=1,        /* ticket request */
       +        AuthChal=2,        /* challenge box request */
       +        AuthPass=3,        /* change password */
       +        AuthOK=4,        /* fixed length reply follows */
       +        AuthErr=5,        /* error follows */
       +        AuthMod=6,        /* modify user */
       +        AuthApop=7,        /* apop authentication for pop3 */
       +        AuthOKvar=9,        /* variable length reply follows */
       +        AuthChap=10,        /* chap authentication for ppp */
       +        AuthMSchap=11,        /* MS chap authentication for ppp */
       +        AuthCram=12,        /* CRAM verification for IMAP (RFC2195 & rfc2104) */
       +        AuthHttp=13,        /* http domain login */
       +        AuthVNC=14,        /* VNC server login (deprecated) */
       +
       +
       +        AuthTs=64,        /* ticket encrypted with server's key */
       +        AuthTc,                /* ticket encrypted with client's key */
       +        AuthAs,                /* server generated authenticator */
       +        AuthAc,                /* client generated authenticator */
       +        AuthTp,                /* ticket encrypted with client's key for password change */
       +        AuthHr,                /* http reply */
       +};
       +
       +struct Ticketreq
       +{
       +        char        type;
       +        char        authid[ANAMELEN];        /* server's encryption id */
       +        char        authdom[DOMLEN];        /* server's authentication domain */
       +        char        chal[CHALLEN];                /* challenge from server */
       +        char        hostid[ANAMELEN];        /* host's encryption id */
       +        char        uid[ANAMELEN];                /* uid of requesting user on host */
       +};
       +#define        TICKREQLEN        (3*ANAMELEN+CHALLEN+DOMLEN+1)
       +
       +struct Ticket
       +{
       +        char        num;                        /* replay protection */
       +        char        chal[CHALLEN];                /* server challenge */
       +        char        cuid[ANAMELEN];                /* uid on client */
       +        char        suid[ANAMELEN];                /* uid on server */
       +        char        key[DESKEYLEN];                /* nonce DES key */
       +};
       +#define        TICKETLEN        (CHALLEN+2*ANAMELEN+DESKEYLEN+1)
       +
       +struct Authenticator
       +{
       +        char        num;                        /* replay protection */
       +        char        chal[CHALLEN];
       +        ulong        id;                        /* authenticator id, ++'d with each auth */
       +};
       +#define        AUTHENTLEN        (CHALLEN+4+1)
       +
       +struct Passwordreq
       +{
       +        char        num;
       +        char        old[ANAMELEN];
       +        char        new[ANAMELEN];
       +        char        changesecret;
       +        char        secret[SECRETLEN];        /* new secret */
       +};
       +#define        PASSREQLEN        (2*ANAMELEN+1+1+SECRETLEN)
       +
       +struct        OChapreply
       +{
       +        uchar        id;
       +        char        uid[ANAMELEN];
       +        char        resp[OMD5LEN];
       +};
       +
       +struct        OMSchapreply
       +{
       +        char        uid[ANAMELEN];
       +        char        LMresp[24];                /* Lan Manager response */
       +        char        NTresp[24];                /* NT response */
       +};
       +
       +/*
       + *  convert to/from wire format
       + */
       +extern        int        convT2M(Ticket*, char*, char*);
       +extern        void        convM2T(char*, Ticket*, char*);
       +extern        void        convM2Tnoenc(char*, Ticket*);
       +extern        int        convA2M(Authenticator*, char*, char*);
       +extern        void        convM2A(char*, Authenticator*, char*);
       +extern        int        convTR2M(Ticketreq*, char*);
       +extern        void        convM2TR(char*, Ticketreq*);
       +extern        int        convPR2M(Passwordreq*, char*, char*);
       +extern        void        convM2PR(char*, Passwordreq*, char*);
       +
       +/*
       + *  convert ascii password to DES key
       + */
       +extern        int        opasstokey(char*, char*);
       +extern        int        passtokey(char*, char*);
       +
       +/*
       + *  Nvram interface
       + */
       +enum {
       +        NVwrite = 1<<0,                /* always prompt and rewrite nvram */
       +        NVwriteonerr = 1<<1,        /* prompt and rewrite nvram when corrupt */
       +};
       +
       +struct Nvrsafe
       +{
       +        char        machkey[DESKEYLEN];
       +        uchar        machsum;
       +        char        authkey[DESKEYLEN];
       +        uchar        authsum;
       +        char        config[CONFIGLEN];
       +        uchar        configsum;
       +        char        authid[ANAMELEN];
       +        uchar        authidsum;
       +        char        authdom[DOMLEN];
       +        uchar        authdomsum;
       +};
       +
       +extern        uchar        nvcsum(void*, int);
       +extern int        readnvram(Nvrsafe*, int);
       +
       +/*
       + *  call up auth server
       + */
       +extern        int        authdial(char *netroot, char *authdom);
       +
       +/*
       + *  exchange messages with auth server
       + */
       +extern        int        _asgetticket(int, char*, char*);
       +extern        int        _asrdresp(int, char*, int);
       +extern        int        sslnegotiate(int, Ticket*, char**, char**);
       +extern        int        srvsslnegotiate(int, Ticket*, char**, char**);
       +#ifdef __cplusplus
       +}
       +#endif
       +#endif
 (DIR) diff --git a/include/mp.h b/include/mp.h
       t@@ -0,0 +1,153 @@
       +#ifndef __MP_H__
       +#define __MP_H__ 1
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +
       +/*
       +#pragma        src        "/sys/src/libmp"
       +#pragma        lib        "libmp.a"
       +*/
       +
       +#define _MPINT 1
       +
       +typedef long mpdigit;
       +
       +// the code assumes mpdigit to be at least an int
       +// mpdigit must be an atomic type.  mpdigit is defined
       +// in the architecture specific u.h
       +
       +typedef struct mpint mpint;
       +
       +struct mpint
       +{
       +        int        sign;        // +1 or -1
       +        int        size;        // allocated digits
       +        int        top;        // significant digits
       +        mpdigit        *p;
       +        char        flags;
       +};
       +
       +enum
       +{
       +        MPstatic=        0x01,
       +        Dbytes=                sizeof(mpdigit),        // bytes per digit
       +        Dbits=                Dbytes*8                // bits per digit
       +};
       +
       +// allocation
       +void        mpsetminbits(int n);        // newly created mpint's get at least n bits
       +mpint*        mpnew(int n);                // create a new mpint with at least n bits
       +void        mpfree(mpint *b);
       +void        mpbits(mpint *b, int n);        // ensure that b has at least n bits
       +void        mpnorm(mpint *b);                // dump leading zeros
       +mpint*        mpcopy(mpint *b);
       +void        mpassign(mpint *old, mpint *new);
       +
       +// random bits
       +mpint*        mprand(int bits, void (*gen)(uchar*, int), mpint *b);
       +
       +// conversion
       +mpint*        strtomp(char*, char**, int, mpint*);        // ascii
       +int        mpfmt(Fmt*);
       +char*        mptoa(mpint*, int, char*, int);
       +mpint*        letomp(uchar*, uint, mpint*);        // byte array, little-endian
       +int        mptole(mpint*, uchar*, uint, uchar**);
       +mpint*        betomp(uchar*, uint, mpint*);        // byte array, little-endian
       +int        mptobe(mpint*, uchar*, uint, uchar**);
       +uint        mptoui(mpint*);                        // unsigned int
       +mpint*        uitomp(uint, mpint*);
       +int        mptoi(mpint*);                        // int
       +mpint*        itomp(int, mpint*);
       +uvlong        mptouv(mpint*);                        // unsigned vlong
       +mpint*        uvtomp(uvlong, mpint*);
       +vlong        mptov(mpint*);                        // vlong
       +mpint*        vtomp(vlong, mpint*);
       +
       +// divide 2 digits by one
       +void        mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
       +
       +// in the following, the result mpint may be
       +// the same as one of the inputs.
       +void        mpadd(mpint *b1, mpint *b2, mpint *sum);        // sum = b1+b2
       +void        mpsub(mpint *b1, mpint *b2, mpint *diff);        // diff = b1-b2
       +void        mpleft(mpint *b, int shift, mpint *res);        // res = b<<shift
       +void        mpright(mpint *b, int shift, mpint *res);        // res = b>>shift
       +void        mpmul(mpint *b1, mpint *b2, mpint *prod);        // prod = b1*b2
       +void        mpexp(mpint *b, mpint *e, mpint *m, mpint *res);        // res = b**e mod m
       +void        mpmod(mpint *b, mpint *m, mpint *remainder);        // remainder = b mod m
       +
       +// quotient = dividend/divisor, remainder = dividend % divisor
       +void        mpdiv(mpint *dividend, mpint *divisor,  mpint *quotient, mpint *remainder);
       +
       +// return neg, 0, pos as b1-b2 is neg, 0, pos
       +int        mpcmp(mpint *b1, mpint *b2);
       +
       +// extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d
       +void        mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y);
       +
       +// res = b**-1 mod m
       +void        mpinvert(mpint *b, mpint *m, mpint *res);
       +
       +// bit counting
       +int        mpsignif(mpint*);        // number of sigificant bits in mantissa
       +int        mplowbits0(mpint*);        // k, where n = 2**k * q for odd q
       +
       +// well known constants
       +extern mpint        *mpzero, *mpone, *mptwo;
       +
       +// sum[0:alen] = a[0:alen-1] + b[0:blen-1]
       +// prereq: alen >= blen, sum has room for alen+1 digits
       +void        mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum);
       +
       +// diff[0:alen-1] = a[0:alen-1] - b[0:blen-1]
       +// prereq: alen >= blen, diff has room for alen digits
       +void        mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff);
       +
       +// p[0:n] += m * b[0:n-1]
       +// prereq: p has room for n+1 digits
       +void        mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p);
       +
       +// p[0:n] -= m * b[0:n-1]
       +// prereq: p has room for n+1 digits
       +int        mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p);
       +
       +// p[0:alen*blen-1] = a[0:alen-1] * b[0:blen-1]
       +// prereq: alen >= blen, p has room for m*n digits
       +void        mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p);
       +
       +// sign of a - b or zero if the same
       +int        mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen);
       +
       +// divide the 2 digit dividend by the one digit divisor and stick in quotient
       +// we assume that the result is one digit - overflow is all 1's
       +void        mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient);
       +
       +// playing with magnitudes
       +int        mpmagcmp(mpint *b1, mpint *b2);
       +void        mpmagadd(mpint *b1, mpint *b2, mpint *sum);        // sum = b1+b2
       +void        mpmagsub(mpint *b1, mpint *b2, mpint *sum);        // sum = b1+b2
       +
       +// chinese remainder theorem
       +typedef struct CRTpre        CRTpre;                // precomputed values for converting
       +                                        //  twixt residues and mpint
       +typedef struct CRTres        CRTres;                // residue form of an mpint
       +
       +struct CRTres
       +{
       +        int        n;                // number of residues
       +        mpint        *r[1];                // residues
       +};
       +
       +CRTpre*        crtpre(int, mpint**);                        // precompute conversion values
       +CRTres*        crtin(CRTpre*, mpint*);                        // convert mpint to residues
       +void        crtout(CRTpre*, CRTres*, mpint*);        // convert residues to mpint
       +void        crtprefree(CRTpre*);
       +void        crtresfree(CRTres*);
       +
       +
       +/* #pragma        varargck        type        "B"        mpint* */
       +#ifdef __cplusplus
       +}
       +#endif
       +#endif
 (DIR) diff --git a/include/nfs3.h b/include/nfs3.h
       t@@ -0,0 +1,801 @@
       +/* 
       + * NFS mounter V3;  see RFC 1813
       + */
       +/*
       +#pragma lib "libsunrpc.a"
       +#pragma src "/sys/src/libsunrpc"
       +*/
       +#define _NFS3H_ /* sorry */
       +
       +enum {
       +        NfsMount1HandleSize = 32,
       +        NfsMount3MaxPathSize = 1024,
       +        NfsMount3MaxNameSize = 255,
       +        NfsMount3MaxHandleSize = 64,
       +        NfsMount3Program = 100005,
       +        NfsMount3Version = 3,
       +        NfsMount1Program = 100005,
       +        NfsMount1Version = 1
       +};
       +typedef struct NfsMount3TNull NfsMount3TNull;
       +typedef struct NfsMount3RNull NfsMount3RNull;
       +typedef struct NfsMount3TMnt NfsMount3TMnt;
       +typedef struct NfsMount3RMnt NfsMount3RMnt;
       +typedef struct NfsMount3TDump NfsMount3TDump;
       +typedef struct NfsMount3Entry NfsMount3Entry;
       +typedef struct NfsMount3RDump NfsMount3RDump;
       +typedef struct NfsMount3TUmnt NfsMount3TUmnt;
       +typedef struct NfsMount3RUmnt NfsMount3RUmnt;
       +typedef struct NfsMount3Export NfsMount3Export;
       +typedef struct NfsMount3TUmntall NfsMount3TUmntall;
       +typedef struct NfsMount3RUmntall NfsMount3RUmntall;
       +typedef struct NfsMount3TExport NfsMount3TExport;
       +typedef struct NfsMount3RExport NfsMount3RExport;
       +
       +typedef enum
       +{
       +        NfsMount3CallTNull,
       +        NfsMount3CallRNull,
       +        NfsMount3CallTMnt,
       +        NfsMount3CallRMnt,
       +        NfsMount3CallTDump,
       +        NfsMount3CallRDump,
       +        NfsMount3CallTUmnt,
       +        NfsMount3CallRUmnt,
       +        NfsMount3CallTUmntall,
       +        NfsMount3CallRUmntall,
       +        NfsMount3CallTExport,
       +        NfsMount3CallRExport
       +} NfsMount3CallType;
       +
       +struct NfsMount3TNull {
       +        SunCall call;
       +};
       +
       +struct NfsMount3RNull {
       +        SunCall call;
       +};
       +
       +struct NfsMount3TMnt {
       +        SunCall call;
       +        char *path;
       +};
       +
       +struct NfsMount3RMnt {
       +        SunCall call;
       +        uint status;
       +        uchar *handle;
       +        uint len;
       +        u32int *auth;
       +        u32int nauth;
       +};
       +
       +struct NfsMount3TDump {
       +        SunCall call;
       +};
       +
       +struct NfsMount3Entry {
       +        char *host;
       +        char *path;
       +};
       +
       +struct NfsMount3RDump {
       +        SunCall call;
       +        uchar *data;
       +        u32int count;
       +};
       +
       +struct NfsMount3TUmnt {
       +        SunCall call;
       +        char *path;
       +};
       +
       +struct NfsMount3RUmnt {
       +        SunCall call;
       +};
       +
       +struct NfsMount3Export {
       +        char *path;
       +        char **g;
       +        u32int ng;
       +};
       +
       +struct NfsMount3TUmntall {
       +        SunCall call;
       +};
       +
       +struct NfsMount3RUmntall {
       +        SunCall call;
       +};
       +
       +struct NfsMount3TExport {
       +        SunCall call;
       +};
       +
       +struct NfsMount3RExport {
       +        SunCall call;
       +        uchar *data;
       +        u32int count;
       +};
       +
       +uint nfsmount3exportgroupsize(uchar*);
       +uint nfsmount3exportsize(NfsMount3Export*);
       +int nfsmount3exportpack(uchar*, uchar*, uchar**, NfsMount3Export*);
       +int nfsmount3exportunpack(uchar*, uchar*, uchar**, char**, char***, NfsMount3Export*);
       +int nfsmount3entrypack(uchar*, uchar*, uchar**, NfsMount3Entry*);
       +int nfsmount3entryunpack(uchar*, uchar*, uchar**, NfsMount3Entry*);
       +uint nfsmount3entrysize(NfsMount3Entry*);
       +
       +extern SunProg nfsmount3prog;
       +
       +/*
       + * NFS V3; see RFC 1813
       + */
       +enum {
       +        Nfs3MaxHandleSize = 64,
       +        Nfs3CookieVerfSize = 8,
       +        Nfs3CreateVerfSize = 8,
       +        Nfs3WriteVerfSize = 8,
       +        Nfs3AccessRead = 1,
       +        Nfs3AccessLookup = 2,
       +        Nfs3AccessModify = 4,
       +        Nfs3AccessExtend = 8,
       +        Nfs3AccessDelete = 16,
       +        Nfs3AccessExecute = 32,
       +        Nfs3FsHasLinks = 1,
       +        Nfs3FsHasSymlinks = 2,
       +        Nfs3FsHomogeneous = 8,
       +        Nfs3FsCanSetTime = 16,
       +
       +        Nfs3Version = 3,        
       +        Nfs3Program = 100003,
       +};
       +typedef enum
       +{
       +        Nfs3Ok = 0,
       +        Nfs3ErrNotOwner = 1,
       +        Nfs3ErrNoEnt = 2,
       +        Nfs3ErrIo = 5,
       +        Nfs3ErrNxio = 6,
       +        Nfs3ErrNoMem = 12,
       +        Nfs3ErrAcces = 13,
       +        Nfs3ErrExist = 17,
       +        Nfs3ErrXDev = 18,
       +        Nfs3ErrNoDev = 19,
       +        Nfs3ErrNotDir = 20,
       +        Nfs3ErrIsDir = 21,
       +        Nfs3ErrInval = 22,
       +        Nfs3ErrFbig = 27,
       +        Nfs3ErrNoSpc = 28,
       +        Nfs3ErrRoFs = 30,
       +        Nfs3ErrMLink = 31,
       +        Nfs3ErrNameTooLong = 63,
       +        Nfs3ErrNotEmpty = 66,
       +        Nfs3ErrDQuot = 69,
       +        Nfs3ErrStale = 70,
       +        Nfs3ErrRemote = 71,
       +        Nfs3ErrBadHandle = 10001,
       +        Nfs3ErrNotSync = 10002,
       +        Nfs3ErrBadCookie = 10003,
       +        Nfs3ErrNotSupp = 10004,
       +        Nfs3ErrTooSmall = 10005,
       +        Nfs3ErrServerFault = 10006,
       +        Nfs3ErrBadType = 10007,
       +        Nfs3ErrJukebox = 10008,
       +        Nfs3ErrFprintNotFound = 10009,
       +        Nfs3ErrAborted = 10010,
       +} Nfs3Status;
       +
       +void nfs3Errstr(Nfs3Status);
       +
       +typedef enum
       +{
       +        Nfs3FileReg = 1,
       +        Nfs3FileDir = 2,
       +        Nfs3FileBlock = 3,
       +        Nfs3FileChar = 4,
       +        Nfs3FileSymlink = 5,
       +        Nfs3FileSocket = 6,
       +        Nfs3FileFifo = 7,
       +} Nfs3FileType;
       +
       +enum
       +{
       +        Nfs3ModeSetUid = 0x800,
       +        Nfs3ModeSetGid = 0x400,
       +        Nfs3ModeSticky = 0x200,
       +};
       +
       +typedef enum
       +{
       +        Nfs3CallTNull,
       +        Nfs3CallRNull,
       +        Nfs3CallTGetattr,
       +        Nfs3CallRGetattr,
       +        Nfs3CallTSetattr,
       +        Nfs3CallRSetattr,
       +        Nfs3CallTLookup,
       +        Nfs3CallRLookup,
       +        Nfs3CallTAccess,
       +        Nfs3CallRAccess,
       +        Nfs3CallTReadlink,
       +        Nfs3CallRReadlink,
       +        Nfs3CallTRead,
       +        Nfs3CallRRead,
       +        Nfs3CallTWrite,
       +        Nfs3CallRWrite,
       +        Nfs3CallTCreate,
       +        Nfs3CallRCreate,
       +        Nfs3CallTMkdir,
       +        Nfs3CallRMkdir,
       +        Nfs3CallTSymlink,
       +        Nfs3CallRSymlink,
       +        Nfs3CallTMknod,
       +        Nfs3CallRMknod,
       +        Nfs3CallTRemove,
       +        Nfs3CallRRemove,
       +        Nfs3CallTRmdir,
       +        Nfs3CallRRmdir,
       +        Nfs3CallTRename,
       +        Nfs3CallRRename,
       +        Nfs3CallTLink,
       +        Nfs3CallRLink,
       +        Nfs3CallTReadDir,
       +        Nfs3CallRReadDir,
       +        Nfs3CallTReadDirPlus,
       +        Nfs3CallRReadDirPlus,
       +        Nfs3CallTFsStat,
       +        Nfs3CallRFsStat,
       +        Nfs3CallTFsInfo,
       +        Nfs3CallRFsInfo,
       +        Nfs3CallTPathconf,
       +        Nfs3CallRPathconf,
       +        Nfs3CallTCommit,
       +        Nfs3CallRCommit,
       +} Nfs3CallType;
       +
       +typedef struct Nfs3Handle Nfs3Handle;
       +typedef struct Nfs3Time Nfs3Time;
       +typedef struct Nfs3Attr Nfs3Attr;
       +typedef struct Nfs3WccAttr Nfs3WccAttr;
       +typedef struct Nfs3Wcc Nfs3Wcc;
       +typedef enum
       +{
       +        Nfs3SetTimeDont = 0,
       +        Nfs3SetTimeServer = 1,
       +        Nfs3SetTimeClient = 2,
       +} Nfs3SetTime;
       +
       +typedef struct Nfs3SetAttr Nfs3SetAttr;
       +typedef struct Nfs3TNull Nfs3TNull;
       +typedef struct Nfs3RNull Nfs3RNull;
       +typedef struct Nfs3TGetattr Nfs3TGetattr;
       +typedef struct Nfs3RGetattr Nfs3RGetattr;
       +typedef struct Nfs3TSetattr Nfs3TSetattr;
       +typedef struct Nfs3RSetattr Nfs3RSetattr;
       +typedef struct Nfs3TLookup Nfs3TLookup;
       +typedef struct Nfs3RLookup Nfs3RLookup;
       +typedef struct Nfs3TAccess Nfs3TAccess;
       +typedef struct Nfs3RAccess Nfs3RAccess;
       +typedef struct Nfs3TReadlink Nfs3TReadlink;
       +typedef struct Nfs3RReadlink Nfs3RReadlink;
       +typedef struct Nfs3TRead Nfs3TRead;
       +typedef struct Nfs3RRead Nfs3RRead;
       +typedef enum
       +{
       +        Nfs3SyncNone = 0,
       +        Nfs3SyncData = 1,
       +        Nfs3SyncFile = 2,
       +} Nfs3Sync;
       +
       +typedef struct Nfs3TWrite Nfs3TWrite;
       +typedef struct Nfs3RWrite Nfs3RWrite;
       +typedef enum
       +{
       +        Nfs3CreateUnchecked = 0,
       +        Nfs3CreateGuarded = 1,
       +        Nfs3CreateExclusive = 2,
       +} Nfs3Create;
       +
       +typedef struct Nfs3TCreate Nfs3TCreate;
       +typedef struct Nfs3RCreate Nfs3RCreate;
       +typedef struct Nfs3TMkdir Nfs3TMkdir;
       +typedef struct Nfs3RMkdir Nfs3RMkdir;
       +typedef struct Nfs3TSymlink Nfs3TSymlink;
       +typedef struct Nfs3RSymlink Nfs3RSymlink;
       +typedef struct Nfs3TMknod Nfs3TMknod;
       +typedef struct Nfs3RMknod Nfs3RMknod;
       +typedef struct Nfs3TRemove Nfs3TRemove;
       +typedef struct Nfs3RRemove Nfs3RRemove;
       +typedef struct Nfs3TRmdir Nfs3TRmdir;
       +typedef struct Nfs3RRmdir Nfs3RRmdir;
       +typedef struct Nfs3TRename Nfs3TRename;
       +typedef struct Nfs3RRename Nfs3RRename;
       +typedef struct Nfs3TLink Nfs3TLink;
       +typedef struct Nfs3RLink Nfs3RLink;
       +typedef struct Nfs3TReadDir Nfs3TReadDir;
       +typedef struct Nfs3Entry Nfs3Entry;
       +typedef struct Nfs3RReadDir Nfs3RReadDir;
       +typedef struct Nfs3TReadDirPlus Nfs3TReadDirPlus;
       +typedef struct Nfs3EntryPlus Nfs3EntryPlus;
       +typedef struct Nfs3RReadDirPlus Nfs3RReadDirPlus;
       +typedef struct Nfs3TFsStat Nfs3TFsStat;
       +typedef struct Nfs3RFsStat Nfs3RFsStat;
       +typedef struct Nfs3TFsInfo Nfs3TFsInfo;
       +typedef struct Nfs3RFsInfo Nfs3RFsInfo;
       +typedef struct Nfs3TPathconf Nfs3TPathconf;
       +typedef struct Nfs3RPathconf Nfs3RPathconf;
       +typedef struct Nfs3TCommit Nfs3TCommit;
       +typedef struct Nfs3RCommit Nfs3RCommit;
       +
       +struct Nfs3Handle {
       +        uchar h[Nfs3MaxHandleSize];
       +        u32int len;
       +};
       +
       +struct Nfs3Time {
       +        u32int sec;
       +        u32int nsec;
       +};
       +
       +struct Nfs3Attr {
       +        Nfs3FileType type;
       +        u32int mode;
       +        u32int nlink;
       +        u32int uid;
       +        u32int gid;
       +        u64int size;
       +        u64int used;
       +        u32int major;
       +        u32int minor;
       +        u64int fsid;
       +        u64int fileid;
       +        Nfs3Time atime;
       +        Nfs3Time mtime;
       +        Nfs3Time ctime;
       +};
       +
       +struct Nfs3WccAttr {
       +        u64int size;
       +        Nfs3Time mtime;
       +        Nfs3Time ctime;
       +};
       +
       +struct Nfs3Wcc {
       +        u1int haveWccAttr;
       +        Nfs3WccAttr wccAttr;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +};
       +
       +struct Nfs3SetAttr {
       +        u1int setMode;
       +        u32int mode;
       +        u1int setUid;
       +        u32int uid;
       +        u1int setGid;
       +        u32int gid;
       +        u1int setSize;
       +        u64int size;
       +        Nfs3SetTime setAtime;
       +        Nfs3Time atime;
       +        Nfs3SetTime setMtime;
       +        Nfs3Time mtime;
       +};
       +
       +struct Nfs3TNull {
       +        SunCall call;
       +};
       +
       +struct Nfs3RNull {
       +        SunCall call;
       +};
       +
       +struct Nfs3TGetattr {
       +        SunCall call;
       +        Nfs3Handle handle;
       +};
       +
       +struct Nfs3RGetattr {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Attr attr;
       +};
       +
       +struct Nfs3TSetattr {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        Nfs3SetAttr attr;
       +        u1int checkCtime;
       +        Nfs3Time ctime;
       +};
       +
       +struct Nfs3RSetattr {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Wcc wcc;
       +};
       +
       +struct Nfs3TLookup {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +};
       +
       +struct Nfs3RLookup {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Handle handle;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u1int haveDirAttr;
       +        Nfs3Attr dirAttr;
       +};
       +
       +struct Nfs3TAccess {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        u32int access;
       +};
       +
       +struct Nfs3RAccess {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u32int access;
       +};
       +
       +struct Nfs3TReadlink {
       +        SunCall call;
       +        Nfs3Handle handle;
       +};
       +
       +struct Nfs3RReadlink {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        char *data;
       +};
       +
       +struct Nfs3TRead {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        u64int offset;
       +        u32int count;
       +};
       +
       +struct Nfs3RRead {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u32int count;
       +        u1int eof;
       +        uchar *data;
       +        u32int ndata;
       +};
       +
       +struct Nfs3TWrite {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        u64int offset;
       +        u32int count;
       +        Nfs3Sync stable;
       +        uchar *data;
       +        u32int ndata;
       +};
       +
       +struct Nfs3RWrite {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Wcc wcc;
       +        u32int count;
       +        Nfs3Sync committed;
       +        uchar verf[Nfs3WriteVerfSize];
       +};
       +
       +struct Nfs3TCreate {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +        Nfs3Create mode;
       +        Nfs3SetAttr attr;
       +        uchar verf[Nfs3CreateVerfSize];
       +};
       +
       +struct Nfs3RCreate {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveHandle;
       +        Nfs3Handle handle;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        Nfs3Wcc dirWcc;
       +};
       +
       +struct Nfs3TMkdir {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +        Nfs3SetAttr attr;
       +};
       +
       +struct Nfs3RMkdir {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveHandle;
       +        Nfs3Handle handle;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        Nfs3Wcc dirWcc;
       +};
       +
       +struct Nfs3TSymlink {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +        Nfs3SetAttr attr;
       +        char *data;
       +};
       +
       +struct Nfs3RSymlink {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveHandle;
       +        Nfs3Handle handle;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        Nfs3Wcc dirWcc;
       +};
       +
       +struct Nfs3TMknod {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +        Nfs3FileType type;
       +        Nfs3SetAttr attr;
       +        u32int major;
       +        u32int minor;
       +};
       +
       +struct Nfs3RMknod {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveHandle;
       +        Nfs3Handle handle;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        Nfs3Wcc dirWcc;
       +};
       +
       +struct Nfs3TRemove {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +};
       +
       +struct Nfs3RRemove {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Wcc wcc;
       +};
       +
       +struct Nfs3TRmdir {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        char *name;
       +};
       +
       +struct Nfs3RRmdir {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Wcc wcc;
       +};
       +
       +struct Nfs3TRename {
       +        SunCall call;
       +        struct {
       +                Nfs3Handle handle;
       +                char *name;
       +        } from;
       +        struct {
       +                Nfs3Handle handle;
       +                char *name;
       +        } to;
       +};
       +
       +struct Nfs3RRename {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Wcc fromWcc;
       +        Nfs3Wcc toWcc;
       +};
       +
       +struct Nfs3TLink {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        struct {
       +                Nfs3Handle handle;
       +                char *name;
       +        } link;
       +};
       +
       +struct Nfs3RLink {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        Nfs3Wcc dirWcc;
       +};
       +
       +struct Nfs3TReadDir {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        u64int cookie;
       +        uchar verf[Nfs3CookieVerfSize];
       +        u32int count;
       +};
       +
       +struct Nfs3RReadDir {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        uchar verf[Nfs3CookieVerfSize];
       +        uchar *data;
       +        u32int count;
       +        u1int eof;
       +};
       +
       +struct Nfs3TReadDirPlus {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        u64int cookie;
       +        uchar verf[Nfs3CookieVerfSize];
       +        u32int dirCount;
       +        u32int maxCount;
       +};
       +
       +struct Nfs3Entry {
       +        u64int fileid;
       +        char *name;
       +        u64int cookie;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u1int haveHandle;
       +        Nfs3Handle handle;
       +};
       +
       +struct Nfs3RReadDirPlus {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        uchar verf[Nfs3CookieVerfSize];
       +        uchar *data;
       +        u32int count;
       +        u1int eof;
       +};
       +
       +struct Nfs3TFsStat {
       +        SunCall call;
       +        Nfs3Handle handle;
       +};
       +
       +struct Nfs3RFsStat {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u64int totalBytes;
       +        u64int freeBytes;
       +        u64int availBytes;
       +        u64int totalFiles;
       +        u64int freeFiles;
       +        u64int availFiles;
       +        u32int invarSec;
       +};
       +
       +struct Nfs3TFsInfo {
       +        SunCall call;
       +        Nfs3Handle handle;
       +};
       +
       +struct Nfs3RFsInfo {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u32int readMax;
       +        u32int readPref;
       +        u32int readMult;
       +        u32int writeMax;
       +        u32int writePref;
       +        u32int writeMult;
       +        u32int readDirPref;
       +        u64int maxFileSize;
       +        Nfs3Time timePrec;
       +        u32int flags;
       +};
       +
       +struct Nfs3TPathconf {
       +        SunCall call;
       +        Nfs3Handle handle;
       +};
       +
       +struct Nfs3RPathconf {
       +        SunCall call;
       +        Nfs3Status status;
       +        u1int haveAttr;
       +        Nfs3Attr attr;
       +        u32int maxLink;
       +        u32int maxName;
       +        u1int noTrunc;
       +        u1int chownRestricted;
       +        u1int caseInsensitive;
       +        u1int casePreserving;
       +};
       +
       +struct Nfs3TCommit {
       +        SunCall call;
       +        Nfs3Handle handle;
       +        u64int offset;
       +        u32int count;
       +};
       +
       +struct Nfs3RCommit {
       +        SunCall call;
       +        Nfs3Status status;
       +        Nfs3Wcc wcc;
       +        uchar verf[Nfs3WriteVerfSize];
       +};
       +
       +char *nfs3statusstr(Nfs3Status);
       +char *nfs3typestr(SunCallType);
       +char *nfs3settimestr(Nfs3SetTime);
       +char *nfs3syncstr(Nfs3Sync);
       +
       +void nfs3handleprint(Fmt*, Nfs3Handle*);
       +u32int nfs3handlesize(Nfs3Handle*);
       +int nfs3handlepack(uchar*, uchar*, uchar**, Nfs3Handle*);
       +int nfs3handleunpack(uchar*, uchar*, uchar**, Nfs3Handle*);
       +
       +void nfs3timeprint(Fmt*, Nfs3Time*);
       +u32int nfs3timesize(Nfs3Time*);
       +int nfs3timepack(uchar*, uchar*, uchar**, Nfs3Time*);
       +int nfs3timeunpack(uchar*, uchar*, uchar**, Nfs3Time*);
       +
       +void nfs3attrprint(Fmt*, Nfs3Attr*);
       +u32int nfs3attrsize(Nfs3Attr*);
       +int nfs3attrpack(uchar*, uchar*, uchar**, Nfs3Attr*);
       +int nfs3attrunpack(uchar*, uchar*, uchar**, Nfs3Attr*);
       +
       +void nfs3wccattrprint(Fmt*, Nfs3WccAttr*);
       +u32int nfs3wccattrsize(Nfs3WccAttr*);
       +int nfs3wccattrpack(uchar*, uchar*, uchar**, Nfs3WccAttr*);
       +int nfs3wccattrunpack(uchar*, uchar*, uchar**, Nfs3WccAttr*);
       +
       +void nfs3wccprint(Fmt*, Nfs3Wcc*);
       +u32int nfs3wccsize(Nfs3Wcc*);
       +int nfs3wccpack(uchar*, uchar*, uchar**, Nfs3Wcc*);
       +int nfs3wccunpack(uchar*, uchar*, uchar**, Nfs3Wcc*);
       +
       +void nfs3setattrprint(Fmt*, Nfs3SetAttr*);
       +u32int nfs3setattrsize(Nfs3SetAttr*);
       +int nfs3setattrpack(uchar*, uchar*, uchar**, Nfs3SetAttr*);
       +int nfs3setattrunpack(uchar*, uchar*, uchar**, Nfs3SetAttr*);
       +
       +extern SunProg nfs3prog;
       +
       +void nfs3entryprint(Fmt*, Nfs3Entry*);
       +u32int nfs3entrysize(Nfs3Entry*);
       +int nfs3entrypack(uchar*, uchar*, uchar**, Nfs3Entry*);
       +int nfs3entryunpack(uchar*, uchar*, uchar**, Nfs3Entry*);
       +
       +void nfs3entryplusprint(Fmt*, Nfs3Entry*);
       +u32int nfs3entryplussize(Nfs3Entry*);
       +int nfs3entrypluspack(uchar*, uchar*, uchar**, Nfs3Entry*);
       +int nfs3entryplusunpack(uchar*, uchar*, uchar**, Nfs3Entry*);
       +
 (DIR) diff --git a/include/sunrpc.h b/include/sunrpc.h
       t@@ -0,0 +1,400 @@
       +/*
       + * Sun RPC; see RFC 1057
       + */
       +
       +/*
       +#pragma lib "libsunrpc.a"
       +#pragma src "/sys/src/libsunrpc"
       +*/
       +
       +typedef uchar u1int;
       +
       +typedef struct SunAuthInfo SunAuthInfo;
       +typedef struct SunAuthUnix SunAuthUnix;
       +typedef struct SunRpc SunRpc;
       +typedef struct SunCall SunCall;
       +
       +enum
       +{
       +        /* Authinfo.flavor */
       +        SunAuthNone = 0,
       +        SunAuthSys,
       +        SunAuthShort,
       +        SunAuthDes,
       +};
       +
       +typedef enum {
       +        SunAcceptError = 0x10000,
       +        SunRejectError = 0x20000,
       +        SunAuthError = 0x40000,
       +
       +        /* Reply.status */
       +        SunSuccess = 0,
       +
       +        SunProgUnavail = SunAcceptError | 1,
       +        SunProgMismatch,
       +        SunProcUnavail,
       +        SunGarbageArgs,
       +        SunSystemErr,
       +
       +        SunRpcMismatch = SunRejectError | 0,
       +
       +        SunAuthBadCred = SunAuthError | 1,
       +        SunAuthRejectedCred,
       +        SunAuthBadVerf,
       +        SunAuthRejectedVerf,
       +        SunAuthTooWeak,
       +        SunAuthInvalidResp,
       +        SunAuthFailed,
       +} SunStatus;
       +
       +struct SunAuthInfo
       +{
       +        uint flavor;
       +        uchar *data;
       +        uint ndata;
       +};
       +
       +struct SunAuthUnix
       +{
       +        u32int stamp;
       +        char *sysname;
       +        u32int uid;
       +        u32int gid;
       +        u32int g[16];
       +        u32int ng;
       +};
       +
       +struct SunRpc
       +{
       +        u32int xid;
       +        uint iscall;
       +
       +        /*
       +         * only sent on wire in call
       +         * caller fills in for the reply unpackers.
       +         */
       +        u32int proc;
       +
       +        /* call */
       +        // uint proc;
       +        u32int prog, vers;
       +        SunAuthInfo cred;
       +        SunAuthInfo verf;
       +        uchar *data;
       +        uint ndata;
       +
       +        /* reply */
       +        u32int status;
       +        // SunAuthInfo verf;
       +        u32int low, high;
       +        // uchar *data;
       +        // uint ndata;
       +};
       +
       +typedef enum
       +{
       +        SunCallTypeTNull,
       +        SunCallTypeRNull,
       +} SunCallType;
       +
       +struct SunCall
       +{
       +        SunRpc rpc;
       +        SunCallType type;
       +};
       +
       +void sunerrstr(SunStatus);
       +
       +void sunrpcprint(Fmt*, SunRpc*);
       +uint sunrpcsize(SunRpc*);
       +SunStatus sunrpcpack(uchar*, uchar*, uchar**, SunRpc*);
       +SunStatus sunrpcunpack(uchar*, uchar*, uchar**, SunRpc*);
       +
       +void sunauthinfoprint(Fmt*, SunAuthInfo*);
       +uint sunauthinfosize(SunAuthInfo*);
       +int sunauthinfopack(uchar*, uchar*, uchar**, SunAuthInfo*);
       +int sunauthinfounpack(uchar*, uchar*, uchar**, SunAuthInfo*);
       +
       +void sunauthunixprint(Fmt*, SunAuthUnix*);
       +uint sunauthunixsize(SunAuthUnix*);
       +int sunauthunixpack(uchar*, uchar*, uchar**, SunAuthUnix*);
       +int sunauthunixunpack(uchar*, uchar*, uchar**, SunAuthUnix*);
       +
       +int sunenumpack(uchar*, uchar*, uchar**, int*);
       +int sunenumunpack(uchar*, uchar*, uchar**, int*);
       +int sunuint1pack(uchar*, uchar*, uchar**, u1int*);
       +int sunuint1unpack(uchar*, uchar*, uchar**, u1int*);
       +
       +int sunstringpack(uchar*, uchar*, uchar**, char**, u32int);
       +int sunstringunpack(uchar*, uchar*, uchar**, char**, u32int);
       +uint sunstringsize(char*);
       +
       +int sunuint32pack(uchar*, uchar*, uchar**, u32int*);
       +int sunuint32unpack(uchar*, uchar*, uchar**, u32int*);
       +int sunuint64pack(uchar*, uchar*, uchar**, u64int*);
       +int sunuint64unpack(uchar*, uchar*, uchar**, u64int*);
       +
       +int sunvaropaquepack(uchar*, uchar*, uchar**, uchar**, u32int*, u32int);
       +int sunvaropaqueunpack(uchar*, uchar*, uchar**, uchar**, u32int*, u32int);
       +uint sunvaropaquesize(u32int);
       +
       +int sunfixedopaquepack(uchar*, uchar*, uchar**, uchar*, u32int);
       +int sunfixedopaqueunpack(uchar*, uchar*, uchar**, uchar*, u32int);
       +uint sunfixedopaquesize(u32int);
       +
       +/*
       + * Sun RPC Program
       + */
       +typedef struct SunProc SunProc;
       +typedef struct SunProg SunProg;
       +struct SunProg
       +{
       +        uint prog;
       +        uint vers;
       +        SunProc *proc;
       +        int nproc;
       +};
       +
       +struct SunProc
       +{
       +        int (*pack)(uchar*, uchar*, uchar**, SunCall*);
       +        int (*unpack)(uchar*, uchar*, uchar**, SunCall*);
       +        uint (*size)(SunCall*);
       +        void (*fmt)(Fmt*, SunCall*);
       +        uint sizeoftype;
       +};
       +
       +SunStatus suncallpack(SunProg*, uchar*, uchar*, uchar**, SunCall*);
       +SunStatus suncallunpack(SunProg*, uchar*, uchar*, uchar**, SunCall*);
       +SunStatus suncallunpackalloc(SunProg*, SunCallType, uchar*, uchar*, uchar**, SunCall**);
       +void suncallsetup(SunCall*, SunProg*, uint);
       +uint suncallsize(SunProg*, SunCall*);
       +
       +/*
       + * Formatting
       +#pragma varargck type "B" SunRpc*
       +#pragma varargck type "C" SunCall*
       + */
       +
       +int        sunrpcfmt(Fmt*);
       +int        suncallfmt(Fmt*);
       +void        sunfmtinstall(SunProg*);
       +
       +
       +/*
       + * Sun RPC Server
       + */
       +typedef struct SunMsg SunMsg;
       +typedef struct SunSrv SunSrv;
       +
       +enum
       +{
       +        SunStackSize = 32768,
       +};
       +
       +struct SunMsg
       +{
       +        uchar *data;
       +        int count;
       +        SunSrv *srv;
       +        SunRpc rpc;
       +        SunProg *pg;
       +        SunCall *call;
       +        Channel *creply;        /* chan(SunMsg*) */
       +};
       +
       +struct SunSrv
       +{
       +        int chatty;
       +        int cachereplies;
       +        int alwaysreject;
       +        int localonly;
       +        int localparanoia;
       +        SunProg **map;
       +        Channel *crequest;
       +
       +/* implementation use only */
       +        Channel **cdispatch;
       +        SunProg **prog;
       +        int nprog;
       +        void *cache;
       +        Channel *creply;
       +        Channel *cthread;
       +};
       +
       +SunSrv *sunsrv(void);
       +
       +void        sunsrvprog(SunSrv *srv, SunProg *prog, Channel *c);
       +int        sunsrvannounce(SunSrv *srv, char *address);
       +int        sunsrvudp(SunSrv *srv, char *address);
       +int        sunsrvnet(SunSrv *srv, char *address);
       +int        sunsrvfd(SunSrv *srv, int fd);
       +void        sunsrvthreadcreate(SunSrv *srv, void (*fn)(void*), void*);
       +void        sunsrvclose(SunSrv*);
       +
       +int        sunmsgreply(SunMsg*, SunCall*);
       +int        sunmsgdrop(SunMsg*);
       +int        sunmsgreplyerror(SunMsg*, SunStatus);
       +
       +/*
       + * Sun RPC Client
       + */
       +typedef struct SunClient SunClient;
       +
       +struct SunClient
       +{
       +        int                fd;
       +        int                chatty;
       +        int                needcount;
       +        ulong        maxwait;
       +        ulong        xidgen;
       +        int                nsend;
       +        int                nresend;
       +        struct {
       +                ulong min;
       +                ulong max;
       +                ulong avg;
       +        } rtt;
       +        Channel        *dying;
       +        Channel        *rpcchan;
       +        Channel        *timerchan;
       +        Channel        *flushchan;
       +        Channel        *readchan;
       +        SunProg        **prog;
       +        int                nprog;
       +        int                 timertid;
       +        int                 nettid;
       +};
       +
       +SunClient        *sundial(char*);
       +
       +int        sunclientrpc(SunClient*, ulong, SunCall*, SunCall*, uchar**);
       +void        sunclientclose(SunClient*);
       +void        sunclientflushrpc(SunClient*, ulong);
       +void        sunclientprog(SunClient*, SunProg*);
       +
       +
       +/*
       + * Provided by callers.
       + * Should remove dependence on this, but hard.
       + */
       +void        *emalloc(ulong);
       +void *erealloc(void*, ulong);
       +
       +
       +/*
       + * Sun RPC port mapper; see RFC 1057 Appendix A
       + */
       +
       +typedef struct PortMap PortMap;
       +typedef struct PortTNull PortTNull;
       +typedef struct PortRNull PortRNull;
       +typedef struct PortTSet PortTSet;
       +typedef struct PortRSet PortRSet;
       +typedef struct PortTUnset PortTUnset;
       +typedef struct PortRUnset PortRUnset;
       +typedef struct PortTGetport PortTGetport;
       +typedef struct PortRGetport PortRGetport;
       +typedef struct PortTDump PortTDump;
       +typedef struct PortRDump PortRDump;
       +typedef struct PortTCallit PortTCallit;
       +typedef struct PortRCallit PortRCallit;
       +
       +typedef enum
       +{
       +        PortCallTNull,
       +        PortCallRNull,
       +        PortCallTSet,
       +        PortCallRSet,
       +        PortCallTUnset,
       +        PortCallRUnset,
       +        PortCallTGetport,
       +        PortCallRGetport,
       +        PortCallTDump,
       +        PortCallRDump,
       +        PortCallTCallit,
       +        PortCallRCallit,
       +} PortCallType;
       +
       +enum
       +{
       +        PortProgram        = 100000,
       +        PortVersion        = 2,
       +
       +        PortProtoTcp        = 6,        /* protocol number for TCP/IP */
       +        PortProtoUdp        = 17        /* protocol number for UDP/IP */
       +};
       +
       +struct PortMap {
       +        u32int prog;
       +        u32int vers;
       +        u32int prot;
       +        u32int port;
       +};
       +
       +struct PortTNull {
       +        SunCall call;
       +};
       +
       +struct PortRNull {
       +        SunCall call;
       +};
       +
       +struct PortTSet {
       +        SunCall call;
       +        PortMap map;
       +};
       +
       +struct PortRSet {
       +        SunCall call;
       +        u1int b;
       +};
       +
       +struct PortTUnset {
       +        SunCall call;
       +        PortMap map;
       +};
       +
       +struct PortRUnset {
       +        SunCall call;
       +        u1int b;
       +};
       +
       +struct PortTGetport {
       +        SunCall call;
       +        PortMap map;
       +};
       +
       +struct PortRGetport {
       +        SunCall call;
       +        u32int port;
       +};
       +
       +struct PortTDump {
       +        SunCall call;
       +};
       +
       +struct PortRDump {
       +        SunCall call;
       +        PortMap *map;
       +        int nmap;
       +};
       +
       +struct PortTCallit {
       +        SunCall call;
       +        u32int prog;
       +        u32int vers;
       +        u32int proc;
       +        uchar *data;
       +        u32int count;
       +};
       +
       +struct PortRCallit {
       +        SunCall call;
       +        u32int port;
       +        uchar *data;
       +        u32int count;
       +};
       +
       +extern SunProg portprog;
 (DIR) diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c
       t@@ -27,8 +27,6 @@ char                wdir[512] = ".";
        Reffont        *reffonts[2];
        int                snarffd = -1;
        int                mainpid;
       -int                plumbsendfd;
       -int                plumbeditfd;
        
        enum{
                NSnarf = 1000        /* less than 1024, I/O buffer size */
       t@@ -180,6 +178,8 @@ threadmain(int argc, char *argv[])
                        exits("keyboard");
                }
                mainpid = getpid();
       +        startplumbing();
       +/*
                plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
                if(plumbeditfd < 0)
                        fprint(2, "acme: can't initialize plumber: %r\n");
       t@@ -188,6 +188,7 @@ threadmain(int argc, char *argv[])
                        threadcreate(plumbproc, nil, STACK);
                }
                plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
       +*/
        
                fsysinit();
        
       t@@ -355,6 +356,7 @@ acmeerrorinit(void)
                threadcreate(acmeerrorproc, nil, STACK);
        }
        
       +/*
        void
        plumbproc(void *v)
        {
       t@@ -369,6 +371,7 @@ plumbproc(void *v)
                        sendp(cplumb, m);
                }
        }
       +*/
        
        void
        keyboardthread(void *v)
       t@@ -674,7 +677,7 @@ waitthread(void *v)
                                                textsetselect(t, 0, 0);
                                        }
                                        if(w->msg[0])
       -                                        warning(c->md, "%s: %s\n", c->name, w->msg);
       +                                        warning(c->md, "%S: %s\n", c->name, w->msg);
                                        flushimage(display, 1);
                                }
                                qunlock(&row.lk);
 (DIR) diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
       t@@ -524,8 +524,6 @@ char                        *home;
        char                        *fontnames[2];
        Image                *tagcols[NCOL];
        Image                *textcols[NCOL];
       -int                        plumbsendfd;
       -int                        plumbeditfd;
        extern char                wdir[]; /* must use extern because no dimension given */
        int                        editing;
        int                        erroutfd;
 (DIR) diff --git a/src/cmd/acme/elog.c b/src/cmd/acme/elog.c
       t@@ -170,7 +170,7 @@ eloginsert(File *f, int q0, Rune *r, int nr)
                        elogflush(f);
                }
                /* try to merge with previous */
       -        if(f->elog.type==Insert && q0==f->elog.q0 && (q0+nr)-f->elog.q0<Maxstring){
       +        if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
                        runemove(f->elog.r+f->elog.nr, r, nr);
                        f->elog.nr += nr;
                        return;
 (DIR) diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c
       t@@ -1472,6 +1472,7 @@ Hard:
                        }
                }
                threadexecl(cpid, sfd, "rc", "rc", "-c", t, nil);
       +        warning(nil, "exec rc: %r\n");
        
           Fail:
                /* threadexec hasn't happened, so send a zero */
 (DIR) diff --git a/src/cmd/acme/fns.h b/src/cmd/acme/fns.h
       t@@ -87,6 +87,7 @@ Rune*        skipbl(Rune*, int, int*);
        Rune*        findbl(Rune*, int, int*);
        char*        edittext(Window*, int, Rune*, int);
        void                flushwarnings(int);
       +void                startplumbing(void);
        
        #define        runemalloc(a)                (Rune*)emalloc((a)*sizeof(Rune))
        #define        runerealloc(a, b)        (Rune*)erealloc((a), (b)*sizeof(Rune))
 (DIR) diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c
       t@@ -8,15 +8,50 @@
        #include <frame.h>
        #include <fcall.h>
        #include <regexp.h>
       +#define Fid FsFid
       +#include <fs.h>
        #include <plumb.h>
       +#undef Fid
        #include "dat.h"
        #include "fns.h"
        
       +FsFid *plumbsendfid;
       +FsFid *plumbeditfid;
       +
        Window*        openfile(Text*, Expand*);
        
        int        nuntitled;
        
        void
       +plumbproc(void *v)
       +{
       +        Plumbmsg *m;
       +
       +        USED(v);
       +        threadsetname("plumbproc");
       +        for(;;){
       +                m = plumbrecvfid(plumbeditfid);
       +                if(m == nil)
       +                        threadexits(nil);
       +                sendp(cplumb, m);
       +        }
       +}
       +
       +void
       +startplumbing(void)
       +{
       +        plumbeditfid = plumbopenfid("edit", OREAD|OCEXEC);
       +        if(plumbeditfid == nil)
       +                fprint(2, "acme: can't initialize plumber: %r\n");
       +        else{
       +                cplumb = chancreate(sizeof(Plumbmsg*), 0);
       +                threadcreate(plumbproc, nil, STACK);
       +        }
       +        plumbsendfid = plumbopenfid("send", OWRITE|OCEXEC);
       +}
       +
       +
       +void
        look3(Text *t, uint q0, uint q1, int external)
        {
                int n, c, f, expanded;
       t@@ -79,7 +114,7 @@ look3(Text *t, uint q0, uint q1, int external)
                        free(r);
                        goto Return;
                }
       -        if(plumbsendfd >= 0){
       +        if(plumbsendfid != nil){
                        /* send whitespace-delimited word to plumber */
                        m = emalloc(sizeof(Plumbmsg));
                        m->src = estrdup("acme");
       t@@ -121,7 +156,7 @@ look3(Text *t, uint q0, uint q1, int external)
                        m->data = runetobyte(r, q1-q0);
                        m->ndata = strlen(m->data);
                        free(r);
       -                if(m->ndata<messagesize-1024 && plumbsend(plumbsendfd, m) >= 0){
       +                if(m->ndata<messagesize-1024 && plumbsendtofid(plumbsendfid, m) >= 0){
                                plumbfree(m);
                                goto Return;
                        }
 (DIR) diff --git a/src/cmd/factotum/BUGS b/src/cmd/factotum/BUGS
       t@@ -0,0 +1 @@
       +key, delkey, wipe should be in ctl not rpc.
 (DIR) diff --git a/src/cmd/factotum/apop.c b/src/cmd/factotum/apop.c
       t@@ -0,0 +1,348 @@
       +/*
       + * APOP, CRAM - MD5 challenge/response authentication
       + *
       + * The client does not authenticate the server, hence no CAI.
       + *
       + * Protocol:
       + *
       + *        S -> C:        random@domain
       + *        C -> S:        hex-response
       + *        S -> C:        ok
       + *
       + * Note that this is the protocol between factotum and the local
       + * program, not between the two factotums.  The information 
       + * exchanged here is wrapped in the APOP protocol by the local
       + * programs.
       + *
       + * If S sends "bad [msg]" instead of "ok", that is a hint that the key is bad.
       + * The protocol goes back to "C -> S: user".
       + */
       +
       +#include "std.h"
       +#include "dat.h"
       +
       +static int
       +apopcheck(Key *k)
       +{
       +        if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
       +                werrstr("need user and !password attributes");
       +                return -1;
       +        }
       +        return 0;
       +}
       +
       +static int
       +apopclient(Conv *c)
       +{
       +        char *chal, *pw, *res;
       +        int astype, nchal, npw, ntry, ret;
       +        uchar resp[MD5dlen];
       +        Attr *attr;
       +        DigestState *ds;
       +        Key *k;
       +        
       +        chal = nil;
       +        k = nil;
       +        res = nil;
       +        ret = -1;
       +        attr = c->attr;
       +
       +        if(c->proto == &apop)
       +                astype = AuthApop;
       +        else if(c->proto == &cram)
       +                astype = AuthCram;
       +        else{
       +                werrstr("bad proto");
       +                goto out;
       +        }
       +
       +        c->state = "find key";
       +        k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
       +        if(k == nil)
       +                goto out;
       +
       +        c->state = "read challenge";
       +        if((nchal = convreadm(c, &chal)) < 0)
       +                goto out;
       +
       +          for(ntry=1;; ntry++){
       +                if(c->attr != attr)
       +                        freeattr(c->attr);
       +                c->attr = addattrs(copyattr(attr), k->attr);
       +                if((pw = strfindattr(k->privattr, "!password")) == nil){
       +                        werrstr("key has no password (cannot happen?)");
       +                        goto out;
       +                }
       +                npw = strlen(pw);
       +
       +                switch(astype){
       +                case AuthApop:
       +                        ds = md5((uchar*)chal, nchal, nil, nil);
       +                        md5((uchar*)pw, npw, resp, ds);
       +                        break;
       +                case AuthCram:
       +                        hmac_md5((uchar*)chal, nchal, (uchar*)pw, npw, resp, nil);
       +                        break;
       +                }
       +
       +                /* C->S: APOP user hex-response\n */
       +                if(ntry == 1)
       +                        c->state = "write user";
       +                else{
       +                        sprint(c->statebuf, "write user (auth attempt #%d)", ntry);
       +                        c->state = c->statebuf;
       +                }
       +                if(convprint(c, "%s", strfindattr(k->attr, "user")) < 0)
       +                        goto out;
       +
       +                c->state = "write response";
       +                if(convprint(c, "%.*H", sizeof resp, resp) < 0)
       +                        goto out;
       +
       +                c->state = "read result";
       +                if(convreadm(c, &res) < 0)
       +                        goto out;
       +
       +                if(strcmp(res, "ok") == 0)
       +                        break;
       +
       +                if(strncmp(res, "bad ", 4) != 0){
       +                        werrstr("bad result: %s", res);
       +                        goto out;
       +                }
       +
       +                c->state = "replace key";
       +                if((k = keyreplace(c, k, "%s", res+4)) == nil){
       +                        c->state = "auth failed";
       +                        werrstr("%s", res+4);
       +                        goto out;
       +                }
       +                free(res);
       +                res = nil;
       +        }
       +
       +        werrstr("succeeded");
       +        ret = 0;
       +
       +out:
       +        keyclose(k);
       +        free(chal);
       +        if(c->attr != attr)
       +                freeattr(attr);
       +        return ret;
       +}
       +
       +/* shared with auth dialing routines */
       +typedef struct ServerState ServerState;
       +struct ServerState
       +{
       +        int asfd;
       +        Key *k;
       +        Ticketreq tr;
       +        Ticket t;
       +        char *dom;
       +        char *hostid;
       +};
       +
       +enum
       +{
       +        APOPCHALLEN = 128,
       +};
       +
       +static int apopchal(ServerState*, int, char[APOPCHALLEN]);
       +static int apopresp(ServerState*, char*, char*);
       +
       +static int
       +apopserver(Conv *c)
       +{
       +        char chal[APOPCHALLEN], *user, *resp;
       +        ServerState s;
       +        int astype, ret;
       +        Attr *a;
       +
       +        ret = -1;
       +        user = nil;
       +        resp = nil;
       +        memset(&s, 0, sizeof s);
       +        s.asfd = -1;
       +
       +        if(c->proto == &apop)
       +                astype = AuthApop;
       +        else if(c->proto == &cram)
       +                astype = AuthCram;
       +        else{
       +                werrstr("bad proto");
       +                goto out;
       +        }
       +
       +        c->state = "find key";
       +        if((s.k = plan9authkey(c->attr)) == nil)
       +                goto out;
       +
       +        a = copyattr(s.k->attr);
       +        a = delattr(a, "proto");
       +        c->attr = addattrs(c->attr, a);
       +        freeattr(a);
       +
       +        c->state = "authdial";
       +        s.hostid = strfindattr(s.k->attr, "user");
       +        s.dom = strfindattr(s.k->attr, "dom");
       +        if((s.asfd = xioauthdial(nil, s.dom)) < 0){
       +                werrstr("authdial %s: %r", s.dom);
       +                goto out;
       +        }
       +
       +        c->state = "authchal";
       +        if(apopchal(&s, astype, chal) < 0)
       +                goto out;
       +
       +        c->state = "write challenge";
       +        if(convprint(c, "%s", chal) < 0)
       +                goto out;
       +
       +        for(;;){
       +                c->state = "read user";
       +                if(convreadm(c, &user) < 0)
       +                        goto out;
       +
       +                c->state = "read response";
       +                if(convreadm(c, &resp) < 0)
       +                        goto out;
       +
       +                c->state = "authwrite";
       +                switch(apopresp(&s, user, resp)){
       +                case -1:
       +                        goto out;
       +                case 0:
       +                        c->state = "write status";
       +                        if(convprint(c, "bad authentication failed") < 0)
       +                                goto out;
       +                        break;
       +                case 1:
       +                        c->state = "write status";
       +                        if(convprint(c, "ok") < 0)
       +                                goto out;
       +                        goto ok;
       +                }
       +                free(user);
       +                free(resp);
       +                user = nil;
       +                resp = nil;
       +        }
       +
       +ok:
       +        ret = 0;
       +        c->attr = addcap(c->attr, c->sysuser, &s.t);
       +
       +out:
       +        keyclose(s.k);
       +        free(user);
       +        free(resp);
       +//        xioclose(s.asfd);
       +        return ret;
       +}
       +
       +static int
       +apopchal(ServerState *s, int astype, char chal[APOPCHALLEN])
       +{
       +        char trbuf[TICKREQLEN];
       +        Ticketreq tr;
       +
       +        memset(&tr, 0, sizeof tr);
       +
       +        tr.type = astype;
       +
       +        if(strlen(s->hostid) >= sizeof tr.hostid){
       +                werrstr("hostid too long");
       +                return -1;
       +        }
       +        strcpy(tr.hostid, s->hostid);
       +
       +        if(strlen(s->dom) >= sizeof tr.authdom){
       +                werrstr("domain too long");
       +                return -1;
       +        }
       +        strcpy(tr.authdom, s->dom);
       +
       +        convTR2M(&tr, trbuf);
       +        if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
       +                return -1;
       +
       +        if(xioasrdresp(s->asfd, chal, APOPCHALLEN) <= 5)
       +                return -1;
       +
       +        s->tr = tr;
       +        return 0;
       +}
       +
       +static int
       +apopresp(ServerState *s, char *user, char *resp)
       +{
       +        char tabuf[TICKETLEN+AUTHENTLEN];
       +        char trbuf[TICKREQLEN];
       +        int len;
       +        Authenticator a;
       +        Ticket t;
       +        Ticketreq tr;
       +
       +        tr = s->tr;
       +        if(memrandom(tr.chal, CHALLEN) < 0)
       +                return -1;
       +
       +        if(strlen(user) >= sizeof tr.uid){
       +                werrstr("uid too long");
       +                return -1;
       +        }
       +        strcpy(tr.uid, user);
       +
       +        convTR2M(&tr, trbuf);
       +        if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
       +                return -1;
       +
       +        len = strlen(resp);
       +        if(xiowrite(s->asfd, resp, len) != len)
       +                return -1;
       +
       +        if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
       +                return 0;
       +
       +        convM2T(tabuf, &t, s->k->priv);
       +        if(t.num != AuthTs
       +        || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
       +                werrstr("key mismatch with auth server");
       +                return -1;
       +        }
       +
       +        convM2A(tabuf+TICKETLEN, &a, t.key);
       +        if(a.num != AuthAc
       +        || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
       +        || a.id != 0){
       +                werrstr("key2 mismatch with auth server");
       +                return -1;
       +        }
       +
       +        s->t = t;
       +        return 1;
       +}
       +
       +static Role
       +apoproles[] = 
       +{
       +        "client",        apopclient,
       +        "server",        apopserver,
       +        0
       +};
       +
       +Proto apop = {
       +.name=                "apop",
       +.roles=                apoproles,
       +.checkkey=        apopcheck,
       +.keyprompt=        "user? !password?",
       +};
       +
       +Proto cram = {
       +.name=                "cram",
       +.roles=                apoproles,
       +.checkkey=        apopcheck,
       +.keyprompt=        "user? !password?",
       +};
 (DIR) diff --git a/src/cmd/factotum/attr.c b/src/cmd/factotum/attr.c
       t@@ -0,0 +1,228 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +Attr*
       +addattr(Attr *a, char *fmt, ...)
       +{
       +        char buf[1024];
       +        va_list arg;
       +        Attr *b;
       +
       +        va_start(arg, fmt);
       +        vseprint(buf, buf+sizeof buf, fmt, arg);
       +        va_end(arg);
       +        b = _parseattr(buf);
       +        a = addattrs(a, b);
       +        setmalloctag(a, getcallerpc(&a));
       +        _freeattr(b);
       +        return a;
       +}
       +
       +/*
       + *  add attributes in list b to list a.  If any attributes are in
       + *  both lists, replace those in a by those in b.
       + */
       +Attr*
       +addattrs(Attr *a, Attr *b)
       +{
       +        int found;
       +        Attr **l, *aa;
       +
       +        for(; b; b=b->next){
       +                switch(b->type){
       +                case AttrNameval:
       +                        for(l=&a; *l; ){
       +                                if(strcmp((*l)->name, b->name) != 0){
       +                                        l=&(*l)->next;
       +                                        continue;
       +                                }
       +                                aa = *l;
       +                                *l = aa->next;
       +                                aa->next = nil;
       +                                freeattr(aa);
       +                        }
       +                        *l = mkattr(AttrNameval, b->name, b->val, nil);
       +                        break;
       +                case AttrQuery:
       +                        found = 0;
       +                        for(l=&a; *l; l=&(*l)->next)
       +                                if((*l)->type==AttrNameval && strcmp((*l)->name, b->name) == 0)
       +                                        found++;
       +                        if(!found)
       +                                *l = mkattr(AttrQuery, b->name, b->val, nil);
       +                        break;
       +                }
       +        }
       +        return a;                
       +}
       +
       +void
       +setmalloctaghere(void *v)
       +{
       +        setmalloctag(v, getcallerpc(&v));
       +}
       +
       +Attr*
       +sortattr(Attr *a)
       +{
       +        int i;
       +        Attr *anext, *a0, *a1, **l;
       +
       +        if(a == nil || a->next == nil)
       +                return a;
       +
       +        /* cut list in halves */
       +        a0 = nil;
       +        a1 = nil;
       +        i = 0;
       +        for(; a; a=anext){
       +                anext = a->next;
       +                if(i++%2){
       +                        a->next = a0;
       +                        a0 = a;
       +                }else{
       +                        a->next = a1;
       +                        a1 = a;
       +                }
       +        }
       +
       +        /* sort */
       +        a0 = sortattr(a0);
       +        a1 = sortattr(a1);
       +
       +        /* merge */
       +        l = &a;
       +        while(a0 || a1){
       +                if(a1==nil){
       +                        anext = a0;
       +                        a0 = a0->next;
       +                }else if(a0==nil){
       +                        anext = a1;
       +                        a1 = a1->next;
       +                }else if(strcmp(a0->name, a1->name) < 0){
       +                        anext = a0;
       +                        a0 = a0->next;
       +                }else{
       +                        anext = a1;
       +                        a1 = a1->next;
       +                }
       +                *l = anext;
       +                l = &(*l)->next;
       +        }
       +        *l = nil;
       +        return a;
       +}
       +
       +int
       +attrnamefmt(Fmt *fmt)
       +{
       +        char *b, buf[1024], *ebuf;
       +        Attr *a;
       +
       +        ebuf = buf+sizeof buf;
       +        b = buf;
       +        strcpy(buf, " ");
       +        for(a=va_arg(fmt->args, Attr*); a; a=a->next){
       +                if(a->name == nil)
       +                        continue;
       +                b = seprint(b, ebuf, " %q?", a->name);
       +        }
       +        return fmtstrcpy(fmt, buf+1);
       +}
       +
       +static int
       +hasqueries(Attr *a)
       +{
       +        for(; a; a=a->next)
       +                if(a->type == AttrQuery)
       +                        return 1;
       +        return 0;
       +}
       +
       +char *ignored[] = {
       +        "role",
       +};
       +
       +static int
       +ignoreattr(char *s)
       +{
       +        int i;
       +
       +        for(i=0; i<nelem(ignored); i++)
       +                if(strcmp(ignored[i], s)==0)
       +                        return 1;
       +        return 0;
       +}
       +
       +static int
       +hasname(Attr *a0, Attr *a1, char *name)
       +{
       +        return _findattr(a0, name) || _findattr(a1, name);
       +}
       +
       +static int
       +hasnameval(Attr *a0, Attr *a1, char *name, char *val)
       +{
       +        Attr *a;
       +
       +        for(a=_findattr(a0, name); a; a=_findattr(a->next, name))
       +                if(strcmp(a->val, val) == 0)
       +                        return 1;
       +        for(a=_findattr(a1, name); a; a=_findattr(a->next, name))
       +                if(strcmp(a->val, val) == 0)
       +                        return 1;
       +        return 0;
       +}
       +
       +int
       +matchattr(Attr *pat, Attr *a0, Attr *a1)
       +{
       +        int type;
       +
       +        for(; pat; pat=pat->next){
       +                type = pat->type;
       +                if(ignoreattr(pat->name))
       +                        type = AttrDefault;
       +                switch(type){
       +                case AttrQuery:                /* name=something be present */
       +                        if(!hasname(a0, a1, pat->name))
       +                                return 0;
       +                        break;
       +                case AttrNameval:        /* name=val must be present */
       +                        if(!hasnameval(a0, a1, pat->name, pat->val))
       +                                return 0;
       +                        break;
       +                case AttrDefault:        /* name=val must be present if name=anything is present */
       +                        if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val))
       +                                return 0;
       +                        break;
       +                }
       +        }
       +        return 1;                
       +}
       +
       +Attr*
       +parseattrfmtv(char *fmt, va_list arg)
       +{
       +        char *s;
       +        Attr *a;
       +
       +        s = vsmprint(fmt, arg);
       +        if(s == nil)
       +                sysfatal("vsmprint: out of memory");
       +        a = parseattr(s);
       +        free(s);
       +        return a;
       +}
       +
       +Attr*
       +parseattrfmt(char *fmt, ...)
       +{
       +        va_list arg;
       +        Attr *a;
       +
       +        va_start(arg, fmt);
       +        a = parseattrfmtv(fmt, arg);
       +        va_end(arg);
       +        return a;
       +}
 (DIR) diff --git a/src/cmd/factotum/chap.c b/src/cmd/factotum/chap.c
       t@@ -0,0 +1,424 @@
       +/*
       + * CHAP, MSCHAP
       + * 
       + * The client does not authenticate the server, hence no CAI
       + *
       + * Protocol:
       + *
       + *        S -> C: random 8-byte challenge
       + *        C -> S: user in UTF-8
       + *        C -> S: Chapreply or MSchapreply structure
       + *        S -> C: ok or 'bad why'
       + *
       + * The chap protocol requires the client to give it id=%d, the id of
       + * the PPP message containing the challenge, which is used
       + * as part of the response.  Because the client protocol is message-id
       + * specific, there is no point in looping to try multiple keys.
       + *
       + * The MS chap protocol actually uses two different hashes, an
       + * older insecure one called the LM (Lan Manager) hash, and a newer
       + * more secure one called the NT hash.  By default we send back only
       + * the NT hash, because the LM hash can help an eavesdropper run
       + * a brute force attack.  If the key has an lm attribute, then we send only the
       + * LM hash.
       + */
       +
       +#include "std.h"
       +#include "dat.h"
       +
       +enum {
       +        ChapChallen = 8,
       +
       +        MShashlen = 16,
       +        MSchallen = 8,
       +        MSresplen = 24,
       +};
       +
       +static int
       +chapcheck(Key *k)
       +{
       +        if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
       +                werrstr("need user and !password attributes");
       +                return -1;
       +        }
       +        return 0;
       +}
       +
       +static void
       +nthash(uchar hash[MShashlen], char *passwd)
       +{
       +        uchar buf[512];
       +        int i;
       +        
       +        for(i=0; *passwd && i<sizeof(buf); passwd++) {
       +                buf[i++] = *passwd;
       +                buf[i++] = 0;
       +        }
       +
       +        memset(hash, 0, 16);
       +
       +        md4(buf, i, hash, 0);
       +}
       +
       +static void
       +desencrypt(uchar data[8], uchar key[7])
       +{
       +        ulong ekey[32];
       +
       +        key_setup(key, ekey);
       +        block_cipher(ekey, data, 0);
       +}
       +
       +static void
       +lmhash(uchar hash[MShashlen], char *passwd)
       +{
       +        uchar buf[14];
       +        char *stdtext = "KGS!@#$%";
       +        int i;
       +
       +        strncpy((char*)buf, passwd, sizeof(buf));
       +        for(i=0; i<sizeof(buf); i++)
       +                if(buf[i] >= 'a' && buf[i] <= 'z')
       +                        buf[i] += 'A' - 'a';
       +
       +        memset(hash, 0, 16);
       +        memcpy(hash, stdtext, 8);
       +        memcpy(hash+8, stdtext, 8);
       +
       +        desencrypt(hash, buf);
       +        desencrypt(hash+8, buf+7);
       +}
       +
       +static void
       +mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
       +{
       +        int i;
       +        uchar buf[21];
       +        
       +        memset(buf, 0, sizeof(buf));
       +        memcpy(buf, hash, MShashlen);
       +
       +        for(i=0; i<3; i++) {
       +                memmove(resp+i*MSchallen, chal, MSchallen);
       +                desencrypt(resp+i*MSchallen, buf+i*7);
       +        }
       +}
       +
       +static int
       +chapclient(Conv *c)
       +{
       +        int id, astype, nchal, npw, ret;
       +        uchar *chal;
       +        char *s, *pw, *user, *res;
       +        Attr *attr;
       +        Key *k;
       +        Chapreply cr;
       +        MSchapreply mscr;
       +        DigestState *ds;
       +
       +        ret = -1;
       +        chal = nil;
       +        k = nil;
       +        attr = c->attr;
       +
       +        if(c->proto == &chap){
       +                astype = AuthChap;
       +                s = strfindattr(attr, "id");
       +                if(s == nil || *s == 0){
       +                        werrstr("need id=n attr in start message");
       +                        goto out;
       +                }
       +                id = strtol(s, &s, 10);
       +                if(*s != 0 || id < 0 || id >= 256){
       +                        werrstr("bad id=n attr in start message");
       +                        goto out;
       +                }
       +                cr.id = id;
       +        }else if(c->proto == &mschap)
       +                astype = AuthMSchap;
       +        else{
       +                werrstr("bad proto");
       +                goto out;
       +        }
       +
       +        c->state = "find key";
       +        k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
       +        if(k == nil)
       +                goto out;
       +
       +        c->attr = addattrs(copyattr(attr), k->attr);
       +
       +        c->state = "read challenge";
       +        if((nchal = convreadm(c, (char**)&chal)) < 0)
       +                goto out;
       +        if(astype == AuthMSchap && nchal != MSchallen)
       +        c->state = "write user";
       +        if((user = strfindattr(k->attr, "user")) == nil){
       +                werrstr("key has no user (cannot happen?)");
       +                goto out;
       +        }
       +        if(convprint(c, "%s", user) < 0)
       +                goto out;
       +
       +        c->state = "write response";
       +        if((pw = strfindattr(k->privattr, "!password")) == nil){
       +                werrstr("key has no password (cannot happen?)");
       +                goto out;
       +        }
       +        npw = strlen(pw);
       +
       +        if(astype == AuthChap){
       +                ds = md5(&cr.id, 1, 0, 0);
       +                md5((uchar*)pw, npw, 0, ds);
       +                md5(chal, nchal, (uchar*)cr.resp, ds);
       +                if(convwrite(c, &cr, sizeof cr) < 0)
       +                        goto out;
       +        }else{
       +                uchar hash[MShashlen];
       +
       +                memset(&mscr, 0, sizeof mscr);
       +                if(strfindattr(k->attr, "lm")){
       +                        lmhash(hash, pw);
       +                        mschalresp((uchar*)mscr.LMresp, hash, chal);
       +                }else{
       +                        nthash(hash, pw);
       +                        mschalresp((uchar*)mscr.NTresp, hash, chal);
       +                }
       +                if(convwrite(c, &mscr, sizeof mscr) < 0)
       +                        goto out;
       +        }
       +
       +        c->state = "read result";
       +        if(convreadm(c, &res) < 0)
       +                goto out;
       +        if(strcmp(res, "ok") == 0){
       +                ret = 0;
       +                werrstr("succeeded");
       +                goto out;
       +        }
       +        if(strncmp(res, "bad ", 4) != 0){
       +                werrstr("bad result: %s", res);
       +                goto out;
       +        }
       +
       +        c->state = "replace key";
       +        keyevict(c, k, "%s", res+4);
       +        werrstr("%s", res+4);
       +
       +out:
       +        free(res);
       +        keyclose(k);
       +        free(chal);
       +        if(c->attr != attr)
       +                freeattr(attr);
       +        return ret;
       +}
       +
       +/* shared with auth dialing routines */
       +typedef struct ServerState ServerState;
       +struct ServerState
       +{
       +        int asfd;
       +        Key *k;
       +        Ticketreq tr;
       +        Ticket t;
       +        char *dom;
       +        char *hostid;
       +};
       +
       +static int chapchal(ServerState*, int, char[ChapChallen]);
       +static int chapresp(ServerState*, char*, char*);
       +
       +static int
       +chapserver(Conv *c)
       +{
       +        char chal[ChapChallen], *user, *resp;
       +        ServerState s;
       +        int astype, ret;
       +        Attr *a;
       +
       +        ret = -1;
       +        user = nil;
       +        resp = nil;
       +        memset(&s, 0, sizeof s);
       +        s.asfd = -1;
       +
       +        if(c->proto == &chap)
       +                astype = AuthChap;
       +        else if(c->proto == &mschap)
       +                astype = AuthMSchap;
       +        else{
       +                werrstr("bad proto");
       +                goto out;
       +        }
       +
       +        c->state = "find key";
       +        if((s.k = plan9authkey(c->attr)) == nil)
       +                goto out;
       +
       +        a = copyattr(s.k->attr);
       +        a = delattr(a, "proto");
       +        c->attr = addattrs(c->attr, a);
       +        freeattr(a);
       +
       +        c->state = "authdial";
       +        s.hostid = strfindattr(s.k->attr, "user");
       +        s.dom = strfindattr(s.k->attr, "dom");
       +        if((s.asfd = xioauthdial(nil, s.dom)) < 0){
       +                werrstr("authdial %s: %r", s.dom);
       +                goto out;
       +        }
       +
       +        c->state = "authchal";
       +        if(chapchal(&s, astype, chal) < 0)
       +                goto out;
       +
       +        c->state = "write challenge";
       +        if(convprint(c, "%s", chal) < 0)
       +                goto out;
       +
       +        c->state = "read user";
       +        if(convreadm(c, &user) < 0)
       +                goto out;
       +
       +        c->state = "read response";
       +        if(convreadm(c, &resp) < 0)
       +                goto out;
       +
       +        c->state = "authwrite";
       +        switch(chapresp(&s, user, resp)){
       +        default:
       +                fprint(2, "factotum: bad result from chapresp\n");
       +                goto out;
       +        case -1:
       +                goto out;
       +        case 0:
       +                c->state = "write status";
       +                if(convprint(c, "bad authentication failed") < 0)
       +                        goto out;
       +                goto out;
       +
       +        case 1:
       +                c->state = "write status";
       +                if(convprint(c, "ok") < 0)
       +                        goto out;
       +                goto ok;
       +        }
       +
       +ok:
       +        ret = 0;
       +        c->attr = addcap(c->attr, c->sysuser, &s.t);
       +
       +out:
       +        keyclose(s.k);
       +        free(user);
       +        free(resp);
       +//        xioclose(s.asfd);
       +        return ret;
       +}
       +
       +static int
       +chapchal(ServerState *s, int astype, char chal[ChapChallen])
       +{
       +        char trbuf[TICKREQLEN];
       +        Ticketreq tr;
       +
       +        memset(&tr, 0, sizeof tr);
       +
       +        tr.type = astype;
       +
       +        if(strlen(s->hostid) >= sizeof tr.hostid){
       +                werrstr("hostid too long");
       +                return -1;
       +        }
       +        strcpy(tr.hostid, s->hostid);
       +
       +        if(strlen(s->dom) >= sizeof tr.authdom){
       +                werrstr("domain too long");
       +                return -1;
       +        }
       +        strcpy(tr.authdom, s->dom);
       +
       +        convTR2M(&tr, trbuf);
       +        if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
       +                return -1;
       +
       +        if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5)
       +                return -1;
       +
       +        s->tr = tr;
       +        return 0;
       +}
       +
       +static int
       +chapresp(ServerState *s, char *user, char *resp)
       +{
       +        char tabuf[TICKETLEN+AUTHENTLEN];
       +        char trbuf[TICKREQLEN];
       +        int len;
       +        Authenticator a;
       +        Ticket t;
       +        Ticketreq tr;
       +
       +        tr = s->tr;
       +        if(memrandom(tr.chal, CHALLEN) < 0)
       +                return -1;
       +
       +        if(strlen(user) >= sizeof tr.uid){
       +                werrstr("uid too long");
       +                return -1;
       +        }
       +        strcpy(tr.uid, user);
       +
       +        convTR2M(&tr, trbuf);
       +        if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
       +                return -1;
       +
       +        len = strlen(resp);
       +        if(xiowrite(s->asfd, resp, len) != len)
       +                return -1;
       +
       +        if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
       +                return 0;
       +
       +        convM2T(tabuf, &t, s->k->priv);
       +        if(t.num != AuthTs
       +        || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
       +                werrstr("key mismatch with auth server");
       +                return -1;
       +        }
       +
       +        convM2A(tabuf+TICKETLEN, &a, t.key);
       +        if(a.num != AuthAc
       +        || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
       +        || a.id != 0){
       +                werrstr("key2 mismatch with auth server");
       +                return -1;
       +        }
       +
       +        s->t = t;
       +        return 1;
       +}
       +
       +static Role
       +chaproles[] = 
       +{
       +        "client",        chapclient,
       +        "server",        chapserver,
       +        0
       +};
       +
       +Proto chap = {
       +.name=                "chap",
       +.roles=                chaproles,
       +.checkkey=        chapcheck,
       +.keyprompt=        "user? !password?",
       +};
       +
       +Proto mschap = {
       +.name=                "mschap",
       +.roles=                chaproles,
       +.checkkey=        chapcheck,
       +.keyprompt=        "user? !password?",
       +};
       +
 (DIR) diff --git a/src/cmd/factotum/confirm.c b/src/cmd/factotum/confirm.c
       t@@ -0,0 +1,139 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +Logbuf confbuf;
       +
       +void
       +confirmread(Req *r)
       +{
       +        lbread(&confbuf, r);
       +}
       +
       +void
       +confirmflush(Req *r)
       +{
       +        lbflush(&confbuf, r);
       +}
       +
       +int
       +confirmwrite(char *s)
       +{
       +        char *t, *ans;
       +        int allow;
       +        ulong tag;
       +        Attr *a;
       +        Conv *c;
       +
       +        a = _parseattr(s);
       +        if(a == nil){
       +                werrstr("bad attr");
       +                return -1;
       +        }
       +        if((t = _strfindattr(a, "tag")) == nil){
       +                werrstr("no tag");
       +                return -1;
       +        }
       +        tag = strtoul(t, 0, 0);
       +        if((ans = _strfindattr(a, "answer")) == nil){
       +                werrstr("no answer");
       +                return -1;
       +        }
       +        if(strcmp(ans, "yes") == 0)
       +                allow = 1;
       +        else if(strcmp(ans, "no") == 0)
       +                allow = 0;
       +        else{
       +                werrstr("bad answer");
       +                return -1;
       +        }
       +        for(c=conv; c; c=c->next){
       +                if(tag == c->tag){
       +                        nbsendul(c->keywait, allow);
       +                        break;
       +                }
       +        }
       +        if(c == nil){
       +                werrstr("tag not found");
       +                return -1;
       +        }
       +        return 0;
       +}
       +
       +int
       +confirmkey(Conv *c, Key *k)
       +{
       +        if(*confirminuse == 0)
       +                return -1;
       +
       +        lbappend(&confbuf, "confirm tag=%lud %A %N", c->tag, k->attr, k->privattr);
       +        c->state = "keyconfirm";
       +        return recvul(c->keywait);
       +}
       +
       +Logbuf needkeybuf;
       +
       +void
       +needkeyread(Req *r)
       +{
       +        lbread(&needkeybuf, r);
       +}
       +
       +void
       +needkeyflush(Req *r)
       +{
       +        lbflush(&needkeybuf, r);
       +}
       +
       +int
       +needkeywrite(char *s)
       +{
       +        char *t;
       +        ulong tag;
       +        Attr *a;
       +        Conv *c;
       +
       +        a = _parseattr(s);
       +        if(a == nil){
       +                werrstr("empty write");
       +                return -1;
       +        }
       +        if((t = _strfindattr(a, "tag")) == nil){
       +                werrstr("no tag");
       +                freeattr(a);
       +                return -1;
       +        }
       +        tag = strtoul(t, 0, 0);
       +        for(c=conv; c; c=c->next)
       +                if(c->tag == tag){
       +                        nbsendul(c->keywait, 0);
       +                        break;
       +                }
       +        if(c == nil){
       +                werrstr("tag not found");
       +                freeattr(a);
       +                return -1;
       +        }
       +        freeattr(a);
       +        return 0;
       +}
       +
       +int
       +needkey(Conv *c, Attr *a)
       +{
       +        if(c == nil || *needkeyinuse == 0)
       +                return -1;
       +
       +        lbappend(&needkeybuf, "needkey tag=%lud %A", c->tag, a);
       +        return nbrecvul(c->keywait);
       +}
       +
       +int
       +badkey(Conv *c, Key *k, char *msg, Attr *a)
       +{
       +        if(c == nil || *needkeyinuse == 0)
       +                return -1;
       +
       +        lbappend(&needkeybuf, "badkey tag=%lud %A %N\n%s\n%A",
       +                c->tag, k->attr, k->privattr, msg, a);
       +        return nbrecvul(c->keywait);
       +}
 (DIR) diff --git a/src/cmd/factotum/conv.c b/src/cmd/factotum/conv.c
       t@@ -0,0 +1,254 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +Conv *conv;
       +
       +ulong taggen = 1;
       +
       +Conv*
       +convalloc(char *sysuser)
       +{
       +        Conv *c;
       +
       +        c = mallocz(sizeof(Conv), 1);
       +        if(c == nil)
       +                return nil;
       +        c->ref = 1;
       +        c->tag = taggen++;
       +        c->next = conv;
       +        c->sysuser = estrdup(sysuser);
       +        c->state = "nascent";
       +        c->rpcwait = chancreate(sizeof(void*), 0);
       +        c->keywait = chancreate(sizeof(void*), 0);
       +        strcpy(c->err, "protocol has not started");
       +        conv = c;
       +        convreset(c);
       +        return c;
       +}
       +
       +void
       +convreset(Conv *c)
       +{
       +        if(c->ref != 1){
       +                c->hangup = 1;
       +                nbsendp(c->rpcwait, 0);
       +                while(c->ref > 1)
       +                        yield();
       +                c->hangup = 0;
       +        }
       +        c->state = "nascent";
       +        c->err[0] = '\0';
       +        freeattr(c->attr);
       +        c->attr = nil;
       +        c->proto = nil;
       +        c->rpc.op = 0;
       +        c->active = 0;
       +        c->done = 0;
       +        c->hangup = 0;
       +}
       +
       +void
       +convhangup(Conv *c)
       +{
       +        c->hangup = 1;
       +        c->rpc.op = 0;
       +        (*c->kickreply)(c);
       +        nbsendp(c->rpcwait, 0);
       +}
       +
       +void
       +convclose(Conv *c)
       +{
       +        Conv *p;
       +
       +        if(c == nil)
       +                return;
       +
       +        if(--c->ref > 0)
       +                return;
       +
       +        if(c == conv){
       +                conv = c->next;
       +                goto free;
       +        }
       +        for(p=conv; p && p->next!=c; p=p->next)
       +                ;
       +        if(p == nil){
       +                print("cannot find conv in list\n");
       +                return;
       +        }
       +        p->next = c->next;
       +
       +free:
       +        c->next = nil;
       +        free(c);
       +}
       +
       +static Rpc*
       +convgetrpc(Conv *c, int want)
       +{
       +        for(;;){
       +                if(c->hangup){
       +                        werrstr("hangup");
       +                        return nil;
       +                }
       +                if(c->rpc.op == RpcUnknown){
       +                        recvp(c->rpcwait);
       +                        if(c->hangup){
       +                                werrstr("hangup");
       +                                return nil;
       +                        }
       +                        if(c->rpc.op == RpcUnknown)
       +                                continue;
       +                }
       +                if(want < 0 || c->rpc.op == want)
       +                        return &c->rpc;
       +                rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]);
       +        }
       +        return nil;        /* not reached */
       +}
       +
       +/* read until the done function tells us that's enough */
       +int
       +convreadfn(Conv *c, int (*done)(void*, int), char **ps)
       +{
       +        int n;
       +        Rpc *r;
       +        char *s;
       +
       +        for(;;){
       +                r = convgetrpc(c, RpcWrite);
       +                if(r == nil)
       +                        return -1;
       +                n = (*done)(r->data, r->count);
       +                if(n == r->count)
       +                        break;
       +                rpcrespond(c, "toosmall %d", n);
       +        }
       +
       +        s = emalloc(r->count+1);
       +        memmove(s, r->data, r->count);
       +        s[r->count] = 0;
       +        *ps = s;
       +        rpcrespond(c, "ok");
       +        return r->count;
       +}
       +
       +/*
       + * read until we get a non-zero write.  assumes remote side
       + * knows something about the protocol (is not auth_proxy).
       + * the remote side typically won't bother with the zero-length
       + * write to find out the length -- the loop is there only so the
       + * test program can call auth_proxy on both sides of a pipe
       + * to play a conversation.
       + */
       +int
       +convreadm(Conv *c, char **ps)
       +{
       +        char *s;
       +        Rpc *r;
       +
       +        for(;;){
       +                r = convgetrpc(c, RpcWrite);
       +                if(r == nil)
       +                        return -1;
       +                if(r->count > 0)
       +                        break;
       +                rpcrespond(c, "toosmall %d", AuthRpcMax);
       +        }
       +        s = emalloc(r->count+1);
       +        memmove(s, r->data, r->count);
       +        s[r->count] = 0;
       +        *ps = s;
       +        rpcrespond(c, "ok");
       +        return r->count;
       +}
       +
       +/* read exactly count bytes */
       +int
       +convread(Conv *c, void *data, int count)
       +{
       +        Rpc *r;
       +
       +        for(;;){
       +                r = convgetrpc(c, RpcWrite);
       +                if(r == nil)
       +                        return -1;
       +                if(r->count == count)
       +                        break;
       +                if(r->count < count)
       +                        rpcrespond(c, "toosmall %d", count);
       +                else
       +                        rpcrespond(c, "error too much data; want %d got %d", count, r->count);
       +        }
       +        memmove(data, r->data, count);
       +        rpcrespond(c, "ok");
       +        return 0;
       +}
       +
       +/* write exactly count bytes */
       +int
       +convwrite(Conv *c, void *data, int count)
       +{
       +        Rpc *r;
       +
       +        for(;;){
       +                r = convgetrpc(c, RpcRead);
       +                if(r == nil)
       +                        return -1;
       +                break;
       +        }
       +        rpcrespondn(c, "ok", data, count);
       +        return 0;
       +}
       +
       +/* print to the conversation */
       +int
       +convprint(Conv *c, char *fmt, ...)
       +{
       +        char *s;
       +        va_list arg;
       +        int ret;
       +
       +        va_start(arg, fmt);
       +        s = vsmprint(fmt, arg);
       +        va_end(arg);
       +        if(s == nil)
       +                return -1;
       +        ret = convwrite(c, s, strlen(s));
       +        free(s);
       +        return ret;
       +}
       +
       +/* ask for a key */
       +int
       +convneedkey(Conv *c, Attr *a)
       +{
       +        /*
       +         * Piggyback key requests in the usual RPC channel.
       +         * Wait for the next RPC and then send a key request
       +         * in response.  The keys get added out-of-band (via the
       +         * ctl file), so assume the key has been added when the
       +         * next request comes in.
       +         */
       +        if(convgetrpc(c, -1) == nil)
       +                return -1;
       +        rpcrespond(c, "needkey %A", a);
       +        if(convgetrpc(c, -1) == nil)
       +                return -1;
       +        return 0;
       +}
       +
       +/* ask for a replacement for a bad key*/
       +int
       +convbadkey(Conv *c, Key *k, char *msg, Attr *a)
       +{
       +        if(convgetrpc(c, -1) == nil)
       +                return -1;
       +        rpcrespond(c, "badkey %A %N\n%s\n%A",
       +                k->attr, k->privattr, msg, a);
       +        if(convgetrpc(c, -1) == nil)
       +                return -1;
       +        return 0;
       +}
       +
 (DIR) diff --git a/src/cmd/factotum/cpu.c b/src/cmd/factotum/cpu.c
       t@@ -0,0 +1,1117 @@
       +/*
       + * cpu.c - Make a connection to a cpu server
       + *
       + *           Invoked by listen as 'cpu -R | -N service net netdir'
       + *                       by users  as 'cpu [-h system] [-c cmd args ...]'
       + */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <libsec.h>
       +
       +#define        Maxfdata 8192
       +
       +void        remoteside(int);
       +void        fatal(int, char*, ...);
       +void        lclnoteproc(int);
       +void        rmtnoteproc(void);
       +void        catcher(void*, char*);
       +void        usage(void);
       +void        writestr(int, char*, char*, int);
       +int        readstr(int, char*, int);
       +char        *rexcall(int*, char*, char*);
       +int        setamalg(char*);
       +
       +int         notechan;
       +char        system[32];
       +int        cflag;
       +int        hflag;
       +int        dbg;
       +char        *user;
       +
       +char        *srvname = "ncpu";
       +char        *exportfs = "/bin/exportfs";
       +char        *ealgs = "rc4_256 sha1";
       +
       +/* message size for exportfs; may be larger so we can do big graphics in CPU window */
       +int        msgsize = 8192+IOHDRSZ;
       +
       +/* authentication mechanisms */
       +static int        netkeyauth(int);
       +static int        netkeysrvauth(int, char*);
       +static int        p9auth(int);
       +static int        srvp9auth(int, char*);
       +static int        noauth(int);
       +static int        srvnoauth(int, char*);
       +
       +typedef struct AuthMethod AuthMethod;
       +struct AuthMethod {
       +        char        *name;                        /* name of method */
       +        int        (*cf)(int);                /* client side authentication */
       +        int        (*sf)(int, char*);        /* server side authentication */
       +} authmethod[] =
       +{
       +        { "p9",                p9auth,                srvp9auth,},
       +        { "netkey",        netkeyauth,        netkeysrvauth,},
       +//        { "none",        noauth,                srvnoauth,},
       +        { nil,        nil}
       +};
       +AuthMethod *am = authmethod;        /* default is p9 */
       +
       +char *p9authproto = "p9any";
       +
       +int setam(char*);
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: cpu [-h system] [-a authmethod] [-e 'crypt hash'] [-c cmd args ...]\n");
       +        exits("usage");
       +}
       +int fdd;
       +
       +void
       +main(int argc, char **argv)
       +{
       +        char dat[128], buf[128], cmd[128], *p, *err;
       +        int fd, ms, kms, data;
       +
       +        /* see if we should use a larger message size */
       +        fd = open("/dev/draw", OREAD);
       +        if(fd > 0){
       +                ms = iounit(fd);
       +                if(ms != 0 && ms < ms+IOHDRSZ)
       +                        msgsize = ms+IOHDRSZ;
       +                close(fd);
       +        }
       +        kms = kiounit();
       +        if(msgsize > kms-IOHDRSZ-100)        /* 100 for network packets, etc. */
       +                msgsize = kms-IOHDRSZ-100;
       +
       +        user = getuser();
       +        if(user == nil)
       +                fatal(1, "can't read user name");
       +        ARGBEGIN{
       +        case 'a':
       +                p = EARGF(usage());
       +                if(setam(p) < 0)
       +                        fatal(0, "unknown auth method %s", p);
       +                break;
       +        case 'e':
       +                ealgs = EARGF(usage());
       +                if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
       +                        ealgs = nil;
       +                break;
       +        case 'd':
       +                dbg++;
       +                break;
       +        case 'f':
       +                /* ignored but accepted for compatibility */
       +                break;
       +        case 'O':
       +                p9authproto = "p9sk2";
       +                remoteside(1);                                /* From listen */
       +                break;
       +        case 'R':                                /* From listen */
       +                remoteside(0);
       +                break;
       +        case 'h':
       +                hflag++;
       +                p = EARGF(usage());
       +                strcpy(system, p);
       +                break;
       +        case 'c':
       +                cflag++;
       +                cmd[0] = '!';
       +                cmd[1] = '\0';
       +                while(p = ARGF()) {
       +                        strcat(cmd, " ");
       +                        strcat(cmd, p);
       +                }
       +                break;
       +        case 'o':
       +                p9authproto = "p9sk2";
       +                srvname = "cpu";
       +                break;
       +        case 'u':
       +                user = EARGF(usage());
       +                break;
       +        default:
       +                usage();
       +        }ARGEND;
       +
       +
       +        if(argc != 0)
       +                usage();
       +
       +        if(hflag == 0) {
       +                p = getenv("cpu");
       +                if(p == 0)
       +                        fatal(0, "set $cpu");
       +                strcpy(system, p);
       +        }
       +
       +        if(err = rexcall(&data, system, srvname))
       +                fatal(1, "%s: %s", err, system);
       +
       +        /* Tell the remote side the command to execute and where our working directory is */
       +        if(cflag)
       +                writestr(data, cmd, "command", 0);
       +        if(getwd(dat, sizeof(dat)) == 0)
       +                writestr(data, "NO", "dir", 0);
       +        else
       +                writestr(data, dat, "dir", 0);
       +
       +        /* start up a process to pass along notes */
       +        lclnoteproc(data);
       +
       +        /* 
       +         *  Wait for the other end to execute and start our file service
       +         *  of /mnt/term
       +         */
       +        if(readstr(data, buf, sizeof(buf)) < 0)
       +                fatal(1, "waiting for FS");
       +        if(strncmp("FS", buf, 2) != 0) {
       +                print("remote cpu: %s", buf);
       +                exits(buf);
       +        }
       +
       +        /* Begin serving the gnot namespace */
       +        close(0);
       +        dup(data, 0);
       +        close(data);
       +        sprint(buf, "%d", msgsize);
       +        if(dbg)
       +                execl(exportfs, exportfs, "-dm", buf, 0);
       +        else
       +                execl(exportfs, exportfs, "-m", buf, 0);
       +        fatal(1, "starting exportfs");
       +}
       +
       +void
       +fatal(int syserr, char *fmt, ...)
       +{
       +        char buf[ERRMAX];
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        doprint(buf, buf+sizeof(buf), fmt, arg);
       +        va_end(arg);
       +        if(syserr)
       +                fprint(2, "cpu: %s: %r\n", buf);
       +        else
       +                fprint(2, "cpu: %s\n", buf);
       +        exits(buf);
       +}
       +
       +char *negstr = "negotiating authentication method";
       +
       +char bug[256];
       +
       +int
       +old9p(int fd)
       +{
       +        int p[2];
       +
       +        if(pipe(p) < 0)
       +                fatal(1, "pipe");
       +
       +        switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
       +        case -1:
       +                fatal(1, "rfork srvold9p");
       +        case 0:
       +                if(fd != 1){
       +                        dup(fd, 1);
       +                        close(fd);
       +                }
       +                if(p[0] != 0){
       +                        dup(p[0], 0);
       +                        close(p[0]);
       +                }
       +                close(p[1]);
       +                if(0){
       +                        fd = open("/sys/log/cpu", OWRITE);
       +                        if(fd != 2){
       +                                dup(fd, 2);
       +                                close(fd);
       +                        }
       +                        execl("/bin/srvold9p", "srvold9p", "-ds", 0);
       +                } else
       +                        execl("/bin/srvold9p", "srvold9p", "-s", 0);
       +                fatal(1, "exec srvold9p");
       +        default:
       +                close(fd);
       +                close(p[0]);
       +        }
       +        return p[1];        
       +}
       +
       +/* Invoked with stdin, stdout and stderr connected to the network connection */
       +void
       +remoteside(int old)
       +{
       +        char user[128], home[128], buf[128], xdir[128], cmd[128];
       +        int i, n, fd, badchdir, gotcmd;
       +
       +        fd = 0;
       +
       +        /* negotiate authentication mechanism */
       +        n = readstr(fd, cmd, sizeof(cmd));
       +        if(n < 0)
       +                fatal(1, "authenticating");
       +        if(setamalg(cmd) < 0){
       +                writestr(fd, "unsupported auth method", nil, 0);
       +                fatal(1, "bad auth method %s", cmd);
       +        } else
       +                writestr(fd, "", "", 1);
       +
       +        fd = (*am->sf)(fd, user);
       +        if(fd < 0)
       +                fatal(1, "srvauth");
       +
       +        /* Set environment values for the user */
       +        putenv("user", user);
       +        sprint(home, "/usr/%s", user);
       +        putenv("home", home);
       +
       +        /* Now collect invoking cpu's current directory or possibly a command */
       +        gotcmd = 0;
       +        if(readstr(fd, xdir, sizeof(xdir)) < 0)
       +                fatal(1, "dir/cmd");
       +        if(xdir[0] == '!') {
       +                strcpy(cmd, &xdir[1]);
       +                gotcmd = 1;
       +                if(readstr(fd, xdir, sizeof(xdir)) < 0)
       +                        fatal(1, "dir");
       +        }
       +
       +        /* Establish the new process at the current working directory of the
       +         * gnot */
       +        badchdir = 0;
       +        if(strcmp(xdir, "NO") == 0)
       +                chdir(home);
       +        else if(chdir(xdir) < 0) {
       +                badchdir = 1;
       +                chdir(home);
       +        }
       +
       +        /* Start the gnot serving its namespace */
       +        writestr(fd, "FS", "FS", 0);
       +        writestr(fd, "/", "exportfs dir", 0);
       +
       +        n = read(fd, buf, sizeof(buf));
       +        if(n != 2 || buf[0] != 'O' || buf[1] != 'K')
       +                exits("remote tree");
       +
       +        if(old)
       +                fd = old9p(fd);
       +
       +        /* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */
       +        strcpy(buf, VERSION9P);
       +        if(fversion(fd, 64*1024, buf, sizeof buf) < 0)
       +                exits("fversion failed");
       +        if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0)
       +                exits("mount failed");
       +
       +        close(fd);
       +
       +        /* the remote noteproc uses the mount so it must follow it */
       +        rmtnoteproc();
       +
       +        for(i = 0; i < 3; i++)
       +                close(i);
       +
       +        if(open("/mnt/term/dev/cons", OREAD) != 0)
       +                exits("open stdin");
       +        if(open("/mnt/term/dev/cons", OWRITE) != 1)
       +                exits("open stdout");
       +        dup(1, 2);
       +
       +        if(badchdir)
       +                print("cpu: failed to chdir to '%s'\n", xdir);
       +
       +        if(gotcmd)
       +                execl("/bin/rc", "rc", "-lc", cmd, 0);
       +        else
       +                execl("/bin/rc", "rc", "-li", 0);
       +        fatal(1, "exec shell");
       +}
       +
       +char*
       +rexcall(int *fd, char *host, char *service)
       +{
       +        char *na;
       +        char dir[128];
       +        char err[ERRMAX];
       +        char msg[128];
       +        int n;
       +
       +        na = netmkaddr(host, 0, service);
       +        if((*fd = dial(na, 0, dir, 0)) < 0)
       +                return "can't dial";
       +
       +        /* negotiate authentication mechanism */
       +        if(ealgs != nil)
       +                snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);
       +        else
       +                snprint(msg, sizeof(msg), "%s", am->name);
       +        writestr(*fd, msg, negstr, 0);
       +        n = readstr(*fd, err, sizeof err);
       +        if(n < 0)
       +                return negstr;
       +        if(*err){
       +                werrstr(err);
       +                return negstr;
       +        }
       +
       +        /* authenticate */
       +        *fd = (*am->cf)(*fd);
       +        if(*fd < 0)
       +                return "can't authenticate";
       +        return 0;
       +}
       +
       +void
       +writestr(int fd, char *str, char *thing, int ignore)
       +{
       +        int l, n;
       +
       +        l = strlen(str);
       +        n = write(fd, str, l+1);
       +        if(!ignore && n < 0)
       +                fatal(1, "writing network: %s", thing);
       +}
       +
       +int
       +readstr(int fd, char *str, int len)
       +{
       +        int n;
       +
       +        while(len) {
       +                n = read(fd, str, 1);
       +                if(n < 0) 
       +                        return -1;
       +                if(*str == '\0')
       +                        return 0;
       +                str++;
       +                len--;
       +        }
       +        return -1;
       +}
       +
       +static int
       +readln(char *buf, int n)
       +{
       +        char *p = buf;
       +
       +        n--;
       +        while(n > 0){
       +                if(read(0, p, 1) != 1)
       +                        break;
       +                if(*p == '\n' || *p == '\r'){
       +                        *p = 0;
       +                        return p-buf;
       +                }
       +                p++;
       +        }
       +        *p = 0;
       +        return p-buf;
       +}
       +
       +/*
       + *  user level challenge/response
       + */
       +static int
       +netkeyauth(int fd)
       +{
       +        char chall[32];
       +        char resp[32];
       +
       +        strcpy(chall, getuser());
       +        print("user[%s]: ", chall);
       +        if(readln(resp, sizeof(resp)) < 0)
       +                return -1;
       +        if(*resp != 0)
       +                strcpy(chall, resp);
       +        writestr(fd, chall, "challenge/response", 1);
       +
       +        for(;;){
       +                if(readstr(fd, chall, sizeof chall) < 0)
       +                        break;
       +                if(*chall == 0)
       +                        return fd;
       +                print("challenge: %s\nresponse: ", chall);
       +                if(readln(resp, sizeof(resp)) < 0)
       +                        break;
       +                writestr(fd, resp, "challenge/response", 1);
       +        }
       +        return -1;
       +}
       +
       +static int
       +netkeysrvauth(int fd, char *user)
       +{
       +        char response[32];
       +        Chalstate *ch;
       +        int tries;
       +        AuthInfo *ai;
       +
       +        if(readstr(fd, user, 32) < 0)
       +                return -1;
       +
       +        ai = nil;
       +        ch = nil;
       +        for(tries = 0; tries < 10; tries++){
       +                if((ch = auth_challenge("p9cr", user, nil)) == nil)
       +                        return -1;
       +                writestr(fd, ch->chal, "challenge", 1);
       +                if(readstr(fd, response, sizeof response) < 0)
       +                        return -1;
       +                ch->resp = response;
       +                ch->nresp = strlen(response);
       +                if((ai = auth_response(ch)) != nil)
       +                        break;
       +        }
       +        auth_freechal(ch);
       +        if(ai == nil)
       +                return -1;
       +        writestr(fd, "", "challenge", 1);
       +        if(auth_chuid(ai, 0) < 0)
       +                fatal(1, "newns");
       +        auth_freeAI(ai);
       +        return fd;
       +}
       +
       +static void
       +mksecret(char *t, uchar *f)
       +{
       +        sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
       +                f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
       +}
       +
       +/*
       + *  plan9 authentication followed by rc4 encryption
       + */
       +static int
       +p9auth(int fd)
       +{
       +        uchar key[16];
       +        uchar digest[SHA1dlen];
       +        char fromclientsecret[21];
       +        char fromserversecret[21];
       +        int i;
       +        AuthInfo *ai;
       +
       +        ai = auth_proxy(fd, auth_getkey, "proto=%q user=%q role=client", p9authproto, user);
       +        if(ai == nil)
       +                return -1;
       +        memmove(key+4, ai->secret, ai->nsecret);
       +        if(ealgs == nil)
       +                return fd;
       +
       +        /* exchange random numbers */
       +        srand(truerand());
       +        for(i = 0; i < 4; i++)
       +                key[i] = rand();
       +        if(write(fd, key, 4) != 4)
       +                return -1;
       +        if(readn(fd, key+12, 4) != 4)
       +                return -1;
       +
       +        /* scramble into two secrets */
       +        sha1(key, sizeof(key), digest, nil);
       +        mksecret(fromclientsecret, digest);
       +        mksecret(fromserversecret, digest+10);
       +
       +        /* set up encryption */
       +        i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
       +        if(i < 0)
       +                werrstr("can't establish ssl connection: %r");
       +        return i;
       +}
       +
       +static int
       +noauth(int fd)
       +{
       +        ealgs = nil;
       +        return fd;
       +}
       +
       +static int
       +srvnoauth(int fd, char *user)
       +{
       +        strcpy(user, getuser());
       +        ealgs = nil;
       +        return fd;
       +}
       +
       +void
       +loghex(uchar *p, int n)
       +{
       +        char buf[100];
       +        int i;
       +
       +        for(i = 0; i < n; i++)
       +                sprint(buf+2*i, "%2.2ux", p[i]);
       +        syslog(0, "cpu", buf);
       +}
       +
       +static int
       +srvp9auth(int fd, char *user)
       +{
       +        uchar key[16];
       +        uchar digest[SHA1dlen];
       +        char fromclientsecret[21];
       +        char fromserversecret[21];
       +        int i;
       +        AuthInfo *ai;
       +
       +        ai = auth_proxy(0, nil, "proto=%q role=server", p9authproto);
       +        if(ai == nil)
       +                return -1;
       +        if(auth_chuid(ai, nil) < 0)
       +                return -1;
       +        strcpy(user, ai->cuid);
       +        memmove(key+4, ai->secret, ai->nsecret);
       +
       +        if(ealgs == nil)
       +                return fd;
       +
       +        /* exchange random numbers */
       +        srand(truerand());
       +        for(i = 0; i < 4; i++)
       +                key[i+12] = rand();
       +        if(readn(fd, key, 4) != 4)
       +                return -1;
       +        if(write(fd, key+12, 4) != 4)
       +                return -1;
       +
       +        /* scramble into two secrets */
       +        sha1(key, sizeof(key), digest, nil);
       +        mksecret(fromclientsecret, digest);
       +        mksecret(fromserversecret, digest+10);
       +
       +        /* set up encryption */
       +        i = pushssl(fd, ealgs, fromserversecret, fromclientsecret, nil);
       +        if(i < 0)
       +                werrstr("can't establish ssl connection: %r");
       +        return i;
       +}
       +
       +/*
       + *  set authentication mechanism
       + */
       +int
       +setam(char *name)
       +{
       +        for(am = authmethod; am->name != nil; am++)
       +                if(strcmp(am->name, name) == 0)
       +                        return 0;
       +        am = authmethod;
       +        return -1;
       +}
       +
       +/*
       + *  set authentication mechanism and encryption/hash algs
       + */
       +int
       +setamalg(char *s)
       +{
       +        ealgs = strchr(s, ' ');
       +        if(ealgs != nil)
       +                *ealgs++ = 0;
       +        return setam(s);
       +}
       +
       +char *rmtnotefile = "/mnt/term/dev/cpunote";
       +
       +/*
       + *  loop reading /mnt/term/dev/note looking for notes.
       + *  The child returns to start the shell.
       + */
       +void
       +rmtnoteproc(void)
       +{
       +        int n, fd, pid, notepid;
       +        char buf[256];
       +
       +        /* new proc returns to start shell */
       +        pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM);
       +        switch(pid){
       +        case -1:
       +                syslog(0, "cpu", "cpu -R: can't start noteproc: %r");
       +                return;
       +        case 0:
       +                return;
       +        }
       +
       +        /* new proc reads notes from other side and posts them to shell */
       +        switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){
       +        case -1:
       +                syslog(0, "cpu", "cpu -R: can't start wait proc: %r");
       +                _exits(0);
       +        case 0:
       +                fd = open(rmtnotefile, OREAD);
       +                if(fd < 0){
       +                        syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile);
       +                        _exits(0);
       +                }
       +        
       +                for(;;){
       +                        n = read(fd, buf, sizeof(buf)-1);
       +                        if(n <= 0){
       +                                postnote(PNGROUP, pid, "hangup");
       +                                _exits(0);
       +                        }
       +                        buf[n] = 0;
       +                        postnote(PNGROUP, pid, buf);
       +                }
       +                break;
       +        }
       +
       +        /* original proc waits for shell proc to die and kills note proc */
       +        for(;;){
       +                n = waitpid();
       +                if(n < 0 || n == pid)
       +                        break;
       +        }
       +        postnote(PNPROC, notepid, "kill");
       +        _exits(0);
       +}
       +
       +enum
       +{
       +        Qdir,
       +        Qcpunote,
       +
       +        Nfid = 32,
       +};
       +
       +struct {
       +        char        *name;
       +        Qid        qid;
       +        ulong        perm;
       +} fstab[] =
       +{
       +        [Qdir]                { ".",                {Qdir, 0, QTDIR},        DMDIR|0555        },
       +        [Qcpunote]        { "cpunote",        {Qcpunote, 0},                0444                },
       +};
       +
       +typedef struct Note Note;
       +struct Note
       +{
       +        Note *next;
       +        char msg[ERRMAX];
       +};
       +
       +typedef struct Request Request;
       +struct Request
       +{
       +        Request *next;
       +        Fcall f;
       +};
       +
       +typedef struct Fid Fid;
       +struct Fid
       +{
       +        int        fid;
       +        int        file;
       +};
       +Fid fids[Nfid];
       +
       +struct {
       +        Lock;
       +        Note *nfirst, *nlast;
       +        Request *rfirst, *rlast;
       +} nfs;
       +
       +int
       +fsreply(int fd, Fcall *f)
       +{
       +        uchar buf[IOHDRSZ+Maxfdata];
       +        int n;
       +
       +        if(dbg)
       +                fprint(2, "<-%F\n", f);
       +        n = convS2M(f, buf, sizeof buf);
       +        if(n > 0){
       +                if(write(fd, buf, n) != n){
       +                        close(fd);
       +                        return -1;
       +                }
       +        }
       +        return 0;
       +}
       +
       +/* match a note read request with a note, reply to the request */
       +int
       +kick(int fd)
       +{
       +        Request *rp;
       +        Note *np;
       +        int rv;
       +
       +        for(;;){
       +                lock(&nfs);
       +                rp = nfs.rfirst;
       +                np = nfs.nfirst;
       +                if(rp == nil || np == nil){
       +                        unlock(&nfs);
       +                        break;
       +                }
       +                nfs.rfirst = rp->next;
       +                nfs.nfirst = np->next;
       +                unlock(&nfs);
       +
       +                rp->f.type = Rread;
       +                rp->f.count = strlen(np->msg);
       +                rp->f.data = np->msg;
       +                rv = fsreply(fd, &rp->f);
       +                free(rp);
       +                free(np);
       +                if(rv < 0)
       +                        return -1;
       +        }
       +        return 0;
       +}
       +
       +void
       +flushreq(int tag)
       +{
       +        Request **l, *rp;
       +
       +        lock(&nfs);
       +        for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){
       +                rp = *l;
       +                if(rp->f.tag == tag){
       +                        *l = rp->next;
       +                        unlock(&nfs);
       +                        free(rp);
       +                        return;
       +                }
       +        }
       +        unlock(&nfs);
       +}
       +
       +Fid*
       +getfid(int fid)
       +{
       +        int i, freefid;
       +
       +        freefid = -1;
       +        for(i = 0; i < Nfid; i++){
       +                if(freefid < 0 && fids[i].file < 0)
       +                        freefid = i;
       +                if(fids[i].fid == fid)
       +                        return &fids[i];
       +        }
       +        if(freefid >= 0){
       +                fids[freefid].fid = fid;
       +                return &fids[freefid];
       +        }
       +        return nil;
       +}
       +
       +int
       +fsstat(int fd, Fid *fid, Fcall *f)
       +{
       +        Dir d;
       +        uchar statbuf[256];
       +
       +        memset(&d, 0, sizeof(d));
       +        d.name = fstab[fid->file].name;
       +        d.uid = user;
       +        d.gid = user;
       +        d.muid = user;
       +        d.qid = fstab[fid->file].qid;
       +        d.mode = fstab[fid->file].perm;
       +        d.atime = d.mtime = time(0);
       +        f->stat = statbuf;
       +        f->nstat = convD2M(&d, statbuf, sizeof statbuf);
       +        return fsreply(fd, f);
       +}
       +
       +int
       +fsread(int fd, Fid *fid, Fcall *f)
       +{
       +        Dir d;
       +        uchar buf[256];
       +        Request *rp;
       +
       +        switch(fid->file){
       +        default:
       +                return -1;
       +        case Qdir:
       +                if(f->offset == 0 && f->count >0){
       +                        memset(&d, 0, sizeof(d));
       +                        d.name = fstab[Qcpunote].name;
       +                        d.uid = user;
       +                        d.gid = user;
       +                        d.muid = user;
       +                        d.qid = fstab[Qcpunote].qid;
       +                        d.mode = fstab[Qcpunote].perm;
       +                        d.atime = d.mtime = time(0);
       +                        f->count = convD2M(&d, buf, sizeof buf);
       +                        f->data = (char*)buf;
       +                } else
       +                        f->count = 0;
       +                return fsreply(fd, f);
       +        case Qcpunote:
       +                rp = mallocz(sizeof(*rp), 1);
       +                if(rp == nil)
       +                        return -1;
       +                rp->f = *f;
       +                lock(&nfs);
       +                if(nfs.rfirst == nil)
       +                        nfs.rfirst = rp;
       +                else
       +                        nfs.rlast->next = rp;
       +                nfs.rlast = rp;
       +                unlock(&nfs);
       +                return kick(fd);;
       +        }
       +}
       +
       +char Eperm[] = "permission denied";
       +char Enofile[] = "out of files";
       +char Enotdir[] = "not a directory";
       +
       +void
       +notefs(int fd)
       +{
       +        uchar buf[IOHDRSZ+Maxfdata];
       +        int i, j, n;
       +        char err[ERRMAX];
       +        Fcall f;
       +        Fid *fid, *nfid;
       +        int doreply;
       +
       +        rfork(RFNOTEG);
       +        fmtinstall('F', fcallconv);
       +
       +        for(n = 0; n < Nfid; n++)
       +                fids[n].file = -1;
       +
       +        for(;;){
       +                n = read9pmsg(fd, buf, sizeof(buf));
       +                if(n <= 0){
       +                        if(dbg)
       +                                fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n);
       +                        break;
       +                }
       +                if(convM2S(buf, n, &f) < 0)
       +                        break;
       +                if(dbg)
       +                        fprint(2, "->%F\n", &f);
       +                doreply = 1;
       +                fid = getfid(f.fid);
       +                if(fid == nil){
       +nofids:
       +                        f.type = Rerror;
       +                        f.ename = Enofile;
       +                        fsreply(fd, &f);
       +                        continue;
       +                }
       +                switch(f.type++){
       +                default:
       +                        f.type = Rerror;
       +                        f.ename = "unknown type";
       +                        break;
       +                case Tflush:
       +                        flushreq(f.oldtag);
       +                        break;
       +                case Tversion:
       +                        if(f.msize > IOHDRSZ+Maxfdata)
       +                                f.msize = IOHDRSZ+Maxfdata;
       +                        break;
       +                case Tauth:
       +                        f.type = Rerror;
       +                        f.ename = "cpu: authentication not required";
       +                        break;
       +                case Tattach:
       +                        f.qid = fstab[Qdir].qid;
       +                        fid->file = Qdir;
       +                        break;
       +                case Twalk:
       +                        nfid = nil;
       +                        if(f.newfid != f.fid){
       +                                nfid = getfid(f.newfid);
       +                                if(nfid == nil)
       +                                        goto nofids;
       +                                nfid->file = fid->file;
       +                                fid = nfid;
       +                        }
       +
       +                        f.ename = nil;
       +                        for(i=0; i<f.nwname; i++){
       +                                if(i > MAXWELEM){
       +                                        f.type = Rerror;
       +                                        f.ename = "too many name elements";
       +                                        break;
       +                                }
       +                                if(fid->file != Qdir){
       +                                        f.type = Rerror;
       +                                        f.ename = Enotdir;
       +                                        break;
       +                                }
       +                                if(strcmp(f.wname[i], "cpunote") == 0){
       +                                        fid->file = Qcpunote;
       +                                        f.wqid[i] = fstab[Qcpunote].qid;
       +                                        continue;
       +                                }
       +                                f.type = Rerror;
       +                                f.ename = err;
       +                                strcpy(err, "cpu: file \"");
       +                                for(j=0; j<=i; j++){
       +                                        if(strlen(err)+1+strlen(f.wname[j])+32 > sizeof err)
       +                                                break;
       +                                        if(j != 0)
       +                                                strcat(err, "/");
       +                                        strcat(err, f.wname[j]);
       +                                }
       +                                strcat(err, "\" does not exist");
       +                                break;
       +                        }
       +                        if(nfid != nil && (f.ename != nil || i < f.nwname))
       +                                nfid ->file = -1;
       +                        if(f.type != Rerror)
       +                                f.nwqid = i;
       +                        break;
       +                case Topen:
       +                        if(f.mode != OREAD){
       +                                f.type = Rerror;
       +                                f.ename = Eperm;
       +                        }
       +                        f.qid = fstab[fid->file].qid;
       +                        break;
       +                case Tcreate:
       +                        f.type = Rerror;
       +                        f.ename = Eperm;
       +                        break;
       +                case Tread:
       +                        if(fsread(fd, fid, &f) < 0)
       +                                goto err;
       +                        doreply = 0;
       +                        break;
       +                case Twrite:
       +                        f.type = Rerror;
       +                        f.ename = Eperm;
       +                        break;
       +                case Tclunk:
       +                        fid->file = -1;
       +                        break;
       +                case Tremove:
       +                        f.type = Rerror;
       +                        f.ename = Eperm;
       +                        break;
       +                case Tstat:
       +                        if(fsstat(fd, fid, &f) < 0)
       +                                goto err;
       +                        doreply = 0;
       +                        break;
       +                case Twstat:
       +                        f.type = Rerror;
       +                        f.ename = Eperm;
       +                        break;
       +                }
       +                if(doreply)
       +                        if(fsreply(fd, &f) < 0)
       +                                break;
       +        }
       +err:
       +        if(dbg)
       +                fprint(2, "notefs exiting: %r\n");
       +        close(fd);
       +}
       +
       +char         notebuf[ERRMAX];
       +
       +void
       +catcher(void*, char *text)
       +{
       +        int n;
       +
       +        n = strlen(text);
       +        if(n >= sizeof(notebuf))
       +                n = sizeof(notebuf)-1;
       +        memmove(notebuf, text, n);
       +        notebuf[n] = '\0';
       +        noted(NCONT);
       +}
       +
       +/*
       + *  mount in /dev a note file for the remote side to read.
       + */
       +void
       +lclnoteproc(int netfd)
       +{
       +        int exportfspid;
       +        Waitmsg *w;
       +        Note *np;
       +        int pfd[2];
       +
       +        if(pipe(pfd) < 0){
       +                fprint(2, "cpu: can't start note proc: pipe: %r\n");
       +                return;
       +        }
       +
       +        /* new proc mounts and returns to start exportfs */
       +        switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){
       +        case -1:
       +                fprint(2, "cpu: can't start note proc: rfork: %r\n");
       +                return;
       +        case 0:
       +                close(pfd[0]);
       +                if(mount(pfd[1], -1, "/dev", MBEFORE, "") < 0)
       +                        fprint(2, "cpu: can't mount note proc: %r\n");
       +                close(pfd[1]);
       +                return;
       +        }
       +
       +        close(netfd);
       +        close(pfd[1]);
       +
       +        /* new proc listens for note file system rpc's */
       +        switch(rfork(RFPROC|RFNAMEG|RFMEM)){
       +        case -1:
       +                fprint(2, "cpu: can't start note proc: rfork1: %r\n");
       +                _exits(0);
       +        case 0:
       +                notefs(pfd[0]);
       +                _exits(0);
       +        }
       +
       +        /* original proc waits for notes */
       +        notify(catcher);
       +        w = nil;
       +        for(;;) {
       +                *notebuf = 0;
       +                free(w);
       +                w = wait();
       +                if(w == nil) {
       +                        if(*notebuf == 0)
       +                                break;
       +                        np = mallocz(sizeof(Note), 1);
       +                        if(np != nil){
       +                                strcpy(np->msg, notebuf);
       +                                lock(&nfs);
       +                                if(nfs.nfirst == nil)
       +                                        nfs.nfirst = np;
       +                                else
       +                                        nfs.nlast->next = np;
       +                                nfs.nlast = np;
       +                                unlock(&nfs);
       +                                kick(pfd[0]);
       +                        }
       +                        unlock(&nfs);
       +                } else if(w->pid == exportfspid)
       +                        break;
       +        }
       +
       +        if(w == nil)
       +                exits(nil);
       +        exits(w->msg);
       +}
 (DIR) diff --git a/src/cmd/factotum/ctl.c b/src/cmd/factotum/ctl.c
       t@@ -0,0 +1,158 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +/*
       + *        key attr=val... - add a key
       + *                the attr=val pairs are protocol-specific.
       + *                for example, both of these are valid:
       + *                        key p9sk1 gre cs.bell-labs.com mysecret
       + *                        key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex
       + *        delkey ... - delete a key
       + *                if given, the attr=val pairs are used to narrow the search
       + *                [maybe should require a password?]
       + *
       + *        debug - toggle debugging
       + */
       +
       +static char *msg[] = {
       +        "key",
       +        "delkey",
       +        "debug",
       +};
       +
       +static int
       +classify(char *s)
       +{
       +        int i;
       +
       +        for(i=0; i<nelem(msg); i++)
       +                if(strcmp(msg[i], s) == 0)
       +                        return i;
       +        return -1;
       +}
       +
       +int
       +ctlwrite(char *a)
       +{
       +        char *p;
       +        int i, nmatch, ret;
       +        Attr *attr, **l, **lpriv, **lprotos, *pa, *priv, *protos;
       +        Key *k;
       +        Proto *proto;
       +
       +        if(a[0] == '#' || a[0] == '\0')
       +                return 0;
       +
       +        /*
       +         * it would be nice to emit a warning of some sort here.
       +         * we ignore all but the first line of the write.  this helps
       +         * both with things like "echo delkey >/mnt/factotum/ctl"
       +         * and writes that (incorrectly) contain multiple key lines.
       +         */
       +        if(p = strchr(a, '\n')){
       +                if(p[1] != '\0'){
       +                        werrstr("multiline write not allowed");
       +                        return -1;
       +                }
       +                *p = '\0';
       +        }
       +
       +        if((p = strchr(a, ' ')) == nil)
       +                p = "";
       +        else
       +                *p++ = '\0';
       +        switch(classify(a)){
       +        default:
       +                werrstr("unknown verb");
       +                return -1;
       +        case 0:        /* key */
       +                attr = parseattr(p);
       +                /* separate out proto= attributes */
       +                lprotos = &protos;
       +                for(l=&attr; (*l); ){
       +                        if(strcmp((*l)->name, "proto") == 0){
       +                                *lprotos = *l;
       +                                lprotos = &(*l)->next;
       +                                *l = (*l)->next;
       +                        }else
       +                                l = &(*l)->next;
       +                }
       +                *lprotos = nil;
       +                if(protos == nil){
       +                        werrstr("key without protos");
       +                        freeattr(attr);
       +                        return -1;
       +                }
       +
       +                /* separate out private attributes */
       +                lpriv = &priv;
       +                for(l=&attr; (*l); ){
       +                        if((*l)->name[0] == '!'){
       +                                *lpriv = *l;
       +                                lpriv = &(*l)->next;
       +                                *l = (*l)->next;
       +                        }else
       +                                l = &(*l)->next;
       +                }
       +                *lpriv = nil;
       +
       +                /* add keys */
       +                ret = 0;
       +                for(pa=protos; pa; pa=pa->next){
       +                        if((proto = protolookup(pa->val)) == nil){
       +                                werrstr("unknown proto %s", pa->val);
       +                                ret = -1;
       +                                continue;
       +                        }
       +                        if(proto->checkkey == nil){
       +                                werrstr("proto %s does not accept keys", proto->name);
       +                                ret = -1;
       +                                continue;
       +                        }
       +                        k = emalloc(sizeof(Key));
       +                        k->attr = mkattr(AttrNameval, "proto", proto->name, copyattr(attr));
       +                        k->privattr = copyattr(priv);
       +                        k->ref = 1;
       +                        k->proto = proto;
       +                        if((*proto->checkkey)(k) < 0){
       +                                ret = -1;
       +                                keyclose(k);
       +                                continue;
       +                        }
       +                        keyadd(k);
       +                        keyclose(k);
       +                }
       +                freeattr(attr);
       +                freeattr(priv);
       +                freeattr(protos);
       +                return ret;
       +        case 1:        /* delkey */
       +                nmatch = 0;
       +                attr = parseattr(p);
       +                for(pa=attr; pa; pa=pa->next){
       +                        if(pa->type != AttrQuery && pa->name[0]=='!'){
       +                                werrstr("only !private? patterns are allowed for private fields");
       +                                freeattr(attr);
       +                                return -1;
       +                        }
       +                }
       +                for(i=0; i<ring.nkey; ){
       +                        if(matchattr(attr, ring.key[i]->attr, ring.key[i]->privattr)){
       +                                nmatch++;
       +                                keyclose(ring.key[i]);
       +                                ring.nkey--;
       +                                memmove(&ring.key[i], &ring.key[i+1], (ring.nkey-i)*sizeof(ring.key[0]));
       +                        }else
       +                                i++;
       +                }
       +                freeattr(attr);
       +                if(nmatch == 0){
       +                        werrstr("found no keys to delete");
       +                        return -1;
       +                }
       +                return 0;
       +        case 2:        /* debug */
       +                debug ^= 1;
       +                return 0;
       +        }
       +}
 (DIR) diff --git a/src/cmd/factotum/dat.h b/src/cmd/factotum/dat.h
       t@@ -0,0 +1,224 @@
       +enum
       +{
       +        MaxRpc = 2048,        /* max size of any protocol message */
       +
       +        /* keep in sync with rpc.c:/rpcname */
       +        RpcUnknown = 0,                /* Rpc.op */
       +        RpcAuthinfo,
       +        RpcAttr,
       +        RpcRead,
       +        RpcStart,
       +        RpcWrite,
       +
       +        /* thread stack size */
       +        STACK = 8192,
       +};
       +
       +typedef struct Conv Conv;
       +typedef struct Key Key;
       +typedef struct Logbuf Logbuf;
       +typedef struct Proto Proto;
       +typedef struct Ring Ring;
       +typedef struct Role Role;
       +typedef struct Rpc Rpc;
       +
       +struct Rpc
       +{
       +        int op;
       +        void *data;
       +        int count;
       +};
       +
       +struct Conv
       +{
       +        int ref;                        /* ref count */
       +        int hangup;                /* flag: please hang up */
       +        int active;                        /* flag: there is an active thread */
       +        int done;                        /* flag: conversation finished successfully */
       +        ulong tag;                        /* identifying tag */
       +        Conv *next;                /* in linked list */
       +        char *sysuser;                /* system name for user speaking to us */
       +        char *state;                /* for debugging */
       +        char statebuf[128];        /* for formatted states */
       +        char err[ERRMAX];        /* last error */
       +
       +        Attr *attr;                        /* current attributes */
       +        Proto *proto;                /* protocol */
       +
       +        Channel *rpcwait;                /* wait here for an rpc */
       +        Rpc rpc;                                /* current rpc. op==RpcUnknown means none */
       +        char rpcbuf[MaxRpc];        /* buffer for rpc */
       +        char reply[MaxRpc];                /* buffer for response */
       +        int nreply;                                /* count of response */
       +        void (*kickreply)(Conv*);        /* call to send response */
       +        Req *req;                                /* 9P call to read response */
       +
       +        Channel *keywait;        /* wait here for key confirmation */
       +        
       +};
       +
       +struct Key
       +{
       +        int ref;                        /* ref count */
       +        ulong tag;                        /* identifying tag: sequence number */
       +        Attr *attr;                        /* public attributes */
       +        Attr *privattr;                /* private attributes, like !password */
       +        Proto *proto;                /* protocol owner of key */
       +        void *priv;                /* protocol-specific storage */
       +};
       +
       +struct Logbuf
       +{
       +        Req *wait;
       +        Req **waitlast;
       +        int rp;
       +        int wp;
       +        char *msg[128];
       +};
       +
       +struct Ring
       +{
       +        Key **key;
       +        int nkey;
       +};
       +
       +struct Proto
       +{
       +        char *name;                /* name of protocol */
       +        Role *roles;                /* list of roles and service functions */
       +        char *keyprompt;        /* required attributes for key proto=name */
       +        int (*checkkey)(Key*);        /* initialize k->priv or reject key */
       +        void (*closekey)(Key*);        /* free k->priv */
       +};
       +
       +struct Role
       +{
       +        char *name;                /* name of role */
       +        int (*fn)(Conv*);        /* service function */
       +};
       +
       +extern char        *authaddr;        /* plan9.c */
       +extern int                *confirminuse;        /* fs.c */
       +extern Conv*        conv;                /* conv.c */
       +extern int                debug;                /* main.c */
       +extern char        *factname;        /* main.c */
       +extern Srv                fs;                        /* fs.c */
       +extern int                *needkeyinuse;        /* fs.c */
       +extern char        *owner;                /* main.c */
       +extern Proto        *prototab[];        /* main.c */
       +extern Ring        ring;                        /* key.c */
       +extern char        *rpcname[];        /* rpc.c */
       +
       +extern char        Easproto[];        /* err.c */
       +
       +extern Proto        apop;                /* apop.c */
       +extern Proto        chap;                /* chap.c */
       +extern Proto        cram;                /* cram.c */
       +extern Proto        mschap;                /* mschap.c */
       +extern Proto        p9any;                /* p9any.c */
       +extern Proto        p9sk1;                /* p9sk1.c */
       +extern Proto        p9sk2;                /* p9sk2.c */
       +
       +/* provided by lib9p */
       +#define emalloc        emalloc9p
       +#define erealloc        erealloc9p
       +#define estrdup        estrdup9p
       +
       +/* hidden in libauth */
       +#define attrfmt                _attrfmt
       +#define copyattr        _copyattr
       +#define delattr                _delattr
       +#define findattr                _findattr
       +#define freeattr                _freeattr
       +#define mkattr                _mkattr
       +#define parseattr        _parseattr
       +#define strfindattr        _strfindattr
       +
       +extern Attr*        addattr(Attr*, char*, ...);
       +/* #pragma varargck argpos addattr 2 */
       +extern Attr*        addattrs(Attr*, Attr*);
       +extern Attr*        sortattr(Attr*);
       +extern int                attrnamefmt(Fmt*);
       +/* #pragma varargck type "N" Attr* */
       +extern int                matchattr(Attr*, Attr*, Attr*);
       +extern Attr*        parseattrfmt(char*, ...);
       +/* #pragma varargck argpos parseattrfmt 1 */
       +extern Attr*        parseattrfmtv(char*, va_list);
       +
       +extern void        confirmflush(Req*);
       +extern void        confirmread(Req*);
       +extern int                confirmwrite(char*);
       +extern int                needkey(Conv*, Attr*);
       +extern int                badkey(Conv*, Key*, char*, Attr*);
       +extern int                confirmkey(Conv*, Key*);
       +
       +extern Conv*        convalloc(char*);
       +extern void        convclose(Conv*);
       +extern void        convhangup(Conv*);
       +extern int                convneedkey(Conv*, Attr*);
       +extern int                convbadkey(Conv*, Key*, char*, Attr*);
       +extern int                convread(Conv*, void*, int);
       +extern int                convreadm(Conv*, char**);
       +extern int                convprint(Conv*, char*, ...);
       +/* #pragma varargck argpos convprint 2 */
       +extern int                convreadfn(Conv*, int(*)(void*, int), char**);
       +extern void        convreset(Conv*);
       +extern int                convwrite(Conv*, void*, int);
       +
       +extern int                ctlwrite(char*);
       +
       +extern char*        estrappend(char*, char*, ...);
       +/* #pragma varargck argpos estrappend 2 */
       +extern int                hexparse(char*, uchar*, int);
       +
       +extern void        keyadd(Key*);
       +extern Key*        keylookup(char*, ...);
       +/* #pragma varargck argpos keylookup 1 */
       +extern Key*        keyfetch(Conv*, char*, ...);
       +/* #pragma varargck argpos keyfetch 2 */
       +extern void        keyclose(Key*);
       +extern void        keyevict(Conv*, Key*, char*, ...);
       +/* #pragma varargck argpos keyevict 3 */
       +extern Key*        keyreplace(Conv*, Key*, char*, ...);
       +/* #pragma varargck argpos keyreplace 3 */
       +
       +extern void        lbkick(Logbuf*);
       +extern void        lbappend(Logbuf*, char*, ...);
       +extern void        lbvappend(Logbuf*, char*, va_list);
       +/* #pragma varargck argpos lbappend 2 */
       +extern void        lbread(Logbuf*, Req*);
       +extern void        lbflush(Logbuf*, Req*);
       +extern void        flog(char*, ...);
       +/* #pragma varargck argpos flog 1 */
       +
       +extern void        logflush(Req*);
       +extern void        logread(Req*);
       +extern void        logwrite(Req*);
       +
       +extern void        needkeyread(Req*);
       +extern void        needkeyflush(Req*);
       +extern int                needkeywrite(char*);
       +extern int                needkeyqueue(void);
       +
       +extern Attr*        addcap(Attr*, char*, Ticket*);
       +extern Key*        plan9authkey(Attr*);
       +extern int                _authdial(char*, char*);
       +
       +extern int                memrandom(void*, int);
       +
       +extern Proto*        protolookup(char*);
       +
       +extern char*        readcons(char *prompt, char *def, int raw);
       +
       +extern int                rpcwrite(Conv*, void*, int);
       +extern void        rpcrespond(Conv*, char*, ...);
       +/* #pragma varargck argpos rpcrespond 2 */
       +extern void        rpcrespondn(Conv*, char*, void*, int);
       +extern void        rpcexec(Conv*);
       +
       +extern int                xioauthdial(char*, char*);
       +extern void        xioclose(int);
       +extern int                xiodial(char*, char*, char*, int*);
       +extern int                xiowrite(int, void*, int);
       +extern int                xioasrdresp(int, void*, int);
       +extern int                xioasgetticket(int, char*, char*);
 (DIR) diff --git a/src/cmd/factotum/fs.acid b/src/cmd/factotum/fs.acid
       t@@ -0,0 +1,1686 @@
       +sizeof_1_ = 8;
       +aggr _1_
       +{
       +        'D' 0 lo;
       +        'D' 4 hi;
       +};
       +
       +defn
       +_1_(addr) {
       +        complex _1_ addr;
       +        print("        lo        ", addr.lo, "\n");
       +        print("        hi        ", addr.hi, "\n");
       +};
       +
       +sizeofFPdbleword = 8;
       +aggr FPdbleword
       +{
       +        'F' 0 x;
       +        {
       +        'D' 0 lo;
       +        'D' 4 hi;
       +        };
       +};
       +
       +defn
       +FPdbleword(addr) {
       +        complex FPdbleword addr;
       +        print("        x        ", addr.x, "\n");
       +        print("_1_ {\n");
       +                _1_(addr+0);
       +        print("}\n");
       +};
       +
       +UTFmax = 3;
       +Runesync = 128;
       +Runeself = 128;
       +Runeerror = 128;
       +sizeofFconv = 24;
       +aggr Fconv
       +{
       +        'X' 0 out;
       +        'X' 4 eout;
       +        'D' 8 f1;
       +        'D' 12 f2;
       +        'D' 16 f3;
       +        'D' 20 chr;
       +};
       +
       +defn
       +Fconv(addr) {
       +        complex Fconv addr;
       +        print("        out        ", addr.out\X, "\n");
       +        print("        eout        ", addr.eout\X, "\n");
       +        print("        f1        ", addr.f1, "\n");
       +        print("        f2        ", addr.f2, "\n");
       +        print("        f3        ", addr.f3, "\n");
       +        print("        chr        ", addr.chr, "\n");
       +};
       +
       +sizeofTm = 40;
       +aggr Tm
       +{
       +        'D' 0 sec;
       +        'D' 4 min;
       +        'D' 8 hour;
       +        'D' 12 mday;
       +        'D' 16 mon;
       +        'D' 20 year;
       +        'D' 24 wday;
       +        'D' 28 yday;
       +        'a' 32 zone;
       +        'D' 36 tzoff;
       +};
       +
       +defn
       +Tm(addr) {
       +        complex Tm addr;
       +        print("        sec        ", addr.sec, "\n");
       +        print("        min        ", addr.min, "\n");
       +        print("        hour        ", addr.hour, "\n");
       +        print("        mday        ", addr.mday, "\n");
       +        print("        mon        ", addr.mon, "\n");
       +        print("        year        ", addr.year, "\n");
       +        print("        wday        ", addr.wday, "\n");
       +        print("        yday        ", addr.yday, "\n");
       +        print("        zone        ", addr.zone, "\n");
       +        print("        tzoff        ", addr.tzoff, "\n");
       +};
       +
       +PNPROC = 1;
       +PNGROUP = 2;
       +sizeofLock = 4;
       +aggr Lock
       +{
       +        'D' 0 val;
       +};
       +
       +defn
       +Lock(addr) {
       +        complex Lock addr;
       +        print("        val        ", addr.val, "\n");
       +};
       +
       +sizeofQLp = 12;
       +aggr QLp
       +{
       +        'D' 0 inuse;
       +        'A' QLp 4 next;
       +        'C' 8 state;
       +};
       +
       +defn
       +QLp(addr) {
       +        complex QLp addr;
       +        print("        inuse        ", addr.inuse, "\n");
       +        print("        next        ", addr.next\X, "\n");
       +        print("        state        ", addr.state, "\n");
       +};
       +
       +sizeofQLock = 16;
       +aggr QLock
       +{
       +        Lock 0 lock;
       +        'D' 4 locked;
       +        'A' QLp 8 $head;
       +        'A' QLp 12 $tail;
       +};
       +
       +defn
       +QLock(addr) {
       +        complex QLock addr;
       +        print("Lock lock {\n");
       +        Lock(addr.lock);
       +        print("}\n");
       +        print("        locked        ", addr.locked, "\n");
       +        print("        $head        ", addr.$head\X, "\n");
       +        print("        $tail        ", addr.$tail\X, "\n");
       +};
       +
       +sizeofRWLock = 20;
       +aggr RWLock
       +{
       +        Lock 0 lock;
       +        'D' 4 readers;
       +        'D' 8 writer;
       +        'A' QLp 12 $head;
       +        'A' QLp 16 $tail;
       +};
       +
       +defn
       +RWLock(addr) {
       +        complex RWLock addr;
       +        print("Lock lock {\n");
       +        Lock(addr.lock);
       +        print("}\n");
       +        print("        readers        ", addr.readers, "\n");
       +        print("        writer        ", addr.writer, "\n");
       +        print("        $head        ", addr.$head\X, "\n");
       +        print("        $tail        ", addr.$tail\X, "\n");
       +};
       +
       +RFNAMEG = 1;
       +RFENVG = 2;
       +RFFDG = 4;
       +RFNOTEG = 8;
       +RFPROC = 16;
       +RFMEM = 32;
       +RFNOWAIT = 64;
       +RFCNAMEG = 1024;
       +RFCENVG = 2048;
       +RFCFDG = 4096;
       +RFREND = 8192;
       +RFNOMNT = 16384;
       +sizeofQid = 16;
       +aggr Qid
       +{
       +        'W' 0 path;
       +        'U' 8 vers;
       +        'b' 12 type;
       +};
       +
       +defn
       +Qid(addr) {
       +        complex Qid addr;
       +        print("        path        ", addr.path, "\n");
       +        print("        vers        ", addr.vers, "\n");
       +        print("        type        ", addr.type, "\n");
       +};
       +
       +sizeofDir = 60;
       +aggr Dir
       +{
       +        'u' 0 type;
       +        'U' 4 dev;
       +        Qid 8 qid;
       +        'U' 24 mode;
       +        'U' 28 atime;
       +        'U' 32 mtime;
       +        'V' 36 length;
       +        'X' 44 name;
       +        'X' 48 uid;
       +        'X' 52 gid;
       +        'X' 56 muid;
       +};
       +
       +defn
       +Dir(addr) {
       +        complex Dir addr;
       +        print("        type        ", addr.type, "\n");
       +        print("        dev        ", addr.dev, "\n");
       +        print("Qid qid {\n");
       +        Qid(addr.qid);
       +        print("}\n");
       +        print("        mode        ", addr.mode, "\n");
       +        print("        atime        ", addr.atime, "\n");
       +        print("        mtime        ", addr.mtime, "\n");
       +        print("        length        ", addr.length, "\n");
       +        print("        name        ", addr.name\X, "\n");
       +        print("        uid        ", addr.uid\X, "\n");
       +        print("        gid        ", addr.gid\X, "\n");
       +        print("        muid        ", addr.muid\X, "\n");
       +};
       +
       +sizeofWaitmsg = 20;
       +aggr Waitmsg
       +{
       +        'D' 0 pid;
       +        'a' 4 time;
       +        'X' 16 msg;
       +};
       +
       +defn
       +Waitmsg(addr) {
       +        complex Waitmsg addr;
       +        print("        pid        ", addr.pid, "\n");
       +        print("        time        ", addr.time, "\n");
       +        print("        msg        ", addr.msg\X, "\n");
       +};
       +
       +sizeofIOchunk = 8;
       +aggr IOchunk
       +{
       +        'X' 0 addr;
       +        'U' 4 len;
       +};
       +
       +defn
       +IOchunk(addr) {
       +        complex IOchunk addr;
       +        print("        addr        ", addr.addr\X, "\n");
       +        print("        len        ", addr.len, "\n");
       +};
       +
       +MAXCHLEN = 256;
       +MAXNAMELEN = 256;
       +MD5LEN = 16;
       +ARok = 0;
       +ARdone = 1;
       +ARerror = 2;
       +ARneedkey = 3;
       +ARbadkey = 4;
       +ARwritenext = 5;
       +ARtoosmall = 6;
       +ARtoobig = 7;
       +ARrpcfailure = 8;
       +ARphase = 9;
       +AuthRpcMax = 4096;
       +sizeofAuthRpc = 8208;
       +aggr AuthRpc
       +{
       +        'D' 0 afd;
       +        'X' 4 verb;
       +        'a' 8 ibuf;
       +        'a' 4104 obuf;
       +        'X' 8200 arg;
       +        'U' 8204 narg;
       +};
       +
       +defn
       +AuthRpc(addr) {
       +        complex AuthRpc addr;
       +        print("        afd        ", addr.afd, "\n");
       +        print("        verb        ", addr.verb\X, "\n");
       +        print("        ibuf        ", addr.ibuf, "\n");
       +        print("        obuf        ", addr.obuf, "\n");
       +        print("        arg        ", addr.arg\X, "\n");
       +        print("        narg        ", addr.narg, "\n");
       +};
       +
       +sizeofAuthInfo = 20;
       +aggr AuthInfo
       +{
       +        'X' 0 cuid;
       +        'X' 4 suid;
       +        'X' 8 cap;
       +        'D' 12 nsecret;
       +        'X' 16 secret;
       +};
       +
       +defn
       +AuthInfo(addr) {
       +        complex AuthInfo addr;
       +        print("        cuid        ", addr.cuid\X, "\n");
       +        print("        suid        ", addr.suid\X, "\n");
       +        print("        cap        ", addr.cap\X, "\n");
       +        print("        nsecret        ", addr.nsecret, "\n");
       +        print("        secret        ", addr.secret\X, "\n");
       +};
       +
       +sizeofChalstate = 540;
       +aggr Chalstate
       +{
       +        'X' 0 user;
       +        'a' 4 chal;
       +        'D' 260 nchal;
       +        'X' 264 resp;
       +        'D' 268 nresp;
       +        'D' 272 afd;
       +        'A' AuthRpc 276 rpc;
       +        'a' 280 userbuf;
       +        'D' 536 userinchal;
       +};
       +
       +defn
       +Chalstate(addr) {
       +        complex Chalstate addr;
       +        print("        user        ", addr.user\X, "\n");
       +        print("        chal        ", addr.chal, "\n");
       +        print("        nchal        ", addr.nchal, "\n");
       +        print("        resp        ", addr.resp\X, "\n");
       +        print("        nresp        ", addr.nresp, "\n");
       +        print("        afd        ", addr.afd, "\n");
       +        print("        rpc        ", addr.rpc\X, "\n");
       +        print("        userbuf        ", addr.userbuf, "\n");
       +        print("        userinchal        ", addr.userinchal, "\n");
       +};
       +
       +sizeofChapreply = 20;
       +aggr Chapreply
       +{
       +        'b' 0 id;
       +        'a' 1 resp;
       +};
       +
       +defn
       +Chapreply(addr) {
       +        complex Chapreply addr;
       +        print("        id        ", addr.id, "\n");
       +        print("        resp        ", addr.resp, "\n");
       +};
       +
       +sizeofMSchapreply = 48;
       +aggr MSchapreply
       +{
       +        'a' 0 LMresp;
       +        'a' 24 NTresp;
       +};
       +
       +defn
       +MSchapreply(addr) {
       +        complex MSchapreply addr;
       +        print("        LMresp        ", addr.LMresp, "\n");
       +        print("        NTresp        ", addr.NTresp, "\n");
       +};
       +
       +sizeofUserPasswd = 8;
       +aggr UserPasswd
       +{
       +        'X' 0 user;
       +        'X' 4 passwd;
       +};
       +
       +defn
       +UserPasswd(addr) {
       +        complex UserPasswd addr;
       +        print("        user        ", addr.user\X, "\n");
       +        print("        passwd        ", addr.passwd\X, "\n");
       +};
       +
       +ANAMELEN = 28;
       +AERRLEN = 64;
       +DOMLEN = 48;
       +DESKEYLEN = 7;
       +CHALLEN = 8;
       +NETCHLEN = 16;
       +CONFIGLEN = 14;
       +SECRETLEN = 32;
       +KEYDBOFF = 8;
       +OKEYDBLEN = 41;
       +KEYDBLEN = 73;
       +OMD5LEN = 16;
       +AuthTreq = 1;
       +AuthChal = 2;
       +AuthPass = 3;
       +AuthOK = 4;
       +AuthErr = 5;
       +AuthMod = 6;
       +AuthApop = 7;
       +AuthOKvar = 9;
       +AuthChap = 10;
       +AuthMSchap = 11;
       +AuthCram = 12;
       +AuthHttp = 13;
       +AuthVNC = 14;
       +AuthTs = 64;
       +AuthTc = 65;
       +AuthAs = 66;
       +AuthAc = 67;
       +AuthTp = 68;
       +AuthHr = 69;
       +sizeofTicketreq = 144;
       +aggr Ticketreq
       +{
       +        'C' 0 type;
       +        'a' 1 authid;
       +        'a' 29 authdom;
       +        'a' 77 chal;
       +        'a' 85 hostid;
       +        'a' 113 uid;
       +};
       +
       +defn
       +Ticketreq(addr) {
       +        complex Ticketreq addr;
       +        print("        type        ", addr.type, "\n");
       +        print("        authid        ", addr.authid, "\n");
       +        print("        authdom        ", addr.authdom, "\n");
       +        print("        chal        ", addr.chal, "\n");
       +        print("        hostid        ", addr.hostid, "\n");
       +        print("        uid        ", addr.uid, "\n");
       +};
       +
       +sizeofTicket = 72;
       +aggr Ticket
       +{
       +        'C' 0 num;
       +        'a' 1 chal;
       +        'a' 9 cuid;
       +        'a' 37 suid;
       +        'a' 65 key;
       +};
       +
       +defn
       +Ticket(addr) {
       +        complex Ticket addr;
       +        print("        num        ", addr.num, "\n");
       +        print("        chal        ", addr.chal, "\n");
       +        print("        cuid        ", addr.cuid, "\n");
       +        print("        suid        ", addr.suid, "\n");
       +        print("        key        ", addr.key, "\n");
       +};
       +
       +sizeofAuthenticator = 16;
       +aggr Authenticator
       +{
       +        'C' 0 num;
       +        'a' 1 chal;
       +        'U' 12 id;
       +};
       +
       +defn
       +Authenticator(addr) {
       +        complex Authenticator addr;
       +        print("        num        ", addr.num, "\n");
       +        print("        chal        ", addr.chal, "\n");
       +        print("        id        ", addr.id, "\n");
       +};
       +
       +sizeofPasswordreq = 92;
       +aggr Passwordreq
       +{
       +        'C' 0 num;
       +        'a' 1 old;
       +        'a' 29 new;
       +        'C' 57 changesecret;
       +        'a' 58 secret;
       +};
       +
       +defn
       +Passwordreq(addr) {
       +        complex Passwordreq addr;
       +        print("        num        ", addr.num, "\n");
       +        print("        old        ", addr.old, "\n");
       +        print("        new        ", addr.new, "\n");
       +        print("        changesecret        ", addr.changesecret, "\n");
       +        print("        secret        ", addr.secret, "\n");
       +};
       +
       +sizeofOChapreply = 48;
       +aggr OChapreply
       +{
       +        'b' 0 id;
       +        'a' 1 uid;
       +        'a' 29 resp;
       +};
       +
       +defn
       +OChapreply(addr) {
       +        complex OChapreply addr;
       +        print("        id        ", addr.id, "\n");
       +        print("        uid        ", addr.uid, "\n");
       +        print("        resp        ", addr.resp, "\n");
       +};
       +
       +sizeofOMSchapreply = 76;
       +aggr OMSchapreply
       +{
       +        'a' 0 uid;
       +        'a' 28 LMresp;
       +        'a' 52 NTresp;
       +};
       +
       +defn
       +OMSchapreply(addr) {
       +        complex OMSchapreply addr;
       +        print("        uid        ", addr.uid, "\n");
       +        print("        LMresp        ", addr.LMresp, "\n");
       +        print("        NTresp        ", addr.NTresp, "\n");
       +};
       +
       +NVwrite = 1;
       +NVwriteonerr = 2;
       +sizeofNvrsafe = 112;
       +aggr Nvrsafe
       +{
       +        'a' 0 machkey;
       +        'b' 7 machsum;
       +        'a' 8 authkey;
       +        'b' 15 authsum;
       +        'a' 16 config;
       +        'b' 30 configsum;
       +        'a' 31 authid;
       +        'b' 59 authidsum;
       +        'a' 60 authdom;
       +        'b' 108 authdomsum;
       +};
       +
       +defn
       +Nvrsafe(addr) {
       +        complex Nvrsafe addr;
       +        print("        machkey        ", addr.machkey, "\n");
       +        print("        machsum        ", addr.machsum, "\n");
       +        print("        authkey        ", addr.authkey, "\n");
       +        print("        authsum        ", addr.authsum, "\n");
       +        print("        config        ", addr.config, "\n");
       +        print("        configsum        ", addr.configsum, "\n");
       +        print("        authid        ", addr.authid, "\n");
       +        print("        authidsum        ", addr.authidsum, "\n");
       +        print("        authdom        ", addr.authdom, "\n");
       +        print("        authdomsum        ", addr.authdomsum, "\n");
       +};
       +
       +AESbsize = 16;
       +AESmaxkey = 32;
       +AESmaxrounds = 14;
       +sizeofAESstate = 540;
       +aggr AESstate
       +{
       +        'U' 0 setup;
       +        'D' 4 rounds;
       +        'D' 8 keybytes;
       +        'a' 12 key;
       +        'a' 44 ekey;
       +        'a' 284 dkey;
       +        'a' 524 ivec;
       +};
       +
       +defn
       +AESstate(addr) {
       +        complex AESstate addr;
       +        print("        setup        ", addr.setup, "\n");
       +        print("        rounds        ", addr.rounds, "\n");
       +        print("        keybytes        ", addr.keybytes, "\n");
       +        print("        key        ", addr.key, "\n");
       +        print("        ekey        ", addr.ekey, "\n");
       +        print("        dkey        ", addr.dkey, "\n");
       +        print("        ivec        ", addr.ivec, "\n");
       +};
       +
       +BFbsize = 8;
       +BFrounds = 16;
       +sizeofBFstate = 4236;
       +aggr BFstate
       +{
       +        'U' 0 setup;
       +        'a' 4 key;
       +        'a' 60 ivec;
       +        'a' 68 pbox;
       +        'a' 140 sbox;
       +};
       +
       +defn
       +BFstate(addr) {
       +        complex BFstate addr;
       +        print("        setup        ", addr.setup, "\n");
       +        print("        key        ", addr.key, "\n");
       +        print("        ivec        ", addr.ivec, "\n");
       +        print("        pbox        ", addr.pbox, "\n");
       +        print("        sbox        ", addr.sbox, "\n");
       +};
       +
       +DESbsize = 8;
       +sizeofDESstate = 148;
       +aggr DESstate
       +{
       +        'U' 0 setup;
       +        'a' 4 key;
       +        'a' 12 expanded;
       +        'a' 140 ivec;
       +};
       +
       +defn
       +DESstate(addr) {
       +        complex DESstate addr;
       +        print("        setup        ", addr.setup, "\n");
       +        print("        key        ", addr.key, "\n");
       +        print("        expanded        ", addr.expanded, "\n");
       +        print("        ivec        ", addr.ivec, "\n");
       +};
       +
       +DES3E = 0;
       +DES3D = 1;
       +DES3EEE = 0;
       +DES3EDE = 2;
       +DES3DED = 5;
       +DES3DDD = 7;
       +sizeofDES3state = 420;
       +aggr DES3state
       +{
       +        'U' 0 setup;
       +        'a' 4 key;
       +        'a' 28 expanded;
       +        'a' 412 ivec;
       +};
       +
       +defn
       +DES3state(addr) {
       +        complex DES3state addr;
       +        print("        setup        ", addr.setup, "\n");
       +        print("        key        ", addr.key, "\n");
       +        print("        expanded        ", addr.expanded, "\n");
       +        print("        ivec        ", addr.ivec, "\n");
       +};
       +
       +SHA1dlen = 20;
       +MD4dlen = 16;
       +MD5dlen = 16;
       +sizeofDigestState = 160;
       +aggr DigestState
       +{
       +        'U' 0 len;
       +        'a' 4 state;
       +        'a' 24 buf;
       +        'D' 152 blen;
       +        'C' 156 malloced;
       +        'C' 157 seeded;
       +};
       +
       +defn
       +DigestState(addr) {
       +        complex DigestState addr;
       +        print("        len        ", addr.len, "\n");
       +        print("        state        ", addr.state, "\n");
       +        print("        buf        ", addr.buf, "\n");
       +        print("        blen        ", addr.blen, "\n");
       +        print("        malloced        ", addr.malloced, "\n");
       +        print("        seeded        ", addr.seeded, "\n");
       +};
       +
       +sizeofRC4state = 260;
       +aggr RC4state
       +{
       +        'a' 0 state;
       +        'b' 256 x;
       +        'b' 257 y;
       +};
       +
       +defn
       +RC4state(addr) {
       +        complex RC4state addr;
       +        print("        state        ", addr.state, "\n");
       +        print("        x        ", addr.x, "\n");
       +        print("        y        ", addr.y, "\n");
       +};
       +
       +sizeofRSApub = 8;
       +aggr RSApub
       +{
       +        'X' 0 n;
       +        'X' 4 ek;
       +};
       +
       +defn
       +RSApub(addr) {
       +        complex RSApub addr;
       +        print("        n        ", addr.n\X, "\n");
       +        print("        ek        ", addr.ek\X, "\n");
       +};
       +
       +sizeofRSApriv = 32;
       +aggr RSApriv
       +{
       +        RSApub 0 pub;
       +        'X' 8 dk;
       +        'X' 12 p;
       +        'X' 16 q;
       +        'X' 20 kp;
       +        'X' 24 kq;
       +        'X' 28 c2;
       +};
       +
       +defn
       +RSApriv(addr) {
       +        complex RSApriv addr;
       +        print("RSApub pub {\n");
       +        RSApub(addr.pub);
       +        print("}\n");
       +        print("        dk        ", addr.dk\X, "\n");
       +        print("        p        ", addr.p\X, "\n");
       +        print("        q        ", addr.q\X, "\n");
       +        print("        kp        ", addr.kp\X, "\n");
       +        print("        kq        ", addr.kq\X, "\n");
       +        print("        c2        ", addr.c2\X, "\n");
       +};
       +
       +sizeofEGpub = 12;
       +aggr EGpub
       +{
       +        'X' 0 p;
       +        'X' 4 alpha;
       +        'X' 8 key;
       +};
       +
       +defn
       +EGpub(addr) {
       +        complex EGpub addr;
       +        print("        p        ", addr.p\X, "\n");
       +        print("        alpha        ", addr.alpha\X, "\n");
       +        print("        key        ", addr.key\X, "\n");
       +};
       +
       +sizeofEGpriv = 16;
       +aggr EGpriv
       +{
       +        EGpub 0 pub;
       +        'X' 12 secret;
       +};
       +
       +defn
       +EGpriv(addr) {
       +        complex EGpriv addr;
       +        print("EGpub pub {\n");
       +        EGpub(addr.pub);
       +        print("}\n");
       +        print("        secret        ", addr.secret\X, "\n");
       +};
       +
       +sizeofEGsig = 8;
       +aggr EGsig
       +{
       +        'X' 0 r;
       +        'X' 4 s;
       +};
       +
       +defn
       +EGsig(addr) {
       +        complex EGsig addr;
       +        print("        r        ", addr.r\X, "\n");
       +        print("        s        ", addr.s\X, "\n");
       +};
       +
       +sizeofString = 20;
       +aggr String
       +{
       +        {
       +        'D' 0 val;
       +        };
       +        'X' 4 base;
       +        'X' 8 end;
       +        'X' 12 ptr;
       +        'd' 16 ref;
       +        'b' 18 fixed;
       +};
       +
       +defn
       +String(addr) {
       +        complex String addr;
       +        print("Lock {\n");
       +                Lock(addr+0);
       +        print("}\n");
       +        print("        base        ", addr.base\X, "\n");
       +        print("        end        ", addr.end\X, "\n");
       +        print("        ptr        ", addr.ptr\X, "\n");
       +        print("        ref        ", addr.ref, "\n");
       +        print("        fixed        ", addr.fixed, "\n");
       +};
       +
       +sizeofChannel = 156;
       +aggr Channel
       +{
       +        'D' 0 s;
       +        'U' 4 f;
       +        'U' 8 n;
       +        'D' 12 e;
       +        'D' 16 freed;
       +        'U' 20 qused;
       +        'a' 24 qentry;
       +        'a' 152 v;
       +};
       +
       +defn
       +Channel(addr) {
       +        complex Channel addr;
       +        print("        s        ", addr.s, "\n");
       +        print("        f        ", addr.f, "\n");
       +        print("        n        ", addr.n, "\n");
       +        print("        e        ", addr.e, "\n");
       +        print("        freed        ", addr.freed, "\n");
       +        print("        qused        ", addr.qused, "\n");
       +        print("        qentry        ", addr.qentry, "\n");
       +        print("        v        ", addr.v, "\n");
       +};
       +
       +sizeofAlt = 20;
       +aggr Alt
       +{
       +        'A' Channel 0 c;
       +        'X' 4 v;
       +        'D' 8 op;
       +        'A' Channel 12 tag;
       +        'U' 16 q;
       +};
       +
       +defn
       +Alt(addr) {
       +        complex Alt addr;
       +        print("        c        ", addr.c\X, "\n");
       +        print("        v        ", addr.v\X, "\n");
       +        print("        op        ", addr.op, "\n");
       +        print("        tag        ", addr.tag\X, "\n");
       +        print("        q        ", addr.q, "\n");
       +};
       +
       +sizeofRef = 4;
       +aggr Ref
       +{
       +        'D' 0 ref;
       +};
       +
       +defn
       +Ref(addr) {
       +        complex Ref addr;
       +        print("        ref        ", addr.ref, "\n");
       +};
       +
       +sizeof_2_ = 8;
       +aggr _2_
       +{
       +        'U' 0 msize;
       +        'X' 4 version;
       +};
       +
       +defn
       +_2_(addr) {
       +        complex _2_ addr;
       +        print("        msize        ", addr.msize, "\n");
       +        print("        version        ", addr.version\X, "\n");
       +};
       +
       +sizeof_3_ = 4;
       +aggr _3_
       +{
       +        'u' 0 oldtag;
       +};
       +
       +defn
       +_3_(addr) {
       +        complex _3_ addr;
       +        print("        oldtag        ", addr.oldtag, "\n");
       +};
       +
       +sizeof_4_ = 4;
       +aggr _4_
       +{
       +        'X' 0 ename;
       +};
       +
       +defn
       +_4_(addr) {
       +        complex _4_ addr;
       +        print("        ename        ", addr.ename\X, "\n");
       +};
       +
       +sizeof_5_ = 20;
       +aggr _5_
       +{
       +        Qid 0 qid;
       +        'U' 16 iounit;
       +};
       +
       +defn
       +_5_(addr) {
       +        complex _5_ addr;
       +        print("Qid qid {\n");
       +        Qid(addr.qid);
       +        print("}\n");
       +        print("        iounit        ", addr.iounit, "\n");
       +};
       +
       +sizeof_6_ = 16;
       +aggr _6_
       +{
       +        Qid 0 aqid;
       +};
       +
       +defn
       +_6_(addr) {
       +        complex _6_ addr;
       +        print("Qid aqid {\n");
       +        Qid(addr.aqid);
       +        print("}\n");
       +};
       +
       +sizeof_7_ = 12;
       +aggr _7_
       +{
       +        'U' 0 afid;
       +        'X' 4 uname;
       +        'X' 8 aname;
       +};
       +
       +defn
       +_7_(addr) {
       +        complex _7_ addr;
       +        print("        afid        ", addr.afid, "\n");
       +        print("        uname        ", addr.uname\X, "\n");
       +        print("        aname        ", addr.aname\X, "\n");
       +};
       +
       +sizeof_8_ = 12;
       +aggr _8_
       +{
       +        'U' 0 perm;
       +        'X' 4 name;
       +        'b' 8 mode;
       +};
       +
       +defn
       +_8_(addr) {
       +        complex _8_ addr;
       +        print("        perm        ", addr.perm, "\n");
       +        print("        name        ", addr.name\X, "\n");
       +        print("        mode        ", addr.mode, "\n");
       +};
       +
       +sizeof_9_ = 72;
       +aggr _9_
       +{
       +        'U' 0 newfid;
       +        'u' 4 nwname;
       +        'a' 8 wname;
       +};
       +
       +defn
       +_9_(addr) {
       +        complex _9_ addr;
       +        print("        newfid        ", addr.newfid, "\n");
       +        print("        nwname        ", addr.nwname, "\n");
       +        print("        wname        ", addr.wname, "\n");
       +};
       +
       +sizeof_10_ = 260;
       +aggr _10_
       +{
       +        'u' 0 nwqid;
       +        'a' 4 wqid;
       +};
       +
       +defn
       +_10_(addr) {
       +        complex _10_ addr;
       +        print("        nwqid        ", addr.nwqid, "\n");
       +        print("        wqid        ", addr.wqid, "\n");
       +};
       +
       +sizeof_11_ = 16;
       +aggr _11_
       +{
       +        'V' 0 offset;
       +        'U' 8 count;
       +        'X' 12 data;
       +};
       +
       +defn
       +_11_(addr) {
       +        complex _11_ addr;
       +        print("        offset        ", addr.offset, "\n");
       +        print("        count        ", addr.count, "\n");
       +        print("        data        ", addr.data\X, "\n");
       +};
       +
       +sizeof_12_ = 8;
       +aggr _12_
       +{
       +        'u' 0 nstat;
       +        'X' 4 stat;
       +};
       +
       +defn
       +_12_(addr) {
       +        complex _12_ addr;
       +        print("        nstat        ", addr.nstat, "\n");
       +        print("        stat        ", addr.stat\X, "\n");
       +};
       +
       +sizeof_13_ = 260;
       +aggr _13_
       +{
       +        {
       +        'U' 0 msize;
       +        'X' 4 version;
       +        };
       +        {
       +        'u' 0 oldtag;
       +        };
       +        {
       +        'X' 0 ename;
       +        };
       +        {
       +        Qid 0 qid;
       +        'U' 16 iounit;
       +        };
       +        {
       +        Qid 0 aqid;
       +        };
       +        {
       +        'U' 0 afid;
       +        'X' 4 uname;
       +        'X' 8 aname;
       +        };
       +        {
       +        'U' 0 perm;
       +        'X' 4 name;
       +        'b' 8 mode;
       +        };
       +        {
       +        'U' 0 newfid;
       +        'u' 4 nwname;
       +        'a' 8 wname;
       +        };
       +        {
       +        'u' 0 nwqid;
       +        'a' 4 wqid;
       +        };
       +        {
       +        'V' 0 offset;
       +        'U' 8 count;
       +        'X' 12 data;
       +        };
       +        {
       +        'u' 0 nstat;
       +        'X' 4 stat;
       +        };
       +};
       +
       +defn
       +_13_(addr) {
       +        complex _13_ addr;
       +        print("_2_ {\n");
       +                _2_(addr+0);
       +        print("}\n");
       +        print("_3_ {\n");
       +                _3_(addr+0);
       +        print("}\n");
       +        print("_4_ {\n");
       +                _4_(addr+0);
       +        print("}\n");
       +        print("_5_ {\n");
       +                _5_(addr+0);
       +        print("}\n");
       +        print("_6_ {\n");
       +                _6_(addr+0);
       +        print("}\n");
       +        print("_7_ {\n");
       +                _7_(addr+0);
       +        print("}\n");
       +        print("_8_ {\n");
       +                _8_(addr+0);
       +        print("}\n");
       +        print("_9_ {\n");
       +                _9_(addr+0);
       +        print("}\n");
       +        print("_10_ {\n");
       +                _10_(addr+0);
       +        print("}\n");
       +        print("_11_ {\n");
       +                _11_(addr+0);
       +        print("}\n");
       +        print("_12_ {\n");
       +                _12_(addr+0);
       +        print("}\n");
       +};
       +
       +sizeofFcall = 272;
       +aggr Fcall
       +{
       +        'b' 0 type;
       +        'U' 4 fid;
       +        'u' 8 tag;
       +        {
       +        {
       +        'U' 12 msize;
       +        'X' 16 version;
       +        };
       +        {
       +        'u' 12 oldtag;
       +        };
       +        {
       +        'X' 12 ename;
       +        };
       +        {
       +        Qid 12 qid;
       +        'U' 28 iounit;
       +        };
       +        {
       +        Qid 12 aqid;
       +        };
       +        {
       +        'U' 12 afid;
       +        'X' 16 uname;
       +        'X' 20 aname;
       +        };
       +        {
       +        'U' 12 perm;
       +        'X' 16 name;
       +        'b' 20 mode;
       +        };
       +        {
       +        'U' 12 newfid;
       +        'u' 16 nwname;
       +        'a' 20 wname;
       +        };
       +        {
       +        'u' 12 nwqid;
       +        'a' 16 wqid;
       +        };
       +        {
       +        'V' 12 offset;
       +        'U' 20 count;
       +        'X' 24 data;
       +        };
       +        {
       +        'u' 12 nstat;
       +        'X' 16 stat;
       +        };
       +        };
       +};
       +
       +defn
       +Fcall(addr) {
       +        complex Fcall addr;
       +        print("        type        ", addr.type, "\n");
       +        print("        fid        ", addr.fid, "\n");
       +        print("        tag        ", addr.tag, "\n");
       +        print("_13_ {\n");
       +                _13_(addr+12);
       +        print("}\n");
       +};
       +
       +Tversion = 100;
       +Rversion = 101;
       +Tauth = 102;
       +Rauth = 103;
       +Tattach = 104;
       +Rattach = 105;
       +Terror = 106;
       +Rerror = 107;
       +Tflush = 108;
       +Rflush = 109;
       +Twalk = 110;
       +Rwalk = 111;
       +Topen = 112;
       +Ropen = 113;
       +Tcreate = 114;
       +Rcreate = 115;
       +Tread = 116;
       +Rread = 117;
       +Twrite = 118;
       +Rwrite = 119;
       +Tclunk = 120;
       +Rclunk = 121;
       +Tremove = 122;
       +Rremove = 123;
       +Tstat = 124;
       +Rstat = 125;
       +Twstat = 126;
       +Rwstat = 127;
       +Tmax = 128;
       +sizeofFid = 60;
       +aggr Fid
       +{
       +        'U' 0 fid;
       +        'C' 4 omode;
       +        'X' 8 file;
       +        'X' 12 uid;
       +        Qid 16 qid;
       +        'X' 32 aux;
       +        'X' 36 rdir;
       +        Ref 40 ref;
       +        'X' 44 pool;
       +        'V' 48 diroffset;
       +        'D' 56 dirindex;
       +};
       +
       +defn
       +Fid(addr) {
       +        complex Fid addr;
       +        print("        fid        ", addr.fid, "\n");
       +        print("        omode        ", addr.omode, "\n");
       +        print("        file        ", addr.file\X, "\n");
       +        print("        uid        ", addr.uid\X, "\n");
       +        print("Qid qid {\n");
       +        Qid(addr.qid);
       +        print("}\n");
       +        print("        aux        ", addr.aux\X, "\n");
       +        print("        rdir        ", addr.rdir\X, "\n");
       +        print("Ref ref {\n");
       +        Ref(addr.ref);
       +        print("}\n");
       +        print("        pool        ", addr.pool\X, "\n");
       +        print("        diroffset        ", addr.diroffset, "\n");
       +        print("        dirindex        ", addr.dirindex, "\n");
       +};
       +
       +sizeofReq = 656;
       +aggr Req
       +{
       +        'U' 0 tag;
       +        'X' 4 aux;
       +        Fcall 8 ifcall;
       +        Fcall 280 ofcall;
       +        Dir 552 d;
       +        'A' Req 612 oldreq;
       +        'A' Fid 616 fid;
       +        'A' Fid 620 afid;
       +        'A' Fid 624 newfid;
       +        'X' 628 srv;
       +        Ref 632 ref;
       +        'X' 636 pool;
       +        'X' 640 buf;
       +        'b' 644 type;
       +        'b' 645 responded;
       +        'X' 648 error;
       +        'X' 652 rbuf;
       +};
       +
       +defn
       +Req(addr) {
       +        complex Req addr;
       +        print("        tag        ", addr.tag, "\n");
       +        print("        aux        ", addr.aux\X, "\n");
       +        print("Fcall ifcall {\n");
       +        Fcall(addr.ifcall);
       +        print("}\n");
       +        print("Fcall ofcall {\n");
       +        Fcall(addr.ofcall);
       +        print("}\n");
       +        print("Dir d {\n");
       +        Dir(addr.d);
       +        print("}\n");
       +        print("        oldreq        ", addr.oldreq\X, "\n");
       +        print("        fid        ", addr.fid\X, "\n");
       +        print("        afid        ", addr.afid\X, "\n");
       +        print("        newfid        ", addr.newfid\X, "\n");
       +        print("        srv        ", addr.srv\X, "\n");
       +        print("Ref ref {\n");
       +        Ref(addr.ref);
       +        print("}\n");
       +        print("        pool        ", addr.pool\X, "\n");
       +        print("        buf        ", addr.buf\X, "\n");
       +        print("        type        ", addr.type, "\n");
       +        print("        responded        ", addr.responded, "\n");
       +        print("        error        ", addr.error\X, "\n");
       +        print("        rbuf        ", addr.rbuf\X, "\n");
       +};
       +
       +sizeofFidpool = 12;
       +aggr Fidpool
       +{
       +        'X' 0 map;
       +        'X' 4 destroy;
       +        'X' 8 srv;
       +};
       +
       +defn
       +Fidpool(addr) {
       +        complex Fidpool addr;
       +        print("        map        ", addr.map\X, "\n");
       +        print("        destroy        ", addr.destroy\X, "\n");
       +        print("        srv        ", addr.srv\X, "\n");
       +};
       +
       +sizeofReqpool = 12;
       +aggr Reqpool
       +{
       +        'X' 0 map;
       +        'X' 4 destroy;
       +        'X' 8 srv;
       +};
       +
       +defn
       +Reqpool(addr) {
       +        complex Reqpool addr;
       +        print("        map        ", addr.map\X, "\n");
       +        print("        destroy        ", addr.destroy\X, "\n");
       +        print("        srv        ", addr.srv\X, "\n");
       +};
       +
       +sizeofFile = 108;
       +aggr File
       +{
       +        {
       +        'D' 0 ref;
       +        };
       +        {
       +        'u' 4 type;
       +        'U' 8 dev;
       +        Qid 12 qid;
       +        'U' 28 mode;
       +        'U' 32 atime;
       +        'U' 36 mtime;
       +        'V' 40 length;
       +        'X' 48 name;
       +        'X' 52 uid;
       +        'X' 56 gid;
       +        'X' 60 muid;
       +        };
       +        'A' File 64 parent;
       +        'X' 68 aux;
       +        {
       +        Lock 72 lock;
       +        'D' 76 readers;
       +        'D' 80 writer;
       +        'A' QLp 84 $head;
       +        'A' QLp 88 $tail;
       +        };
       +        'X' 92 filelist;
       +        'X' 96 tree;
       +        'D' 100 nchild;
       +        'D' 104 allocd;
       +};
       +
       +defn
       +File(addr) {
       +        complex File addr;
       +        print("Ref {\n");
       +                Ref(addr+0);
       +        print("}\n");
       +        print("Dir {\n");
       +                Dir(addr+4);
       +        print("}\n");
       +        print("        parent        ", addr.parent\X, "\n");
       +        print("        aux        ", addr.aux\X, "\n");
       +        print("RWLock {\n");
       +                RWLock(addr+72);
       +        print("}\n");
       +        print("        filelist        ", addr.filelist\X, "\n");
       +        print("        tree        ", addr.tree\X, "\n");
       +        print("        nchild        ", addr.nchild, "\n");
       +        print("        allocd        ", addr.allocd, "\n");
       +};
       +
       +sizeofTree = 20;
       +aggr Tree
       +{
       +        'A' File 0 root;
       +        'X' 4 destroy;
       +        Lock 8 genlock;
       +        'U' 12 qidgen;
       +        'U' 16 dirqidgen;
       +};
       +
       +defn
       +Tree(addr) {
       +        complex Tree addr;
       +        print("        root        ", addr.root\X, "\n");
       +        print("        destroy        ", addr.destroy\X, "\n");
       +        print("Lock genlock {\n");
       +        Lock(addr.genlock);
       +        print("}\n");
       +        print("        qidgen        ", addr.qidgen, "\n");
       +        print("        dirqidgen        ", addr.dirqidgen, "\n");
       +};
       +
       +sizeofSrv = 136;
       +aggr Srv
       +{
       +        'A' Tree 0 tree;
       +        'X' 4 destroyfid;
       +        'X' 8 destroyreq;
       +        'X' 12 end;
       +        'X' 16 aux;
       +        'X' 20 attach;
       +        'X' 24 auth;
       +        'X' 28 open;
       +        'X' 32 create;
       +        'X' 36 read;
       +        'X' 40 write;
       +        'X' 44 remove;
       +        'X' 48 flush;
       +        'X' 52 stat;
       +        'X' 56 wstat;
       +        'X' 60 walk;
       +        'X' 64 clone;
       +        'X' 68 walk1;
       +        'D' 72 infd;
       +        'D' 76 outfd;
       +        'D' 80 nopipe;
       +        'A' Fidpool 84 fpool;
       +        'A' Reqpool 88 rpool;
       +        'U' 92 msize;
       +        'X' 96 rbuf;
       +        QLock 100 rlock;
       +        'X' 116 wbuf;
       +        QLock 120 wlock;
       +};
       +
       +defn
       +Srv(addr) {
       +        complex Srv addr;
       +        print("        tree        ", addr.tree\X, "\n");
       +        print("        destroyfid        ", addr.destroyfid\X, "\n");
       +        print("        destroyreq        ", addr.destroyreq\X, "\n");
       +        print("        end        ", addr.end\X, "\n");
       +        print("        aux        ", addr.aux\X, "\n");
       +        print("        attach        ", addr.attach\X, "\n");
       +        print("        auth        ", addr.auth\X, "\n");
       +        print("        open        ", addr.open\X, "\n");
       +        print("        create        ", addr.create\X, "\n");
       +        print("        read        ", addr.read\X, "\n");
       +        print("        write        ", addr.write\X, "\n");
       +        print("        remove        ", addr.remove\X, "\n");
       +        print("        flush        ", addr.flush\X, "\n");
       +        print("        stat        ", addr.stat\X, "\n");
       +        print("        wstat        ", addr.wstat\X, "\n");
       +        print("        walk        ", addr.walk\X, "\n");
       +        print("        clone        ", addr.clone\X, "\n");
       +        print("        walk1        ", addr.walk1\X, "\n");
       +        print("        infd        ", addr.infd, "\n");
       +        print("        outfd        ", addr.outfd, "\n");
       +        print("        nopipe        ", addr.nopipe, "\n");
       +        print("        fpool        ", addr.fpool\X, "\n");
       +        print("        rpool        ", addr.rpool\X, "\n");
       +        print("        msize        ", addr.msize, "\n");
       +        print("        rbuf        ", addr.rbuf\X, "\n");
       +        print("QLock rlock {\n");
       +        QLock(addr.rlock);
       +        print("}\n");
       +        print("        wbuf        ", addr.wbuf\X, "\n");
       +        print("QLock wlock {\n");
       +        QLock(addr.wlock);
       +        print("}\n");
       +};
       +
       +OMASK = 3;
       +Maxname = 128;
       +Maxrpc = 4096;
       +Notstarted = -3;
       +Broken = -2;
       +Established = -1;
       +RpcFailure = 0;
       +RpcNeedkey = 1;
       +RpcOk = 2;
       +RpcErrstr = 3;
       +RpcToosmall = 4;
       +RpcPhase = 5;
       +sizeofAttr = 12;
       +aggr Attr
       +{
       +        'A' Attr 0 next;
       +        'A' String 4 name;
       +        'A' String 8 val;
       +};
       +
       +defn
       +Attr(addr) {
       +        complex Attr addr;
       +        print("        next        ", addr.next\X, "\n");
       +        print("        name        ", addr.name\X, "\n");
       +        print("        val        ", addr.val\X, "\n");
       +};
       +
       +sizeof_14_ = 4120;
       +aggr _14_
       +{
       +        'X' 0 arg;
       +        'a' 4 buf;
       +        'X' 4100 verb;
       +        'D' 4104 iverb;
       +        'D' 4108 narg;
       +        'D' 4112 nbuf;
       +        'D' 4116 nwant;
       +};
       +
       +defn
       +_14_(addr) {
       +        complex _14_ addr;
       +        print("        arg        ", addr.arg\X, "\n");
       +        print("        buf        ", addr.buf, "\n");
       +        print("        verb        ", addr.verb\X, "\n");
       +        print("        iverb        ", addr.iverb, "\n");
       +        print("        narg        ", addr.narg, "\n");
       +        print("        nbuf        ", addr.nbuf, "\n");
       +        print("        nwant        ", addr.nwant, "\n");
       +};
       +
       +sizeofFsstate = 4700;
       +aggr Fsstate
       +{
       +        'X' 0 sysuser;
       +        'D' 4 listoff;
       +        _14_ 8 rpc;
       +        'a' 4128 err;
       +        'a' 4256 keyinfo;
       +        'X' 4640 phasename;
       +        'D' 4644 isclient;
       +        'D' 4648 haveai;
       +        'D' 4652 maxphase;
       +        'D' 4656 phase;
       +        'D' 4660 started;
       +        'A' Attr 4664 attr;
       +        AuthInfo 4668 ai;
       +        'X' 4688 proto;
       +        'X' 4692 ps;
       +        'X' 4696 ring;
       +};
       +
       +defn
       +Fsstate(addr) {
       +        complex Fsstate addr;
       +        print("        sysuser        ", addr.sysuser\X, "\n");
       +        print("        listoff        ", addr.listoff, "\n");
       +        print("_14_ rpc {\n");
       +        _14_(addr.rpc);
       +        print("}\n");
       +        print("        err        ", addr.err, "\n");
       +        print("        keyinfo        ", addr.keyinfo, "\n");
       +        print("        phasename        ", addr.phasename\X, "\n");
       +        print("        isclient        ", addr.isclient, "\n");
       +        print("        haveai        ", addr.haveai, "\n");
       +        print("        maxphase        ", addr.maxphase, "\n");
       +        print("        phase        ", addr.phase, "\n");
       +        print("        started        ", addr.started, "\n");
       +        print("        attr        ", addr.attr\X, "\n");
       +        print("AuthInfo ai {\n");
       +        AuthInfo(addr.ai);
       +        print("}\n");
       +        print("        proto        ", addr.proto\X, "\n");
       +        print("        ps        ", addr.ps\X, "\n");
       +        print("        ring        ", addr.ring\X, "\n");
       +};
       +
       +sizeofKey = 20;
       +aggr Key
       +{
       +        'D' 0 ref;
       +        'A' Attr 4 attr;
       +        'A' Attr 8 privattr;
       +        'X' 12 proto;
       +        'X' 16 priv;
       +};
       +
       +defn
       +Key(addr) {
       +        complex Key addr;
       +        print("        ref        ", addr.ref, "\n");
       +        print("        attr        ", addr.attr\X, "\n");
       +        print("        privattr        ", addr.privattr\X, "\n");
       +        print("        proto        ", addr.proto\X, "\n");
       +        print("        priv        ", addr.priv\X, "\n");
       +};
       +
       +sizeofKeyring = 8;
       +aggr Keyring
       +{
       +        'A' Key 0 key;
       +        'D' 4 nkey;
       +};
       +
       +defn
       +Keyring(addr) {
       +        complex Keyring addr;
       +        print("        key        ", addr.key\X, "\n");
       +        print("        nkey        ", addr.nkey, "\n");
       +};
       +
       +sizeofLogbuf = 520;
       +aggr Logbuf
       +{
       +        'D' 0 rp;
       +        'D' 4 wp;
       +        'a' 8 msg;
       +};
       +
       +defn
       +Logbuf(addr) {
       +        complex Logbuf addr;
       +        print("        rp        ", addr.rp, "\n");
       +        print("        wp        ", addr.wp, "\n");
       +        print("        msg        ", addr.msg, "\n");
       +};
       +
       +sizeofProto = 28;
       +aggr Proto
       +{
       +        'X' 0 name;
       +        'X' 4 init;
       +        'X' 8 addkey;
       +        'X' 12 closekey;
       +        'X' 16 write;
       +        'X' 20 read;
       +        'X' 24 close;
       +};
       +
       +defn
       +Proto(addr) {
       +        complex Proto addr;
       +        print("        name        ", addr.name\X, "\n");
       +        print("        init        ", addr.init\X, "\n");
       +        print("        addkey        ", addr.addkey\X, "\n");
       +        print("        closekey        ", addr.closekey\X, "\n");
       +        print("        write        ", addr.write\X, "\n");
       +        print("        read        ", addr.read\X, "\n");
       +        print("        close        ", addr.close\X, "\n");
       +};
       +
       +complex Keyring ring;
       +complex Logbuf logbuf;
       +complex Proto apop;
       +complex Proto cram;
       +complex Proto p9any;
       +complex Proto p9sk1;
       +complex Proto p9sk2;
       +complex Keyring ring;
       +complex Srv fs;
       +complex Proto main:p;
       +Qroot = 0;
       +Qfactotum = 1;
       +Qrpc = 2;
       +Qkeylist = 3;
       +Qprotolist = 4;
       +Qconfirm = 5;
       +Qlog = 6;
       +Qctl = 7;
       +complex Qid mkqid:q;
       +complex Req fsattach:r;
       +sizeof_15_ = 12;
       +aggr _15_
       +{
       +        'X' 0 name;
       +        'D' 4 qidpath;
       +        'U' 8 perm;
       +};
       +
       +defn
       +_15_(addr) {
       +        complex _15_ addr;
       +        print("        name        ", addr.name\X, "\n");
       +        print("        qidpath        ", addr.qidpath, "\n");
       +        print("        perm        ", addr.perm, "\n");
       +};
       +
       +complex Dir fillstat:dir;
       +complex Dir fsdirgen:dir;
       +complex Fid fswalk1:fid;
       +complex Qid fswalk1:qid;
       +complex Req fsstat:r;
       +complex Req fsopen:r;
       +complex Fsstate fsopen:fss;
       +complex Fid fsdestroyfid:fid;
       +complex Req readlist:r;
       +complex Key keylist:k;
       +complex Req fsread:r;
       +complex Fsstate fsread:s;
       +complex Req fswrite:r;
       +complex Srv fs;
 (DIR) diff --git a/src/cmd/factotum/fs.c b/src/cmd/factotum/fs.c
       t@@ -0,0 +1,524 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +enum
       +{
       +        Qroot,
       +        Qfactotum,
       +        Qrpc,
       +        Qkeylist,
       +        Qprotolist,
       +        Qconfirm,
       +        Qlog,
       +        Qctl,
       +        Qneedkey,
       +        Qconv,
       +};
       +
       +Qid
       +mkqid(int type, int path)
       +{
       +        Qid q;
       +
       +        q.type = type;
       +        q.path = path;
       +        q.vers = 0;
       +        return q;
       +}
       +
       +static struct
       +{
       +        char *name;
       +        int qidpath;
       +        ulong perm;
       +} dirtab[] = {
       +        /* positions of confirm and needkey known below */
       +        "confirm",                Qconfirm,                0600|DMEXCL,
       +        "needkey",        Qneedkey,        0600|DMEXCL,
       +        "ctl",                        Qctl,                        0600,
       +        "rpc",                Qrpc,                0666,
       +        "proto",                Qprotolist,        0444,
       +        "log",                Qlog,                0600|DMEXCL,
       +        "conv",                Qconv,                0400,
       +};
       +
       +static void
       +fillstat(Dir *dir, char *name, int type, int path, ulong perm)
       +{
       +        dir->name = estrdup(name);
       +        dir->uid = estrdup(owner);
       +        dir->gid = estrdup(owner);
       +        dir->mode = perm;
       +        dir->length = 0;
       +        dir->qid = mkqid(type, path);
       +        dir->atime = time(0);
       +        dir->mtime = time(0);
       +        dir->muid = estrdup("");
       +}
       +
       +static int
       +rootdirgen(int n, Dir *dir, void *v)
       +{
       +        USED(v);
       +
       +        if(n > 0)
       +                return -1;
       +        
       +        fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555);
       +        return 0;
       +}
       +
       +static int
       +fsdirgen(int n, Dir *dir, void *v)
       +{
       +        USED(v);
       +
       +        if(n >= nelem(dirtab))
       +                return -1;
       +        fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm);
       +        return 0;
       +}
       +
       +static char*
       +fswalk1(Fid *fid, char *name, Qid *qid)
       +{
       +        int i;
       +
       +        switch((int)fid->qid.path){
       +        default:
       +                return "fswalk1: cannot happen";
       +        case Qroot:
       +                if(strcmp(name, factname) == 0){
       +                        *qid = mkqid(QTDIR, Qfactotum);
       +                        fid->qid = *qid;
       +                        return nil;
       +                }
       +                if(strcmp(name, "..") == 0){
       +                        *qid = fid->qid;
       +                        return nil;
       +                }
       +                return "not found";
       +        case Qfactotum:
       +                for(i=0; i<nelem(dirtab); i++)
       +                        if(strcmp(name, dirtab[i].name) == 0){
       +                                *qid = mkqid(0, dirtab[i].qidpath);
       +                                fid->qid = *qid;
       +                                return nil;
       +                        }
       +                if(strcmp(name, "..") == 0){
       +                        *qid = mkqid(QTDIR, Qroot);
       +                        fid->qid = *qid;
       +                        return nil;
       +                }
       +                return "not found";
       +        }
       +}
       +
       +static void
       +fsstat(Req *r)
       +{
       +        int i, path;
       +
       +        path = r->fid->qid.path;
       +        switch(path){
       +        case Qroot:
       +                fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR);
       +                break;
       +        case Qfactotum:
       +                fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR);
       +                break;
       +        default:
       +                for(i=0; i<nelem(dirtab); i++)
       +                        if(dirtab[i].qidpath == path){
       +                                fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm);
       +                                goto Break2;
       +                        }
       +                respond(r, "file not found");
       +                break;
       +        }
       +    Break2:
       +        respond(r, nil);
       +}
       +
       +static int
       +readlist(int off, int (*gen)(int, char*, uint), Req *r)
       +{
       +        char *a, *ea;
       +        int n;
       +
       +        a = r->ofcall.data;
       +        ea = a+r->ifcall.count;
       +        for(;;){
       +                n = (*gen)(off, a, ea-a);
       +                if(n == 0){
       +                        r->ofcall.count = a - (char*)r->ofcall.data;
       +                        return off;
       +                }
       +                a += n;
       +                off++;
       +        }
       +        return -1;                /* not reached */
       +}
       +
       +static int
       +keylist(int i, char *a, uint nn)
       +{
       +        int n;
       +        char buf[512];
       +        Key *k;
       +
       +        if(i >= ring.nkey)
       +                return 0;
       +
       +        k = ring.key[i];
       +        k->attr = sortattr(k->attr);
       +        n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr);
       +        if(n >= sizeof(buf)-5)
       +                strcpy(buf+sizeof(buf)-5, "...\n");
       +        n = strlen(buf);
       +        if(n > nn)
       +                return 0;
       +        memmove(a, buf, n);
       +        return n;
       +}
       +
       +static int
       +protolist(int i, char *a, uint n)
       +{
       +        if(prototab[i] == nil)
       +                return 0;
       +        if(strlen(prototab[i]->name)+1 > n)
       +                return 0;
       +        n = strlen(prototab[i]->name)+1;
       +        memmove(a, prototab[i]->name, n-1);
       +        a[n-1] = '\n';
       +        return n;
       +}
       +
       +/* BUG this is O(n^2) to fill in the list */
       +static int
       +convlist(int i, char *a, uint nn)
       +{
       +        Conv *c;
       +        char buf[512];
       +        int n;
       +
       +        for(c=conv; c && i-- > 0; c=c->next)
       +                ;
       +
       +        if(c == nil)
       +                return 0;
       +
       +        if(c->state)
       +                n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr);
       +        else
       +                n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err);
       +
       +        if(n >= sizeof(buf)-5)
       +                strcpy(buf+sizeof(buf)-5, "...\n");
       +        n = strlen(buf);
       +        if(n > nn)
       +                return 0;
       +        memmove(a, buf, n);
       +        return n;
       +}
       +        
       +static void
       +fskickreply(Conv *c)
       +{
       +        Req *r;
       +
       +        if(c->hangup){
       +                if(c->req){
       +                        respond(c->req, "hangup");
       +                        c->req = nil;
       +                }
       +                return;
       +        }
       +
       +        if(!c->req || !c->nreply)
       +                return;
       +
       +        r = c->req;
       +        r->ofcall.count = c->nreply;
       +        r->ofcall.data = c->reply;
       +        if(r->ofcall.count > r->ifcall.count)
       +                r->ofcall.count = r->ifcall.count;
       +        respond(r, nil);
       +        c->req = nil;
       +        c->nreply = 0;
       +}
       +                
       +/*
       + * Some of the file system work happens in the fs proc, but
       + * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in
       + * the main proc so that they can access the various shared
       + * data structures without worrying about locking.
       + */
       +static int inuse[nelem(dirtab)];
       +int *confirminuse = &inuse[0];
       +int *needkeyinuse = &inuse[1];
       +static void
       +fsopen(Req *r)
       +{
       +        int i, *inusep, perm;
       +        static int need[4] = { 4, 2, 6, 1 };
       +        Conv *c;
       +
       +        inusep = nil;
       +        perm = 5;        /* directory */
       +        for(i=0; i<nelem(dirtab); i++)
       +                if(dirtab[i].qidpath == r->fid->qid.path){
       +                        if(dirtab[i].perm & DMEXCL)
       +                                inusep = &inuse[i];
       +                        if(strcmp(r->fid->uid, owner) == 0)
       +                                perm = dirtab[i].perm>>6;
       +                        else
       +                                perm = dirtab[i].perm;
       +                        break;
       +                }
       +
       +        if((r->ifcall.mode&~(OMASK|OTRUNC))
       +        || (need[r->ifcall.mode&3] & ~perm)){
       +                respond(r, "permission denied");
       +                return;
       +        }
       +
       +        if(inusep){
       +                if(*inusep){
       +                        respond(r, "file in use");
       +                        return;
       +                }
       +                *inusep = 1;
       +        }
       +
       +        if(r->fid->qid.path == Qrpc){
       +                if((c = convalloc(r->fid->uid)) == nil){
       +                        char e[ERRMAX];
       +
       +                        rerrstr(e, sizeof e);
       +                        respond(r, e);
       +                        return;
       +                }
       +                c->kickreply = fskickreply;
       +                r->fid->aux = c;
       +        }
       +        
       +        respond(r, nil);
       +}
       +
       +static void
       +fsread(Req *r)
       +{
       +        Conv *c;
       +
       +        switch((int)r->fid->qid.path){
       +        default:
       +                respond(r, "fsread: cannot happen");
       +                break;
       +        case Qroot:
       +                dirread9p(r, rootdirgen, nil);
       +                respond(r, nil);
       +                break;
       +        case Qfactotum:
       +                dirread9p(r, fsdirgen, nil);
       +                respond(r, nil);
       +                break;
       +        case Qrpc:
       +                c = r->fid->aux;
       +                if(c->rpc.op == RpcUnknown){
       +                        respond(r, "no rpc pending");
       +                        break;
       +                }
       +                if(c->req){
       +                        respond(r, "read already pending");
       +                        break;
       +                }
       +                c->req = r;
       +                if(c->nreply)
       +                        (*c->kickreply)(c);
       +                else
       +                        rpcexec(c);
       +                break;
       +        case Qconfirm:
       +                confirmread(r);
       +                break;
       +        case Qlog:
       +                logread(r);
       +                break;
       +        case Qctl:
       +                r->fid->aux = (void*)readlist((int)r->fid->aux, keylist, r);
       +                respond(r, nil);
       +                break;
       +        case Qneedkey:
       +                needkeyread(r);
       +                break;
       +        case Qprotolist:
       +                r->fid->aux = (void*)readlist((int)r->fid->aux, protolist, r);
       +                respond(r, nil);
       +                break;
       +        case Qconv:
       +                r->fid->aux = (void*)readlist((int)r->fid->aux, convlist, r);
       +                respond(r, nil);
       +                break;
       +        }
       +}
       +
       +static void
       +fswrite(Req *r)
       +{
       +        int ret;
       +        char err[ERRMAX], *s;
       +        int (*strfn)(char*);
       +
       +        switch((int)r->fid->qid.path){
       +        default:
       +                respond(r, "fswrite: cannot happen");
       +                break;
       +        case Qrpc:
       +                if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){
       +                        rerrstr(err, sizeof err);
       +                        respond(r, err);
       +                }else{
       +                        r->ofcall.count = r->ifcall.count;
       +                        respond(r, nil);
       +                }
       +                break;
       +        case Qneedkey:
       +                strfn = needkeywrite;
       +                goto string;
       +        case Qctl:
       +                strfn = ctlwrite;
       +                goto string;
       +        case Qconfirm:
       +                strfn = confirmwrite;
       +        string:
       +                s = emalloc(r->ifcall.count+1);
       +                memmove(s, r->ifcall.data, r->ifcall.count);
       +                s[r->ifcall.count] = '\0';
       +                ret = (*strfn)(s);
       +                free(s);
       +                if(ret < 0){
       +                        rerrstr(err, sizeof err);
       +                        respond(r, err);
       +                }else{
       +                        r->ofcall.count = r->ifcall.count;
       +                        respond(r, nil);
       +                }
       +                break;
       +        }
       +}
       +
       +static void
       +fsflush(Req *r)
       +{
       +        confirmflush(r);
       +        logflush(r);
       +}
       +
       +static void
       +fsdestroyfid(Fid *fid)
       +{
       +        if(fid->qid.path == Qrpc){
       +                convhangup(fid->aux);
       +                convclose(fid->aux);
       +        }
       +}
       +
       +static Channel *creq;
       +static Channel *cfid, *cfidr;
       +
       +static void
       +fsreqthread(void *v)
       +{
       +        Req *r;
       +
       +        USED(v);
       +
       +        creq = chancreate(sizeof(Req*), 0);
       +
       +        while((r = recvp(creq)) != nil){
       +                switch(r->ifcall.type){
       +                default:
       +                        respond(r, "bug in fsreqthread");
       +                        break;
       +                case Topen:
       +                        fsopen(r);
       +                        break;
       +                case Tread:
       +                        fsread(r);
       +                        break;
       +                case Twrite:
       +                        fswrite(r);
       +                        break;
       +                case Tflush:
       +                        fsflush(r);
       +                        break;
       +                }
       +        }
       +}
       +
       +static void
       +fsclunkthread(void *v)
       +{
       +        Fid *f;
       +
       +        USED(v);
       +        cfid = chancreate(sizeof(Fid*), 0);
       +        cfidr = chancreate(sizeof(Fid*), 0);
       +
       +        while((f = recvp(cfid)) != nil){
       +                fsdestroyfid(f);
       +                sendp(cfidr, 0);
       +        }
       +}
       +
       +static void
       +fsproc(void *v)
       +{
       +        USED(v);
       +
       +        threadcreate(fsreqthread, nil, STACK);
       +        threadcreate(fsclunkthread, nil, STACK);
       +        threadexits(nil);
       +}
       +
       +static void
       +fsattach(Req *r)
       +{
       +        static int first = 1;
       +
       +        if(first){
       +                proccreate(fsproc, nil, STACK);
       +                first = 0;
       +        }
       +
       +        r->fid->qid = mkqid(QTDIR, Qroot);
       +        r->ofcall.qid = r->fid->qid;
       +        respond(r, nil);
       +}
       +
       +static void
       +fssend(Req *r)
       +{
       +        sendp(creq, r);
       +}
       +
       +static void
       +fssendclunk(Fid *f)
       +{
       +        sendp(cfid, f);
       +        recvp(cfidr);
       +}
       +
       +Srv fs = {
       +.attach=        fsattach,
       +.walk1=        fswalk1,
       +.open=        fssend,
       +.read=        fssend,
       +.write=        fssend,
       +.stat=        fsstat,
       +.flush=        fssend,
       +.destroyfid=        fssendclunk,
       +};
       +
 (DIR) diff --git a/src/cmd/factotum/guide b/src/cmd/factotum/guide
       t@@ -0,0 +1,3 @@
       +kill 8.out|rc
       +unmount /srv/factotum /mnt
       +8.out
 (DIR) diff --git a/src/cmd/factotum/guide2 b/src/cmd/factotum/guide2
       t@@ -0,0 +1,6 @@
       +kill 8.out|rc
       +unmount /mnt/factotum
       +8.out -m /mnt/factotum
       +cat /mnt/factotum/log &
       +unmount /factotum
       +bind 8.out /factotum
 (DIR) diff --git a/src/cmd/factotum/key.c b/src/cmd/factotum/key.c
       t@@ -0,0 +1,190 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +Ring ring;
       +
       +Key*
       +keylookup(char *fmt, ...)
       +{
       +        int i;
       +        Attr *a;
       +        Key *k;
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        a = parseattrfmtv(fmt, arg);
       +        va_end(arg);
       +
       +        for(i=0; i<ring.nkey; i++){
       +                k = ring.key[i];
       +                if(matchattr(a, k->attr, k->privattr)){
       +                        k->ref++;
       +                        freeattr(a);
       +                        return k;
       +                }
       +        }
       +        freeattr(a);
       +        werrstr("no key found");
       +        return nil;
       +}
       +
       +Key*
       +keyfetch(Conv *c, char *fmt, ...)
       +{
       +        int i, tag;
       +        Attr *a;
       +        Key *k;
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        a = parseattrfmtv(fmt, arg);
       +        va_end(arg);
       +
       +        tag = 0;
       +
       +        for(i=0; i<ring.nkey; i++){
       +                k = ring.key[i];
       +                if(tag < k->tag)
       +                        tag = k->tag;
       +                if(matchattr(a, k->attr, k->privattr)){
       +                        k->ref++;
       +                        if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){
       +                                k->ref--;
       +                                continue;
       +                        }
       +                        freeattr(a);
       +                        return k;
       +                }
       +        }
       +
       +        if(needkey(c, a) < 0)
       +                convneedkey(c, a);
       +
       +        for(i=0; i<ring.nkey; i++){
       +                k = ring.key[i];
       +                if(k->tag <= tag)
       +                        continue;
       +                if(matchattr(a, k->attr, k->privattr)){
       +                        k->ref++;
       +                        if(strfindattr(k->attr, "confirm") && confirmkey(c, k) != 1){
       +                                k->ref--;
       +                                continue;
       +                        }
       +                        freeattr(a);
       +                        return k;
       +                }
       +        }
       +        freeattr(a);
       +        werrstr("no key found");
       +        return nil;
       +}
       +
       +static int taggen;
       +
       +void
       +keyadd(Key *k)
       +{
       +        int i;
       +
       +        k->ref++;
       +        k->tag = ++taggen;
       +        for(i=0; i<ring.nkey; i++){
       +                if(matchattr(k->attr, ring.key[i]->attr, nil)
       +                && matchattr(ring.key[i]->attr, k->attr, nil)){
       +                        keyclose(ring.key[i]);
       +                        ring.key[i] = k;
       +                        return;
       +                }
       +        }
       +
       +        ring.key = erealloc(ring.key, (ring.nkey+1)*sizeof(ring.key[0]));
       +        ring.key[ring.nkey++] = k;
       +}
       +
       +void
       +keyclose(Key *k)
       +{
       +        if(k == nil)
       +                return;
       +
       +        if(--k->ref > 0)
       +                return;
       +
       +        if(k->proto->closekey)
       +                (*k->proto->closekey)(k);
       +
       +        freeattr(k->attr);
       +        freeattr(k->privattr);
       +        free(k);
       +}
       +
       +Key*
       +keyreplace(Conv *c, Key *k, char *fmt, ...)
       +{
       +        Key *kk;
       +        char *msg;
       +        Attr *a, *b, *bp;
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        msg = vsmprint(fmt, arg);
       +        if(msg == nil)
       +                sysfatal("out of memory");
       +        va_end(arg);
       +
       +        /* replace prompted values with prompts */        
       +        a = copyattr(k->attr);
       +        bp = parseattr(k->proto->keyprompt);
       +        for(b=bp; b; b=b->next){
       +                a = delattr(a, b->name);
       +                a = addattr(a, "%q?", b->name);
       +        }
       +        freeattr(bp);
       +
       +        if(badkey(c, k, msg, a) < 0)
       +                convbadkey(c, k, msg, a);
       +        kk = keylookup("%A", a);
       +        freeattr(a);
       +        keyclose(k);
       +        if(kk == k){
       +                keyclose(kk);
       +                werrstr("%s", msg);
       +                return nil;
       +        }
       +
       +        if(strfindattr(kk->attr, "confirm")){
       +                if(confirmkey(c, kk) != 1){
       +                        werrstr("key use not confirmed");
       +                        keyclose(kk);
       +                        return nil;
       +                }
       +        }
       +        return kk;
       +}
       +
       +void
       +keyevict(Conv *c, Key *k, char *fmt, ...)
       +{
       +        char *msg;
       +        Attr *a, *b, *bp;
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        msg = vsmprint(fmt, arg);
       +        if(msg == nil)
       +                sysfatal("out of memory");
       +        va_end(arg);
       +
       +        /* replace prompted values with prompts */        
       +        a = copyattr(k->attr);
       +        bp = parseattr(k->proto->keyprompt);
       +        for(b=bp; b; b=b->next){
       +                a = delattr(a, b->name);
       +                a = addattr(a, "%q?", b->name);
       +        }
       +        freeattr(bp);
       +
       +        if(badkey(c, k, msg, nil) < 0)
       +                convbadkey(c, k, msg, nil);
       +        keyclose(k);
       +}
 (DIR) diff --git a/src/cmd/factotum/log.c b/src/cmd/factotum/log.c
       t@@ -0,0 +1,121 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +void
       +lbkick(Logbuf *lb)
       +{
       +        char *s;
       +        int n;
       +        Req *r;
       +
       +        while(lb->wait && lb->rp != lb->wp){
       +                r = lb->wait;
       +                lb->wait = r->aux;
       +                if(lb->wait == nil)
       +                        lb->waitlast = &lb->wait;
       +                r->aux = nil;
       +                if(r->ifcall.count < 5){
       +                        respond(r, "factotum: read request count too short");
       +                        continue;
       +                }
       +                s = lb->msg[lb->rp];
       +                lb->msg[lb->rp] = nil;
       +                if(++lb->rp == nelem(lb->msg))
       +                        lb->rp = 0;
       +                n = r->ifcall.count;
       +                if(n < strlen(s)+1+1){
       +                        memmove(r->ofcall.data, s, n-5);
       +                        n -= 5;
       +                        r->ofcall.data[n] = '\0';
       +                        /* look for first byte of UTF-8 sequence by skipping continuation bytes */
       +                        while(n>0 && (r->ofcall.data[--n]&0xC0)==0x80)
       +                                ;
       +                        strcpy(r->ofcall.data+n, "...\n");
       +                }else{
       +                        strcpy(r->ofcall.data, s);
       +                        strcat(r->ofcall.data, "\n");
       +                }
       +                r->ofcall.count = strlen(r->ofcall.data);
       +                free(s);
       +                respond(r, nil);
       +        }
       +}
       +
       +void
       +lbread(Logbuf *lb, Req *r)
       +{
       +        if(lb->waitlast == nil)
       +                lb->waitlast = &lb->wait;
       +        *(lb->waitlast) = r;
       +        lb->waitlast = (Req**)&r->aux;
       +        r->aux = nil;
       +        lbkick(lb);
       +}
       +
       +void
       +lbflush(Logbuf *lb, Req *r)
       +{
       +        Req **l;
       +
       +        for(l=&lb->wait; *l; l=(Req**)&(*l)->aux){
       +                if(*l == r){
       +                        *l = r->aux;
       +                        r->aux = nil;
       +                        if(*l == nil)
       +                                lb->waitlast = l;
       +                        closereq(r);
       +                        break;
       +                }
       +        }
       +}
       +
       +void
       +lbappend(Logbuf *lb, char *fmt, ...)
       +{
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        lbvappend(lb, fmt, arg);
       +        va_end(arg);
       +}
       +
       +void
       +lbvappend(Logbuf *lb, char *fmt, va_list arg)
       +{
       +        char *s;
       +
       +        s = smprint(fmt, arg);
       +        if(s == nil)
       +                sysfatal("out of memory");
       +        if(lb->msg[lb->wp])
       +                free(lb->msg[lb->wp]);
       +        lb->msg[lb->wp] = s;
       +        if(++lb->wp == nelem(lb->msg))
       +                lb->wp = 0;
       +        lbkick(lb);
       +}
       +
       +Logbuf logbuf;
       +
       +void
       +logread(Req *r)
       +{
       +        lbread(&logbuf, r);
       +}
       +
       +void
       +logflush(Req *r)
       +{
       +        lbflush(&logbuf, r);
       +}
       +
       +void
       +flog(char *fmt, ...)
       +{
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        lbvappend(&logbuf, fmt, arg);
       +        va_end(arg);
       +}
       +
 (DIR) diff --git a/src/cmd/factotum/main.c b/src/cmd/factotum/main.c
       t@@ -0,0 +1,163 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +int debug;
       +char *factname = "factotum";
       +char *service = nil;
       +char *owner;
       +char *authaddr;
       +void gflag(char*);
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: factotum [-Dd] [-a authaddr] [-m mtpt]\n");
       +        fprint(2, " or   factotum -g keypattern\n");
       +        fprint(2, " or   factotum -g 'badkeyattr\nmsg\nkeypattern'");
       +        exits("usage");
       +}
       +
       +void
       +threadmain(int argc, char *argv[])
       +{
       +        char *mtpt;
       +
       +        mtpt = "/mnt";
       +        owner = getuser();
       +        quotefmtinstall();
       +        fmtinstall('A', attrfmt);
       +        fmtinstall('H', encodefmt);
       +        fmtinstall('N', attrnamefmt);
       +
       +        if(argc == 3 && strcmp(argv[1], "-g") == 0){
       +                gflag(argv[2]);
       +                exits(nil);
       +        }
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        case 'D':
       +                chatty9p++;
       +                break;
       +        case 'a':
       +                authaddr = EARGF(usage());
       +                break;
       +        case 'g':
       +                usage();
       +        case 'm':
       +                mtpt = EARGF(usage());
       +                break;
       +        case 's':
       +                service = EARGF(usage());
       +                break;
       +        }ARGEND
       +
       +        if(argc != 0)
       +                usage();
       +
       +        threadpostmountsrv(&fs, service, mtpt, MBEFORE);
       +        threadexits(nil);
       +}
       +
       +/*
       + *  prompt user for a key.  don't care about memory leaks, runs standalone
       + */
       +static Attr*
       +promptforkey(int fd, char *params)
       +{
       +        char *v;
       +        Attr *a, *attr;
       +        char *def;
       +
       +        attr = _parseattr(params);
       +        fprint(fd, "!adding key:");
       +        for(a=attr; a; a=a->next)
       +                if(a->type != AttrQuery && a->name[0] != '!')
       +                        fprint(fd, " %q=%q", a->name, a->val);
       +        fprint(fd, "\n");
       +
       +        for(a=attr; a; a=a->next){
       +                v = a->name;
       +                if(a->type != AttrQuery || v[0]=='!')
       +                        continue;
       +                def = nil;
       +                if(strcmp(v, "user") == 0)
       +                        def = getuser();
       +                a->val = readcons(v, def, 0);
       +                if(a->val == nil)
       +                        sysfatal("user terminated key input");
       +                a->type = AttrNameval;
       +        }
       +        for(a=attr; a; a=a->next){
       +                v = a->name;
       +                if(a->type != AttrQuery || v[0]!='!')
       +                        continue;
       +                def = nil;
       +                if(strcmp(v+1, "user") == 0)
       +                        def = getuser();
       +                a->val = readcons(v+1, def, 1);
       +                if(a->val == nil)
       +                        sysfatal("user terminated key input");
       +                a->type = AttrNameval;
       +        }
       +        fprint(fd, "!\n");
       +        close(fd);
       +        return attr;
       +}
       +
       +/*
       + *  send a key to the mounted factotum
       + */
       +static int
       +sendkey(Attr *attr)
       +{
       +        int fd, rv;
       +        char buf[1024];
       +
       +        fd = open("/mnt/factotum/ctl", ORDWR);
       +        if(fd < 0)
       +                sysfatal("opening /mnt/factotum/ctl: %r");
       +        rv = fprint(fd, "key %A\n", attr);
       +        read(fd, buf, sizeof buf);
       +        close(fd);
       +        return rv;
       +}
       +
       +static void
       +askuser(int fd, char *params)
       +{
       +        Attr *attr;
       +
       +        attr = promptforkey(fd, params);
       +        if(attr == nil)
       +                sysfatal("no key supplied");
       +        if(sendkey(attr) < 0)
       +                sysfatal("sending key to factotum: %r");
       +}
       +
       +void
       +gflag(char *s)
       +{
       +        char *f[4];
       +        int nf;
       +        int fd;
       +
       +        if((fd = open("/dev/cons", ORDWR)) < 0)
       +                sysfatal("open /dev/cons: %r");
       +
       +        nf = getfields(s, f, nelem(f), 0, "\n");
       +        if(nf == 1){        /* needkey or old badkey */
       +                fprint(fd, "\n");
       +                askuser(fd, s);
       +                exits(nil);
       +        }
       +        if(nf == 3){        /* new badkey */
       +                fprint(fd, "\n");
       +                fprint(fd, "!replace: %s\n", f[0]);
       +                fprint(fd, "!because: %s\n", f[1]);
       +                askuser(fd, f[2]);
       +                exits(nil);
       +        }
       +        usage();
       +}
 (DIR) diff --git a/src/cmd/factotum/mkfile b/src/cmd/factotum/mkfile
       t@@ -0,0 +1,34 @@
       +PLAN9=../../..
       +<$PLAN9/src/mkhdr
       +
       +TARG=factotum
       +PROTO=\
       +        apop.$O\
       +        chap.$O\
       +        p9any.$O\
       +        p9sk1.$O\
       +
       +OFILES=\
       +        $PROTO\
       +        attr.$O\
       +        confirm.$O\
       +        conv.$O\
       +        ctl.$O\
       +        fs.$O\
       +        key.$O\
       +        log.$O\
       +        main.$O\
       +        plan9.$O\
       +        proto.$O\
       +        rpc.$O\
       +        util.$O\
       +        xio.$O\
       +
       +HFILES=dat.h
       +
       +SHORTLIB=auth authsrv sec mp 9p thread 9
       +<$PLAN9/src/mkone
       +
       +$O.test: test.$O
       +        $LD -o $target $prereq
       +
 (DIR) diff --git a/src/cmd/factotum/p9any.c b/src/cmd/factotum/p9any.c
       t@@ -0,0 +1,266 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +/*
       + * p9any - protocol negotiator
       + *
       + * Protocol:
       + *        S->C: v.2 proto@dom proto@dom proto@dom... NUL
       + *        C->S: proto dom NUL
       + *        [negotiated proto continues]
       + */
       +
       +static Proto* okproto[] =
       +{
       +        &p9sk1,
       +        nil,
       +};
       +
       +static int
       +rolecall(Role *r, char *name, Conv *c)
       +{
       +        for(; r->name; r++)
       +                if(strcmp(r->name, name) == 0)
       +                        return (*r->fn)(c);
       +        werrstr("unknown role");
       +        return -1;
       +}
       +
       +static int
       +hasnul(void *v, int n)
       +{
       +        char *c;
       +
       +        c = v;
       +        if(n > 0 && c[n-1] == '\0')
       +                return n;
       +        else
       +                return n+1;
       +}
       +
       +static int
       +p9anyserver(Conv *c)
       +{
       +        char *s, *dom;
       +        int i, j, n, m, ret;
       +        char *tok[3];
       +        Attr *attr;
       +        Key *k;
       +
       +        ret = -1;
       +        s = estrdup("v.2");
       +        n = 0;
       +        attr = delattr(copyattr(c->attr), "proto");
       +
       +        for(i=0; i<ring.nkey; i++){
       +                k = ring.key[i];
       +                for(j=0; okproto[j]; j++)
       +                        if(k->proto == okproto[j]
       +                        && (dom = strfindattr(k->attr, "dom")) != nil
       +                        && matchattr(attr, k->attr, k->privattr)){
       +                                s = estrappend(s, " %s@%s", k->proto->name, dom);
       +                                n++;
       +                        }
       +        }
       +
       +        if(n == 0){
       +                werrstr("no valid keys");
       +                goto out;
       +        }
       +
       +        c->state = "write offer";
       +        if(convwrite(c, s, strlen(s)+1) < 0)
       +                goto out;
       +        free(s);
       +        s = nil;
       +
       +        c->state = "read choice";
       +        if(convreadfn(c, hasnul, &s) < 0)
       +                goto out;
       +
       +        m = tokenize(s, tok, nelem(tok));
       +        if(m != 2){
       +                werrstr("bad protocol message");
       +                goto out;
       +        }
       +
       +        for(i=0; okproto[i]; i++)
       +                if(strcmp(okproto[i]->name, tok[0]) == 0)
       +                        break;
       +        if(!okproto[i]){
       +                werrstr("bad chosen protocol %q", tok[0]);
       +                goto out;
       +        }
       +
       +        c->state = "write ok";
       +        if(convwrite(c, "OK\0", 3) < 0)
       +                goto out;
       +
       +        c->state = "start choice";
       +        attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
       +        free(c->attr);
       +        c->attr = attr;
       +        attr = nil;
       +        c->proto = okproto[i];
       +
       +        if(rolecall(c->proto->roles, "server", c) < 0){
       +                werrstr("%s: %r", tok[0]);
       +                goto out;
       +        }
       +
       +        ret = 0;
       +        
       +out:
       +        free(s);
       +        freeattr(attr);
       +        return ret;
       +}
       +
       +static int
       +p9anyclient(Conv *c)
       +{
       +        char *s, **f, *tok[20], ok[3], *q, *user, *dom;
       +        int i, n, ret, version;
       +        Key *k;
       +        Attr *attr;
       +        Proto *p;
       +
       +        ret = -1;
       +        s = nil;
       +        k = nil;
       +
       +        user = strfindattr(c->attr, "user");
       +        dom = strfindattr(c->attr, "dom");
       +
       +        /*
       +         * if the user is the factotum owner, any key will do.
       +         * if not, then if we have a speakfor key,
       +         * we will only vouch for the user's local identity.
       +         *
       +         * this logic is duplicated in p9sk1.c
       +         */
       +        attr = delattr(copyattr(c->attr), "role");
       +        attr = delattr(attr, "proto");
       +        if(strcmp(c->sysuser, owner) == 0)
       +                attr = addattr(attr, "role=client");
       +        else if(user==nil || strcmp(c->sysuser, user)==0){
       +                attr = delattr(attr, "user");
       +                attr = addattr(attr, "role=speakfor");
       +        }else{
       +                werrstr("will not authenticate for %q as %q", c->sysuser, user);
       +                goto out;
       +        }
       +
       +        c->state = "read offer";
       +        if(convreadfn(c, hasnul, &s) < 0)
       +                goto out;
       +
       +        c->state = "look for keys";
       +        n = tokenize(s, tok, nelem(tok));
       +        f = tok;
       +        version = 1;
       +        if(n > 0 && memcmp(f[0], "v.", 2) == 0){
       +                version = atoi(f[0]+2);
       +                if(version != 2){
       +                        werrstr("unknown p9any version: %s", f[0]);
       +                        goto out;
       +                }
       +                f++;
       +                n--;
       +        }
       +
       +        /* look for keys that don't need confirmation */
       +        for(i=0; i<n; i++){
       +                if((q = strchr(f[i], '@')) == nil)
       +                        continue;
       +                if(dom && strcmp(q+1, dom) != 0)
       +                        continue;
       +                *q++ = '\0';
       +                if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
       +                && strfindattr(k->attr, "confirm") == nil)
       +                        goto found;
       +                *--q = '@';
       +        }
       +
       +        /* look for any keys at all */
       +        for(i=0; i<n; i++){
       +                if((q = strchr(f[i], '@')) == nil)
       +                        continue;
       +                if(dom && strcmp(q+1, dom) != 0)
       +                        continue;
       +                *q++ = '\0';
       +                if(k = keyfetch(c, "%A proto=%q dom=%q", attr, f[i], q))
       +                        goto found;
       +                *--q = '@';
       +        }
       +
       +        /* ask for new keys */
       +        c->state = "ask for keys";
       +        for(i=0; i<n; i++){
       +                if((q = strchr(f[i], '@')) == nil)
       +                        continue;
       +                if(dom && strcmp(q+1, dom) != 0)
       +                        continue;
       +                *q++ = '\0';
       +                p = protolookup(f[i]);
       +                if(p == nil || p->keyprompt == nil){
       +                        *--q = '@';
       +                        continue;
       +                }
       +                if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
       +                        goto found;
       +                *--q = '@';
       +        }
       +
       +        /* nothing worked */
       +        werrstr("unable to find common key");
       +        goto out;
       +
       +found:
       +        /* f[i] is the chosen protocol, q the chosen domain */
       +        attr = addattr(attr, "proto=%q dom=%q", f[i], q);
       +        c->state = "write choice";
       +        /* have a key: go for it */
       +        if(convprint(c, "%q %q", f[i], q) < 0
       +        || convwrite(c, "\0", 1) < 0)
       +                goto out;
       +
       +        if(version == 2){
       +                c->state = "read ok";
       +                if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
       +                        goto out;
       +        }
       +
       +        c->state = "start choice";
       +        c->proto = protolookup(f[i]);
       +        freeattr(c->attr);
       +        c->attr = attr;
       +        attr = nil;
       +
       +        if(rolecall(c->proto->roles, "client", c) < 0){
       +                werrstr("%s: %r", c->proto->name);
       +                goto out;
       +        }
       +
       +        ret = 0;
       +
       +out:
       +        keyclose(k);
       +        freeattr(attr);
       +        free(s);
       +        return ret;
       +}
       +
       +static Role
       +p9anyroles[] = 
       +{
       +        "client",        p9anyclient,
       +        "server",        p9anyserver,
       +        0
       +};
       +
       +Proto p9any = {
       +.name=                "p9any",
       +.roles=                p9anyroles,
       +};
       +
 (DIR) diff --git a/src/cmd/factotum/p9cr.c b/src/cmd/factotum/p9cr.c
       t@@ -0,0 +1,545 @@
       +/*
       + * p9cr, vnc - one-sided challenge/response authentication
       + *
       + * Protocol:
       + *
       + *        C -> S: user
       + *        S -> C: challenge
       + *        C -> S: response
       + *        S -> C: ok or bad
       + *
       + * Note that this is the protocol between factotum and the local
       + * program, not between the two factotums.  The information 
       + * exchanged here is wrapped in other protocols by the local
       + * programs.
       + */
       +
       +#include "std.h"
       +#include "dat.h"
       +
       +static int
       +p9crcheck(Key *k)
       +{
       +        if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
       +                werrstr("need user and !password attributes");
       +                return -1;
       +        }
       +        return 0;
       +}
       +
       +static int
       +p9crclient(Conv *c)
       +{
       +        char *chal, *pw, *res, *user;
       +        int astype, nchal, npw, ntry, ret;
       +        uchar resp[MD5dlen];
       +        Attr *attr;
       +        DigestState *ds;
       +        Key *k;
       +        
       +        chal = nil;
       +        k = nil;
       +        res = nil;
       +        ret = -1;
       +        attr = c->attr;
       +
       +        if(c->proto == &p9cr){
       +                astype = AuthChal;
       +                challen = NETCHLEN;
       +        }else if(c->proto == &vnc){
       +                astype = AuthVnc;
       +                challen = MAXCHAL;
       +        }else{
       +                werrstr("bad proto");
       +                goto out;
       +        }
       +
       +        c->state = "find key";
       +        k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
       +        if(k == nil)
       +                goto out;
       +
       +        for(ntry=1;; ntry++){
       +                if(c->attr != attr)
       +                        freeattr(c->attr);
       +                c->attr = addattrs(copyattr(attr), k->attr);
       +                if((pw = strfindattr(k->privattr, "!password")) == nil){
       +                        werrstr("key has no !password (cannot happen)");
       +                        goto out;
       +                }
       +                npw = strlen(pw);
       +
       +                if((user = strfindattr(k->attr, "user")) == nil){
       +                        werrstr("key has no user (cannot happen)");
       +                        goto out;
       +                }
       +
       +                if(convprint(c, "%s", user) < 0)
       +                        goto out;
       +
       +                if(convreadm(c, &chal) < 0)
       +                        goto out;
       +
       +                if((nresp = (*response)(chal, resp)) < 0)
       +                        goto out;
       +
       +                if(convwrite(c, resp, nresp) < 0)
       +                        goto out;
       +
       +                if(convreadm(c, &res) < 0)
       +                        goto out;
       +
       +                if(strcmp(res, "ok") == 0)
       +                        break;
       +
       +                if((k = keyreplace(c, k, "%s", res)) == nil){
       +                        c->state = "auth failed";
       +                        werrstr("%s", res);
       +                        goto out;
       +                }
       +        }
       +
       +        werrstr("succeeded");
       +        ret = 0;
       +
       +out:
       +        keyclose(k);
       +        free(chal);
       +        if(c->attr != attr)
       +                freeattr(attr);
       +        return ret;
       +}
       +
       +static int
       +p9crserver(Conv *c)
       +{
       +        char chal[APOPCHALLEN], *user, *resp;
       +        ServerState s;
       +        int astype, ret;
       +        Attr *a;
       +
       +        ret = -1;
       +        user = nil;
       +        resp = nil;
       +        memset(&s, 0, sizeof s);
       +        s.asfd = -1;
       +
       +        if(c->proto == &apop)
       +                astype = AuthApop;
       +        else if(c->proto == &cram)
       +                astype = AuthCram;
       +        else{
       +                werrstr("bad proto");
       +                goto out;
       +        }
       +
       +        c->state = "find key";
       +        if((s.k = plan9authkey(c->attr)) == nil)
       +                goto out;
       +
       +        a = copyattr(s.k->attr);
       +        a = delattr(a, "proto");
       +        c->attr = addattrs(c->attr, a);
       +        freeattr(a);
       +
       +        c->state = "authdial";
       +        s.hostid = strfindattr(s.k->attr, "user");
       +        s.dom = strfindattr(s.k->attr, "dom");
       +        if((s.asfd = xioauthdial(nil, s.dom)) < 0){
       +                werrstr("authdial %s: %r", s.dom);
       +                goto out;
       +        }
       +
       +        c->state = "authchal";
       +        if(p9crchal(&s, astype, chal) < 0)
       +                goto out;
       +
       +        c->state = "write challenge";
       +        if(convprint(c, "%s", chal) < 0)
       +                goto out;
       +
       +        for(;;){
       +                c->state = "read user";
       +                if(convreadm(c, &user) < 0)
       +                        goto out;
       +
       +                c->state = "read response";
       +                if(convreadm(c, &resp) < 0)
       +                        goto out;
       +
       +                c->state = "authwrite";
       +                switch(apopresp(&s, user, resp)){
       +                case -1:
       +                        goto out;
       +                case 0:
       +                        c->state = "write status";
       +                        if(convprint(c, "bad authentication failed") < 0)
       +                                goto out;
       +                        break;
       +                case 1:
       +                        c->state = "write status";
       +                        if(convprint(c, "ok") < 0)
       +                                goto out;
       +                        goto ok;
       +                }
       +                free(user);
       +                free(resp);
       +                user = nil;
       +                resp = nil;
       +        }
       +
       +ok:
       +        ret = 0;
       +        c->attr = addcap(c->attr, c->sysuser, &s.t);
       +
       +out:
       +        keyclose(s.k);
       +        free(user);
       +        free(resp);
       +//        xioclose(s.asfd);
       +        return ret;
       +}
       +
       +enum
       +{
       +        MAXCHAL = 64,
       +};
       +
       +typedef struct State State;
       +struct State
       +{
       +        Key        *key;
       +        int        astype;
       +        int        asfd;
       +        Ticket        t;
       +        Ticketreq tr;
       +        char        chal[MAXCHAL];
       +        int        challen;
       +        char        resp[MAXCHAL];
       +        int        resplen;
       +};
       +
       +enum
       +{
       +        CNeedChal,
       +        CHaveResp,
       +
       +        SHaveChal,
       +        SNeedResp,
       +
       +        Maxphase,
       +};
       +
       +static char *phasenames[Maxphase] =
       +{
       +[CNeedChal]        "CNeedChal",
       +[CHaveResp]        "CHaveResp",
       +
       +[SHaveChal]        "SHaveChal",
       +[SNeedResp]        "SNeedResp",
       +};
       +
       +static void
       +p9crclose(Fsstate *fss)
       +{
       +        State *s;
       +
       +        s = fss->ps;
       +        if(s->asfd >= 0){
       +                close(s->asfd);
       +                s->asfd = -1;
       +        }
       +        free(s);
       +}
       +
       +static int getchal(State*, Fsstate*);
       +
       +static int
       +p9crinit(Proto *p, Fsstate *fss)
       +{
       +        int iscli, ret;
       +        char *user;
       +        State *s;
       +        Attr *attr;
       +
       +        if((iscli = isclient(_str_findattr(fss->attr, "role"))) < 0)
       +                return failure(fss, nil);
       +        
       +        s = emalloc(sizeof(*s));
       +        s->asfd = -1;
       +        if(p == &p9cr){
       +                s->astype = AuthChal;
       +                s->challen = NETCHLEN;
       +        }else if(p == &vnc){
       +                s->astype = AuthVNC;
       +                s->challen = Maxchal;
       +        }else
       +                abort();
       +
       +        if(iscli){
       +                fss->phase = CNeedChal;
       +                if(p == &p9cr)
       +                        attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
       +                else
       +                        attr = nil;
       +                ret = findkey(&s->key, fss, Kuser, 0, attr ? attr : fss->attr,
       +                        "role=client %s", p->keyprompt);
       +                _freeattr(attr);
       +                if(ret != RpcOk){
       +                        free(s);
       +                        return ret;
       +                }
       +                fss->ps = s;
       +        }else{
       +                if((ret = findp9authkey(&s->key, fss)) != RpcOk){
       +                        free(s);
       +                        return ret;
       +                }
       +                if((user = _str_findattr(fss->attr, "user")) == nil){
       +                        free(s);
       +                        return failure(fss, "no user name specified in start msg");
       +                }
       +                if(strlen(user) >= sizeof s->tr.uid){
       +                        free(s);
       +                        return failure(fss, "user name too long");
       +                }
       +                fss->ps = s;
       +                strcpy(s->tr.uid, user);
       +                ret = getchal(s, fss);
       +                if(ret != RpcOk){
       +                        p9crclose(fss);        /* frees s */
       +                        fss->ps = nil;
       +                }
       +        }
       +        fss->phasename = phasenames;
       +        fss->maxphase = Maxphase;
       +        return ret;
       +}
       +
       +static int
       +p9crread(Fsstate *fss, void *va, uint *n)
       +{
       +        int m;
       +        State *s;
       +
       +        s = fss->ps;
       +        switch(fss->phase){
       +        default:
       +                return phaseerror(fss, "read");
       +
       +        case CHaveResp:
       +                if(s->resplen < *n)
       +                        *n = s->resplen;
       +                memmove(va, s->resp, *n);
       +                fss->phase = Established;
       +                return RpcOk;
       +
       +        case SHaveChal:
       +                if(s->astype == AuthChal)
       +                        m = strlen(s->chal);        /* ascii string */
       +                else
       +                        m = s->challen;                /* fixed length binary */
       +                if(m > *n)
       +                        return toosmall(fss, m);
       +                *n = m;
       +                memmove(va, s->chal, m);
       +                fss->phase = SNeedResp;
       +                return RpcOk;
       +        }
       +}
       +
       +static int
       +p9response(Fsstate *fss, State *s)
       +{
       +        char key[DESKEYLEN];
       +        uchar buf[8];
       +        ulong chal;
       +        char *pw;
       +
       +        pw = _str_findattr(s->key->privattr, "!password");
       +        if(pw == nil)
       +                return failure(fss, "vncresponse cannot happen");
       +        passtokey(key, pw);
       +        memset(buf, 0, 8);
       +        sprint((char*)buf, "%d", atoi(s->chal));
       +        if(encrypt(key, buf, 8) < 0)
       +                return failure(fss, "can't encrypt response");
       +        chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
       +        s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
       +        return RpcOk;
       +}
       +
       +static uchar tab[256];
       +
       +/* VNC reverses the bits of each byte before using as a des key */
       +static void
       +mktab(void)
       +{
       +        int i, j, k;
       +        static int once;
       +
       +        if(once)
       +                return;
       +        once = 1;
       +
       +        for(i=0; i<256; i++) {
       +                j=i;
       +                tab[i] = 0;
       +                for(k=0; k<8; k++) {
       +                        tab[i] = (tab[i]<<1) | (j&1);
       +                        j >>= 1;
       +                }
       +        }
       +}
       +
       +static int
       +vncaddkey(Key *k)
       +{
       +        uchar *p;
       +        char *s;
       +
       +        k->priv = emalloc(8+1);
       +        if(s = _str_findattr(k->privattr, "!password")){
       +                mktab();
       +                memset(k->priv, 0, 8+1);
       +                strncpy((char*)k->priv, s, 8);
       +                for(p=k->priv; *p; p++)
       +                        *p = tab[*p];
       +        }else{
       +                werrstr("no key data");
       +                return -1;
       +        }
       +        return replacekey(k);
       +}
       +
       +static void
       +vncclosekey(Key *k)
       +{
       +        free(k->priv);
       +}
       +
       +static int
       +vncresponse(Fsstate*, State *s)
       +{
       +        DESstate des;
       +
       +        memmove(s->resp, s->chal, sizeof s->chal);
       +        setupDESstate(&des, s->key->priv, nil);
       +        desECBencrypt((uchar*)s->resp, s->challen, &des);
       +        s->resplen = s->challen;
       +        return RpcOk;
       +}
       +
       +static int
       +p9crwrite(Fsstate *fss, void *va, uint n)
       +{
       +        char tbuf[TICKETLEN+AUTHENTLEN];
       +        State *s;
       +        char *data = va;
       +        Authenticator a;
       +        char resp[Maxchal];
       +        int ret;
       +
       +        s = fss->ps;
       +        switch(fss->phase){
       +        default:
       +                return phaseerror(fss, "write");
       +
       +        case CNeedChal:
       +                if(n >= sizeof(s->chal))
       +                        return failure(fss, Ebadarg);
       +                memset(s->chal, 0, sizeof s->chal);
       +                memmove(s->chal, data, n);
       +                s->challen = n;
       +
       +                if(s->astype == AuthChal)
       +                        ret = p9response(fss, s);
       +                else
       +                        ret = vncresponse(fss, s);
       +                if(ret != RpcOk)
       +                        return ret;
       +                fss->phase = CHaveResp;
       +                return RpcOk;
       +
       +        case SNeedResp:
       +                /* send response to auth server and get ticket */
       +                if(n > sizeof(resp))
       +                        return failure(fss, Ebadarg);
       +                memset(resp, 0, sizeof resp);
       +                memmove(resp, data, n);
       +                if(write(s->asfd, resp, s->challen) != s->challen)
       +                        return failure(fss, Easproto);
       +
       +                /* get ticket plus authenticator from auth server */
       +                if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0)
       +                        return failure(fss, nil);
       +
       +                /* check ticket */
       +                convM2T(tbuf, &s->t, s->key->priv);
       +                if(s->t.num != AuthTs
       +                || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0)
       +                        return failure(fss, Easproto);
       +                convM2A(tbuf+TICKETLEN, &a, s->t.key);
       +                if(a.num != AuthAc
       +                || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
       +                || a.id != 0)
       +                        return failure(fss, Easproto);
       +
       +                fss->haveai = 1;
       +                fss->ai.cuid = s->t.cuid;
       +                fss->ai.suid = s->t.suid;
       +                fss->ai.nsecret = 0;
       +                fss->ai.secret = nil;
       +                fss->phase = Established;
       +                return RpcOk;
       +        }
       +}
       +
       +static int
       +getchal(State *s, Fsstate *fss)
       +{
       +        char trbuf[TICKREQLEN];
       +        int n;
       +
       +        safecpy(s->tr.hostid, _str_findattr(s->key->attr, "user"), sizeof(s->tr.hostid));
       +        safecpy(s->tr.authdom, _str_findattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
       +        s->tr.type = s->astype;
       +        convTR2M(&s->tr, trbuf);
       +
       +        /* get challenge from auth server */
       +        s->asfd = _authdial(nil, _str_findattr(s->key->attr, "dom"));
       +        if(s->asfd < 0)
       +                return failure(fss, Easproto);
       +        if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
       +                return failure(fss, Easproto);
       +        n = _asrdresp(s->asfd, s->chal, s->challen);
       +        if(n <= 0){
       +                if(n == 0)
       +                        werrstr("_asrdresp short read");
       +                return failure(fss, nil);
       +        }
       +        s->challen = n;
       +        fss->phase = SHaveChal;
       +        return RpcOk;
       +}
       +
       +Proto p9cr =
       +{
       +.name=                "p9cr",
       +.init=                p9crinit,
       +.write=                p9crwrite,
       +.read=                p9crread,
       +.close=                p9crclose,
       +.keyprompt=        "user? !password?",
       +};
       +
       +Proto vnc =
       +{
       +.name=                "vnc",
       +.init=                p9crinit,
       +.write=                p9crwrite,
       +.read=                p9crread,
       +.close=                p9crclose,
       +.keyprompt=        "!password?",
       +.addkey=        vncaddkey,
       +};
 (DIR) diff --git a/src/cmd/factotum/p9sk1.c b/src/cmd/factotum/p9sk1.c
       t@@ -0,0 +1,352 @@
       +/*
       + * p9sk1, p9sk2 - Plan 9 secret (private) key authentication.
       + * p9sk2 is an incomplete flawed variant of p9sk1.
       + *
       + * Client protocol:
       + *        write challenge[challen]        (p9sk1 only)
       + *        read tickreq[tickreqlen]
       + *        write ticket[ticketlen]
       + *        read authenticator[authentlen]
       + *
       + * Server protocol:
       + *         read challenge[challen]        (p9sk1 only)
       + *        write tickreq[tickreqlen]
       + *        read ticket[ticketlen]
       + *        write authenticator[authentlen]
       + */
       +
       +#include "std.h"
       +#include "dat.h"
       +
       +static int gettickets(Ticketreq*, char*, Key*);
       +
       +#define max(a, b) ((a) > (b) ? (a) : (b))
       +enum
       +{
       +        MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN))
       +};
       +
       +static int
       +p9skclient(Conv *c)
       +{
       +        char *user;
       +        char cchal[CHALLEN];
       +        uchar secret[8];
       +        char buf[MAXAUTH];
       +        int speakfor, ret;
       +        Attr *a;
       +        Authenticator au;
       +        Key *k;
       +        Ticket t;
       +        Ticketreq tr;
       +
       +        ret = -1;
       +        a = nil;
       +        k = nil;
       +
       +        /* p9sk1: send client challenge */
       +        if(c->proto == &p9sk1){
       +                c->state = "write challenge";
       +                memrandom(cchal, CHALLEN);
       +                if(convwrite(c, cchal, CHALLEN) < 0)
       +                        goto out;
       +        }
       +
       +        /* read ticket request */
       +        c->state = "read tickreq";
       +        if(convread(c, buf, TICKREQLEN) < 0)
       +                goto out;
       +        convM2TR(buf, &tr);
       +
       +        /* p9sk2: use server challenge as client challenge */
       +        if(c->proto == &p9sk2)
       +                memmove(cchal, tr.chal, CHALLEN);
       +
       +        /*
       +         * find a key.
       +         *
       +         * if the user is the factotum owner, any key will do.
       +         * if not, then if we have a speakfor key,
       +         * we will only vouch for the user's local identity.
       +         *
       +         * this logic is duplicated in p9any.c
       +         */
       +        user = strfindattr(c->attr, "user");
       +        a = delattr(copyattr(c->attr), "role");
       +        a = addattr(a, "proto=p9sk1");
       +
       +        if(strcmp(c->sysuser, owner) == 0){
       +                speakfor = 0;
       +                a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom);
       +        }else if(user==nil || strcmp(c->sysuser, user)==0){
       +                speakfor = 1;
       +                a = delattr(a, "user");
       +                a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom);
       +        }else{
       +                werrstr("will not authenticate for %q as %q", c->sysuser, user);
       +                goto out;
       +        }
       +
       +        for(;;){
       +                c->state = "find key";
       +                k = keyfetch(c, "%A", a);
       +                if(k == nil)
       +                        goto out;
       +                
       +                /* relay ticket request to auth server, get tickets */
       +                strcpy(tr.hostid, strfindattr(k->attr, "user"));
       +                if(speakfor)
       +                        strcpy(tr.uid, c->sysuser);
       +                else
       +                        strcpy(tr.uid, tr.hostid);
       +
       +                c->state = "get tickets";
       +                if(gettickets(&tr, buf, k) < 0)
       +                        goto out;
       +
       +                convM2T(buf, &t, k->priv);
       +                if(t.num == AuthTc)
       +                        break;
       +
       +                /* we don't agree with the auth server about the key; try again */
       +                c->state = "replace key";
       +                if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){
       +                        werrstr("key mismatch with auth server");
       +                        goto out;
       +                }
       +        }
       +
       +        /* send second ticket and authenticator to server */
       +        c->state = "write ticket+auth";
       +        memmove(buf, buf+TICKETLEN, TICKETLEN);
       +        au.num = AuthAc;
       +        memmove(au.chal, tr.chal, CHALLEN);
       +        au.id = 0;
       +        convA2M(&au, buf+TICKETLEN, t.key);
       +        if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0)
       +                goto out;
       +
       +        /* read authenticator from server */
       +        c->state = "read auth";
       +        if(convread(c, buf, AUTHENTLEN) < 0)
       +                goto out;
       +        convM2A(buf, &au, t.key);
       +        if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){
       +                werrstr("server lies through his teeth");
       +                goto out;
       +        }
       +
       +        /* success */
       +        c->attr = addcap(c->attr, c->sysuser, &t);
       +        des56to64((uchar*)t.key, secret);
       +        c->attr = addattr(c->attr, "secret=%.8H", secret);
       +        ret = 0;
       +
       +out:
       +        freeattr(a);
       +        keyclose(k);
       +        return ret;
       +}
       +
       +static int
       +p9skserver(Conv *c)
       +{
       +        char cchal[CHALLEN], buf[MAXAUTH];
       +        uchar secret[8];
       +        int ret;
       +        Attr *a;
       +        Authenticator au;
       +        Key *k;
       +        Ticketreq tr;
       +        Ticket t;
       +
       +        ret = -1;
       +
       +        a = addattr(copyattr(c->attr), "user? dom?");
       +        a = addattr(a, "user? dom? proto=p9sk1");
       +        if((k = keyfetch(c, "%A", a)) == nil)
       +                goto out;
       +
       +        /* p9sk1: read client challenge */
       +        if(c->proto == &p9sk1){
       +                if(convread(c, cchal, CHALLEN) < 0)
       +                        goto out;
       +        }
       +
       +        /* send ticket request */
       +        memset(&tr, 0, sizeof tr);
       +        tr.type = AuthTreq;
       +        strcpy(tr.authid, strfindattr(k->attr, "user"));
       +        strcpy(tr.authdom, strfindattr(k->attr, "dom"));
       +        memrandom(tr.chal, sizeof tr.chal);
       +        convTR2M(&tr, buf);
       +        if(convwrite(c, buf, TICKREQLEN) < 0)
       +                goto out;
       +
       +        /* p9sk2: use server challenge as client challenge */
       +        if(c->proto == &p9sk2)
       +                memmove(cchal, tr.chal, sizeof tr.chal);
       +
       +        /* read ticket+authenticator */
       +        if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0)
       +                goto out;
       +
       +        convM2T(buf, &t, k->priv);
       +        if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){
       +                /* BUG badkey */
       +                werrstr("key mismatch with auth server");
       +                goto out;
       +        }
       +
       +        convM2A(buf+TICKETLEN, &au, t.key);
       +        if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){
       +                werrstr("client lies through his teeth");
       +                goto out;
       +        }
       +
       +        /* send authenticator */
       +        au.num = AuthAs;
       +        memmove(au.chal, cchal, CHALLEN);
       +        convA2M(&au, buf, t.key);
       +        if(convwrite(c, buf, AUTHENTLEN) < 0)
       +                goto out;
       +
       +        /* success */
       +        c->attr = addcap(c->attr, c->sysuser, &t);
       +        des56to64((uchar*)t.key, secret);
       +        c->attr = addattr(c->attr, "secret=%.8H", secret);
       +        ret = 0;
       +
       +out:
       +        freeattr(a);
       +        keyclose(k);
       +        return ret;
       +}
       +
       +int
       +_asgetticket(int fd, char *trbuf, char *tbuf)
       +{
       +        if(write(fd, trbuf, TICKREQLEN) < 0){
       +                close(fd);
       +                return -1;
       +        }
       +        return _asrdresp(fd, tbuf, 2*TICKETLEN);
       +}
       +static int
       +getastickets(Ticketreq *tr, char *buf)
       +{
       +        int asfd;
       +        int ret;
       +
       +        if((asfd = xioauthdial(nil, tr->authdom)) < 0)
       +                return -1;
       +        convTR2M(tr, buf);
       +        ret = xioasgetticket(asfd, buf, buf);
       +        xioclose(asfd);
       +        return ret;
       +}
       +
       +static int
       +mktickets(Ticketreq *tr, char *buf, Key *k)
       +{
       +        Ticket t;
       +
       +        if(strcmp(tr->authid, tr->hostid) != 0)
       +                return -1;
       +
       +        memset(&t, 0, sizeof t);
       +        memmove(t.chal, tr->chal, CHALLEN);
       +        strcpy(t.cuid, tr->uid);
       +        strcpy(t.suid, tr->uid);
       +        memrandom(t.key, DESKEYLEN);
       +        t.num = AuthTc;
       +        convT2M(&t, buf, k->priv);
       +        t.num = AuthTs;
       +        convT2M(&t, buf+TICKETLEN, k->priv);
       +        return 0;
       +}
       +
       +static int
       +gettickets(Ticketreq *tr, char *buf, Key *k)
       +{
       +        if(getastickets(tr, buf) == 0)
       +                return 0;
       +        if(mktickets(tr, buf, k) == 0)
       +                return 0;
       +        werrstr("gettickets: %r");
       +        return -1;
       +}
       +
       +static int
       +p9sk1check(Key *k)
       +{
       +        char *user, *dom, *pass;
       +        Ticketreq tr;
       +
       +        user = strfindattr(k->attr, "user");
       +        dom = strfindattr(k->attr, "dom");
       +        if(user==nil || dom==nil){
       +                werrstr("need user and dom attributes");
       +                return -1;
       +        }
       +        if(strlen(user) >= sizeof tr.authid){
       +                werrstr("user name too long");
       +                return -1;
       +        }
       +        if(strlen(dom) >= sizeof tr.authdom){
       +                werrstr("auth dom name too long");
       +                return -1;
       +        }
       +
       +        k->priv = emalloc(DESKEYLEN);
       +        if(pass = strfindattr(k->privattr, "!password"))
       +                passtokey(k->priv, pass);
       +        else if(pass = strfindattr(k->privattr, "!hex")){
       +                if(hexparse(pass, k->priv, 7) < 0){
       +                        werrstr("malformed !hex key data");
       +                        return -1;
       +                }
       +        }else{
       +                werrstr("need !password or !hex attribute");
       +                return -1;
       +        }
       +
       +        return 0;
       +}
       +
       +static void
       +p9sk1close(Key *k)
       +{
       +        free(k->priv);
       +        k->priv = nil;
       +}
       +
       +static Role
       +p9sk1roles[] = 
       +{
       +        "client",        p9skclient,
       +        "server",        p9skserver,
       +        0
       +};
       +
       +static Role
       +p9sk2roles[] = 
       +{
       +        "client",        p9skclient,
       +        "server",        p9skserver,
       +        0
       +};
       +
       +Proto p9sk1 = {
       +.name=                "p9sk1",
       +.roles=                p9sk1roles,
       +.checkkey=        p9sk1check,
       +.closekey=        p9sk1close,
       +.keyprompt=        "user? dom? !password?",
       +};
       +
       +Proto p9sk2 = {
       +.name=                "p9sk2",
       +.roles=                p9sk2roles,
       +};
       +
 (DIR) diff --git a/src/cmd/factotum/pass.c b/src/cmd/factotum/pass.c
       t@@ -0,0 +1,100 @@
       +/*
       + * This is just a repository for a password.
       + * We don't want to encourage this, there's
       + * no server side.
       + */
       +
       +#include "dat.h"
       +
       +typedef struct State State;
       +struct State 
       +{
       +        Key *key;
       +};
       +
       +enum
       +{
       +        HavePass,
       +        Maxphase,
       +};
       +
       +static char *phasenames[Maxphase] =
       +{
       +[HavePass]        "HavePass",
       +};
       +
       +static int
       +passinit(Proto *p, Fsstate *fss)
       +{
       +        int ask;
       +        Key *k;
       +        State *s;
       +
       +        k = findkey(fss, Kuser, &ask, 0, fss->attr, "%s", p->keyprompt);
       +        if(k == nil){
       +                if(ask)
       +                        return RpcNeedkey;
       +                return failure(fss, nil);
       +        }
       +        setattrs(fss->attr, k->attr);
       +        s = emalloc(sizeof(*s));
       +        s->key = k;
       +        fss->ps = s;
       +        return RpcOk;
       +}
       +
       +static void
       +passclose(Fsstate *fss)
       +{
       +        State *s;
       +
       +        s = fss->ps;
       +        if(s->key)
       +                closekey(s->key);
       +        free(s);
       +}
       +
       +static int
       +passread(Fsstate *fss, void *va, uint *n)
       +{
       +        int m;
       +        char buf[500];
       +        char *pass, *user;
       +        State *s;
       +
       +        s = fss->ps;
       +        switch(fss->phase){
       +        default:
       +                return phaseerror(fss, "read");
       +
       +        case HavePass:
       +                user = strfindattr(s->key->attr, "user");
       +                pass = strfindattr(s->key->privattr, "!password");
       +                if(user==nil || pass==nil)
       +                        return failure(fss, "passread cannot happen");
       +                snprint(buf, sizeof buf, "%q %q", user, pass);
       +                m = strlen(buf);
       +                if(m > *n)
       +                        return toosmall(fss, m);
       +                *n = m;
       +                memmove(va, buf, m);
       +                return RpcOk;
       +        }
       +}
       +
       +static int
       +passwrite(Fsstate *fss, void*, uint)
       +{
       +        return phaseerror(fss, "write");
       +}
       +
       +Proto pass =
       +{
       +.name=                "pass",
       +.init=                passinit,
       +.write=                passwrite,
       +.read=                passread,
       +.close=                passclose,
       +.addkey=                replacekey,
       +.keyprompt=        "user? !password?",
       +};
 (DIR) diff --git a/src/cmd/factotum/plan9.c b/src/cmd/factotum/plan9.c
       t@@ -0,0 +1,189 @@
       +#include "std.h"
       +#include "dat.h"
       +#include <bio.h>
       +
       +int
       +memrandom(void *p, int n)
       +{
       +        uchar *cp;
       +
       +        for(cp = (uchar*)p; n > 0; n--)
       +                *cp++ = fastrand();
       +        return 0;
       +}
       +
       +/*
       + *  create a change uid capability 
       + */
       +static int caphashfd;
       +
       +static char*
       +mkcap(char *from, char *to)
       +{
       +        uchar rand[20];
       +        char *cap;
       +        char *key;
       +        int nfrom, nto;
       +        uchar hash[SHA1dlen];
       +
       +        if(caphashfd < 0)
       +                return nil;
       +
       +        /* create the capability */
       +        nto = strlen(to);
       +        nfrom = strlen(from);
       +        cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1);
       +        sprint(cap, "%s@%s", from, to);
       +        memrandom(rand, sizeof(rand));
       +        key = cap+nfrom+1+nto+1;
       +        enc64(key, sizeof(rand)*3, rand, sizeof(rand));
       +
       +        /* hash the capability */
       +        hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
       +
       +        /* give the kernel the hash */
       +        key[-1] = '@';
       +        if(write(caphashfd, hash, SHA1dlen) < 0){
       +                free(cap);
       +                return nil;
       +        }
       +
       +        return cap;
       +}
       +
       +Attr*
       +addcap(Attr *a, char *from, Ticket *t)
       +{
       +        char *cap;
       +
       +        cap = mkcap(from, t->suid);
       +        return addattr(a, "cuid=%q suid=%q cap=%q", t->cuid, t->suid, cap);
       +}
       +
       +/* bind in the default network and cs */
       +static int
       +bindnetcs(void)
       +{
       +        int srvfd;
       +
       +        if(access("/net/tcp", AEXIST) < 0)
       +                bind("#I", "/net", MBEFORE);
       +
       +        if(access("/net/cs", AEXIST) < 0){
       +                if((srvfd = open("#s/cs", ORDWR)) >= 0){
       +                        /* mount closes srvfd on success */
       +                        if(mount(srvfd, -1, "/net", MBEFORE, "") >= 0)
       +                                return 0;
       +                        close(srvfd);
       +                }
       +                return -1;
       +        }
       +        return 0;
       +}
       +
       +int
       +_authdial(char *net, char *authdom)
       +{
       +        int vanilla;
       +
       +        vanilla = net==nil || strcmp(net, "/net")==0;
       +
       +        if(!vanilla || bindnetcs()>=0)
       +                return authdial(net, authdom);
       +
       +        /* use the auth sever passed to us as an arg */
       +        if(authaddr == nil)
       +                return -1;
       +        return dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0);
       +}
       +
       +Key*
       +plan9authkey(Attr *a)
       +{
       +        char *dom;
       +        Key *k;
       +
       +        /*
       +         * The only important part of a is dom.
       +         * We don't care, for example, about user name.
       +         */
       +        dom = strfindattr(a, "dom");
       +        if(dom)
       +                k = keylookup("proto=p9sk1 role=server user? dom=%q", dom);
       +        else
       +                k = keylookup("proto=p9sk1 role=server user? dom?");
       +        if(k == nil)
       +                werrstr("could not find plan 9 auth key dom %q", dom);
       +        return k;
       +}
       +
       +/*
       + *  prompt for a string with a possible default response
       + */
       +char*
       +readcons(char *prompt, char *def, int raw)
       +{
       +        int fdin, fdout, ctl, n;
       +        char line[10];
       +        char *s;
       +
       +        fdin = open("/dev/cons", OREAD);
       +        if(fdin < 0)
       +                fdin = 0;
       +        fdout = open("/dev/cons", OWRITE);
       +        if(fdout < 0)
       +                fdout = 1;
       +        if(def != nil)
       +                fprint(fdout, "%s[%s]: ", prompt, def);
       +        else
       +                fprint(fdout, "%s: ", prompt);
       +        if(raw){
       +                ctl = open("/dev/consctl", OWRITE);
       +                if(ctl >= 0)
       +                        write(ctl, "rawon", 5);
       +        } else
       +                ctl = -1;
       +        s = estrdup("");
       +        for(;;){
       +                n = read(fdin, line, 1);
       +                if(n == 0){
       +                Error:
       +                        close(fdin);
       +                        close(fdout);
       +                        if(ctl >= 0)
       +                                close(ctl);
       +                        free(s);
       +                        return nil;
       +                }
       +                if(n < 0)
       +                        goto Error;
       +                if(line[0] == 0x7f)
       +                        goto Error;
       +                if(n == 0 || line[0] == '\n' || line[0] == '\r'){
       +                        if(raw){
       +                                write(ctl, "rawoff", 6);
       +                                write(fdout, "\n", 1);
       +                        }
       +                        close(ctl);
       +                        close(fdin);
       +                        close(fdout);
       +                        if(*s == 0 && def != nil)
       +                                s = estrappend(s, "%s", def);
       +                        return s;
       +                }
       +                if(line[0] == '\b'){
       +                        if(strlen(s) > 0)
       +                                s[strlen(s)-1] = 0;
       +                } else if(line[0] == 0x15) {        /* ^U: line kill */
       +                        if(def != nil)
       +                                fprint(fdout, "\n%s[%s]: ", prompt, def);
       +                        else
       +                                fprint(fdout, "\n%s: ", prompt);
       +                        
       +                        s[0] = 0;
       +                } else {
       +                        s = estrappend(s, "%c", line[0]);
       +                }
       +        }
       +        return nil; /* not reached */
       +}
 (DIR) diff --git a/src/cmd/factotum/privattr b/src/cmd/factotum/privattr
 (DIR) diff --git a/src/cmd/factotum/proto.c b/src/cmd/factotum/proto.c
       t@@ -0,0 +1,22 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +Proto *prototab[] = {
       +        &apop,
       +        &cram,
       +        &p9any,
       +        &p9sk1,
       +        &p9sk2,
       +        nil,
       +};
       +
       +Proto*
       +protolookup(char *name)
       +{
       +        int i;
       +
       +        for(i=0; prototab[i]; i++)
       +                if(strcmp(prototab[i]->name, name) == 0)
       +                        return prototab[i];
       +        return nil;
       +}
 (DIR) diff --git a/src/cmd/factotum/rpc.c b/src/cmd/factotum/rpc.c
       t@@ -0,0 +1,315 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +/*
       + * Factotum RPC
       + *
       + * Must be paired write/read cycles on /mnt/factotum/rpc.
       + * The format of a request is verb, single space, data.
       + * Data format is verb-dependent; in particular, it can be binary.
       + * The format of a response is the same.  The write only sets up
       + * the RPC.  The read tries to execute it.  If the /mnt/factotum/key
       + * file is open, we ask for new keys using that instead of returning
       + * an error in the RPC.  This means the read blocks.
       + * Textual arguments are parsed with tokenize, so rc-style quoting
       + * rules apply.
       + *
       + * Only authentication protocol messages go here.  Configuration
       + * is still via ctl (below).
       + *
       + * Request RPCs are:
       + *        start attrs - initializes protocol for authentication, can fail.
       + *                returns "ok read" or "ok write" on success.
       + *        read - execute protocol read
       + *        write - execute protocol write
       + *        authinfo - if the protocol is finished, return the AI if any
       + *        attr - return protocol information
       + * Return values are:
       + *        error message - an error happened.
       + *        ok [data] - success, possible data is request dependent.
       + *        needkey attrs - request aborted, get me this key and try again
       + *        badkey attrs - request aborted, this key might be bad
       + *        done [haveai] - authentication is done [haveai: you can get an ai with authinfo]
       + */
       +
       +char *rpcname[] = 
       +{
       +        "unknown",
       +        "authinfo",
       +        "attr",
       +        "read",
       +        "start",
       +        "write",
       +};
       +
       +static int
       +classify(char *s)
       +{
       +        int i;
       +
       +        for(i=1; i<nelem(rpcname); i++)
       +                if(strcmp(s, rpcname[i]) == 0)
       +                        return i;
       +        return RpcUnknown;
       +}
       +
       +int
       +rpcwrite(Conv *c, void *data, int count)
       +{
       +        int op;
       +        uchar *p;
       +
       +        if(count >= MaxRpc){
       +                werrstr("rpc too large");
       +                return -1;
       +        }
       +
       +        /* cancel any current rpc */
       +        c->rpc.op = RpcUnknown;
       +        c->nreply = 0;
       +
       +        /* parse new rpc */
       +        memmove(c->rpcbuf, data, count);
       +        c->rpcbuf[count] = 0;
       +        if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){
       +                *p++ = '\0';
       +                c->rpc.data = p;
       +                c->rpc.count = count - (p - (uchar*)c->rpcbuf);
       +        }else{
       +                c->rpc.data = "";
       +                c->rpc.count = 0;
       +        }
       +        op = classify(c->rpcbuf);
       +        if(op == RpcUnknown){
       +                werrstr("bad rpc verb: %s", c->rpcbuf);
       +                return -1;
       +        }
       +
       +        c->rpc.op = op;
       +        return 0;
       +}
       +
       +void
       +convthread(void *v)
       +{
       +        Conv *c;
       +        Attr *a;
       +        char *role, *proto;
       +        Proto *p;
       +        Role *r;
       +
       +        c = v;
       +        a = parseattr(c->rpc.data);
       +        if(a == nil){
       +                werrstr("empty attr");
       +                goto out;
       +        }
       +        c->attr = a;
       +        proto = strfindattr(a, "proto");
       +        role = strfindattr(a, "role");
       +
       +        if(proto == nil){
       +                werrstr("no proto in attrs");
       +                goto out;
       +        }
       +        if(role == nil){
       +                werrstr("no role in attrs");
       +                goto out;
       +        }
       +
       +        p = protolookup(proto);
       +        if(p == nil){
       +                werrstr("unknown proto %s", proto);
       +                goto out;
       +        }
       +
       +        c->proto = p;
       +        for(r=p->roles; r->name; r++){
       +                if(strcmp(r->name, role) != 0)
       +                        continue;
       +                rpcrespond(c, "ok");
       +                c->active = 1;
       +                if((*r->fn)(c) == 0){
       +                        c->done = 1;
       +                        werrstr("protocol finished");
       +                }else
       +                        werrstr("%s %s %s: %r", p->name, r->name, c->state);
       +                goto out;
       +        }
       +        werrstr("unknown role");
       +
       +out:
       +        c->active = 0;
       +        c->state = 0;
       +        rerrstr(c->err, sizeof c->err);
       +        rpcrespond(c, "error %r");
       +        convclose(c);
       +}
       +
       +static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex);
       +
       +void
       +rpcexec(Conv *c)
       +{
       +        uchar *p;
       +
       +        switch(c->rpc.op){
       +        case RpcRead:
       +                if(c->rpc.count > 0){
       +                        rpcrespond(c, "error read takes no parameters");
       +                        break;
       +                }
       +                /* fall through */
       +        default:
       +                if(!c->active){
       +                        if(c->done)
       +                                rpcrespond(c, "done");
       +                        else
       +                                rpcrespond(c, "error %s", c->err);
       +                        break;
       +                }
       +                nbsendp(c->rpcwait, 0);
       +                break;
       +        case RpcUnknown:
       +                break;
       +        case RpcAuthinfo:
       +                /* deprecated */
       +                if(c->active)
       +                        rpcrespond(c, "error conversation still active");
       +                else if(!c->done)
       +                        rpcrespond(c, "error conversation not successful");
       +                else{
       +                        /* make up an auth info using the attr */
       +                        p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3, 
       +                                strfindattr(c->attr, "cuid"),
       +                                strfindattr(c->attr, "suid"),
       +                                strfindattr(c->attr, "cap"),
       +                                strfindattr(c->attr, "secret"));
       +                        if(p == nil)
       +                                rpcrespond(c, "error %r");
       +                        else
       +                                rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3));
       +                }
       +                break;
       +        case RpcAttr:
       +                rpcrespond(c, "ok %A", c->attr);
       +                break;
       +        case RpcStart:
       +                convreset(c);
       +                c->ref++;
       +                threadcreate(convthread, c, STACK);
       +                break;
       +        }
       +}
       +
       +void
       +rpcrespond(Conv *c, char *fmt, ...)
       +{
       +        va_list arg;
       +
       +        if(c->hangup)
       +                return;
       +
       +        if(fmt == nil)
       +                fmt = "";
       +
       +        va_start(arg, fmt);
       +        c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg);
       +        va_end(arg);
       +        (*c->kickreply)(c);
       +        c->rpc.op = RpcUnknown;
       +}
       +
       +void
       +rpcrespondn(Conv *c, char *verb, void *data, int count)
       +{
       +        char *p;
       +
       +        if(c->hangup)
       +                return;
       +
       +        if(strlen(verb)+1+count > sizeof c->reply){
       +                print("RPC response too large; caller %#lux", getcallerpc(&c));
       +                return;
       +        }
       +
       +        strcpy(c->reply, verb);
       +        p = c->reply + strlen(c->reply);
       +        *p++ = ' ';
       +        memmove(p, data, count);
       +        c->nreply = count + (p - c->reply);
       +        (*c->kickreply)(c);
       +        c->rpc.op = RpcUnknown;
       +}
       +
       +/* deprecated */
       +static uchar*
       +pstring(uchar *p, uchar *e, char *s)
       +{
       +        uint n;
       +
       +        if(p == nil)
       +                return nil;
       +        if(s == nil)
       +                s = "";
       +        n = strlen(s);
       +        if(p+n+BIT16SZ >= e)
       +                return nil;
       +        PBIT16(p, n);
       +        p += BIT16SZ;
       +        memmove(p, s, n);
       +        p += n;
       +        return p;
       +}
       +
       +static uchar*
       +pcarray(uchar *p, uchar *e, uchar *s, uint n)
       +{
       +        if(p == nil)
       +                return nil;
       +        if(s == nil){
       +                if(n > 0)
       +                        sysfatal("pcarray");
       +                s = (uchar*)"";
       +        }
       +        if(p+n+BIT16SZ >= e)
       +                return nil;
       +        PBIT16(p, n);
       +        p += BIT16SZ;
       +        memmove(p, s, n);
       +        p += n;
       +        return p;
       +}
       +
       +static uchar*
       +convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex)
       +{
       +        uchar *e = p+n;
       +        uchar *secret;
       +        int nsecret;
       +
       +        if(cuid == nil)
       +                cuid = "";
       +        if(suid == nil)
       +                suid = "";
       +        if(cap == nil)
       +                cap = "";
       +        if(hex == nil)
       +                hex = "";
       +        nsecret = strlen(hex)/2;
       +        secret = emalloc(nsecret);
       +        if(hexparse(hex, secret, nsecret) < 0){
       +                werrstr("hexparse %s failed", hex);        /* can't happen */
       +                free(secret);
       +                return nil;
       +        }
       +        p = pstring(p, e, cuid);
       +        p = pstring(p, e, suid);
       +        p = pstring(p, e, cap);
       +        p = pcarray(p, e, secret, nsecret);
       +        free(secret);
       +        if(p == nil)
       +                werrstr("authinfo too big");
       +        return p;
       +}
       +
 (DIR) diff --git a/src/cmd/factotum/ssh.c b/src/cmd/factotum/ssh.c
       t@@ -0,0 +1,135 @@
       +#include "dat.h"
       +#include <mp.h>
       +#include <libsec.h>
       +
       +typedef struct Sshrsastate Sshrsastate;
       +
       +enum {
       +        CReadpub,
       +        CWritechal,
       +        CReadresp,
       +};
       +struct State
       +{
       +        RSApriv *priv;
       +        Key *k;
       +        mpint *resp;
       +        int phase;
       +};
       +
       +static RSApriv*
       +readrsapriv(char *s)
       +{
       +        RSApriv *priv;
       +
       +        priv = rsaprivalloc();
       +
       +        strtoul(s, &s, 10);
       +        if((priv->pub.ek=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->dk=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->pub.n=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->p=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->q=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->kp=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->kq=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +        if((priv->c2=strtomp(s, &s, 16, nil)) == nil)
       +                goto Error;
       +
       +        return priv;
       +
       +Error:
       +        rsaprivfree(priv);
       +        return nil;
       +}
       +
       +int
       +sshinit(Fsstate *fss, 
       +sshrsaopen(Key *k, char*, int client)
       +{
       +        Sshrsastate *s;
       +
       +        fmtinstall('B', mpconv);
       +        assert(client);
       +        s = emalloc(sizeof *s);
       +        s->priv = readrsapriv(s_to_c(k->data));
       +        s->k = k;
       +        if(s->priv == nil){
       +                agentlog("error parsing ssh key %s", k->file);
       +                free(s);
       +                return nil;
       +        }
       +        return s;
       +}
       +
       +int
       +sshrsaread(void *va, void *buf, int n)
       +{
       +        Sshrsastate *s;
       +
       +        s = va;
       +        switch(s->phase){
       +        case Readpub:
       +                s->phase = Done;
       +                return snprint(buf, n, "%B", s->priv->pub.n);
       +        case Readresp:
       +                s->phase = Done;
       +                return snprint(buf, n, "%B", s->resp);
       +        default:
       +                return 0;
       +        }
       +}
       +
       +int
       +sshrsawrite(void *va, void *vbuf, int n)
       +{
       +        mpint *m;
       +        char *buf;
       +        Sshrsastate *s;
       +
       +        s = va;
       +        if((s->k->flags&Fconfirmuse) && confirm("ssh use") < 0)
       +                return -1;
       +
       +        buf = emalloc(n+1);
       +        memmove(buf, vbuf, n);
       +        buf[n] = '\0';
       +        m = strtomp(buf, nil, 16, nil);
       +        free(buf);
       +        if(m == nil){
       +                werrstr("bad bignum");
       +                return -1;
       +        }
       +
       +        agentlog("ssh use");
       +        m = rsadecrypt(s->priv, m, m);
       +        s->resp = m;
       +        s->phase = Readresp;
       +        return n;
       +}
       +
       +void
       +sshrsaclose(void *v)
       +{
       +        Sshrsastate *s;
       +
       +        s = v;
       +        rsaprivfree(s->priv);
       +        mpfree(s->resp);
       +        free(s);
       +}
       +
       +Proto sshrsa = {
       +.name=        "ssh-rsa",
       +.perm=        0666,
       +.open=        sshrsaopen,
       +.read=        sshrsaread,
       +.write=        sshrsawrite,
       +.close=        sshrsaclose,
       +};
 (DIR) diff --git a/src/cmd/factotum/sshrsa.c b/src/cmd/factotum/sshrsa.c
       t@@ -0,0 +1,172 @@
       +/*
       + * SSH RSA authentication.
       + * 
       + * Client protocol:
       + *        read public key
       + *                if you don't like it, read another, repeat
       + *        write challenge
       + *        read response
       + * all numbers are hexadecimal biginits parsable with strtomp.
       + */
       +
       +#include "dat.h"
       +
       +enum {
       +        CHavePub,
       +        CHaveResp,
       +
       +        Maxphase,
       +};
       +
       +static char *phasenames[] = {
       +[CHavePub]        "CHavePub",
       +[CHaveResp]        "CHaveResp",
       +};
       +
       +struct State
       +{
       +        RSApriv *priv;
       +        mpint *resp;
       +        int off;
       +        Key *key;
       +};
       +
       +static RSApriv*
       +readrsapriv(Key *k)
       +{
       +        char *a;
       +        RSApriv *priv;
       +
       +        priv = rsaprivalloc();
       +
       +        if((a=strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        if((a=strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil)
       +                goto Error;
       +        return priv;
       +
       +Error:
       +        rsaprivfree(priv);
       +        return nil;
       +}
       +
       +static int
       +sshrsainit(Proto*, Fsstate *fss)
       +{
       +        int iscli;
       +        State *s;
       +
       +        if((iscli = isclient(strfindattr(fss->attr, "role"))) < 0)
       +                return failure(fss, nil);
       +        if(iscli==0)
       +                return failure(fss, "sshrsa server unimplemented");
       +
       +        s = emalloc(sizeof *s);
       +        fss->phasename = phasenames;
       +        fss->maxphase = Maxphase;
       +        fss->phase = CHavePub;
       +        fss->ps = s;
       +        return RpcOk;
       +}
       +
       +static int
       +sshrsaread(Fsstate *fss, void *va, uint *n)
       +{
       +        RSApriv *priv;
       +        State *s;
       +
       +        s = fss->ps;
       +        switch(fss->phase){
       +        default:
       +                return phaseerror(fss, "read");
       +        case CHavePub:
       +                if(s->key){
       +                        closekey(s->key);
       +                        s->key = nil;
       +                }
       +                if((s->key = findkey(fss, Kuser, nil, s->off, fss->attr, nil)) == nil)
       +                        return failure(fss, nil);
       +                s->off++;
       +                priv = s->key->priv;
       +                *n = snprint(va, *n, "%B", priv->pub.n);
       +                return RpcOk;
       +        case CHaveResp:
       +                *n = snprint(va, *n, "%B", s->resp);
       +                fss->phase = Established;
       +                return RpcOk;
       +        }
       +}
       +
       +static int
       +sshrsawrite(Fsstate *fss, void *va, uint)
       +{
       +        mpint *m;
       +        State *s;
       +
       +        s = fss->ps;
       +        switch(fss->phase){
       +        default:
       +                return phaseerror(fss, "write");
       +        case CHavePub:
       +                if(s->key == nil)
       +                        return failure(fss, "no current key");
       +                m = strtomp(va, nil, 16, nil);
       +                m = rsadecrypt(s->key->priv, m, m);
       +                s->resp = m;
       +                fss->phase = CHaveResp;
       +                return RpcOk;
       +        }
       +}
       +
       +static void
       +sshrsaclose(Fsstate *fss)
       +{
       +        State *s;
       +
       +        s = fss->ps;
       +        if(s->key)
       +                closekey(s->key);
       +        if(s->resp)
       +                mpfree(s->resp);
       +        free(s);
       +}
       +
       +static int
       +sshrsaaddkey(Key *k)
       +{
       +        fmtinstall('B', mpconv);
       +
       +        if((k->priv = readrsapriv(k)) == nil){
       +                werrstr("malformed key data");
       +                return -1;
       +        }
       +        return replacekey(k);
       +}
       +
       +static void
       +sshrsaclosekey(Key *k)
       +{
       +        rsaprivfree(k->priv);
       +}
       +
       +Proto sshrsa = {
       +.name=        "sshrsa",
       +.init=                sshrsainit,
       +.write=        sshrsawrite,
       +.read=        sshrsaread,
       +.close=        sshrsaclose,
       +.addkey=        sshrsaaddkey,
       +.closekey=        sshrsaclosekey,
       +};
 (DIR) diff --git a/src/cmd/factotum/std.h b/src/cmd/factotum/std.h
       t@@ -0,0 +1,10 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <authsrv.h>
       +#include <mp.h>
       +#include <libsec.h>
       +#include <thread.h>
       +#include <fcall.h>
       +#include <9p.h>
       +
 (DIR) diff --git a/src/cmd/factotum/test.c b/src/cmd/factotum/test.c
       t@@ -0,0 +1,121 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +
       +typedef struct Test Test;
       +
       +struct Test
       +{
       +        char *name;
       +        int (*server)(Test*, AuthRpc*, int);
       +        int (*client)(Test*, int);
       +};
       +
       +int
       +ai2status(AuthInfo *ai)
       +{
       +        if(ai == nil)
       +                return -1;
       +        auth_freeAI(ai);
       +        return 0;
       +}
       +
       +int
       +proxyserver(Test *t, AuthRpc *rpc, int fd)
       +{
       +        char buf[1024];
       +
       +        sprint(buf, "proto=%q role=server", t->name);
       +        return ai2status(fauth_proxy(fd, rpc, nil, buf));
       +}
       +
       +int
       +proxyclient(Test *t, int fd)
       +{
       +        return ai2status(auth_proxy(fd, auth_getkey, "proto=%q role=client", t->name));
       +}
       +
       +Test test[] =
       +{
       +        "apop",                proxyserver,                proxyclient,
       +        "cram",                proxyserver,                proxyclient,
       +        "p9sk1",                proxyserver,                proxyclient,
       +        "p9sk2",                proxyserver,                proxyclient,
       +        "p9any",                proxyserver,                proxyclient,
       +};
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: test [name]...\n");
       +        exits("usage");
       +}
       +
       +void
       +runtest(AuthRpc *srpc, Test *t)
       +{
       +        int p[2], bad;
       +        Waitmsg *w;
       +
       +        if(pipe(p) < 0)
       +                sysfatal("pipe: %r");
       +
       +        print("%s...", t->name);
       +
       +        switch(fork()){
       +        case -1:
       +                sysfatal("fork: %r");
       +
       +        case 0:
       +                close(p[0]);
       +                if((*t->server)(t, srpc, p[1]) < 0){
       +                        print("\n\tserver: %r");
       +                        _exits("oops");
       +                }
       +                close(p[1]);
       +                _exits(nil);
       +        default:
       +                close(p[1]);
       +                if((*t->client)(t, p[0]) < 0){
       +                        print("\n\tclient: %r");
       +                        bad = 1;
       +                }
       +                close(p[0]);
       +                break;
       +        }
       +        w = wait();
       +        if(w->msg[0])
       +                bad = 1;
       +        print("\n");
       +}
       +
       +void
       +main(int argc, char **argv)
       +{
       +        int i, j;
       +        int afd;
       +        AuthRpc *srpc;
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        quotefmtinstall();
       +        afd = open("/n/kremvax/factotum/rpc", ORDWR);
       +        if(afd < 0)
       +                sysfatal("open /n/kremvax/factotum/rpc: %r");
       +        srpc = auth_allocrpc(afd);
       +        if(srpc == nil)
       +                sysfatal("auth_allocrpc: %r");
       +
       +        if(argc == 0)
       +                for(i=0; i<nelem(test); i++)
       +                        runtest(srpc, &test[i]);
       +        else
       +                for(i=0; i<argc; i++)
       +                        for(j=0; j<nelem(test); j++)
       +                                if(strcmp(argv[i], test[j].name) == 0)
       +                                        runtest(srpc, &test[j]);
       +        exits(nil);
       +}
 (DIR) diff --git a/src/cmd/factotum/testsetup b/src/cmd/factotum/testsetup
       t@@ -0,0 +1,11 @@
       +#!/bin/rc
       +
       +slay 8.out|rc
       +8.out $* -s fact.s -m /n/kremvax
       +8.out $* -s fact.c
       +ramfs -m /n/sid >[2]/dev/null
       +auth/aescbc -d < /usr/rsc/lib/factotum.aes >/n/sid/all
       +read -m /n/sid/all >/n/kremvax/factotum/ctl
       +read -m /n/sid/all >/mnt/factotum/ctl
       +unmount /n/sid
       +
 (DIR) diff --git a/src/cmd/factotum/util.c b/src/cmd/factotum/util.c
       t@@ -0,0 +1,52 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +static int
       +unhex(char c)
       +{
       +        if('0' <= c && c <= '9')
       +                return c-'0';
       +        if('a' <= c && c <= 'f')
       +                return c-'a'+10;
       +        if('A' <= c && c <= 'F')
       +                return c-'A'+10;
       +        abort();
       +        return -1;
       +}
       +
       +int
       +hexparse(char *hex, uchar *dat, int ndat)
       +{
       +        int i, n;
       +
       +        n = strlen(hex);
       +        if(n%2)
       +                return -1;
       +        n /= 2;
       +        if(n > ndat)
       +                return -1;
       +        if(hex[strspn(hex, "0123456789abcdefABCDEF")] != '\0')
       +                return -1;
       +        for(i=0; i<n; i++)
       +                dat[i] = (unhex(hex[2*i])<<4)|unhex(hex[2*i+1]);
       +        return n;
       +}
       +
       +char*
       +estrappend(char *s, char *fmt, ...)
       +{
       +        char *t;
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        t = vsmprint(fmt, arg);
       +        if(t == nil)
       +                sysfatal("out of memory");
       +        va_end(arg);
       +        s = erealloc(s, strlen(s)+strlen(t)+1);
       +        strcat(s, t);
       +        free(t);
       +        return s;
       +}
       +
       +
 (DIR) diff --git a/src/cmd/factotum/x.c b/src/cmd/factotum/x.c
       t@@ -0,0 +1,15 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +
       +void
       +f(void*)
       +{
       +}
       +
       +void
       +main(void)
       +{
       +        f(auth_challenge);
       +        f(auth_response);
       +}
 (DIR) diff --git a/src/cmd/factotum/xio.c b/src/cmd/factotum/xio.c
       t@@ -0,0 +1,165 @@
       +#include "std.h"
       +#include "dat.h"
       +
       +static Ioproc *cache[5];
       +static int ncache;
       +
       +static Ioproc*
       +xioproc(void)
       +{
       +        Ioproc *c;
       +        int i;
       +        
       +        for(i=0; i<ncache; i++){
       +                if(c = cache[i]){
       +                        cache[i] = nil;
       +                        return c;
       +                }
       +        }
       +
       +        return ioproc();
       +}
       +
       +static void
       +closexioproc(Ioproc *io)
       +{
       +        int i;
       +
       +        for(i=0; i<ncache; i++)
       +                if(cache[i] == nil){
       +                        cache[i] = io;
       +                        return;
       +                }
       +
       +        closeioproc(io);
       +}
       +
       +int
       +xiodial(char *ds, char *local, char *dir, int *cfdp)
       +{
       +        int fd;
       +        Ioproc *io;
       +
       +        if((io = xioproc()) == nil)
       +                return -1;
       +        fd = iodial(io, ds, local, dir, cfdp);
       +        closexioproc(io);
       +        return fd;
       +}
       +
       +void
       +xioclose(int fd)
       +{
       +        Ioproc *io;
       +
       +        if((io = xioproc()) == nil){
       +                close(fd);
       +                return;
       +        }
       +
       +        ioclose(io, fd);
       +        closexioproc(io);
       +}
       +
       +int
       +xiowrite(int fd, void *v, int n)
       +{
       +        int m;
       +        Ioproc *io;
       +
       +        if((io = xioproc()) == nil)
       +                return -1;
       +        m = iowrite(io, fd, v, n);
       +        closexioproc(io);
       +        if(m != n)
       +                return -1;
       +        return n;
       +}
       +
       +static long
       +_ioauthdial(va_list *arg)
       +{
       +        char *net;
       +        char *dom;
       +        int fd;
       +
       +        net = va_arg(*arg, char*);
       +        dom = va_arg(*arg, char*);
       +        fd = _authdial(net, dom);
       +        if(fd < 0)
       +                fprint(2, "authdial: %r");
       +        return fd;
       +}
       +
       +int
       +xioauthdial(char *net, char *dom)
       +{
       +        int fd;
       +        Ioproc *io;
       +
       +        if((io = xioproc()) == nil)
       +                return -1;
       +        fd = iocall(io, _ioauthdial, net, dom);
       +        closexioproc(io);
       +        return fd;
       +}
       +
       +static long
       +_ioasrdresp(va_list *arg)
       +{
       +        int fd;
       +        void *a;
       +        int n;
       +
       +        fd = va_arg(*arg, int);
       +        a = va_arg(*arg, void*);
       +        n = va_arg(*arg, int);
       +
       +        return _asrdresp(fd, a, n);
       +}
       +
       +int
       +xioasrdresp(int fd, void *a, int n)
       +{
       +        Ioproc *io;
       +
       +        if((io = xioproc()) == nil)
       +                return -1;
       +
       +        n = iocall(io, _ioasrdresp, fd, a, n);
       +        closexioproc(io);
       +        return n;
       +}
       +
       +static long
       +_ioasgetticket(va_list *arg)
       +{
       +        int asfd;
       +        char *trbuf;
       +        char *tbuf;
       +
       +        asfd = va_arg(*arg, int);
       +        trbuf = va_arg(*arg, char*);
       +        tbuf = va_arg(*arg, char*);
       +
       +        return _asgetticket(asfd, trbuf, tbuf);
       +}
       +
       +int
       +xioasgetticket(int fd, char *trbuf, char *tbuf)
       +{
       +        int n;
       +        Ioproc *io;
       +
       +        if((io = xioproc()) == nil)
       +                return -1;
       +
       +        n = iocall(io, _ioasgetticket, fd, trbuf, tbuf);
       +        closexioproc(io);
       +        if(n != 2*TICKETLEN)
       +                n = -1;
       +        else
       +                n = 0;
       +        return n;
       +}
       +
 (DIR) diff --git a/src/cmd/mkfile b/src/cmd/mkfile
       t@@ -7,7 +7,7 @@ SHORTLIB=sec fs mux regexp9 thread bio 9
        
        <$PLAN9/src/mkmany
        
       -BUGGERED='CVS|faces|factotum|oplumb|plumb2|mk|vac|9term|upas|venti|htmlfmt'
       +BUGGERED='CVS|9term|faces|factotum|htmlfmt|mk|rio|upas|vac|venti'
        DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERED)$"`
        
        <$PLAN9/src/mkdirs
 (DIR) diff --git a/src/cmd/plumb/plumber.c b/src/cmd/plumb/plumber.c
       t@@ -54,6 +54,8 @@ threadmain(int argc, char *argv[])
                        error("can't initialize $user or $home: %r");
                if(plumbfile == nil){
                        sprint(buf, "%s/lib/plumbing", home);
       +                if(access(buf, 0) < 0)
       +                        sprint(buf, "#9/plumb/initial.plumbing");
                        plumbfile = estrdup(buf);
                }
        
 (DIR) diff --git a/src/cmd/rc/plan9ish.c b/src/cmd/rc/plan9ish.c
       t@@ -81,7 +81,7 @@ void Vinit(void){
                        for(s=*env;*s && *s!='(' && *s!='=';s++);
                        switch(*s){
                        case '\0':
       -                        pfmt(err, "environment %q?\n", *env);
       +                        pfmt(err, "rc: odd environment %q?\n", *env);
                                break;
                        case '=':
                                *s='\0';
 (DIR) diff --git a/src/cmd/rc/rc.h b/src/cmd/rc/rc.h
       t@@ -26,6 +26,9 @@
        #include "x.tab.h"
        #endif
        #endif
       +
       +#undef pipe        /* so that /dev/fd works */
       +
        typedef struct tree tree;
        typedef struct word word;
        typedef struct io io;
 (DIR) diff --git a/src/cmd/samterm/plan9.c b/src/cmd/samterm/plan9.c
       t@@ -10,6 +10,12 @@
        #include <cursor.h>
        #include <keyboard.h>
        #include <frame.h>
       +#define Tversion Tversion9p
       +#define Twrite Twrite9p
       +#include <fcall.h>
       +#undef Tversion
       +#undef Twrite
       +#include <fs.h>
        #include <plumb.h>
        #include "flayer.h"
        #include "samterm.h"
       t@@ -212,27 +218,22 @@ plumbformat(Plumbmsg *m, int i)
        }
        
        void
       -plumbproc(void *argv)
       +plumbproc(void *arg)
        {
       -        Channel *c;
       -        int i, *fdp;
       -        void **arg;
       +        Fid *fid;
       +        int i;
                Plumbmsg *m;
        
       -        arg = argv;
       -        c = arg[0];
       -        fdp = arg[1];
       -
       +        fid = arg;
                i = 0;
       -        threadfdnoblock(*fdp);
                for(;;){
       -                m = threadplumbrecv(*fdp);
       +                m = plumbrecvfid(fid);
                        if(m == nil){
                                fprint(2, "samterm: plumb read error: %r\n");
                                threadexits("plumb");        /* not a fatal error */
                        }
                        if(plumbformat(m, i)){
       -                        send(c, &i);
       +                        send(plumbc, &i);
                                i = 1-i;        /* toggle */
                        }
                }
       t@@ -241,21 +242,18 @@ plumbproc(void *argv)
        int
        plumbstart(void)
        {
       -        static int fd;
       -        static void *arg[2];
       +        Fid *fid;
        
                plumbfd = plumbopen("send", OWRITE|OCEXEC);        /* not open is ok */
       -        fd = plumbopen("edit", OREAD|OCEXEC);
       -        if(fd < 0)
       +        fid = plumbopenfid("edit", OREAD|OCEXEC);
       +        if(fid == nil)
                        return -1;
                plumbc = chancreate(sizeof(int), 0);
                if(plumbc == nil){
       -                close(fd);
       +                fsclose(fid);
                        return -1;
                }
       -        arg[0] = plumbc;
       -        arg[1] = &fd;
       -        threadcreate(plumbproc, arg, STACK);
       +        threadcreate(plumbproc, fid, STACK);
                return 1;
        }
        
 (DIR) diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c
       t@@ -1,8 +1,11 @@
       +#include <sys/stat.h>
        #include "stdinc.h"
        #include "vac.h"
        #include "dat.h"
        #include "fns.h"
        
       +int mainstacksize = 128*1024;
       +
        typedef struct Sink Sink;
        typedef struct MetaSink MetaSink;
        typedef struct DirSink DirSink;
       t@@ -170,6 +173,9 @@ threadmain(int argc, char *argv[])
                        break;
                }ARGEND;
        
       +        if(argc == 0)
       +                usage();
       +
                if(bsize < 512)
                        bsize = 512;
                if(bsize > VtMaxLumpSize)
       t@@ -215,8 +221,6 @@ vacwrite(VtConn *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
                        sha1(buf, n, score, nil);
                        return 0;
                }
       -sha1(buf, n, score, nil);
       -fprint(2, "write %V %d\n", score, type);
                return vtwrite(z, score, type, buf, n);
        }
        
       t@@ -377,6 +381,18 @@ isexcluded(char *name)
                return 0;
        }
        
       +static int
       +islink(char *name)
       +{
       +        struct stat st;
       +
       +        if(lstat(name, &st) < 0)
       +                return 0;
       +        if((st.st_mode&S_IFMT) == S_IFLNK)
       +                return 1;
       +        return 0;
       +}
       +
        static void
        vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
        {
       t@@ -393,6 +409,9 @@ vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
                if(merge && vacmerge(dsink, lname, sname) >= 0)
                        return;
        
       +        if(islink(sname))
       +                return;
       +
                fd = open(sname, OREAD);
                if(fd < 0) {
                        warn("could not open file: %s: %r", lname);
       t@@ -820,10 +839,8 @@ sinkclose(Sink *k)
                        if(k->pbuf[n] > k->buf + kd->psize*n)
                                break;
        
       -fprint(2, "type %d -> ", kd->type);
                base = kd->type&~VtTypeDepthMask;
                kd->type = base + sizetodepth(kd->size, kd->psize, kd->dsize);
       -fprint(2, "%d ", kd->type);
        
                /* skip full part of tree */
                for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
       t@@ -831,7 +848,6 @@ fprint(2, "%d ", kd->type);
        
                /* is the tree completely full */
                if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
       -fprint(2, "full\n");
                        memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
                        return;
                }
       t@@ -846,7 +862,6 @@ fprint(2, "full\n");
                        k->pbuf[i+1] += VtScoreSize;
                }
                memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
       -fprint(2, "%V\n", kd->score);
        }
        
        void
       t@@ -881,7 +896,6 @@ dirsinkwrite(DirSink *k, VtEntry *dir)
                        sinkwrite(k->sink, k->buf, k->p - k->buf);
                        k->p = k->buf;
                }
       -fprint(2, "write entry %V %d\n", dir->score, dir->type);
                vtentrypack(dir, k->p, 0);
                k->nentry++;
                k->p += VtEntrySize;
 (DIR) diff --git a/src/lib9/getenv.c b/src/lib9/getenv.c
       t@@ -22,6 +22,5 @@ p9putenv(char *s, char *v)
                if(t == nil)
                        return -1;
                putenv(t);
       -        free(t);
                return 0;
        }
 (DIR) diff --git a/src/lib9/mkfile b/src/lib9/mkfile
       t@@ -107,6 +107,8 @@ LIB9OFILES=\
                getuser.$O\
                getwd.$O\
                jmp.$O\
       +        lrand.$O\
       +        lnrand.$O\
                lock.$O\
                main.$O\
                malloc.$O\
       t@@ -119,6 +121,7 @@ LIB9OFILES=\
                nrand.$O\
                nulldir.$O\
                open.$O\
       +        opentemp.$O\
                pipe.$O\
                post9p.$O\
                postnote.$O\
 (DIR) diff --git a/src/lib9/pipe.c b/src/lib9/pipe.c
       t@@ -3,6 +3,11 @@
        #include <libc.h>
        #include <sys/socket.h>
        
       +/*
       + * We use socketpair to get a two-way pipe.
       + * The pipe still doesn't preserve message boundaries.
       + * Worse, it cannot be reopened via /dev/fd/NNN on Linux.
       + */
        int
        p9pipe(int fd[2])
        {
 (DIR) diff --git a/src/lib9p/_post.c b/src/lib9p/_post.c
       t@@ -0,0 +1,77 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +#include <auth.h>
       +#include "post.h"
       +
       +Postcrud*
       +_post1(Srv *s, char *name, char *mtpt, int flag)
       +{
       +        Postcrud *p;
       +
       +        p = emalloc9p(sizeof *p);
       +        if(!s->nopipe){
       +                if(pipe(p->fd) < 0)
       +                        sysfatal("pipe: %r");
       +                s->infd = s->outfd = p->fd[1];
       +                s->srvfd = p->fd[0];
       +        }
       +        if(name)
       +                if(postfd(name, s->srvfd) < 0)
       +                        sysfatal("postfd %s: %r", name);
       +        p->s = s;
       +        p->mtpt = mtpt;
       +        p->flag = flag;
       +        return p;
       +}
       +
       +void
       +_post2(void *v)
       +{
       +        Srv *s;
       +
       +        s = v;
       +        rfork(RFNOTEG);
       +        if(!s->leavefdsopen){
       +                rendezvous((ulong)s, 0);
       +                close(s->srvfd);
       +        }
       +        srv(s);
       +}
       +
       +void
       +_post3(Postcrud *p)
       +{
       +        /*
       +         * Normally the server is posting as the last thing it does
       +         * before exiting, so the correct thing to do is drop into
       +         * a different fd space and close the 9P server half of the
       +         * pipe before trying to mount the kernel half.  This way,
       +         * if the file server dies, we don't have a ref to the 9P server
       +         * half of the pipe.  Then killing the other procs will drop
       +         * all the refs on the 9P server half, and the mount will fail.
       +         * Otherwise the mount hangs forever.
       +         *
       +         * Libthread in general and acme win in particular make
       +         * it hard to make this fd bookkeeping work out properly,
       +         * so leaveinfdopen is a flag that win sets to opt out of this
       +         * safety net.
       +         */
       +        if(!p->s->leavefdsopen){
       +                rfork(RFFDG);
       +                rendezvous((ulong)p->s, 0);
       +                close(p->s->infd);
       +                if(p->s->infd != p->s->outfd)
       +                        close(p->s->outfd);
       +        }
       +
       +        if(p->mtpt){
       +                if(amount(p->s->srvfd, p->mtpt, p->flag, "") == -1)
       +                        sysfatal("mount %s: %r", p->mtpt);
       +        }else
       +                close(p->s->srvfd);
       +        free(p);
       +}
       +
 (DIR) diff --git a/src/lib9p/dirread.c b/src/lib9p/dirread.c
       t@@ -0,0 +1,40 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +void
       +dirread9p(Req *r, Dirgen *gen, void *aux)
       +{
       +        int start;
       +        uchar *p, *ep;
       +        uint rv;
       +        Dir d;
       +
       +        if(r->ifcall.offset == 0)
       +                start = 0;
       +        else
       +                start = r->fid->dirindex;
       +
       +        p = (uchar*)r->ofcall.data;
       +        ep = p+r->ifcall.count;
       +
       +        while(p < ep){
       +                memset(&d, 0, sizeof d);
       +                if((*gen)(start, &d, aux) < 0)
       +                        break;
       +                rv = convD2M(&d, p, ep-p);
       +                free(d.name);
       +                free(d.muid);
       +                free(d.uid);
       +                free(d.gid);
       +                if(rv <= BIT16SZ)
       +                        break;
       +                p += rv;
       +                start++;
       +        }
       +        r->fid->dirindex = start;
       +        r->ofcall.count = p - (uchar*)r->ofcall.data;
       +}
 (DIR) diff --git a/src/lib9p/fid.c b/src/lib9p/fid.c
       t@@ -0,0 +1,81 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include "9p.h"
       +
       +static void
       +incfidref(void *v)
       +{
       +        Fid *f;
       +
       +        f = v;
       +        if(f)
       +                incref(&f->ref);
       +}
       +
       +Fidpool*
       +allocfidpool(void (*destroy)(Fid*))
       +{
       +        Fidpool *f;
       +
       +        f = emalloc9p(sizeof *f);
       +        f->map = allocmap(incfidref);
       +        f->destroy = destroy;
       +        return f;
       +}
       +
       +void
       +freefidpool(Fidpool *p)
       +{
       +        freemap(p->map, (void(*)(void*))p->destroy);
       +        free(p);
       +}
       +
       +Fid*
       +allocfid(Fidpool *pool, ulong fid)
       +{
       +        Fid *f;
       +
       +        f = emalloc9p(sizeof *f);
       +        f->fid = fid;
       +        f->omode = -1;
       +        f->pool = pool;
       +
       +        incfidref(f);
       +        incfidref(f);
       +        if(caninsertkey(pool->map, fid, f) == 0){
       +                closefid(f);
       +                return nil;
       +        }
       +
       +        return f;
       +}
       +
       +Fid*
       +lookupfid(Fidpool *pool, ulong fid)
       +{
       +        return lookupkey(pool->map, fid);
       +}
       +
       +void
       +closefid(Fid *f)
       +{
       +        if(decref(&f->ref) == 0) {
       +                if(f->rdir)
       +                        closedirfile(f->rdir);
       +                if(f->pool->destroy)
       +                        f->pool->destroy(f);
       +                if(f->file)
       +                        closefile(f->file);
       +                free(f->uid);
       +                free(f);
       +        }
       +}
       +
       +Fid*
       +removefid(Fidpool *pool, ulong fid)
       +{
       +        return deletekey(pool->map, fid);
       +}
 (DIR) diff --git a/src/lib9p/file.c b/src/lib9p/file.c
       t@@ -0,0 +1,372 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +/*
       + * To avoid deadlock, the following rules must be followed.
       + * Always lock child then parent, never parent then child.
       + * If holding the free file lock, do not lock any Files.
       + */
       +struct Filelist {
       +        File *f;
       +        Filelist *link;
       +};
       +
       +static QLock filelk;
       +static File *freefilelist;
       +
       +static File*
       +allocfile(void)
       +{
       +        int i, a;
       +        File *f;
       +        enum { N = 16 };
       +
       +        qlock(&filelk);
       +        if(freefilelist == nil){
       +                f = emalloc9p(N*sizeof(*f));
       +                for(i=0; i<N-1; i++)
       +                        f[i].aux = &f[i+1];
       +                f[N-1].aux = nil;
       +                f[0].allocd = 1;
       +                freefilelist = f;
       +        }
       +
       +        f = freefilelist;
       +        freefilelist = f->aux;
       +        qunlock(&filelk);
       +
       +        a = f->allocd;
       +        memset(f, 0, sizeof *f);
       +        f->allocd = a;
       +        return f;
       +}
       +
       +static void
       +freefile(File *f)
       +{
       +        Filelist *fl, *flnext;
       +
       +        for(fl=f->filelist; fl; fl=flnext){
       +                flnext = fl->link;
       +                assert(fl->f == nil);
       +                free(fl);
       +        }
       +
       +        free(f->dir.name);
       +        free(f->dir.uid);
       +        free(f->dir.gid);
       +        free(f->dir.muid);
       +        qlock(&filelk);
       +        assert(f->ref.ref == 0);
       +        f->aux = freefilelist;
       +        freefilelist = f;
       +        qunlock(&filelk);
       +}
       +
       +void
       +closefile(File *f)
       +{
       +        if(decref(&f->ref) == 0){
       +                f->tree->destroy(f);
       +                freefile(f);
       +        }
       +}
       +
       +static void
       +nop(File *f)
       +{
       +        USED(f);
       +}
       +
       +int
       +removefile(File *f)
       +{
       +        File *fp;
       +        Filelist *fl;
       +        
       +        fp = f->parent;
       +        if(fp == nil){
       +                werrstr("no parent");
       +                closefile(f);
       +                return -1;
       +        }
       +
       +        if(fp == f){
       +                werrstr("cannot remove root");
       +                closefile(f);
       +                return -1;
       +        }
       +
       +        wlock(&fp->rwlock);
       +        wlock(&f->rwlock);
       +        if(f->nchild != 0){
       +                werrstr("has children");
       +                wunlock(&f->rwlock);
       +                wunlock(&fp->rwlock);
       +                closefile(f);
       +                return -1;
       +        }
       +
       +        if(f->parent != fp){
       +                werrstr("parent changed underfoot");
       +                wunlock(&f->rwlock);
       +                wunlock(&fp->rwlock);
       +                closefile(f);
       +                return -1;
       +        }
       +
       +        for(fl=fp->filelist; fl; fl=fl->link)
       +                if(fl->f == f)
       +                        break;
       +        assert(fl != nil && fl->f == f);
       +
       +        fl->f = nil;
       +        fp->nchild--;
       +        f->parent = nil;
       +        wunlock(&fp->rwlock);
       +        wunlock(&f->rwlock);
       +
       +        closefile(fp);        /* reference from child */
       +        closefile(f);        /* reference from tree */
       +        closefile(f);
       +        return 0;
       +}
       +
       +File*
       +createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
       +{
       +        File *f;
       +        Filelist *fl, *freel;
       +        Tree *t;
       +
       +        if((fp->dir.qid.type&QTDIR) == 0){
       +                werrstr("create in non-directory");
       +                return nil;
       +        }
       +
       +        freel = nil;
       +        wlock(&fp->rwlock);
       +        for(fl=fp->filelist; fl; fl=fl->link){
       +                if(fl->f == nil)
       +                        freel = fl;
       +                else if(strcmp(fl->f->dir.name, name) == 0){
       +                        wunlock(&fp->rwlock);
       +                        werrstr("file already exists");
       +                        return nil;
       +                }
       +        }
       +
       +        if(freel == nil){
       +                freel = emalloc9p(sizeof *freel);
       +                freel->link = fp->filelist;
       +                fp->filelist = freel;
       +        }
       +
       +        f = allocfile();
       +        f->dir.name = estrdup9p(name);
       +        f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid);
       +        f->dir.gid = estrdup9p(fp->dir.gid);
       +        f->dir.muid = estrdup9p(uid ? uid : "unknown");
       +        f->aux = aux;
       +        f->dir.mode = perm;
       +
       +        t = fp->tree;
       +        lock(&t->genlock);
       +        f->dir.qid.path = t->qidgen++;
       +        unlock(&t->genlock);
       +        if(perm & DMDIR)
       +                f->dir.qid.type |= QTDIR;
       +        if(perm & DMAPPEND)
       +                f->dir.qid.type |= QTAPPEND;
       +        if(perm & DMEXCL)
       +                f->dir.qid.type |= QTEXCL;
       +
       +        f->dir.mode = perm;
       +        f->dir.atime = f->dir.mtime = time(0);
       +        f->dir.length = 0;
       +        f->parent = fp;
       +        incref(&fp->ref);
       +        f->tree = fp->tree;
       +
       +        incref(&f->ref);        /* being returned */
       +        incref(&f->ref);        /* for the tree */
       +        freel->f = f;
       +        fp->nchild++;
       +        wunlock(&fp->rwlock);
       +
       +        return f;
       +}
       +
       +static File*
       +walkfile1(File *dir, char *elem)
       +{
       +        File *fp;
       +        Filelist *fl;
       +
       +        rlock(&dir->rwlock);
       +        if(strcmp(elem, "..") == 0){
       +                fp = dir->parent;
       +                incref(&fp->ref);
       +                runlock(&dir->rwlock);
       +                closefile(dir);
       +                return fp;
       +        }
       +
       +        fp = nil;
       +        for(fl=dir->filelist; fl; fl=fl->link)
       +                if(fl->f && strcmp(fl->f->dir.name, elem)==0){
       +                        fp = fl->f;
       +                        incref(&fp->ref);
       +                        break;
       +                }
       +
       +        runlock(&dir->rwlock);
       +        closefile(dir);
       +        return fp;
       +}
       +
       +File*
       +walkfile(File *f, char *path)
       +{
       +        char *os, *s, *nexts;
       +        File *nf;
       +
       +        if(strchr(path, '/') == nil)
       +                return walkfile1(f, path);        /* avoid malloc */
       +
       +        os = s = estrdup9p(path);
       +        incref(&f->ref);
       +        for(; *s; s=nexts){
       +                if(nexts = strchr(s, '/'))
       +                        *nexts++ = '\0';
       +                else
       +                        nexts = s+strlen(s);
       +                nf = walkfile1(f, s);
       +                decref(&f->ref);
       +                f = nf;
       +                if(f == nil)
       +                        break;
       +        }
       +        free(os);
       +        return f;
       +}
       +                        
       +Tree*
       +alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
       +{
       +        char *muid;
       +        Tree *t;
       +        File *f;
       +
       +        t = emalloc9p(sizeof *t);
       +        f = allocfile();
       +        f->dir.name = estrdup9p("/");
       +        if(uid == nil){
       +                if(uid = getuser())
       +                        uid = estrdup9p(uid);
       +        }
       +        if(uid == nil)
       +                uid = estrdup9p("none");
       +        else
       +                uid = estrdup9p(uid);
       +
       +        if(gid == nil)
       +                gid = estrdup9p(uid);
       +        else
       +                gid = estrdup9p(gid);
       +
       +        muid = estrdup9p(uid);
       +
       +        f->dir.qid = (Qid){0, 0, QTDIR};
       +        f->dir.length = 0;
       +        f->dir.atime = f->dir.mtime = time(0);
       +        f->dir.mode = DMDIR | mode;
       +        f->tree = t;
       +        f->parent = f;
       +        f->dir.uid = uid;
       +        f->dir.gid = gid;
       +        f->dir.muid = muid;
       +
       +        incref(&f->ref);
       +        t->root = f;
       +        t->qidgen = 0;
       +        t->dirqidgen = 1;
       +        if(destroy == nil)
       +                destroy = nop;
       +        t->destroy = destroy;
       +
       +        return t;
       +}
       +
       +static void
       +_freefiles(File *f)
       +{
       +        Filelist *fl, *flnext;
       +
       +        for(fl=f->filelist; fl; fl=flnext){
       +                flnext = fl->link;
       +                _freefiles(fl->f);
       +                free(fl);
       +        }
       +
       +        f->tree->destroy(f);
       +        freefile(f);
       +}
       +
       +void
       +freetree(Tree *t)
       +{
       +        _freefiles(t->root);
       +        free(t);
       +}
       +
       +struct Readdir {
       +        Filelist *fl;
       +};
       +
       +Readdir*
       +opendirfile(File *dir)
       +{
       +        Readdir *r;
       +
       +        rlock(&dir->rwlock);
       +        if((dir->dir.mode & DMDIR)==0){
       +                runlock(&dir->rwlock);
       +                return nil;
       +        }
       +        r = emalloc9p(sizeof(*r));
       +
       +        /*
       +         * This reference won't go away while we're using it
       +         * since we are dir->rdir.
       +         */
       +        r->fl = dir->filelist;
       +        runlock(&dir->rwlock);
       +        return r;
       +}
       +
       +long
       +readdirfile(Readdir *r, uchar *buf, long n)
       +{
       +        long x, m;
       +        Filelist *fl;
       +
       +        for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
       +                if(fl->f == nil)
       +                        x = 0;
       +                else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ)
       +                        break;
       +        }
       +        r->fl = fl;
       +        return m;
       +}
       +
       +void
       +closedirfile(Readdir *r)
       +{
       +        free(r);
       +}
 (DIR) diff --git a/src/lib9p/ftest.c b/src/lib9p/ftest.c
       t@@ -0,0 +1,29 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include "9p.h"
       +
       +void
       +main(void)
       +{
       +        Tree *t;
       +        File *hello, *goodbye, *world;
       +
       +        t = mktree();
       +
       +        hello = fcreate(t->root, "hello", CHDIR|0777);
       +        assert(hello != nil);
       +
       +        goodbye = fcreate(t->root, "goodbye", CHDIR|0777);
       +        assert(goodbye != nil);
       +
       +        world = fcreate(hello, "world", 0666);
       +        assert(world != nil);
       +        world = fcreate(goodbye, "world", 0666);
       +        assert(world != nil);
       +        fdump(t->root, 0);
       +
       +        fremove(world);
       +        fdump(t->root, 0);
       +}
 (DIR) diff --git a/src/lib9p/intmap.c b/src/lib9p/intmap.c
       t@@ -0,0 +1,166 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +enum {
       +        NHASH = 128
       +};
       +
       +typedef struct Intlist        Intlist;
       +struct Intlist
       +{
       +        ulong        id;
       +        void*        aux;
       +        Intlist*        link;
       +};
       +
       +struct Intmap
       +{
       +        RWLock        rwlock;
       +        Intlist*        hash[NHASH];
       +        void (*inc)(void*);
       +};
       +
       +static ulong
       +hashid(ulong id)
       +{
       +        return id%NHASH;
       +}
       +
       +static void
       +nop(void *v)
       +{
       +        USED(v);
       +}
       +
       +Intmap*
       +allocmap(void (*inc)(void*))
       +{
       +        Intmap *m;
       +
       +        m = emalloc9p(sizeof(*m));
       +        if(inc == nil)
       +                inc = nop;
       +        m->inc = inc;
       +        return m;
       +}
       +
       +void
       +freemap(Intmap *map, void (*destroy)(void*))
       +{
       +        int i;
       +        Intlist *p, *nlink;
       +
       +        if(destroy == nil)
       +                destroy = nop;
       +        for(i=0; i<NHASH; i++){
       +                for(p=map->hash[i]; p; p=nlink){
       +                        nlink = p->link;
       +                        destroy(p->aux);
       +                        free(p);
       +                }
       +        }
       +                        
       +        free(map);
       +}
       +
       +static Intlist**
       +llookup(Intmap *map, ulong id)
       +{
       +        Intlist **lf;
       +
       +        for(lf=&map->hash[hashid(id)]; *lf; lf=&(*lf)->link)
       +                if((*lf)->id == id)
       +                        break;
       +        return lf;        
       +}
       +
       +/*
       + * The RWlock is used as expected except that we allow
       + * inc() to be called while holding it.  This is because we're
       + * locking changes to the tree structure, not to the references.
       + * Inc() is expected to have its own locking.
       + */
       +void*
       +lookupkey(Intmap *map, ulong id)
       +{
       +        Intlist *f;
       +        void *v;
       +
       +        rlock(&map->rwlock);
       +        if(f = *llookup(map, id)){
       +                v = f->aux;
       +                map->inc(v);
       +        }else
       +                v = nil;
       +        runlock(&map->rwlock);
       +        return v;
       +}
       +
       +void*
       +insertkey(Intmap *map, ulong id, void *v)
       +{
       +        Intlist *f;
       +        void *ov;
       +        ulong h;
       +
       +        wlock(&map->rwlock);
       +        if(f = *llookup(map, id)){
       +                /* no decrement for ov because we're returning it */
       +                ov = f->aux;
       +                f->aux = v;
       +        }else{
       +                f = emalloc9p(sizeof(*f));
       +                f->id = id;
       +                f->aux = v;
       +                h = hashid(id);
       +                f->link = map->hash[h];
       +                map->hash[h] = f;
       +                ov = nil;
       +        }
       +        wunlock(&map->rwlock);
       +        return ov;        
       +}
       +
       +int
       +caninsertkey(Intmap *map, ulong id, void *v)
       +{
       +        Intlist *f;
       +        int rv;
       +        ulong h;
       +
       +        wlock(&map->rwlock);
       +        if(*llookup(map, id))
       +                rv = 0;
       +        else{
       +                f = emalloc9p(sizeof *f);
       +                f->id = id;
       +                f->aux = v;
       +                h = hashid(id);
       +                f->link = map->hash[h];
       +                map->hash[h] = f;
       +                rv = 1;
       +        }
       +        wunlock(&map->rwlock);
       +        return rv;        
       +}
       +
       +void*
       +deletekey(Intmap *map, ulong id)
       +{
       +        Intlist **lf, *f;
       +        void *ov;
       +
       +        wlock(&map->rwlock);
       +        if(f = *(lf = llookup(map, id))){
       +                ov = f->aux;
       +                *lf = f->link;
       +                free(f);
       +        }else
       +                ov = nil;
       +        wunlock(&map->rwlock);
       +        return ov;
       +}
 (DIR) diff --git a/src/lib9p/mem.c b/src/lib9p/mem.c
       t@@ -0,0 +1,49 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include "9p.h"
       +
       +void*
       +emalloc9p(ulong sz)
       +{
       +        void *v;
       +
       +        if((v = malloc(sz)) == nil) {
       +                fprint(2, "out of memory allocating %lud\n", sz);
       +                exits("mem");
       +        }
       +        memset(v, 0, sz);
       +        setmalloctag(v, getcallerpc(&sz));
       +        return v;
       +}
       +
       +void*
       +erealloc9p(void *v, ulong sz)
       +{
       +        void *nv;
       +
       +        if((nv = realloc(v, sz)) == nil) {
       +                fprint(2, "out of memory allocating %lud\n", sz);
       +                exits("mem");
       +        }
       +        if(v == nil)
       +                setmalloctag(nv, getcallerpc(&v));
       +        setrealloctag(nv, getcallerpc(&v));
       +        return nv;
       +}
       +
       +char*
       +estrdup9p(char *s)
       +{
       +        char *t;
       +
       +        if((t = strdup(s)) == nil) {
       +                fprint(2, "out of memory in strdup(%.10s)\n", s);
       +                exits("mem");
       +        }
       +        setmalloctag(t, getcallerpc(&s));
       +        return t;
       +}
       +
 (DIR) diff --git a/src/lib9p/mkfile b/src/lib9p/mkfile
       t@@ -0,0 +1,20 @@
       +PLAN9=../..
       +<$PLAN9/src/mkhdr
       +
       +LIB=lib9p.a
       +OFILES=\
       +        _post.$O\
       +        dirread.$O\
       +        fid.$O\
       +        file.$O\
       +        intmap.$O\
       +        mem.$O\
       +        req.$O\
       +        parse.$O\
       +        post.$O\
       +        srv.$O\
       +        tpost.$O\
       +        uid.$O\
       +        util.$O\
       +
       +<$PLAN9/src/mksyslib
 (DIR) diff --git a/src/lib9p/parse.c b/src/lib9p/parse.c
       t@@ -0,0 +1,115 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +/*
       + * Generous estimate of number of fields, including terminal nil pointer
       + */
       +static int
       +ncmdfield(char *p, int n)
       +{
       +        int white, nwhite;
       +        char *ep;
       +        int nf;
       +
       +        if(p == nil)
       +                return 1;
       +
       +        nf = 0;
       +        ep = p+n;
       +        white = 1;        /* first text will start field */
       +        while(p < ep){
       +                nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0);        /* UTF is irrelevant */
       +                if(white && !nwhite)        /* beginning of field */
       +                        nf++;
       +                white = nwhite;
       +        }
       +        return nf+1;        /* +1 for nil */
       +}
       +
       +/*
       + *  parse a command written to a device
       + */
       +Cmdbuf*
       +parsecmd(char *p, int n)
       +{
       +        Cmdbuf *cb;
       +        int nf;
       +        char *sp;
       +
       +        nf = ncmdfield(p, n);
       +
       +        /* allocate Cmdbuf plus string pointers plus copy of string including \0 */
       +        sp = emalloc9p(sizeof(*cb) + nf * sizeof(char*) + n + 1);
       +        cb = (Cmdbuf*)sp;
       +        cb->f = (char**)(&cb[1]);
       +        cb->buf = (char*)(&cb->f[nf]);
       +
       +        memmove(cb->buf, p, n);
       +
       +        /* dump new line and null terminate */
       +        if(n > 0 && cb->buf[n-1] == '\n')
       +                n--;
       +        cb->buf[n] = '\0';
       +
       +        cb->nf = tokenize(cb->buf, cb->f, nf-1);
       +        cb->f[cb->nf] = nil;
       +
       +        return cb;
       +}
       +
       +/*
       + * Reconstruct original message, for error diagnostic
       + */
       +void
       +respondcmderror(Req *r, Cmdbuf *cb, char *fmt, ...)
       +{
       +        int i;
       +        va_list arg;
       +        char *p, *e;
       +        char err[ERRMAX];
       +        
       +        e = err+ERRMAX-10;
       +        va_start(arg, fmt);
       +        p = vseprint(err, e, fmt, arg);
       +        va_end(arg);
       +        p = seprint(p, e, ": \"");
       +        for(i=0; i<cb->nf; i++){
       +                if(i > 0)
       +                        p = seprint(p, e, " ");
       +                p = seprint(p, e, "%q", cb->f[i]);
       +        }
       +        strcpy(p, "\"");
       +        respond(r, err);
       +}
       +
       +/*
       + * Look up entry in table
       + */
       +Cmdtab*
       +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
       +{
       +        int i;
       +        Cmdtab *ct;
       +
       +        if(cb->nf == 0){
       +                werrstr("empty control message");
       +                return nil;
       +        }
       +
       +        for(ct = ctab, i=0; i<nctab; i++, ct++){
       +                if(strcmp(ct->cmd, "*") !=0)        /* wildcard always matches */
       +                if(strcmp(ct->cmd, cb->f[0]) != 0)
       +                        continue;
       +                if(ct->narg != 0 && ct->narg != cb->nf){
       +                        werrstr("bad # args to command");
       +                        return nil;
       +                }
       +                return ct;
       +        }
       +
       +        werrstr("unknown control message");
       +        return nil;
       +}
 (DIR) diff --git a/src/lib9p/post.c b/src/lib9p/post.c
       t@@ -0,0 +1,24 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +#include "post.h"
       +
       +void
       +postmountsrv(Srv *s, char *name, char *mtpt, int flag)
       +{
       +        Postcrud *p;
       +
       +        p = _post1(s, name, mtpt, flag);
       +        switch(rfork(RFPROC|RFNOTEG|RFNAMEG|RFMEM)){
       +        case -1:
       +                sysfatal("rfork: %r");
       +        case 0:
       +                _post2(s);
       +                exits(nil);
       +        default:
       +                _post3(p);
       +        }
       +}
       +
 (DIR) diff --git a/src/lib9p/post.h b/src/lib9p/post.h
       t@@ -0,0 +1,13 @@
       +typedef struct Postcrud Postcrud;
       +struct Postcrud
       +{
       +        int fd[2];
       +        Srv *s;
       +        char *name;
       +        char *mtpt;
       +        int flag;
       +};
       +
       +Postcrud *_post1(Srv*, char*, char*, int);
       +void _post2(void*);
       +void _post3(Postcrud*);
 (DIR) diff --git a/src/lib9p/ramfs.c b/src/lib9p/ramfs.c
       t@@ -0,0 +1,163 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +static char Ebad[] = "something bad happened";
       +static char Enomem[] = "no memory";
       +
       +typedef struct Ramfile        Ramfile;
       +struct Ramfile {
       +        char *data;
       +        int ndata;
       +};
       +
       +void
       +fsread(Req *r)
       +{
       +        Ramfile *rf;
       +        vlong offset;
       +        long count;
       +
       +        rf = r->fid->file->aux;
       +        offset = r->ifcall.offset;
       +        count = r->ifcall.count;
       +
       +//print("read %ld %lld\n", *count, offset);
       +        if(offset >= rf->ndata){
       +                r->ofcall.count = 0;
       +                respond(r, nil);
       +                return;
       +        }
       +
       +        if(offset+count >= rf->ndata)
       +                count = rf->ndata - offset;
       +
       +        memmove(r->ofcall.data, rf->data+offset, count);
       +        r->ofcall.count = count;
       +        respond(r, nil);
       +}
       +
       +void
       +fswrite(Req *r)
       +{
       +        void *v;
       +        Ramfile *rf;
       +        vlong offset;
       +        long count;
       +
       +        rf = r->fid->file->aux;
       +        offset = r->ifcall.offset;
       +        count = r->ifcall.count;
       +
       +        if(offset+count >= rf->ndata){
       +                v = realloc(rf->data, offset+count);
       +                if(v == nil){
       +                        respond(r, Enomem);
       +                        return;
       +                }
       +                rf->data = v;
       +                rf->ndata = offset+count;
       +                r->fid->file->length = rf->ndata;
       +        }
       +        memmove(rf->data+offset, r->ifcall.data, count);
       +        r->ofcall.count = count;
       +        respond(r, nil);
       +}
       +
       +void
       +fscreate(Req *r)
       +{
       +        Ramfile *rf;
       +        File *f;
       +
       +        if(f = createfile(r->fid->file, r->ifcall.name, r->fid->uid, r->ifcall.perm, nil)){
       +                rf = emalloc9p(sizeof *rf);
       +                f->aux = rf;
       +                r->fid->file = f;
       +                r->ofcall.qid = f->qid;
       +                respond(r, nil);
       +                return;
       +        }
       +        respond(r, Ebad);
       +}
       +
       +void
       +fsopen(Req *r)
       +{
       +        Ramfile *rf;
       +
       +        rf = r->fid->file->aux;
       +
       +        if(rf && (r->ifcall.mode&OTRUNC)){
       +                rf->ndata = 0;
       +                r->fid->file->length = 0;
       +        }
       +
       +        respond(r, nil);
       +}
       +
       +void
       +fsdestroyfile(File *f)
       +{
       +        Ramfile *rf;
       +
       +//fprint(2, "clunk\n");
       +        rf = f->aux;
       +        if(rf){
       +                free(rf->data);
       +                free(rf);
       +        }
       +}
       +
       +Srv fs = {
       +        .open=        fsopen,
       +        .read=        fsread,
       +        .write=        fswrite,
       +        .create=        fscreate,
       +};
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: ramfs [-D] [-s srvname] [-m mtpt]\n");
       +        exits("usage");
       +}
       +
       +void
       +main(int argc, char **argv)
       +{
       +        char *srvname = nil;
       +        char *mtpt = nil;
       +        Qid q;
       +
       +        fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
       +        q = fs.tree->root->qid;
       +
       +        ARGBEGIN{
       +        case 'D':
       +                chatty9p++;
       +                break;
       +        case 's':
       +                srvname = EARGF(usage());
       +                break;
       +        case 'm':
       +                mtpt = EARGF(usage());
       +                break;
       +        default:
       +                usage();
       +        }ARGEND;
       +
       +        if(argc)
       +                usage();
       +
       +        if(chatty9p)
       +                fprint(2, "ramsrv.nopipe %d srvname %s mtpt %s\n", fs.nopipe, srvname, mtpt);
       +        if(srvname == nil && mtpt == nil)
       +                sysfatal("you should at least specify a -s or -m option");
       +
       +        postmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
       +        exits(0);
       +}
 (DIR) diff --git a/src/lib9p/req.c b/src/lib9p/req.c
       t@@ -0,0 +1,112 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +static void
       +increqref(void *v)
       +{
       +        Req *r;
       +
       +        r = v;
       +        if(r){
       +if(chatty9p > 1)
       +        fprint(2, "increfreq %p %ld\n", r, r->ref.ref);
       +                incref(&r->ref);
       +        }
       +}
       +
       +Reqpool*
       +allocreqpool(void (*destroy)(Req*))
       +{
       +        Reqpool *f;
       +
       +        f = emalloc9p(sizeof *f);
       +        f->map = allocmap(increqref);
       +        f->destroy = destroy;
       +        return f;
       +}
       +
       +void
       +freereqpool(Reqpool *p)
       +{
       +        freemap(p->map, (void(*)(void*))p->destroy);
       +        free(p);
       +}        
       +
       +Req*
       +allocreq(Reqpool *pool, ulong tag)
       +{
       +        Req *r;
       +
       +        r = emalloc9p(sizeof *r);
       +        r->tag = tag;
       +        r->pool = pool;
       +
       +        increqref(r);
       +        increqref(r);
       +        if(caninsertkey(pool->map, tag, r) == 0){
       +                closereq(r);
       +                return nil;
       +        }
       +
       +        return r;
       +}
       +
       +Req*
       +lookupreq(Reqpool *pool, ulong tag)
       +{
       +if(chatty9p > 1)
       +        fprint(2, "lookupreq %lud\n", tag);
       +        return lookupkey(pool->map, tag);
       +}
       +
       +void
       +closereq(Req *r)
       +{
       +        int i;
       +
       +        if(r == nil)
       +                return;
       +
       +if(chatty9p > 1)
       +        fprint(2, "closereq %p %ld\n", r, r->ref.ref);
       +
       +        if(decref(&r->ref) == 0){
       +                if(r->fid)
       +                        closefid(r->fid);
       +                if(r->newfid)
       +                        closefid(r->newfid);
       +                if(r->afid)
       +                        closefid(r->afid);
       +                if(r->oldreq)
       +                        closereq(r->oldreq);
       +                for(i=0; i<r->nflush; i++)
       +                        respond(r->flush[i], nil);
       +                free(r->flush);
       +                switch(r->ifcall.type){
       +                case Tstat:
       +                        free(r->ofcall.stat);
       +                        free(r->d.name);
       +                        free(r->d.uid);
       +                        free(r->d.gid);
       +                        free(r->d.muid);
       +                        break;
       +                }
       +                if(r->pool->destroy)
       +                        r->pool->destroy(r);
       +                free(r->buf);
       +                free(r->rbuf);
       +                free(r);
       +        }
       +}
       +
       +Req*
       +removereq(Reqpool *pool, ulong tag)
       +{
       +if(chatty9p > 1)
       +        fprint(2, "removereq %lud\n", tag);
       +        return deletekey(pool->map, tag);
       +}
 (DIR) diff --git a/src/lib9p/srv.c b/src/lib9p/srv.c
       t@@ -0,0 +1,837 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +// static char Ebadattach[] = "unknown specifier in attach";
       +static char Ebadoffset[] = "bad offset";
       +// static char Ebadcount[] = "bad count";
       +static char Ebotch[] = "9P protocol botch";
       +static char Ecreatenondir[] = "create in non-directory";
       +static char Edupfid[] = "duplicate fid";
       +static char Eduptag[] = "duplicate tag";
       +static char Eisdir[] = "is a directory";
       +static char Enocreate[] = "create prohibited";
       +// static char Enomem[] = "out of memory";
       +static char Enoremove[] = "remove prohibited";
       +static char Enostat[] = "stat prohibited";
       +static char Enotfound[] = "file not found";
       +// static char Enowrite[] = "write prohibited";
       +static char Enowstat[] = "wstat prohibited";
       +static char Eperm[] = "permission denied";
       +static char Eunknownfid[] = "unknown fid";
       +static char Ebaddir[] = "bad directory in wstat";
       +static char Ewalknodir[] = "walk in non-directory";
       +
       +static void
       +setfcallerror(Fcall *f, char *err)
       +{
       +        f->ename = err;
       +        f->type = Rerror;
       +}
       +
       +static void
       +changemsize(Srv *srv, int msize)
       +{
       +        if(srv->rbuf && srv->wbuf && srv->msize == msize)
       +                return;
       +        qlock(&srv->rlock);
       +        qlock(&srv->wlock);
       +        srv->msize = msize;
       +        free(srv->rbuf);
       +        free(srv->wbuf);
       +        srv->rbuf = emalloc9p(msize);
       +        srv->wbuf = emalloc9p(msize);
       +        qunlock(&srv->rlock);
       +        qunlock(&srv->wlock);
       +}
       +
       +static Req*
       +getreq(Srv *s)
       +{
       +        long n;
       +        uchar *buf;
       +        Fcall f;
       +        Req *r;
       +
       +        qlock(&s->rlock);
       +        if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
       +                qunlock(&s->rlock);
       +                return nil;
       +        }
       +
       +        buf = emalloc9p(n);
       +        memmove(buf, s->rbuf, n);
       +        qunlock(&s->rlock);
       +
       +        if(convM2S(buf, n, &f) != n){
       +                free(buf);
       +                return nil;
       +        }
       +
       +        if((r=allocreq(s->rpool, f.tag)) == nil){        /* duplicate tag: cons up a fake Req */
       +                r = emalloc9p(sizeof *r);
       +                incref(&r->ref);
       +                r->tag = f.tag;
       +                r->ifcall = f;
       +                r->error = Eduptag;
       +                r->buf = buf;
       +                r->responded = 0;
       +                r->type = 0;
       +                r->srv = s;
       +                r->pool = nil;
       +if(chatty9p)
       +        fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
       +                return r;
       +        }
       +
       +        r->srv = s;
       +        r->responded = 0;
       +        r->buf = buf;
       +        r->ifcall = f;
       +        memset(&r->ofcall, 0, sizeof r->ofcall);
       +        r->type = r->ifcall.type;
       +
       +if(chatty9p)
       +        if(r->error)
       +                fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
       +        else        
       +                fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
       +
       +        return r;
       +}
       +
       +static void
       +filewalk(Req *r)
       +{
       +        int i;
       +        File *f;
       +
       +        f = r->fid->file;
       +        assert(f != nil);
       +
       +        incref(&f->ref);
       +        for(i=0; i<r->ifcall.nwname; i++)
       +                if(f = walkfile(f, r->ifcall.wname[i]))
       +                        r->ofcall.wqid[i] = f->dir.qid;
       +                else
       +                        break;
       +
       +        r->ofcall.nwqid = i;
       +        if(f){
       +                r->newfid->file = f;
       +                r->newfid->qid = r->newfid->file->dir.qid;
       +        }
       +        respond(r, nil);
       +}
       +
       +void
       +walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
       +{
       +        int i;
       +        char *e;
       +
       +        if(r->fid == r->newfid && r->ifcall.nwname > 1){
       +                respond(r, "lib9p: unused documented feature not implemented");
       +                return;
       +        }
       +
       +        if(r->fid != r->newfid){
       +                r->newfid->qid = r->fid->qid;
       +                if(clone && (e = clone(r->fid, r->newfid, arg))){
       +                        respond(r, e);
       +                        return;
       +                }
       +        }
       +
       +        e = nil;
       +        for(i=0; i<r->ifcall.nwname; i++){
       +                if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
       +                        break;
       +                r->ofcall.wqid[i] = r->newfid->qid;
       +        }
       +
       +        r->ofcall.nwqid = i;
       +        if(e && i==0)
       +                respond(r, e);
       +        else
       +                respond(r, nil);
       +}
       +
       +static void
       +sversion(Srv *srv, Req *r)
       +{
       +        USED(srv);
       +
       +        if(strncmp(r->ifcall.version, "9P", 2) != 0){
       +                r->ofcall.version = "unknown";
       +                respond(r, nil);
       +                return;
       +        }
       +
       +        r->ofcall.version = "9P2000";
       +        r->ofcall.msize = r->ifcall.msize;
       +        respond(r, nil);
       +}
       +static void
       +rversion(Req *r, char *error)
       +{
       +        assert(error == nil);
       +        changemsize(r->srv, r->ofcall.msize);
       +}
       +
       +static void
       +sauth(Srv *srv, Req *r)
       +{
       +        char e[ERRMAX];
       +
       +        if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
       +                respond(r, Edupfid);
       +                return;
       +        }
       +        if(srv->auth)
       +                srv->auth(r);
       +        else{
       +                snprint(e, sizeof e, "%s: authentication not required", argv0);
       +                respond(r, e);
       +        }
       +}
       +static void
       +rauth(Req *r, char *error)
       +{
       +        if(error && r->afid)
       +                closefid(removefid(r->srv->fpool, r->afid->fid));
       +}
       +
       +static void
       +sattach(Srv *srv, Req *r)
       +{
       +        if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Edupfid);
       +                return;
       +        }
       +        r->afid = nil;
       +        if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        r->fid->uid = estrdup9p(r->ifcall.uname);
       +        if(srv->tree){
       +                r->fid->file = srv->tree->root;
       +                /* BUG? incref(r->fid->file) ??? */
       +                r->ofcall.qid = r->fid->file->dir.qid;
       +                r->fid->qid = r->ofcall.qid;
       +        }
       +        if(srv->attach)
       +                srv->attach(r);
       +        else
       +                respond(r, nil);
       +        return;
       +}
       +static void
       +rattach(Req *r, char *error)
       +{
       +        if(error && r->fid)
       +                closefid(removefid(r->srv->fpool, r->fid->fid));
       +}
       +
       +static void
       +sflush(Srv *srv, Req *r)
       +{
       +        r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
       +        if(r->oldreq == nil || r->oldreq == r)
       +                respond(r, nil);
       +        else if(srv->flush)
       +                srv->flush(r);
       +        else
       +                respond(r, nil);
       +}
       +static int
       +rflush(Req *r, char *error)
       +{
       +        Req *or;
       +
       +        assert(error == nil);
       +        or = r->oldreq;
       +        if(or){
       +                qlock(&or->lk);
       +                if(or->responded == 0){
       +                        or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
       +                        or->flush[or->nflush++] = r;
       +                        qunlock(&or->lk);
       +                        return -1;                /* delay response until or is responded */
       +                }
       +                qunlock(&or->lk);
       +                closereq(or);
       +        }
       +        r->oldreq = nil;
       +        return 0;
       +}
       +
       +static char*
       +oldwalk1(Fid *fid, char *name, void *arg)
       +{
       +        char *e;
       +        Qid qid;
       +        Srv *srv;
       +
       +        srv = arg;
       +        e = srv->walk1(fid, name, &qid);
       +        if(e)
       +                return e;
       +        fid->qid = qid;
       +        return nil;
       +}
       +
       +static char*
       +oldclone(Fid *fid, Fid *newfid, void *arg)
       +{
       +        Srv *srv;
       +
       +        srv = arg;
       +        if(srv->clone == nil)
       +                return nil;
       +        return srv->clone(fid, newfid);
       +}
       +
       +static void
       +swalk(Srv *srv, Req *r)
       +{
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        if(r->fid->omode != -1){
       +                respond(r, "cannot clone open fid");
       +                return;
       +        }
       +        if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
       +                respond(r, Ewalknodir);
       +                return;
       +        }
       +        if(r->ifcall.fid != r->ifcall.newfid){
       +                if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
       +                        respond(r, Edupfid);
       +                        return;
       +                }
       +                r->newfid->uid = estrdup9p(r->fid->uid);
       +        }else{
       +                incref(&r->fid->ref);
       +                r->newfid = r->fid;
       +        }
       +        if(r->fid->file){
       +                filewalk(r);
       +        }else if(srv->walk1)
       +                walkandclone(r, oldwalk1, oldclone, srv);
       +        else if(srv->walk)
       +                srv->walk(r);
       +        else
       +                sysfatal("no walk function, no file trees");
       +}
       +static void
       +rwalk(Req *r, char *error)
       +{
       +        if(error || r->ofcall.nwqid < r->ifcall.nwname){
       +                if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
       +                        closefid(removefid(r->srv->fpool, r->newfid->fid));
       +                if (r->ofcall.nwqid==0){
       +                        if(error==nil && r->ifcall.nwname!=0)
       +                                r->error = Enotfound;
       +                }else
       +                        r->error = nil;        // No error on partial walks
       +        }else{
       +                if(r->ofcall.nwqid == 0){
       +                        /* Just a clone */
       +                        r->newfid->qid = r->fid->qid;
       +                }else{
       +                        /* if file trees are in use, filewalk took care of the rest */
       +                        r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
       +                }
       +        }
       +}
       +
       +static void
       +sopen(Srv *srv, Req *r)
       +{
       +        int p;
       +
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        if(r->fid->omode != -1){
       +                respond(r, Ebotch);
       +                return;
       +        }
       +        if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
       +                respond(r, Eisdir);
       +                return;
       +        }
       +        r->ofcall.qid = r->fid->qid;
       +        switch(r->ifcall.mode&3){
       +        default:
       +                assert(0);
       +        case OREAD:
       +                p = AREAD;        
       +                break;
       +        case OWRITE:
       +                p = AWRITE;
       +                break;
       +        case ORDWR:
       +                p = AREAD|AWRITE;
       +                break;
       +        case OEXEC:
       +                p = AEXEC;        
       +                break;
       +        }
       +        if(r->ifcall.mode&OTRUNC)
       +                p |= AWRITE;
       +        if((r->fid->qid.type&QTDIR) && p!=AREAD){
       +                respond(r, Eperm);
       +                return;
       +        }
       +        if(r->fid->file){
       +                if(!hasperm(r->fid->file, r->fid->uid, p)){
       +                        respond(r, Eperm);
       +                        return;
       +                }
       +        /* BUG RACE */
       +                if((r->ifcall.mode&ORCLOSE)
       +                && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
       +                        respond(r, Eperm);
       +                        return;
       +                }
       +                r->ofcall.qid = r->fid->file->dir.qid;
       +                if((r->ofcall.qid.type&QTDIR)
       +                && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
       +                        respond(r, "opendirfile failed");
       +                        return;
       +                }
       +        }
       +        if(srv->open)
       +                srv->open(r);
       +        else
       +                respond(r, nil);
       +}
       +static void
       +ropen(Req *r, char *error)
       +{
       +        char errbuf[ERRMAX];
       +        if(error)
       +                return;
       +        if(chatty9p){
       +                snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
       +                write(2, errbuf, strlen(errbuf));
       +        }
       +        r->fid->omode = r->ifcall.mode;
       +        r->fid->qid = r->ofcall.qid;
       +        if(r->ofcall.qid.type&QTDIR)
       +                r->fid->diroffset = 0;
       +}
       +
       +static void
       +screate(Srv *srv, Req *r)
       +{
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
       +                respond(r, Eunknownfid);
       +        else if(r->fid->omode != -1)
       +                respond(r, Ebotch);
       +        else if(!(r->fid->qid.type&QTDIR))
       +                respond(r, Ecreatenondir);
       +        else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
       +                respond(r, Eperm);
       +        else if(srv->create)
       +                srv->create(r);
       +        else
       +                respond(r, Enocreate);
       +}
       +static void
       +rcreate(Req *r, char *error)
       +{
       +        if(error)
       +                return;
       +        r->fid->omode = r->ifcall.mode;
       +        r->fid->qid = r->ofcall.qid;
       +}
       +
       +static void
       +sread(Srv *srv, Req *r)
       +{
       +        int o;
       +
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        if(r->ifcall.count < 0){
       +                respond(r, Ebotch);
       +                return;
       +        }
       +        if(r->ifcall.offset < 0
       +        || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
       +                respond(r, Ebadoffset);
       +                return;
       +        }
       +
       +        if(r->ifcall.count > srv->msize - IOHDRSZ)
       +                r->ifcall.count = srv->msize - IOHDRSZ;
       +        r->rbuf = emalloc9p(r->ifcall.count);
       +        r->ofcall.data = r->rbuf;
       +        o = r->fid->omode & 3;
       +        if(o != OREAD && o != ORDWR && o != OEXEC){
       +                respond(r, Ebotch);
       +                return;
       +        }
       +        if((r->fid->qid.type&QTDIR) && r->fid->file){
       +                r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
       +                respond(r, nil);
       +                return;
       +        }
       +        if(srv->read)
       +                srv->read(r);
       +        else
       +                respond(r, "no srv->read");
       +}
       +static void
       +rread(Req *r, char *error)
       +{
       +        if(error==nil && (r->fid->qid.type&QTDIR))
       +                r->fid->diroffset += r->ofcall.count;
       +}
       +
       +static void
       +swrite(Srv *srv, Req *r)
       +{
       +        int o;
       +        char e[ERRMAX];
       +
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        if(r->ifcall.count < 0){
       +                respond(r, Ebotch);
       +                return;
       +        }
       +        if(r->ifcall.offset < 0){
       +                respond(r, Ebotch);
       +                return;
       +        }
       +        if(r->ifcall.count > srv->msize - IOHDRSZ)
       +                r->ifcall.count = srv->msize - IOHDRSZ;
       +        o = r->fid->omode & 3;
       +        if(o != OWRITE && o != ORDWR){
       +                snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
       +                respond(r, e);
       +                return;
       +        }
       +        if(srv->write)
       +                srv->write(r);
       +        else
       +                respond(r, "no srv->write");
       +}
       +static void
       +rwrite(Req *r, char *error)
       +{
       +        if(error)
       +                return;
       +        if(r->fid->file)
       +                r->fid->file->dir.qid.vers++;
       +}
       +
       +static void
       +sclunk(Srv *srv, Req *r)
       +{
       +        if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
       +                respond(r, Eunknownfid);
       +        else
       +                respond(r, nil);
       +}
       +static void
       +rclunk(Req *r, char *msg)
       +{
       +        USED(r);
       +        USED(msg);
       +}
       +
       +static void
       +sremove(Srv *srv, Req *r)
       +{
       +        if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        /* BUG RACE */
       +        if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
       +                respond(r, Eperm);
       +                return;
       +        }
       +        if(srv->remove)
       +                srv->remove(r);
       +        else
       +                respond(r, r->fid->file ? nil : Enoremove);
       +}
       +static void
       +rremove(Req *r, char *error, char *errbuf)
       +{
       +        if(error)
       +                return;
       +        if(r->fid->file){
       +                if(removefile(r->fid->file) < 0){
       +                        snprint(errbuf, ERRMAX, "remove %s: %r", 
       +                                r->fid->file->dir.name);
       +                        r->error = errbuf;
       +                }
       +                r->fid->file = nil;
       +        }
       +}
       +
       +static void
       +sstat(Srv *srv, Req *r)
       +{
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        if(r->fid->file){
       +                r->d = r->fid->file->dir;
       +                if(r->d.name)
       +                        r->d.name = estrdup9p(r->d.name);
       +                if(r->d.uid)
       +                        r->d.uid = estrdup9p(r->d.uid);
       +                if(r->d.gid)
       +                        r->d.gid = estrdup9p(r->d.gid);
       +                if(r->d.muid)
       +                        r->d.muid = estrdup9p(r->d.muid);
       +        }
       +        if(srv->stat)        
       +                srv->stat(r);        
       +        else if(r->fid->file)
       +                respond(r, nil);
       +        else
       +                respond(r, Enostat);
       +}
       +static void
       +rstat(Req *r, char *error)
       +{
       +        int n;
       +        uchar *statbuf;
       +        uchar tmp[BIT16SZ];
       +
       +        if(error)
       +                return;
       +        if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
       +                r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
       +                return;
       +        }
       +        n = GBIT16(tmp)+BIT16SZ;
       +        statbuf = emalloc9p(n);
       +        if(statbuf == nil){
       +                r->error = "out of memory";
       +                return;
       +        }
       +        r->ofcall.nstat = convD2M(&r->d, statbuf, n);
       +        r->ofcall.stat = statbuf;        /* freed in closereq */
       +        if(r->ofcall.nstat <= BIT16SZ){
       +                r->error = "convD2M fails";
       +                free(statbuf);
       +                return;
       +        }
       +}
       +
       +static void
       +swstat(Srv *srv, Req *r)
       +{
       +        if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
       +                respond(r, Eunknownfid);
       +                return;
       +        }
       +        if(srv->wstat == nil){
       +                respond(r, Enowstat);
       +                return;
       +        }
       +        if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
       +                respond(r, Ebaddir);
       +                return;
       +        }
       +        if((ushort)~r->d.type){
       +                respond(r, "wstat -- attempt to change type");
       +                return;
       +        }
       +        if((uint)~r->d.dev){
       +                respond(r, "wstat -- attempt to change dev");
       +                return;
       +        }
       +        if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
       +                respond(r, "wstat -- attempt to change qid");
       +                return;
       +        }
       +        if(r->d.muid && r->d.muid[0]){
       +                respond(r, "wstat -- attempt to change muid");
       +                return;
       +        }
       +        if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
       +                respond(r, "wstat -- attempt to change DMDIR bit");
       +                return;
       +        }
       +        srv->wstat(r);
       +}
       +static void
       +rwstat(Req *r, char *msg)
       +{
       +        USED(r);
       +        USED(msg);
       +}
       +
       +void
       +srv(Srv *srv)
       +{
       +        Req *r;
       +
       +        fmtinstall('D', dirfmt);
       +        fmtinstall('F', fcallfmt);
       +
       +        if(srv->fpool == nil)
       +                srv->fpool = allocfidpool(srv->destroyfid);
       +        if(srv->rpool == nil)
       +                srv->rpool = allocreqpool(srv->destroyreq);
       +        if(srv->msize == 0)
       +                srv->msize = 8192+IOHDRSZ;
       +
       +        changemsize(srv, srv->msize);
       +
       +        srv->fpool->srv = srv;
       +        srv->rpool->srv = srv;
       +
       +        while(r = getreq(srv)){
       +                if(r->error){
       +                        respond(r, r->error);
       +                        continue;        
       +                }
       +                switch(r->ifcall.type){
       +                default:
       +                        respond(r, "unknown message");
       +                        break;
       +                case Tversion:        sversion(srv, r);        break;
       +                case Tauth:        sauth(srv, r);        break;
       +                case Tattach:        sattach(srv, r);        break;
       +                case Tflush:        sflush(srv, r);        break;
       +                case Twalk:        swalk(srv, r);        break;
       +                case Topen:        sopen(srv, r);        break;
       +                case Tcreate:        screate(srv, r);        break;
       +                case Tread:        sread(srv, r);        break;
       +                case Twrite:        swrite(srv, r);        break;
       +                case Tclunk:        sclunk(srv, r);        break;
       +                case Tremove:        sremove(srv, r);        break;
       +                case Tstat:        sstat(srv, r);        break;
       +                case Twstat:        swstat(srv, r);        break;
       +                }
       +        }
       +
       +        if(srv->end)
       +                srv->end(srv);
       +}
       +
       +void
       +respond(Req *r, char *error)
       +{
       +        int i, m, n;
       +        char errbuf[ERRMAX];
       +        Srv *srv;
       +
       +        srv = r->srv;
       +        assert(srv != nil);
       +
       +        assert(r->responded == 0);
       +        r->error = error;
       +
       +        switch(r->ifcall.type){
       +        default:
       +                assert(0);
       +        /*
       +         * Flush is special.  If the handler says so, we return
       +         * without further processing.  Respond will be called
       +         * again once it is safe.
       +         */
       +        case Tflush:
       +                if(rflush(r, error)<0)
       +                        return;
       +                break;
       +        case Tversion:        rversion(r, error);        break;
       +        case Tauth:        rauth(r, error);        break;
       +        case Tattach:        rattach(r, error);        break;
       +        case Twalk:        rwalk(r, error);        break;
       +        case Topen:        ropen(r, error);        break;
       +        case Tcreate:        rcreate(r, error);        break;
       +        case Tread:        rread(r, error);        break;
       +        case Twrite:        rwrite(r, error);        break;
       +        case Tclunk:        rclunk(r, error);        break;
       +        case Tremove:        rremove(r, error, errbuf);        break;
       +        case Tstat:        rstat(r, error);        break;
       +        case Twstat:        rwstat(r, error);        break;
       +        }
       +
       +        r->ofcall.tag = r->ifcall.tag;
       +        r->ofcall.type = r->ifcall.type+1;
       +        if(r->error)
       +                setfcallerror(&r->ofcall, r->error);
       +
       +if(chatty9p)
       +        fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
       +
       +        qlock(&srv->wlock);
       +        n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
       +        if(n <= 0){
       +                fprint(2, "n = %d %F\n", n, &r->ofcall);
       +                abort();
       +        }
       +        assert(n > 2);
       +        if(r->pool)        /* not a fake */
       +                closereq(removereq(r->pool, r->ifcall.tag));
       +        m = write(srv->outfd, srv->wbuf, n);
       +        if(m != n)
       +                sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
       +        qunlock(&srv->wlock);
       +
       +        qlock(&r->lk);        /* no one will add flushes now */
       +        r->responded = 1;
       +        qunlock(&r->lk);
       +
       +        for(i=0; i<r->nflush; i++)
       +                respond(r->flush[i], nil);
       +        free(r->flush);
       +
       +        if(r->pool)
       +                closereq(r);
       +        else
       +                free(r);
       +}
       +
       +int
       +postfd(char *name, int pfd)
       +{
       +        int fd;
       +        char buf[80];
       +
       +        snprint(buf, sizeof buf, "/srv/%s", name);
       +        if(chatty9p)
       +                fprint(2, "postfd %s\n", buf);
       +        fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
       +        if(fd < 0){
       +                if(chatty9p)
       +                        fprint(2, "create fails: %r\n");
       +                return -1;
       +        }
       +        if(fprint(fd, "%d", pfd) < 0){
       +                if(chatty9p)
       +                        fprint(2, "write fails: %r\n");
       +                close(fd);
       +                return -1;
       +        }
       +        if(chatty9p)
       +                fprint(2, "postfd successful\n");
       +        return 0;
       +}
       +
 (DIR) diff --git a/src/lib9p/tpost.c b/src/lib9p/tpost.c
       t@@ -0,0 +1,18 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +#include "post.h"
       +
       +void
       +threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag)
       +{
       +        Postcrud *p;
       +
       +        p = _post1(s, name, mtpt, flag);
       +        if(procrfork(_post2, s, 32*1024, RFNAMEG|RFNOTEG) < 0)
       +                sysfatal("procrfork: %r");
       +        _post3(p);
       +}
       +
 (DIR) diff --git a/src/lib9p/uid.c b/src/lib9p/uid.c
       t@@ -0,0 +1,34 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9p.h>
       +
       +/*
       + * simplistic permission checking.  assume that
       + * each user is the leader of her own group.
       + */
       +int
       +hasperm(File *f, char *uid, int p)
       +{
       +        int m;
       +
       +        m = f->dir.mode & 7;        /* other */
       +        if((p & m) == p)
       +                return 1;
       +
       +        if(strcmp(f->dir.uid, uid) == 0) {
       +                m |= (f->dir.mode>>6) & 7;
       +                if((p & m) == p)
       +                        return 1;
       +        }
       +
       +        if(strcmp(f->dir.gid, uid) == 0) {
       +                m |= (f->dir.mode>>3) & 7;
       +                if((p & m) == p)
       +                        return 1;
       +        }
       +
       +        return 0;
       +}
 (DIR) diff --git a/src/lib9p/util.c b/src/lib9p/util.c
       t@@ -0,0 +1,25 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include "9p.h"
       +
       +void
       +readbuf(Req *r, void *s, long n)
       +{
       +        r->ofcall.count = r->ifcall.count;
       +        if(r->ifcall.offset >= n){
       +                r->ofcall.count = 0;
       +                return;
       +        }
       +        if(r->ifcall.offset+r->ofcall.count > n)
       +                r->ofcall.count = n - r->ifcall.offset;
       +        memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count);
       +}
       +
       +void
       +readstr(Req *r, char *s)
       +{
       +        readbuf(r, s, strlen(s));
       +}
 (DIR) diff --git a/src/libfs/fs.c b/src/libfs/fs.c
       t@@ -259,7 +259,7 @@ _fssend(Mux *mux, void *pkt)
                Fsys *fs;
        
                fs = mux->aux;
       -        return write(fs->fd, pkt, GBIT32((uchar*)pkt));
       +        return threadwrite(fs->fd, pkt, GBIT32((uchar*)pkt));
        }
        
        static void*
 (DIR) diff --git a/src/libfs/read.c b/src/libfs/read.c
       t@@ -52,3 +52,21 @@ fsread(Fid *fid, void *buf, long n)
        {
                return fspread(fid, buf, n, -1);
        }
       +
       +long
       +fsreadn(Fid *fid, void *buf, long n)
       +{
       +        long tot, nn;
       +
       +        for(tot=0; tot<n; tot+=nn){
       +                nn = fsread(fid, (char*)buf+tot, n-tot);
       +                if(nn <= 0){
       +                        if(tot == 0)
       +                                return nn;
       +                        break;
       +                }
       +        }
       +        return tot;
       +}
       +                        
       +
 (DIR) diff --git a/src/libhttpd/gethead.c b/src/libhttpd/gethead.c
       t@@ -15,11 +15,15 @@ hgethead(HConnect *c, int many)
                int n;
        
                hin = &c->hin;
       +fprint(2, "hgethead top %p - %p\n", hin->pos, hin->stop);
                for(;;){
                        s = (char*)hin->pos;
                        pp = s;
       +fprint(2, "hgethead %p - %p\n", pp, hin->stop);
                        while(p = memchr(pp, '\n', (char*)hin->stop - pp)){
       -                        if(!many || p == pp || p == pp + 1 && *pp == '\r'){
       +fprint(2, "hgethead %p - %p newline at %p %d\n", pp, hin->stop, p, *pp);
       +                        if(!many || p == pp || (p == pp + 1 && *pp == '\r')){
       +fprint(2, "breaking\n");
                                        pp = p + 1;
                                        break;
                                }
       t@@ -32,6 +36,7 @@ hgethead(HConnect *c, int many)
                        memmove(c->hstop, s, n);
                        c->hstop += n;
                        *c->hstop = '\0';
       +fprint(2, "p %p\n", p);
                        if(p != nil)
                                return 1;
                        if(hreadbuf(hin, hin->pos) == nil || hin->state == Hend)
 (DIR) diff --git a/src/libhttpd/hio.c b/src/libhttpd/hio.c
       t@@ -157,10 +157,15 @@ hreadbuf(Hio *h, void *vsave)
                                memmove(h->start + cpy, hh->pos, in);
                                hh->pos += in;
                        }
       -        }else if(in && (in = read(h->fd, h->start + cpy, in)) < 0){
       -                h->state = Herr;
       -                h->pos = h->stop;
       -                return nil;
       +        }else if(in){
       +fprint(2, "read %d from %d\n", in, h->fd);
       +                if((in = read(h->fd, h->start + cpy, in)) < 0){
       +fprint(2, "got error: %r\n");
       +                        h->state = Herr;
       +                        h->pos = h->stop;
       +                        return nil;
       +                }
       +fprint(2, "got %d\n", in);
                }
                if(in == 0)
                        h->state = Hend;
 (DIR) diff --git a/src/libip/mkfile b/src/libip/mkfile
       t@@ -3,16 +3,17 @@ PLAN9=../..
        
        LIB=libip.a
        OFILES=\
       +        bo.$O\
       +        classmask.$O\
                eipfmt.$O\
       -        parseip.$O\
       -        parseether.$O\
       +        ipaux.$O\
                myetheraddr.$O\
                myipaddr.$O\
       -        classmask.$O\
       -        bo.$O\
       -        readipifc.$O\
       -        ipaux.$O\
       +        parseether.$O\
       +        parseip.$O\
                ptclbsum.$O\
       +        readipifc.$O\
       +        udp.$O\
        
        HFILES=\
                ip.h
 (DIR) diff --git a/src/libplumb/mesg.c b/src/libplumb/mesg.c
       t@@ -13,8 +13,6 @@ static Fid *pfid;
        int
        plumbopen(char *name, int omode)
        {
       -        int fd;
       -
                if(fsplumb == nil)
                        fsplumb = nsmount("plumb", "");
                if(fsplumb == nil)
       t@@ -47,28 +45,44 @@ plumbopen(char *name, int omode)
                        return pfd;
                }
        
       -        fd = fsopenfd(fsplumb, name, omode);
       -        return fd;
       +        return fsopenfd(fsplumb, name, omode);
       +}
       +
       +Fid*
       +plumbopenfid(char *name, int mode)
       +{
       +        if(fsplumb == nil)
       +                fsplumb = nsmount("plumb", "");
       +        if(fsplumb == nil)
       +                return nil;
       +        return fsopen(fsplumb, name, mode);
        }
        
        int
       -plumbsend(int fd, Plumbmsg *m)
       +plumbsendtofid(Fid *fid, Plumbmsg *m)
        {
                char *buf;
                int n;
        
       -        if(fd != pfd){
       -                werrstr("fd is not the plumber");
       -                return -1;
       -        }
                buf = plumbpack(m, &n);
                if(buf == nil)
                        return -1;
       -        n = fswrite(pfid, buf, n);
       +        n = fswrite(fid, buf, n);
       +fprint(2, "fswrite %d\n", n);
                free(buf);
                return n;
        }
        
       +int
       +plumbsend(int fd, Plumbmsg *m)
       +{
       +        if(fd != pfd){
       +                werrstr("fd is not the plumber");
       +                return -1;
       +        }
       +        return plumbsendtofid(pfid, m);
       +}
       +
        static int
        Strlen(char *s)
        {
       t@@ -427,3 +441,31 @@ plumbrecv(int fd)
                free(buf);
                return m;
        }
       +
       +Plumbmsg*
       +plumbrecvfid(Fid *fid)
       +{
       +        char *buf;
       +        Plumbmsg *m;
       +        int n, more;
       +
       +        buf = malloc(8192);
       +        if(buf == nil)
       +                return nil;
       +        n = fsread(fid, buf, 8192);
       +        m = nil;
       +        if(n > 0){
       +                m = plumbunpackpartial(buf, n, &more);
       +                if(m==nil && more>0){
       +                        /* we now know how many more bytes to read for complete message */
       +                        buf = realloc(buf, n+more);
       +                        if(buf == nil)
       +                                return nil;
       +                        if(fsreadn(fid, buf+n, more) == more)
       +                                m = plumbunpackpartial(buf, n+more, nil);
       +                }
       +        }
       +        free(buf);
       +        return m;
       +}
       +
 (DIR) diff --git a/src/libplumb/thread.c b/src/libplumb/thread.c
       t@@ -31,3 +31,4 @@ threadplumbrecv(int fd)
                free(buf);
                return m;
        }
       +
 (DIR) diff --git a/src/libsec/port/aes.c b/src/libsec/port/aes.c
       t@@ -43,7 +43,7 @@ static const u32 Td3[256];
        static const u8  Te4[256];
        
        static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
       -// static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
       +static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
        static int rijndaelKeySetup(u32 erk[/*4*(Nr + 1)*/], u32 drk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits);
        static void        rijndaelEncrypt(const u32int rk[], int Nr, const uchar pt[16], uchar ct[16]);
        static void        rijndaelDecrypt(const u32int rk[], int Nr, const uchar ct[16], uchar pt[16]);
       t@@ -950,7 +950,6 @@ static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int
                return 0;
        }
        
       -#if 0
        /**
         * Expand the cipher key into the decryption key schedule.
         *
       t@@ -995,7 +994,6 @@ static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int
                }
                return Nr;
        }
       -#endif
        
        static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) {
                u32 s0, s1, s2, s3, t0, t1, t2, t3;
 (DIR) diff --git a/src/libsec/port/genrandom.c b/src/libsec/port/genrandom.c
       t@@ -1,4 +1,5 @@
        #include "os.h"
       +#include <mp.h>
        #include <libsec.h>
        
        typedef struct State{
 (DIR) diff --git a/src/libsec/port/mkfile b/src/libsec/port/mkfile
       t@@ -5,14 +5,55 @@ LIB=libsec.a
        
        OFILES=\
                aes.$O\
       +        blowfish.$O\
       +        decodepem.$O\
                des.$O\
       +        des3CBC.$O\
       +        des3ECB.$O\
       +        desCBC.$O\
       +        desECB.$O\
                desmodes.$O\
       +        dsaalloc.$O\
       +        dsagen.$O\
       +        dsaprimes.$O\
       +        dsaprivtopub.$O\
       +        dsasign.$O\
       +        dsaverify.$O\
       +        egalloc.$O\
       +        egdecrypt.$O\
       +        egencrypt.$O\
       +        eggen.$O\
       +        egprivtopub.$O\
       +        egsign.$O\
       +        egverify.$O\
                fastrand.$O\
       +        genprime.$O\
                genrandom.$O\
       +        gensafeprime.$O\
       +        genstrongprime.$O\
       +        hmac.$O\
       +        md4.$O\
                md5.$O\
                md5block.$O\
       +        md5pickle.$O\
       +        nfastrand.$O\
       +        prng.$O\
       +        probably_prime.$O\
       +        rc4.$O\
       +        readcert.$O\
       +        rsaalloc.$O\
       +        rsadecrypt.$O\
       +        rsaencrypt.$O\
       +        rsafill.$O\
       +        rsagen.$O\
       +        rsaprivtopub.$O\
                sha1.$O\
                sha1block.$O\
       +        sha1pickle.$O\
       +        smallprimetest.$O\
       +        thumb.$O\
       +        tlshand.$O\
       +        x509.$O\
        
        HFILES=$PLAN9/include/libsec.h
        
 (DIR) diff --git a/src/libsec/port/sha1.c b/src/libsec/port/sha1.c
       t@@ -1,5 +1,4 @@
       -#include <u.h>
       -#include <libc.h>
       +#include "os.h"
        #include <libsec.h>
        
        static void encode(uchar*, u32int*, ulong);
       t@@ -11,8 +10,6 @@ extern void _sha1block(uchar*, ulong, u32int*);
         *  the last call.  There must be room in the input buffer
         *  to pad.
         */
       -ulong lastlen;
       -
        SHA1state*
        sha1(uchar *p, ulong len, uchar *digest, SHA1state *s)
        {
       t@@ -21,15 +18,12 @@ sha1(uchar *p, ulong len, uchar *digest, SHA1state *s)
                int i;
                uchar *e;
        
       -lastlen = len;
                if(s == nil){
                        s = malloc(sizeof(*s));
                        if(s == nil)
                                return nil;
                        memset(s, 0, sizeof(*s));
                        s->malloced = 1;
       -                assert(!s->seeded);
       -                assert(!s->blen);
                }
        
                if(s->seeded == 0){
       t@@ -42,11 +36,8 @@ lastlen = len;
                        s->seeded = 1;
                }
        
       -assert(len < 100000);
       -
                /* fill out the partial 64 byte block from previous calls */
                if(s->blen){
       -assert(s);
                        i = 64 - s->blen;
                        if(len < i)
                                i = len;
       t@@ -61,11 +52,9 @@ assert(s);
                        }
                }
        
       -assert(len < 1000000);
                /* do 64 byte blocks */
                i = len & ~0x3f;
                if(i){
       -assert(i < 1000000);
                        _sha1block(p, i, s->state);
                        s->len += i;
                        len -= i;
 (DIR) diff --git a/src/libsec/port/sha1block.c b/src/libsec/port/sha1block.c
       t@@ -1,10 +1,4 @@
       -#include <u.h>
       -#include <libc.h>
       -
       -uchar* lastsbp;
       -ulong lastsblen;
       -u32int* lastsbs;
       -Lock slock;
       +#include "os.h"
        
        void
        _sha1block(uchar *p, ulong len, u32int *s)
       t@@ -14,11 +8,6 @@ _sha1block(uchar *p, ulong len, u32int *s)
                u32int *wp, *wend;
                u32int w[80];
        
       -lock(&slock);
       -lastsbp=p;
       -lastsblen=len;
       -lastsbs=s;
       -
                /* at this point, we have a multiple of 64 bytes */
                for(end = p+len; p < end;){
                        a = s[0];
       t@@ -195,5 +184,4 @@ lastsbs=s;
                        s[3] += d;
                        s[4] += e;
                }
       -unlock(&slock);
        }
 (DIR) diff --git a/src/libthread/main.c b/src/libthread/main.c
       t@@ -45,6 +45,7 @@ main(int argc, char **argv)
        
                signal(SIGTERM, _threaddie);
                signal(SIGCHLD, _nop);
       +        signal(SIGALRM, _nop);
        //        signal(SIGINFO, _threadstatus);
        //        rfork(RFREND);
        
 (DIR) diff --git a/src/libventi/server.c b/src/libventi/server.c
       t@@ -55,11 +55,13 @@ listenproc(void *v)
        
                srv = v;
                for(;;){
       +fprint(2, "listen for venti\n");
                        ctl = listen(srv->adir, dir);
                        if(ctl < 0){
                                srv->dead = 1;
                                break;
                        }
       +fprint(2, "got one\n");
                        sc = vtmallocz(sizeof(VtSconn));
                        sc->ctl = ctl;
                        sc->srv = srv;
 (DIR) diff --git a/src/mkfile b/src/mkfile
       t@@ -1,6 +1,6 @@
        <mkhdr
        
       -BUGGERED="String|html|httpd|ip|venti"
       +BUGGERED='9p|html|httpd|ip|venti'
        LIBDIRS=`ls -ld lib* | sed -n 's/^d.* //p' |egrep -v "^lib($BUGGERED)$"`
        
        DIRS=\