1 2277c5d7 2004-03-21 devnull #include "std.h"
2 2277c5d7 2004-03-21 devnull #include "dat.h"
7 2277c5d7 2004-03-21 devnull Qfactotum,
10 2277c5d7 2004-03-21 devnull Qprotolist,
11 2277c5d7 2004-03-21 devnull Qconfirm,
14 2277c5d7 2004-03-21 devnull Qneedkey,
19 2277c5d7 2004-03-21 devnull mkqid(int type, int path)
23 2277c5d7 2004-03-21 devnull q.type = type;
24 2277c5d7 2004-03-21 devnull q.path = path;
25 2277c5d7 2004-03-21 devnull q.vers = 0;
26 2277c5d7 2004-03-21 devnull return q;
29 2277c5d7 2004-03-21 devnull static struct
31 2277c5d7 2004-03-21 devnull char *name;
32 2277c5d7 2004-03-21 devnull int qidpath;
33 2277c5d7 2004-03-21 devnull ulong perm;
34 2277c5d7 2004-03-21 devnull } dirtab[] = {
35 2277c5d7 2004-03-21 devnull /* positions of confirm and needkey known below */
36 2277c5d7 2004-03-21 devnull "confirm", Qconfirm, 0600|DMEXCL,
37 2277c5d7 2004-03-21 devnull "needkey", Qneedkey, 0600|DMEXCL,
38 2277c5d7 2004-03-21 devnull "ctl", Qctl, 0600,
39 2277c5d7 2004-03-21 devnull "rpc", Qrpc, 0666,
40 2277c5d7 2004-03-21 devnull "proto", Qprotolist, 0444,
41 2277c5d7 2004-03-21 devnull "log", Qlog, 0600|DMEXCL,
42 2277c5d7 2004-03-21 devnull "conv", Qconv, 0400,
45 2277c5d7 2004-03-21 devnull static void
46 2277c5d7 2004-03-21 devnull fillstat(Dir *dir, char *name, int type, int path, ulong perm)
48 2277c5d7 2004-03-21 devnull dir->name = estrdup(name);
49 2277c5d7 2004-03-21 devnull dir->uid = estrdup(owner);
50 2277c5d7 2004-03-21 devnull dir->gid = estrdup(owner);
51 2277c5d7 2004-03-21 devnull dir->mode = perm;
52 2277c5d7 2004-03-21 devnull dir->length = 0;
53 2277c5d7 2004-03-21 devnull dir->qid = mkqid(type, path);
54 2277c5d7 2004-03-21 devnull dir->atime = time(0);
55 2277c5d7 2004-03-21 devnull dir->mtime = time(0);
56 2277c5d7 2004-03-21 devnull dir->muid = estrdup("");
59 2277c5d7 2004-03-21 devnull static int
60 2277c5d7 2004-03-21 devnull rootdirgen(int n, Dir *dir, void *v)
64 2277c5d7 2004-03-21 devnull if(n > 0)
65 2277c5d7 2004-03-21 devnull return -1;
67 2277c5d7 2004-03-21 devnull fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555);
68 2277c5d7 2004-03-21 devnull return 0;
71 2277c5d7 2004-03-21 devnull static int
72 2277c5d7 2004-03-21 devnull fsdirgen(int n, Dir *dir, void *v)
76 2277c5d7 2004-03-21 devnull if(n >= nelem(dirtab))
77 2277c5d7 2004-03-21 devnull return -1;
78 2277c5d7 2004-03-21 devnull fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm);
79 2277c5d7 2004-03-21 devnull return 0;
82 2277c5d7 2004-03-21 devnull static char*
83 2277c5d7 2004-03-21 devnull fswalk1(Fid *fid, char *name, Qid *qid)
87 2277c5d7 2004-03-21 devnull switch((int)fid->qid.path){
89 2277c5d7 2004-03-21 devnull return "fswalk1: cannot happen";
90 2277c5d7 2004-03-21 devnull case Qroot:
91 2277c5d7 2004-03-21 devnull if(strcmp(name, factname) == 0){
92 2277c5d7 2004-03-21 devnull *qid = mkqid(QTDIR, Qfactotum);
93 2277c5d7 2004-03-21 devnull fid->qid = *qid;
94 2277c5d7 2004-03-21 devnull return nil;
96 2277c5d7 2004-03-21 devnull if(strcmp(name, "..") == 0){
97 2277c5d7 2004-03-21 devnull *qid = fid->qid;
98 2277c5d7 2004-03-21 devnull return nil;
100 2277c5d7 2004-03-21 devnull return "not found";
101 2277c5d7 2004-03-21 devnull case Qfactotum:
102 2277c5d7 2004-03-21 devnull for(i=0; i<nelem(dirtab); i++)
103 2277c5d7 2004-03-21 devnull if(strcmp(name, dirtab[i].name) == 0){
104 2277c5d7 2004-03-21 devnull *qid = mkqid(0, dirtab[i].qidpath);
105 2277c5d7 2004-03-21 devnull fid->qid = *qid;
106 2277c5d7 2004-03-21 devnull return nil;
108 2277c5d7 2004-03-21 devnull if(strcmp(name, "..") == 0){
109 2277c5d7 2004-03-21 devnull *qid = mkqid(QTDIR, Qroot);
110 2277c5d7 2004-03-21 devnull fid->qid = *qid;
111 2277c5d7 2004-03-21 devnull return nil;
113 2277c5d7 2004-03-21 devnull return "not found";
117 2277c5d7 2004-03-21 devnull static void
118 2277c5d7 2004-03-21 devnull fsstat(Req *r)
120 2277c5d7 2004-03-21 devnull int i, path;
122 2277c5d7 2004-03-21 devnull path = r->fid->qid.path;
123 2277c5d7 2004-03-21 devnull switch(path){
124 2277c5d7 2004-03-21 devnull case Qroot:
125 2277c5d7 2004-03-21 devnull fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR);
127 2277c5d7 2004-03-21 devnull case Qfactotum:
128 2277c5d7 2004-03-21 devnull fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR);
130 2277c5d7 2004-03-21 devnull default:
131 2277c5d7 2004-03-21 devnull for(i=0; i<nelem(dirtab); i++)
132 2277c5d7 2004-03-21 devnull if(dirtab[i].qidpath == path){
133 2277c5d7 2004-03-21 devnull fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm);
134 2277c5d7 2004-03-21 devnull goto Break2;
136 2277c5d7 2004-03-21 devnull respond(r, "file not found");
140 2277c5d7 2004-03-21 devnull respond(r, nil);
143 2277c5d7 2004-03-21 devnull static int
144 2277c5d7 2004-03-21 devnull readlist(int off, int (*gen)(int, char*, uint), Req *r)
146 2277c5d7 2004-03-21 devnull char *a, *ea;
149 2277c5d7 2004-03-21 devnull a = r->ofcall.data;
150 2277c5d7 2004-03-21 devnull ea = a+r->ifcall.count;
151 2277c5d7 2004-03-21 devnull for(;;){
152 2277c5d7 2004-03-21 devnull n = (*gen)(off, a, ea-a);
153 2277c5d7 2004-03-21 devnull if(n == 0){
154 2277c5d7 2004-03-21 devnull r->ofcall.count = a - (char*)r->ofcall.data;
155 2277c5d7 2004-03-21 devnull return off;
160 2277c5d7 2004-03-21 devnull return -1; /* not reached */
163 2277c5d7 2004-03-21 devnull static int
164 2277c5d7 2004-03-21 devnull keylist(int i, char *a, uint nn)
167 2277c5d7 2004-03-21 devnull char buf[512];
170 2277c5d7 2004-03-21 devnull if(i >= ring.nkey)
171 2277c5d7 2004-03-21 devnull return 0;
173 2277c5d7 2004-03-21 devnull k = ring.key[i];
174 2277c5d7 2004-03-21 devnull k->attr = sortattr(k->attr);
175 2277c5d7 2004-03-21 devnull n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr);
176 2277c5d7 2004-03-21 devnull if(n >= sizeof(buf)-5)
177 2277c5d7 2004-03-21 devnull strcpy(buf+sizeof(buf)-5, "...\n");
178 2277c5d7 2004-03-21 devnull n = strlen(buf);
179 2277c5d7 2004-03-21 devnull if(n > nn)
180 2277c5d7 2004-03-21 devnull return 0;
181 2277c5d7 2004-03-21 devnull memmove(a, buf, n);
182 2277c5d7 2004-03-21 devnull return n;
185 2277c5d7 2004-03-21 devnull static int
186 2277c5d7 2004-03-21 devnull protolist(int i, char *a, uint n)
188 2277c5d7 2004-03-21 devnull if(prototab[i] == nil)
189 2277c5d7 2004-03-21 devnull return 0;
190 2277c5d7 2004-03-21 devnull if(strlen(prototab[i]->name)+1 > n)
191 2277c5d7 2004-03-21 devnull return 0;
192 2277c5d7 2004-03-21 devnull n = strlen(prototab[i]->name)+1;
193 2277c5d7 2004-03-21 devnull memmove(a, prototab[i]->name, n-1);
194 2277c5d7 2004-03-21 devnull a[n-1] = '\n';
195 2277c5d7 2004-03-21 devnull return n;
198 2277c5d7 2004-03-21 devnull /* BUG this is O(n^2) to fill in the list */
199 2277c5d7 2004-03-21 devnull static int
200 2277c5d7 2004-03-21 devnull convlist(int i, char *a, uint nn)
202 2277c5d7 2004-03-21 devnull Conv *c;
203 2277c5d7 2004-03-21 devnull char buf[512];
206 2277c5d7 2004-03-21 devnull for(c=conv; c && i-- > 0; c=c->next)
209 2277c5d7 2004-03-21 devnull if(c == nil)
210 2277c5d7 2004-03-21 devnull return 0;
212 2277c5d7 2004-03-21 devnull if(c->state)
213 2277c5d7 2004-03-21 devnull n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr);
215 2277c5d7 2004-03-21 devnull n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err);
217 2277c5d7 2004-03-21 devnull if(n >= sizeof(buf)-5)
218 2277c5d7 2004-03-21 devnull strcpy(buf+sizeof(buf)-5, "...\n");
219 2277c5d7 2004-03-21 devnull n = strlen(buf);
220 2277c5d7 2004-03-21 devnull if(n > nn)
221 2277c5d7 2004-03-21 devnull return 0;
222 2277c5d7 2004-03-21 devnull memmove(a, buf, n);
223 2277c5d7 2004-03-21 devnull return n;
226 2277c5d7 2004-03-21 devnull static void
227 2277c5d7 2004-03-21 devnull fskickreply(Conv *c)
231 2277c5d7 2004-03-21 devnull if(c->hangup){
232 2277c5d7 2004-03-21 devnull if(c->req){
233 2277c5d7 2004-03-21 devnull respond(c->req, "hangup");
234 2277c5d7 2004-03-21 devnull c->req = nil;
239 2277c5d7 2004-03-21 devnull if(!c->req || !c->nreply)
242 2277c5d7 2004-03-21 devnull r = c->req;
243 2277c5d7 2004-03-21 devnull r->ofcall.count = c->nreply;
244 2277c5d7 2004-03-21 devnull r->ofcall.data = c->reply;
245 2277c5d7 2004-03-21 devnull if(r->ofcall.count > r->ifcall.count)
246 2277c5d7 2004-03-21 devnull r->ofcall.count = r->ifcall.count;
247 2277c5d7 2004-03-21 devnull respond(r, nil);
248 2277c5d7 2004-03-21 devnull c->req = nil;
249 2277c5d7 2004-03-21 devnull c->nreply = 0;
253 2277c5d7 2004-03-21 devnull * Some of the file system work happens in the fs proc, but
254 2277c5d7 2004-03-21 devnull * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in
255 2277c5d7 2004-03-21 devnull * the main proc so that they can access the various shared
256 2277c5d7 2004-03-21 devnull * data structures without worrying about locking.
258 2277c5d7 2004-03-21 devnull static int inuse[nelem(dirtab)];
259 2277c5d7 2004-03-21 devnull int *confirminuse = &inuse[0];
260 2277c5d7 2004-03-21 devnull int *needkeyinuse = &inuse[1];
261 2277c5d7 2004-03-21 devnull static void
262 2277c5d7 2004-03-21 devnull fsopen(Req *r)
264 2277c5d7 2004-03-21 devnull int i, *inusep, perm;
265 2277c5d7 2004-03-21 devnull static int need[4] = { 4, 2, 6, 1 };
266 2277c5d7 2004-03-21 devnull Conv *c;
268 2277c5d7 2004-03-21 devnull inusep = nil;
269 2277c5d7 2004-03-21 devnull perm = 5; /* directory */
270 2277c5d7 2004-03-21 devnull for(i=0; i<nelem(dirtab); i++)
271 2277c5d7 2004-03-21 devnull if(dirtab[i].qidpath == r->fid->qid.path){
272 2277c5d7 2004-03-21 devnull if(dirtab[i].perm & DMEXCL)
273 2277c5d7 2004-03-21 devnull inusep = &inuse[i];
274 2277c5d7 2004-03-21 devnull if(strcmp(r->fid->uid, owner) == 0)
275 2277c5d7 2004-03-21 devnull perm = dirtab[i].perm>>6;
277 2277c5d7 2004-03-21 devnull perm = dirtab[i].perm;
281 2277c5d7 2004-03-21 devnull if((r->ifcall.mode&~(OMASK|OTRUNC))
282 2277c5d7 2004-03-21 devnull || (need[r->ifcall.mode&3] & ~perm)){
283 2277c5d7 2004-03-21 devnull respond(r, "permission denied");
287 2277c5d7 2004-03-21 devnull if(inusep){
288 2277c5d7 2004-03-21 devnull if(*inusep){
289 2277c5d7 2004-03-21 devnull respond(r, "file in use");
292 2277c5d7 2004-03-21 devnull *inusep = 1;
295 2277c5d7 2004-03-21 devnull if(r->fid->qid.path == Qrpc){
296 2277c5d7 2004-03-21 devnull if((c = convalloc(r->fid->uid)) == nil){
297 2277c5d7 2004-03-21 devnull char e[ERRMAX];
299 2277c5d7 2004-03-21 devnull rerrstr(e, sizeof e);
300 2277c5d7 2004-03-21 devnull respond(r, e);
303 2277c5d7 2004-03-21 devnull c->kickreply = fskickreply;
304 2277c5d7 2004-03-21 devnull r->fid->aux = c;
307 2277c5d7 2004-03-21 devnull respond(r, nil);
310 2277c5d7 2004-03-21 devnull static void
311 2277c5d7 2004-03-21 devnull fsread(Req *r)
313 2277c5d7 2004-03-21 devnull Conv *c;
315 2277c5d7 2004-03-21 devnull switch((int)r->fid->qid.path){
316 2277c5d7 2004-03-21 devnull default:
317 2277c5d7 2004-03-21 devnull respond(r, "fsread: cannot happen");
319 2277c5d7 2004-03-21 devnull case Qroot:
320 2277c5d7 2004-03-21 devnull dirread9p(r, rootdirgen, nil);
321 2277c5d7 2004-03-21 devnull respond(r, nil);
323 2277c5d7 2004-03-21 devnull case Qfactotum:
324 2277c5d7 2004-03-21 devnull dirread9p(r, fsdirgen, nil);
325 2277c5d7 2004-03-21 devnull respond(r, nil);
327 2277c5d7 2004-03-21 devnull case Qrpc:
328 2277c5d7 2004-03-21 devnull c = r->fid->aux;
329 2277c5d7 2004-03-21 devnull if(c->rpc.op == RpcUnknown){
330 2277c5d7 2004-03-21 devnull respond(r, "no rpc pending");
333 2277c5d7 2004-03-21 devnull if(c->req){
334 2277c5d7 2004-03-21 devnull respond(r, "read already pending");
337 2277c5d7 2004-03-21 devnull c->req = r;
338 2277c5d7 2004-03-21 devnull if(c->nreply)
339 2277c5d7 2004-03-21 devnull (*c->kickreply)(c);
341 2277c5d7 2004-03-21 devnull rpcexec(c);
343 2277c5d7 2004-03-21 devnull case Qconfirm:
344 2277c5d7 2004-03-21 devnull confirmread(r);
346 2277c5d7 2004-03-21 devnull case Qlog:
347 2277c5d7 2004-03-21 devnull logread(r);
349 2277c5d7 2004-03-21 devnull case Qctl:
350 2277c5d7 2004-03-21 devnull r->fid->aux = (void*)readlist((int)r->fid->aux, keylist, r);
351 2277c5d7 2004-03-21 devnull respond(r, nil);
353 2277c5d7 2004-03-21 devnull case Qneedkey:
354 2277c5d7 2004-03-21 devnull needkeyread(r);
356 2277c5d7 2004-03-21 devnull case Qprotolist:
357 2277c5d7 2004-03-21 devnull r->fid->aux = (void*)readlist((int)r->fid->aux, protolist, r);
358 2277c5d7 2004-03-21 devnull respond(r, nil);
360 2277c5d7 2004-03-21 devnull case Qconv:
361 2277c5d7 2004-03-21 devnull r->fid->aux = (void*)readlist((int)r->fid->aux, convlist, r);
362 2277c5d7 2004-03-21 devnull respond(r, nil);
367 2277c5d7 2004-03-21 devnull static void
368 2277c5d7 2004-03-21 devnull fswrite(Req *r)
370 2277c5d7 2004-03-21 devnull int ret;
371 2277c5d7 2004-03-21 devnull char err[ERRMAX], *s;
372 2277c5d7 2004-03-21 devnull int (*strfn)(char*);
374 2277c5d7 2004-03-21 devnull switch((int)r->fid->qid.path){
375 2277c5d7 2004-03-21 devnull default:
376 2277c5d7 2004-03-21 devnull respond(r, "fswrite: cannot happen");
378 2277c5d7 2004-03-21 devnull case Qrpc:
379 2277c5d7 2004-03-21 devnull if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){
380 2277c5d7 2004-03-21 devnull rerrstr(err, sizeof err);
381 2277c5d7 2004-03-21 devnull respond(r, err);
383 2277c5d7 2004-03-21 devnull r->ofcall.count = r->ifcall.count;
384 2277c5d7 2004-03-21 devnull respond(r, nil);
387 2277c5d7 2004-03-21 devnull case Qneedkey:
388 2277c5d7 2004-03-21 devnull strfn = needkeywrite;
389 2277c5d7 2004-03-21 devnull goto string;
390 2277c5d7 2004-03-21 devnull case Qctl:
391 2277c5d7 2004-03-21 devnull strfn = ctlwrite;
392 2277c5d7 2004-03-21 devnull goto string;
393 2277c5d7 2004-03-21 devnull case Qconfirm:
394 2277c5d7 2004-03-21 devnull strfn = confirmwrite;
396 2277c5d7 2004-03-21 devnull s = emalloc(r->ifcall.count+1);
397 2277c5d7 2004-03-21 devnull memmove(s, r->ifcall.data, r->ifcall.count);
398 2277c5d7 2004-03-21 devnull s[r->ifcall.count] = '\0';
399 2277c5d7 2004-03-21 devnull ret = (*strfn)(s);
400 2277c5d7 2004-03-21 devnull free(s);
401 2277c5d7 2004-03-21 devnull if(ret < 0){
402 2277c5d7 2004-03-21 devnull rerrstr(err, sizeof err);
403 2277c5d7 2004-03-21 devnull respond(r, err);
405 2277c5d7 2004-03-21 devnull r->ofcall.count = r->ifcall.count;
406 2277c5d7 2004-03-21 devnull respond(r, nil);
412 2277c5d7 2004-03-21 devnull static void
413 2277c5d7 2004-03-21 devnull fsflush(Req *r)
415 2277c5d7 2004-03-21 devnull confirmflush(r);
416 2277c5d7 2004-03-21 devnull logflush(r);
419 2277c5d7 2004-03-21 devnull static void
420 2277c5d7 2004-03-21 devnull fsdestroyfid(Fid *fid)
422 2277c5d7 2004-03-21 devnull if(fid->qid.path == Qrpc){
423 2277c5d7 2004-03-21 devnull convhangup(fid->aux);
424 2277c5d7 2004-03-21 devnull convclose(fid->aux);
428 2277c5d7 2004-03-21 devnull static Channel *creq;
429 2277c5d7 2004-03-21 devnull static Channel *cfid, *cfidr;
431 2277c5d7 2004-03-21 devnull static void
432 2277c5d7 2004-03-21 devnull fsreqthread(void *v)
436 2277c5d7 2004-03-21 devnull USED(v);
438 2277c5d7 2004-03-21 devnull creq = chancreate(sizeof(Req*), 0);
440 2277c5d7 2004-03-21 devnull while((r = recvp(creq)) != nil){
441 2277c5d7 2004-03-21 devnull switch(r->ifcall.type){
442 2277c5d7 2004-03-21 devnull default:
443 2277c5d7 2004-03-21 devnull respond(r, "bug in fsreqthread");
445 2277c5d7 2004-03-21 devnull case Topen:
446 2277c5d7 2004-03-21 devnull fsopen(r);
448 2277c5d7 2004-03-21 devnull case Tread:
449 2277c5d7 2004-03-21 devnull fsread(r);
451 2277c5d7 2004-03-21 devnull case Twrite:
452 2277c5d7 2004-03-21 devnull fswrite(r);
454 2277c5d7 2004-03-21 devnull case Tflush:
455 2277c5d7 2004-03-21 devnull fsflush(r);
461 2277c5d7 2004-03-21 devnull static void
462 2277c5d7 2004-03-21 devnull fsclunkthread(void *v)
466 2277c5d7 2004-03-21 devnull USED(v);
467 2277c5d7 2004-03-21 devnull cfid = chancreate(sizeof(Fid*), 0);
468 2277c5d7 2004-03-21 devnull cfidr = chancreate(sizeof(Fid*), 0);
470 2277c5d7 2004-03-21 devnull while((f = recvp(cfid)) != nil){
471 2277c5d7 2004-03-21 devnull fsdestroyfid(f);
472 2277c5d7 2004-03-21 devnull sendp(cfidr, 0);
476 2277c5d7 2004-03-21 devnull static void
477 2277c5d7 2004-03-21 devnull fsproc(void *v)
479 2277c5d7 2004-03-21 devnull USED(v);
481 2277c5d7 2004-03-21 devnull threadcreate(fsreqthread, nil, STACK);
482 2277c5d7 2004-03-21 devnull threadcreate(fsclunkthread, nil, STACK);
483 2277c5d7 2004-03-21 devnull threadexits(nil);
486 2277c5d7 2004-03-21 devnull static void
487 2277c5d7 2004-03-21 devnull fsattach(Req *r)
489 2277c5d7 2004-03-21 devnull static int first = 1;
491 2277c5d7 2004-03-21 devnull if(first){
492 2277c5d7 2004-03-21 devnull proccreate(fsproc, nil, STACK);
493 2277c5d7 2004-03-21 devnull first = 0;
496 2277c5d7 2004-03-21 devnull r->fid->qid = mkqid(QTDIR, Qroot);
497 2277c5d7 2004-03-21 devnull r->ofcall.qid = r->fid->qid;
498 2277c5d7 2004-03-21 devnull respond(r, nil);
501 2277c5d7 2004-03-21 devnull static void
502 2277c5d7 2004-03-21 devnull fssend(Req *r)
504 2277c5d7 2004-03-21 devnull sendp(creq, r);
507 2277c5d7 2004-03-21 devnull static void
508 2277c5d7 2004-03-21 devnull fssendclunk(Fid *f)
510 2277c5d7 2004-03-21 devnull sendp(cfid, f);
511 2277c5d7 2004-03-21 devnull recvp(cfidr);
514 2277c5d7 2004-03-21 devnull Srv fs = {
515 2277c5d7 2004-03-21 devnull .attach= fsattach,
516 2277c5d7 2004-03-21 devnull .walk1= fswalk1,
517 2277c5d7 2004-03-21 devnull .open= fssend,
518 2277c5d7 2004-03-21 devnull .read= fssend,
519 2277c5d7 2004-03-21 devnull .write= fssend,
520 2277c5d7 2004-03-21 devnull .stat= fsstat,
521 2277c5d7 2004-03-21 devnull .flush= fssend,
522 2277c5d7 2004-03-21 devnull .destroyfid= fssendclunk,