fsys.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <draw.h>
  12. #include <thread.h>
  13. #include <cursor.h>
  14. #include <mouse.h>
  15. #include <keyboard.h>
  16. #include <frame.h>
  17. #include <fcall.h>
  18. #include <plumb.h>
  19. #include "dat.h"
  20. #include "fns.h"
  21. static int cfd;
  22. static int sfd;
  23. enum
  24. {
  25. Nhash = 16,
  26. DEBUG = 0
  27. };
  28. static Fid *fids[Nhash];
  29. Fid *newfid(int);
  30. static Xfid* fsysflush(Xfid*, Fid*);
  31. static Xfid* fsysauth(Xfid*, Fid*);
  32. static Xfid* fsysversion(Xfid*, Fid*);
  33. static Xfid* fsysattach(Xfid*, Fid*);
  34. static Xfid* fsyswalk(Xfid*, Fid*);
  35. static Xfid* fsysopen(Xfid*, Fid*);
  36. static Xfid* fsyscreate(Xfid*, Fid*);
  37. static Xfid* fsysread(Xfid*, Fid*);
  38. static Xfid* fsyswrite(Xfid*, Fid*);
  39. static Xfid* fsysclunk(Xfid*, Fid*);
  40. static Xfid* fsysremove(Xfid*, Fid*);
  41. static Xfid* fsysstat(Xfid*, Fid*);
  42. static Xfid* fsyswstat(Xfid*, Fid*);
  43. Xfid* (*fcall[Tmax])(Xfid*, Fid*) =
  44. {
  45. [Tflush] = fsysflush,
  46. [Tversion] = fsysversion,
  47. [Tauth] = fsysauth,
  48. [Tattach] = fsysattach,
  49. [Twalk] = fsyswalk,
  50. [Topen] = fsysopen,
  51. [Tcreate] = fsyscreate,
  52. [Tread] = fsysread,
  53. [Twrite] = fsyswrite,
  54. [Tclunk] = fsysclunk,
  55. [Tremove]= fsysremove,
  56. [Tstat] = fsysstat,
  57. [Twstat] = fsyswstat,
  58. };
  59. char Eperm[] = "permission denied";
  60. char Eexist[] = "file does not exist";
  61. char Enotdir[] = "not a directory";
  62. Dirtab dirtab[]=
  63. {
  64. { ".", QTDIR, Qdir, 0500|DMDIR },
  65. { "acme", QTDIR, Qacme, 0500|DMDIR },
  66. { "cons", QTFILE, Qcons, 0600 },
  67. { "consctl", QTFILE, Qconsctl, 0000 },
  68. { "ctl", QTFILE, Qctl, 0600 },
  69. { "draw", QTDIR, Qdraw, 0000|DMDIR }, /* to suppress graphics progs started in acme */
  70. { "editout", QTFILE, Qeditout, 0200 },
  71. { "index", QTFILE, Qindex, 0400 },
  72. { "label", QTFILE, Qlabel, 0600 },
  73. { "new", QTDIR, Qnew, 0500|DMDIR },
  74. { nil, }
  75. };
  76. Dirtab dirtabw[]=
  77. {
  78. { ".", QTDIR, Qdir, 0500|DMDIR },
  79. { "addr", QTFILE, QWaddr, 0600 },
  80. { "body", QTAPPEND, QWbody, 0600|DMAPPEND },
  81. { "ctl", QTFILE, QWctl, 0600 },
  82. { "data", QTFILE, QWdata, 0600 },
  83. { "editout", QTFILE, QWeditout, 0200 },
  84. { "errors", QTFILE, QWerrors, 0200 },
  85. { "event", QTFILE, QWevent, 0600 },
  86. { "rdsel", QTFILE, QWrdsel, 0400 },
  87. { "wrsel", QTFILE, QWwrsel, 0200 },
  88. { "tag", QTAPPEND, QWtag, 0600|DMAPPEND },
  89. { "xdata", QTFILE, QWxdata, 0600 },
  90. { nil, }
  91. };
  92. typedef struct Mnt Mnt;
  93. struct Mnt
  94. {
  95. QLock QLock;
  96. int id;
  97. Mntdir *md;
  98. };
  99. Mnt mnt;
  100. Xfid* respond(Xfid*, Fcall*, char*);
  101. int dostat(int, Dirtab*, uint8_t*, int, uint);
  102. uint getclock(void);
  103. char *user = "Wile E. Coyote";
  104. int clockfd;
  105. static int closing = 0;
  106. int messagesize = Maxblock+IOHDRSZ; /* good start */
  107. void fsysproc(void *);
  108. void
  109. fsysinit()
  110. {
  111. int p[2];
  112. int n, fd;
  113. char buf[256];
  114. if(pipe(p) < 0)
  115. error("can't create pipe");
  116. cfd = p[0];
  117. sfd = p[1];
  118. fmtinstall('F', fcallfmt);
  119. clockfd = open("/dev/time", OREAD|OCEXEC);
  120. fd = open("/dev/user", OREAD);
  121. if(fd >= 0){
  122. n = read(fd, buf, sizeof buf-1);
  123. if(n > 0){
  124. buf[n] = 0;
  125. user = estrdup(buf);
  126. }
  127. close(fd);
  128. }
  129. proccreate(fsysproc, nil, STACK);
  130. }
  131. void
  132. fsysproc(void *v)
  133. {
  134. int n;
  135. Xfid *x;
  136. Fid *f;
  137. Fcall t;
  138. uint8_t *buf;
  139. threadsetname("fsysproc");
  140. x = nil;
  141. for(;;){
  142. buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */
  143. n = read9pmsg(sfd, buf, messagesize);
  144. if(n <= 0){
  145. if(closing)
  146. break;
  147. error("i/o error on server channel");
  148. }
  149. if(x == nil){
  150. sendp(cxfidalloc, nil);
  151. x = recvp(cxfidalloc);
  152. }
  153. x->buf = buf;
  154. if(convM2S(buf, n, &x->Fcall) != n)
  155. error("convert error in convM2S");
  156. if(DEBUG)
  157. fprint(2, "%F\n", &x->Fcall);
  158. if(fcall[x->Fcall.type] == nil)
  159. x = respond(x, &t, "bad fcall type");
  160. else{
  161. switch(x->Fcall.type){
  162. case Tversion:
  163. case Tauth:
  164. case Tflush:
  165. f = nil;
  166. break;
  167. case Tattach:
  168. f = newfid(x->Fcall.fid);
  169. break;
  170. default:
  171. f = newfid(x->Fcall.fid);
  172. if(!f->busy){
  173. x->f = f;
  174. x = respond(x, &t, "fid not in use");
  175. continue;
  176. }
  177. break;
  178. }
  179. x->f = f;
  180. x = (*fcall[x->Fcall.type])(x, f);
  181. }
  182. }
  183. }
  184. Mntdir*
  185. fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
  186. {
  187. Mntdir *m;
  188. int id;
  189. qlock(&mnt.QLock);
  190. id = ++mnt.id;
  191. m = emalloc(sizeof *m);
  192. m->id = id;
  193. m->dir = dir;
  194. m->ref = 1; /* one for Command, one will be incremented in attach */
  195. m->ndir = ndir;
  196. m->next = mnt.md;
  197. m->incl = incl;
  198. m->nincl = nincl;
  199. mnt.md = m;
  200. qunlock(&mnt.QLock);
  201. return m;
  202. }
  203. void
  204. fsysincid(Mntdir *m)
  205. {
  206. qlock(&mnt.QLock);
  207. m->ref++;
  208. qunlock(&mnt.QLock);
  209. }
  210. void
  211. fsysdelid(Mntdir *idm)
  212. {
  213. Mntdir *m, *prev;
  214. int i;
  215. char buf[64];
  216. if(idm == nil)
  217. return;
  218. qlock(&mnt.QLock);
  219. if(--idm->ref > 0){
  220. qunlock(&mnt.QLock);
  221. return;
  222. }
  223. prev = nil;
  224. for(m=mnt.md; m; m=m->next){
  225. if(m == idm){
  226. if(prev)
  227. prev->next = m->next;
  228. else
  229. mnt.md = m->next;
  230. for(i=0; i<m->nincl; i++)
  231. free(m->incl[i]);
  232. free(m->incl);
  233. free(m->dir);
  234. free(m);
  235. qunlock(&mnt.QLock);
  236. return;
  237. }
  238. prev = m;
  239. }
  240. qunlock(&mnt.QLock);
  241. sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
  242. sendp(cerr, estrdup(buf));
  243. }
  244. /*
  245. * Called only in exec.c:/^run(), from a different FD group
  246. */
  247. Mntdir*
  248. fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
  249. {
  250. char buf[256];
  251. Mntdir *m;
  252. /* close server side so don't hang if acme is half-exited */
  253. close(sfd);
  254. m = fsysaddid(dir, ndir, incl, nincl);
  255. sprint(buf, "%d", m->id);
  256. if(mount(cfd, -1, "/mnt/acme", MREPL, buf, 'M') < 0){
  257. fsysdelid(m);
  258. return nil;
  259. }
  260. close(cfd);
  261. bind("/mnt/acme", "/mnt/wsys", MREPL);
  262. if(bind("/mnt/acme", "/dev", MBEFORE) < 0){
  263. fsysdelid(m);
  264. return nil;
  265. }
  266. return m;
  267. }
  268. void
  269. fsysclose(void)
  270. {
  271. closing = 1;
  272. close(cfd);
  273. close(sfd);
  274. }
  275. Xfid*
  276. respond(Xfid *x, Fcall *t, char *err)
  277. {
  278. int n;
  279. if(err){
  280. t->type = Rerror;
  281. t->ename = err;
  282. }else
  283. t->type = x->Fcall.type+1;
  284. t->fid = x->Fcall.fid;
  285. t->tag = x->Fcall.tag;
  286. if(x->buf == nil)
  287. x->buf = emalloc(messagesize);
  288. n = convS2M(t, x->buf, messagesize);
  289. if(n <= 0)
  290. error("convert error in convS2M");
  291. if(write(sfd, x->buf, n) != n)
  292. error("write error in respond");
  293. free(x->buf);
  294. x->buf = nil;
  295. if(DEBUG)
  296. fprint(2, "r: %F\n", t);
  297. return x;
  298. }
  299. static
  300. Xfid*
  301. fsysversion(Xfid *x, Fid*f)
  302. {
  303. Fcall t;
  304. if(x->Fcall.msize < 256)
  305. return respond(x, &t, "version: message size too small");
  306. if(x->Fcall.msize < messagesize)
  307. messagesize = x->Fcall.msize;
  308. t.msize = messagesize;
  309. if(strncmp(x->Fcall.version, "9P2000", 6) != 0)
  310. return respond(x, &t, "unrecognized 9P version");
  311. t.version = "9P2000";
  312. return respond(x, &t, nil);
  313. }
  314. static
  315. Xfid*
  316. fsysauth(Xfid *x, Fid*f)
  317. {
  318. Fcall t;
  319. return respond(x, &t, "acme: authentication not required");
  320. }
  321. static
  322. Xfid*
  323. fsysflush(Xfid *x, Fid*f)
  324. {
  325. sendp(x->c, xfidflush);
  326. return nil;
  327. }
  328. static
  329. Xfid*
  330. fsysattach(Xfid *x, Fid *f)
  331. {
  332. Fcall t;
  333. int id;
  334. Mntdir *m;
  335. if(strcmp(x->Fcall.uname, user) != 0)
  336. return respond(x, &t, Eperm);
  337. f->busy = TRUE;
  338. f->open = FALSE;
  339. f->qid.path = Qdir;
  340. f->qid.type = QTDIR;
  341. f->qid.vers = 0;
  342. f->dir = dirtab;
  343. f->nrpart = 0;
  344. f->w = nil;
  345. t.qid = f->qid;
  346. f->mntdir = nil;
  347. id = atoi(x->Fcall.aname);
  348. qlock(&mnt.QLock);
  349. for(m=mnt.md; m; m=m->next)
  350. if(m->id == id){
  351. f->mntdir = m;
  352. m->ref++;
  353. break;
  354. }
  355. if(m == nil)
  356. sendp(cerr, estrdup("unknown id in attach"));
  357. qunlock(&mnt.QLock);
  358. return respond(x, &t, nil);
  359. }
  360. static
  361. Xfid*
  362. fsyswalk(Xfid *x, Fid *f)
  363. {
  364. Fcall t;
  365. int c, i, j, id;
  366. Qid q;
  367. uint8_t type;
  368. uint32_t path;
  369. Fid *nf;
  370. Dirtab *d, *dir;
  371. Window *w;
  372. char *err;
  373. nf = nil;
  374. w = nil;
  375. if(f->open)
  376. return respond(x, &t, "walk of open file");
  377. if(x->Fcall.fid != x->Fcall.newfid){
  378. nf = newfid(x->Fcall.newfid);
  379. if(nf->busy)
  380. return respond(x, &t, "newfid already in use");
  381. nf->busy = TRUE;
  382. nf->open = FALSE;
  383. nf->mntdir = f->mntdir;
  384. if(f->mntdir)
  385. f->mntdir->ref++;
  386. nf->dir = f->dir;
  387. nf->qid = f->qid;
  388. nf->w = f->w;
  389. nf->nrpart = 0; /* not open, so must be zero */
  390. if(nf->w)
  391. incref(&nf->w->Ref);
  392. f = nf; /* walk f */
  393. }
  394. t.nwqid = 0;
  395. err = nil;
  396. dir = nil;
  397. id = WIN(f->qid);
  398. q = f->qid;
  399. if(x->Fcall.nwname > 0){
  400. for(i=0; i<x->Fcall.nwname; i++){
  401. if((q.type & QTDIR) == 0){
  402. err = Enotdir;
  403. break;
  404. }
  405. if(strcmp(x->Fcall.wname[i], "..") == 0){
  406. type = QTDIR;
  407. path = Qdir;
  408. id = 0;
  409. if(w){
  410. winclose(w);
  411. w = nil;
  412. }
  413. Accept:
  414. if(i == MAXWELEM){
  415. err = "name too long";
  416. break;
  417. }
  418. q.type = type;
  419. q.vers = 0;
  420. q.path = QID(id, path);
  421. t.wqid[t.nwqid++] = q;
  422. continue;
  423. }
  424. /* is it a numeric name? */
  425. for(j=0; (c=x->Fcall.wname[i][j]); j++)
  426. if(c<'0' || '9'<c)
  427. goto Regular;
  428. /* yes: it's a directory */
  429. if(w) /* name has form 27/23; get out before losing w */
  430. break;
  431. id = atoi(x->Fcall.wname[i]);
  432. qlock(&row.QLock);
  433. w = lookid(id, FALSE);
  434. if(w == nil){
  435. qunlock(&row.QLock);
  436. break;
  437. }
  438. incref(&w->Ref); /* we'll drop reference at end if there's an error */
  439. path = Qdir;
  440. type = QTDIR;
  441. qunlock(&row.QLock);
  442. dir = dirtabw;
  443. goto Accept;
  444. Regular:
  445. // if(FILE(f->qid) == Qacme) /* empty directory */
  446. // break;
  447. if(strcmp(x->Fcall.wname[i], "new") == 0){
  448. if(w)
  449. error("w set in walk to new");
  450. sendp(cnewwindow, nil); /* signal newwindowthread */
  451. w = recvp(cnewwindow); /* receive new window */
  452. incref(&w->Ref);
  453. type = QTDIR;
  454. path = QID(w->id, Qdir);
  455. id = w->id;
  456. dir = dirtabw;
  457. goto Accept;
  458. }
  459. if(id == 0)
  460. d = dirtab;
  461. else
  462. d = dirtabw;
  463. d++; /* skip '.' */
  464. for(; d->name; d++)
  465. if(strcmp(x->Fcall.wname[i], d->name) == 0){
  466. path = d->qid;
  467. type = d->type;
  468. dir = d;
  469. goto Accept;
  470. }
  471. break; /* file not found */
  472. }
  473. if(i==0 && err == nil)
  474. err = Eexist;
  475. }
  476. if(err!=nil || t.nwqid<x->Fcall.nwname){
  477. if(nf){
  478. nf->busy = FALSE;
  479. fsysdelid(nf->mntdir);
  480. }
  481. }else if(t.nwqid == x->Fcall.nwname){
  482. if(w){
  483. f->w = w;
  484. w = nil; /* don't drop the reference */
  485. }
  486. if(dir)
  487. f->dir = dir;
  488. f->qid = q;
  489. }
  490. if(w != nil)
  491. winclose(w);
  492. return respond(x, &t, err);
  493. }
  494. static
  495. Xfid*
  496. fsysopen(Xfid *x, Fid *f)
  497. {
  498. Fcall t;
  499. int m;
  500. /* can't truncate anything, so just disregard */
  501. x->Fcall.mode &= ~(OTRUNC|OCEXEC);
  502. /* can't execute or remove anything */
  503. if(x->Fcall.mode==OEXEC || (x->Fcall.mode&ORCLOSE))
  504. goto Deny;
  505. switch(x->Fcall.mode){
  506. default:
  507. goto Deny;
  508. case OREAD:
  509. m = 0400;
  510. break;
  511. case OWRITE:
  512. m = 0200;
  513. break;
  514. case ORDWR:
  515. m = 0600;
  516. break;
  517. }
  518. if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
  519. goto Deny;
  520. sendp(x->c, xfidopen);
  521. return nil;
  522. Deny:
  523. return respond(x, &t, Eperm);
  524. }
  525. static
  526. Xfid*
  527. fsyscreate(Xfid *x, Fid*f)
  528. {
  529. Fcall t;
  530. return respond(x, &t, Eperm);
  531. }
  532. static
  533. int
  534. idcmp(const void *a, const void *b)
  535. {
  536. return *(int*)a - *(int*)b;
  537. }
  538. static
  539. Xfid*
  540. fsysread(Xfid *x, Fid *f)
  541. {
  542. Fcall t;
  543. uint8_t *b;
  544. int i, id, n, o, e, j, k, *ids, nids;
  545. Dirtab *d, dt;
  546. Column *c;
  547. uint clock, len;
  548. char buf[16];
  549. if(f->qid.type & QTDIR){
  550. if(FILE(f->qid) == Qacme){ /* empty dir */
  551. t.data = nil;
  552. t.count = 0;
  553. respond(x, &t, nil);
  554. return x;
  555. }
  556. o = x->Fcall.offset;
  557. e = x->Fcall.offset+x->Fcall.count;
  558. clock = getclock();
  559. b = emalloc(messagesize);
  560. id = WIN(f->qid);
  561. n = 0;
  562. if(id > 0)
  563. d = dirtabw;
  564. else
  565. d = dirtab;
  566. d++; /* first entry is '.' */
  567. for(i=0; d->name!=nil && i<e; i+=len){
  568. len = dostat(WIN(x->f->qid), d, b+n, x->Fcall.count-n, clock);
  569. if(len <= BIT16SZ)
  570. break;
  571. if(i >= o)
  572. n += len;
  573. d++;
  574. }
  575. if(id == 0){
  576. qlock(&row.QLock);
  577. nids = 0;
  578. ids = nil;
  579. for(j=0; j<row.ncol; j++){
  580. c = row.col[j];
  581. for(k=0; k<c->nw; k++){
  582. ids = erealloc(ids, (nids+1)*sizeof(int));
  583. ids[nids++] = c->w[k]->id;
  584. }
  585. }
  586. qunlock(&row.QLock);
  587. qsort(ids, nids, sizeof ids[0], idcmp);
  588. j = 0;
  589. dt.name = buf;
  590. for(; j<nids && i<e; i+=len){
  591. k = ids[j];
  592. sprint(dt.name, "%d", k);
  593. dt.qid = QID(k, Qdir);
  594. dt.type = QTDIR;
  595. dt.perm = DMDIR|0700;
  596. len = dostat(k, &dt, b+n, x->Fcall.count-n, clock);
  597. if(len == 0)
  598. break;
  599. if(i >= o)
  600. n += len;
  601. j++;
  602. }
  603. free(ids);
  604. }
  605. t.data = (char*)b;
  606. t.count = n;
  607. respond(x, &t, nil);
  608. free(b);
  609. return x;
  610. }
  611. sendp(x->c, xfidread);
  612. return nil;
  613. }
  614. static
  615. Xfid*
  616. fsyswrite(Xfid *x, Fid*f)
  617. {
  618. sendp(x->c, xfidwrite);
  619. return nil;
  620. }
  621. static
  622. Xfid*
  623. fsysclunk(Xfid *x, Fid *f)
  624. {
  625. fsysdelid(f->mntdir);
  626. sendp(x->c, xfidclose);
  627. return nil;
  628. }
  629. static
  630. Xfid*
  631. fsysremove(Xfid *x, Fid*f)
  632. {
  633. Fcall t;
  634. return respond(x, &t, Eperm);
  635. }
  636. static
  637. Xfid*
  638. fsysstat(Xfid *x, Fid *f)
  639. {
  640. Fcall t;
  641. t.stat = emalloc(messagesize-IOHDRSZ);
  642. t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
  643. x = respond(x, &t, nil);
  644. free(t.stat);
  645. return x;
  646. }
  647. static
  648. Xfid*
  649. fsyswstat(Xfid *x, Fid*f)
  650. {
  651. Fcall t;
  652. return respond(x, &t, Eperm);
  653. }
  654. Fid*
  655. newfid(int fid)
  656. {
  657. Fid *f, *ff, **fh;
  658. ff = nil;
  659. fh = &fids[fid&(Nhash-1)];
  660. for(f=*fh; f; f=f->next)
  661. if(f->fid == fid)
  662. return f;
  663. else if(ff==nil && f->busy==FALSE)
  664. ff = f;
  665. if(ff){
  666. ff->fid = fid;
  667. return ff;
  668. }
  669. f = emalloc(sizeof *f);
  670. f->fid = fid;
  671. f->next = *fh;
  672. *fh = f;
  673. return f;
  674. }
  675. uint
  676. getclock()
  677. {
  678. char buf[32];
  679. buf[0] = '\0';
  680. pread(clockfd, buf, sizeof buf, 0);
  681. return atoi(buf);
  682. }
  683. int
  684. dostat(int id, Dirtab *dir, uint8_t *buf, int nbuf, uint clock)
  685. {
  686. Dir d;
  687. d.qid.path = QID(id, dir->qid);
  688. d.qid.vers = 0;
  689. d.qid.type = dir->type;
  690. d.mode = dir->perm;
  691. d.length = 0; /* would be nice to do better */
  692. d.name = dir->name;
  693. d.uid = user;
  694. d.gid = user;
  695. d.muid = user;
  696. d.atime = clock;
  697. d.mtime = clock;
  698. return convD2M(&d, buf, nbuf);
  699. }