4 enum { MSIZE = MAXDAT+MAXMSG };
7 mkmode9p1(ulong mode9p2)
12 * Assume this is for an allocated entry.
14 mode = DALLOC|(mode9p2 & 0777);
17 if(mode9p2 & DMAPPEND)
28 mkqid9p1(Qid9p1* qid9p1, Qid* qid)
30 if(qid->path & 0xFFFFFFFF00000000LL)
31 panic("mkqid9p1: path %lluX", (Wideoff)qid->path);
32 qid9p1->path = qid->path & 0xFFFFFFFF;
34 qid9p1->path |= QPDIR;
35 qid9p1->version = qid->vers;
39 mktype9p2(int mode9p1)
57 mkmode9p2(int mode9p1)
61 mode = mode9p1 & 0777;
75 mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode9p1)
77 qid->path = (ulong)(qid9p1->path & ~QPDIR);
78 qid->vers = qid9p1->version;
79 qid->type = mktype9p2(mode9p1);
83 mkdir9p2(Dir* dir, Dentry* dentry, void* strs)
87 memset(dir, 0, sizeof(Dir));
88 mkqid(&dir->qid, dentry, 1);
89 dir->mode = mkmode9p2(dentry->mode);
90 dir->atime = dentry->atime;
91 dir->mtime = dentry->mtime;
92 dir->length = dentry->size;
96 p += sprint(p, "%s", dentry->name)+1;
99 uidtostr(p, dentry->uid, 1);
103 uidtostr(p, dentry->gid, 1);
107 uidtostr(p, dentry->muid, 1);
114 checkname9p2(char* name)
119 * Return error or 0 if OK.
121 if(name == nil || *name == 0)
124 for(p = name; *p != 0; p++){
125 if(p-name >= NAMELEN-1)
127 if((*p & 0xFF) <= 040)
130 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
137 version(Chan* chan, Fcall* f, Fcall* r)
139 if(chan->protocol != nil)
148 * Should check the '.' stuff here.
150 if(strcmp(f->version, VERSION9P) == 0){
151 r->version = VERSION9P;
152 chan->protocol = serve9p2;
153 chan->msize = r->msize;
155 r->version = "unknown";
163 auth(Chan* chan, Fcall* f, Fcall* r)
174 if(noauth || wstatallow)
175 return Eauthdisabled;
180 if(strcmp(f->uname, "none") == 0)
183 if(!aname[0]) /* default */
185 file = filep(chan, f->afid, 1);
196 file->qid.path = authpath.hi++;
198 file->qid.type = QTAUTH;
201 file->open = FREAD+FWRITE;
205 if((file->auth = authnew()) == nil){
220 authorize(Chan* chan, Fcall* f)
225 db = cons.flags & authdebugflag;
227 if(strcmp(f->uname, "none") == 0){
228 uid = strtouid(f->uname);
230 print("permission granted to none: uid %s = %d\n",
235 if(noauth || wstatallow){
236 uid = strtouid(f->uname);
238 print("permission granted by noauth uid %s = %d\n",
243 af = filep(chan, f->afid, 0);
246 print("authorize: af == nil\n");
250 /* fake read to get auth info */
251 authread(af, nil, 0);
254 print("authorize: uid is %d\n", uid);
260 attach(Chan* chan, Fcall* f, Fcall* r)
271 if(!aname[0]) /* default */
275 file = filep(chan, f->fid, 1);
282 if(chan != cons.chan){
283 if(noattach && strcmp(f->uname, "none")) {
287 u = authorize(chan, f);
301 raddr = getraddr(fs->dev);
302 p = getbuf(fs->dev, raddr, Brd);
303 if(p == nil || checktag(p, Tdir, QPROOT)){
308 if(d == nil || !(d->mode & DALLOC)){
312 if (iaccess(file, d, DEXEC) ||
313 file->uid == 0 && fs->dev->type == Devro) {
315 * 'none' not allowed on dump
320 accessdir(p, d, FREAD, file->uid);
321 mkqid(&file->qid, d, 1);
331 strncpy(chan->whoname, f->uname, sizeof(chan->whoname));
332 chan->whotime = time(nil);
345 flush(Chan* chan, Fcall*, Fcall*)
347 runlock(&chan->reflock);
348 wlock(&chan->reflock);
349 wunlock(&chan->reflock);
350 rlock(&chan->reflock);
356 clone(File* nfile, File* file)
360 nfile->qid = file->qid;
363 nfile->wpath = file->wpath;
364 for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up)
368 nfile->fs = file->fs;
369 nfile->addr = file->addr;
370 nfile->slot = file->slot;
371 nfile->uid = file->uid;
372 nfile->open = file->open & ~FREMOV;
376 walkname(File* file, char* wname, Qid* wqid)
387 * File must not have been opened for I/O by an open
388 * or create message and must represent a directory.
395 p = getbuf(file->fs->dev, file->addr, Brd);
396 if(p == nil || checktag(p, Tdir, QPNONE)){
400 d = getdir(p, file->slot);
401 if(d == nil || !(d->mode & DALLOC)){
405 if(!(d->mode & DDIR)){
409 if(error = mkqidcmp(&file->qid, d))
413 * For walked elements the implied user must
414 * have permission to search the directory.
416 if(file->cp != cons.chan && iaccess(file, d, DEXEC)){
420 accessdir(p, d, FREAD, file->uid);
422 if(strcmp(wname, ".") == 0){
428 if(strcmp(wname, "..") == 0){
433 addr = file->wpath->addr;
434 slot = file->wpath->slot;
435 p1 = getbuf(file->fs->dev, addr, Brd);
436 if(p1 == nil || checktag(p1, Tdir, QPNONE)){
440 d1 = getdir(p1, slot);
441 if(d == nil || !(d1->mode & DALLOC)){
447 file->wpath = file->wpath->up;
452 for(addr = 0; ; addr++){
454 p = getbuf(file->fs->dev, file->addr, Brd);
455 if(p == nil || checktag(p, Tdir, QPNONE)){
459 d = getdir(p, file->slot);
460 if(d == nil || !(d->mode & DALLOC)){
466 p1 = dnodebuf1(p, d, addr, 0, file->uid);
468 if(p1 == nil || checktag(p1, Tdir, qpath)){
472 for(slot = 0; slot < DIRPERBUF; slot++){
473 d1 = getdir(p1, slot);
474 if (!(d1->mode & DALLOC) ||
475 strncmp(wname, d1->name, NAMELEN) != 0)
480 if((w = newwp()) == nil){
484 w->addr = file->addr;
485 w->slot = file->slot;
488 slot += DIRPERBUF*addr;
496 file->addr = p1->addr;
497 mkqid(&file->qid, d1, 1);
514 walk(Chan* chan, Fcall* f, Fcall* r)
517 File *file, *nfile, tfile;
520 * The file identified by f->fid must be valid in the
521 * current session and must not have been opened for I/O
522 * by an open or create message.
524 if((file = filep(chan, f->fid, 0)) == nil)
532 * If newfid is not the same as fid, allocate a new file;
533 * a side effect is checking newfid is not already in use (error);
534 * if there are no names to walk this will be equivalent to a
535 * simple 'clone' operation.
536 * Otherwise, fid and newfid are the same and if there are names
537 * to walk make a copy of 'file' to be used during the walk as
538 * 'file' must only be updated on success.
539 * Finally, it's a no-op if newfid is the same as fid and f->nwname
543 if(f->newfid != f->fid){
544 if((nfile = filep(chan, f->newfid, 1)) == nil){
548 } else if(f->nwname != 0){
550 memset(nfile, 0, sizeof(File));
560 * Should check name is not too long.
563 for(nwname = 0; nwname < f->nwname; nwname++){
564 error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
565 if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
571 * Newfid must be different to fid (see above)
572 * so this is a simple 'clone' operation - there's
573 * nothing to do except unlock unless there's
577 freewp(nfile->wpath);
582 } else if(r->nwqid < f->nwname){
584 * Didn't walk all elements, 'clunk' nfile
585 * and leave 'file' alone.
586 * Clear error if some of the elements were
589 freewp(nfile->wpath);
598 * Walked all elements. If newfid is the same
599 * as fid must update 'file' from the temporary
600 * copy used during the walk.
601 * Otherwise just unlock (when using tfile there's
602 * no need to unlock as it's a local).
605 file->qid = nfile->qid;
607 file->wpath = nfile->wpath;
608 file->addr = nfile->addr;
609 file->slot = nfile->slot;
619 fs_open(Chan* chan, Fcall* f, Fcall* r)
626 int error, ro, fmod, wok;
631 if(chan == cons.chan || writeallow)
634 if((file = filep(chan, f->fid, 0)) == nil){
644 * if remove on close, check access here
646 ro = file->fs->dev->type == Devro;
647 if(f->mode & ORCLOSE){
653 * check on parent directory of file to be deleted
655 if(file->wpath == 0 || file->wpath->addr == file->addr){
659 p = getbuf(file->fs->dev, file->wpath->addr, Brd);
660 if(p == nil || checktag(p, Tdir, QPNONE)){
664 d = getdir(p, file->wpath->slot);
665 if(d == nil || !(d->mode & DALLOC)){
669 if(iaccess(file, d, DWRITE)){
675 p = getbuf(file->fs->dev, file->addr, Brd);
676 if(p == nil || checktag(p, Tdir, QPNONE)){
680 d = getdir(p, file->slot);
681 if(d == nil || !(d->mode & DALLOC)){
685 if(error = mkqidcmp(&file->qid, d))
691 if(iaccess(file, d, DREAD) && !wok)
697 if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
708 || (iaccess(file, d, DREAD) && !wok)
709 || (iaccess(file, d, DWRITE) && !wok))
719 if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok))
728 if(f->mode & OTRUNC){
729 if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
738 if((t = tlocked(p, d)) == nil){
743 if(f->mode & ORCLOSE)
746 if((f->mode & OTRUNC) && !(d->mode & DAPND)){
747 dtrunc(p, d, file->uid);
748 qid.vers = d->qid.version;
767 r->iounit = chan->msize-IOHDRSZ;
773 fs_create(Chan* chan, Fcall* f, Fcall* r)
778 int error, slot, slot1, fmod, wok;
779 Off addr, addr1, path;
786 if(chan == cons.chan || writeallow)
789 if((file = filep(chan, f->fid, 0)) == nil){
793 if(file->fs->dev->type == Devro){
797 if(file->qid.type & QTAUTH){
802 p = getbuf(file->fs->dev, file->addr, Brd);
803 if(p == nil || checktag(p, Tdir, QPNONE)){
807 d = getdir(p, file->slot);
808 if(d == nil || !(d->mode & DALLOC)){
812 if(error = mkqidcmp(&file->qid, d))
814 if(!(d->mode & DDIR)){
818 if(iaccess(file, d, DWRITE) && !wok) {
822 accessdir(p, d, FREAD, file->uid);
825 * Check the name is valid (and will fit in an old
826 * directory entry for the moment).
828 if(error = checkname9p2(f->name))
833 for(addr = 0; ; addr++){
834 if((p1 = dnodebuf(p, d, addr, 0, file->uid)) == nil){
837 p1 = dnodebuf(p, d, addr, Tdir, file->uid);
843 if(checktag(p1, Tdir, d->qid.path)){
847 for(slot = 0; slot < DIRPERBUF; slot++){
848 d1 = getdir(p1, slot);
849 if(!(d1->mode & DALLOC)){
852 slot1 = slot + addr*DIRPERBUF;
856 if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
867 case OREAD: /* seems only useful to make directories */
884 if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE))
889 path = qidpathgen(file->fs->dev);
890 if((p1 = getbuf(file->fs->dev, addr1, Brd|Bimm|Bmod)) == nil)
892 d1 = getdir(p1, slot1);
893 if(d1 == nil || checktag(p1, Tdir, d->qid.path)) {
897 if(d1->mode & DALLOC){
902 strncpy(d1->name, f->name, sizeof(d1->name));
903 if(chan == cons.chan){
909 f->perm &= d->mode | ~0666;
911 f->perm &= d->mode | ~0777;
915 d1->mode = DALLOC | (f->perm & 0777);
918 d1->qid.path |= QPDIR;
926 /* if nil, out of tlock structures */
928 accessdir(p1, d1, FWRITE, file->uid);
929 mkqid(&r->qid, d1, 0);
931 accessdir(p, d, FWRITE, file->uid);
934 * do a walk to new directory entry
936 if((w = newwp()) == nil){
940 w->addr = file->addr;
941 w->slot = file->slot;
949 if(f->mode & ORCLOSE)
969 r->iounit = chan->msize-IOHDRSZ;
975 fs_read(Chan* chan, Fcall* f, Fcall* r, uchar* data)
981 Off addr, offset, start;
983 int error, iounit, nread, count, n, o, slot;
993 if((file = filep(chan, f->fid, 0)) == nil){
997 if(!(file->open & FREAD)){
1001 iounit = chan->msize-IOHDRSZ;
1002 if(count < 0 || count > iounit){
1010 if(file->qid.type & QTAUTH){
1011 nread = authread(file, (uchar*)data, count);
1016 p = getbuf(file->fs->dev, file->addr, Brd);
1017 if(p == nil || checktag(p, Tdir, QPNONE)){
1021 d = getdir(p, file->slot);
1022 if(d == nil || !(d->mode & DALLOC)){
1026 if(error = mkqidcmp(&file->qid, d))
1028 if(t = file->tlock){
1030 if(t->time < tim || t->file != file){
1034 /* renew the lock */
1035 t->time = tim + TLOCK;
1037 accessdir(p, d, FREAD, file->uid);
1040 if(offset+count > d->size)
1041 count = d->size - offset;
1044 p = getbuf(file->fs->dev, file->addr, Brd);
1045 if(p == nil || checktag(p, Tdir, QPNONE)){
1049 d = getdir(p, file->slot);
1050 if(d == nil || !(d->mode & DALLOC)){
1055 addr = offset / BUFSIZE;
1056 file->lastra = dbufread(p, d, addr, file->lastra, file->uid);
1057 o = offset % BUFSIZE;
1061 p1 = dnodebuf1(p, d, addr, 0, file->uid);
1064 if(checktag(p1, Tfile, QPNONE)){
1069 memmove(data+nread, p1->iobuf+o, n);
1072 memset(data+nread, 0, n);
1081 * Pick up where we left off last time if nothing has changed,
1082 * otherwise must scan from the beginning.
1084 if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
1085 addr = file->dslot/DIRPERBUF;
1086 slot = file->dslot%DIRPERBUF;
1094 dmb = mballoc(iounit, chan, Mbreply1);
1098 * This is just a check to ensure the entry hasn't
1099 * gone away during the read of each directory block.
1101 p = getbuf(file->fs->dev, file->addr, Brd);
1102 if(p == nil || checktag(p, Tdir, QPNONE)){
1106 d = getdir(p, file->slot);
1107 if(d == nil || !(d->mode & DALLOC)){
1112 p1 = dnodebuf1(p, d, addr, 0, file->uid);
1116 if(checktag(p1, Tdir, QPNONE)){
1122 for(; slot < DIRPERBUF; slot++){
1123 d1 = getdir(p1, slot);
1124 if(!(d1->mode & DALLOC))
1126 mkdir9p2(&dir, d1, dmb->data);
1127 n = convD2M(&dir, data+nread, iounit - nread);
1150 file->doffset = offset;
1151 file->dvers = file->qid.vers;
1152 file->dslot = slot+DIRPERBUF*addr;
1157 * Do we need this any more?
1158 count = f->count - nread;
1160 memset(data+nread, 0, count);
1167 r->data = (char*)data;
1173 fs_write(Chan* chan, Fcall* f, Fcall* r)
1179 Off offset, addr, qpath;
1181 int count, error, nwrite, o, n;
1190 if((file = filep(chan, f->fid, 0)) == nil){
1194 if(!(file->open & FWRITE)){
1198 if(count < 0 || count > chan->msize-IOHDRSZ){
1207 if(file->qid.type & QTAUTH){
1208 nwrite = authwrite(file, (uchar*)f->data, count);
1212 } else if(file->fs->dev->type == Devro){
1217 if ((p = getbuf(file->fs->dev, file->addr, Brd|Bmod)) == nil ||
1218 (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) {
1222 if(error = mkqidcmp(&file->qid, d))
1224 if(t = file->tlock) {
1226 if(t->time < tim || t->file != file){
1230 /* renew the lock */
1231 t->time = tim + TLOCK;
1233 accessdir(p, d, FWRITE, file->uid);
1236 if(offset+count > d->size)
1237 d->size = offset+count;
1240 p = getbuf(file->fs->dev, file->addr, Brd|Bmod);
1245 d = getdir(p, file->slot);
1246 if(d == nil || !(d->mode & DALLOC)){
1251 addr = offset / BUFSIZE;
1252 o = offset % BUFSIZE;
1256 qpath = d->qid.path;
1257 p1 = dnodebuf1(p, d, addr, Tfile, file->uid);
1263 if(checktag(p1, Tfile, qpath)){
1268 memmove(p1->iobuf+o, f->data+nwrite, n);
1287 _clunk(File* file, int remove, int wok)
1293 if(t = file->tlock){
1295 t->time = 0; /* free the lock */
1298 if(remove && (file->qid.type & QTAUTH) == 0)
1299 error = doremove(file, wok);
1301 freewp(file->wpath);
1302 authfree(file->auth);
1311 clunk(Chan* chan, Fcall* f, Fcall*)
1315 if((file = filep(chan, f->fid, 0)) == nil)
1318 _clunk(file, file->open & FREMOV, 0);
1323 fs_remove(Chan* chan, Fcall* f, Fcall*)
1327 if((file = filep(chan, f->fid, 0)) == nil)
1330 return _clunk(file, 1, chan == cons.chan);
1334 fs_stat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
1344 if((file = filep(chan, f->fid, 0)) == nil)
1346 if(file->qid.type & QTAUTH){
1347 memset(&dentry, 0, sizeof dentry);
1349 mkqid9p1(&d->qid, &file->qid);
1350 strcpy(d->name, "#¿");
1354 d->atime = time(nil);
1355 d->mtime = d->atime;
1358 p = getbuf(file->fs->dev, file->addr, Brd);
1359 if(p == nil || checktag(p, Tdir, QPNONE)){
1363 d = getdir(p, file->slot);
1364 if(d == nil || !(d->mode & DALLOC)){
1368 if(error = mkqidcmp(&file->qid, d))
1371 if(d->qid.path == QPROOT) /* stat of root gives time */
1372 d->atime = time(nil);
1374 len = mkdir9p2(&dir, d, data);
1377 if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
1391 fs_wstat(Chan* chan, Fcall* f, Fcall*, char* strs)
1396 int error, err, gid, gl, muid, op, slot, tsync, uid;
1400 if(convM2D(f->stat, f->nstat, &dir, strs) == 0)
1405 * If user 'none' (uid == 0), can't do anything;
1406 * if filesystem is read-only, can't change anything.
1408 if((file = filep(chan, f->fid, 0)) == nil)
1415 if(file->fs->dev->type == Devro){
1419 if(file->qid.type & QTAUTH){
1425 * Get the current entry and check it is still valid.
1427 p = getbuf(file->fs->dev, file->addr, Brd);
1428 if(p == nil || checktag(p, Tdir, QPNONE)){
1432 d = getdir(p, file->slot);
1433 if(d == nil || !(d->mode & DALLOC)){
1437 if(error = mkqidcmp(&file->qid, d))
1441 * Run through each of the (sub-)fields in the provided Dir
1442 * checking for validity and whether it's a default:
1443 * .type, .dev and .atime are completely ignored and not checked;
1444 * .qid.path, .qid.vers and .muid are checked for validity but
1445 * any attempt to change them is an error.
1446 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
1447 * possibly be changed (and .muid iff wstatallow).
1449 * 'Op' flags there are changed fields, i.e. it's not a no-op.
1450 * 'Tsync' flags all fields are defaulted.
1452 * Wstatallow and writeallow are set to allow changes during the
1453 * fileserver bootstrap phase.
1456 if(dir.qid.path != ~0){
1457 if(dir.qid.path != file->qid.path){
1463 if(dir.qid.vers != ~0){
1464 if(dir.qid.vers != file->qid.vers){
1472 * .qid.type and .mode have some bits in common. Only .mode
1473 * is currently needed for comparisons with the old mode but
1474 * if there are changes to the bits also encoded in .qid.type
1475 * then file->qid must be updated appropriately later.
1477 if(dir.qid.type == (uchar)~0){
1479 dir.qid.type = mktype9p2(d->mode);
1481 dir.qid.type = dir.mode>>24;
1485 dir.mode = mkmode9p2(d->mode);
1490 * Check dir.qid.type and dir.mode agree, check for any unknown
1491 * type/mode bits, check for an attempt to change the directory bit.
1493 if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1497 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
1502 op = dir.mode^mkmode9p2(d->mode);
1508 if(dir.mtime != ~0){
1509 if(dir.mtime != d->mtime)
1513 dir.mtime = d->mtime;
1515 if(dir.length == ~(Off)0)
1516 dir.length = d->size;
1518 if (dir.length < 0) {
1521 } else if(dir.length != d->size)
1527 * Check for permission to change .mode, .mtime or .length,
1528 * must be owner or leader of either group, for which test gid
1529 * is needed; permission checks on gid will be done later.
1530 * 'Gl' counts whether neither, one or both groups are led.
1532 if(dir.gid != nil && *dir.gid != '\0'){
1533 gid = strtouid(dir.gid);
1537 gl = leadgroup(file->uid, gid) != 0;
1538 gl += leadgroup(file->uid, d->gid) != 0;
1540 if(op && !wstatallow && d->uid != file->uid && !gl){
1547 * Check .name is valid and different to the current.
1549 if(dir.name != nil && *dir.name != '\0'){
1550 if(error = checkname9p2(dir.name))
1552 if(strncmp(dir.name, d->name, NAMELEN))
1561 * If the name is really to be changed check it's unique
1562 * and there is write permission in the parent.
1564 if(dir.name != d->name){
1567 * Must drop current entry to prevent
1568 * deadlock when searching that new name
1569 * already exists below.
1574 if(file->wpath == nil){
1578 p1 = getbuf(file->fs->dev, file->wpath->addr, Brd);
1579 if(p1 == nil || checktag(p1, Tdir, QPNONE)){
1583 d1 = getdir(p1, file->wpath->slot);
1584 if(d1 == nil || !(d1->mode & DALLOC)){
1590 * Check entries in parent for new name.
1592 for(addr = 0; ; addr++){
1593 if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil)
1595 if(checktag(p, Tdir, d1->qid.path)){
1599 for(slot = 0; slot < DIRPERBUF; slot++){
1600 d = getdir(p, slot);
1601 if(!(d->mode & DALLOC) ||
1602 strncmp(dir.name, d->name, sizeof d->name))
1611 * Reacquire entry and check it's still OK.
1613 p = getbuf(file->fs->dev, file->addr, Brd);
1614 if(p == nil || checktag(p, Tdir, QPNONE)){
1618 d = getdir(p, file->slot);
1619 if(d == nil || !(d->mode & DALLOC)){
1625 * Check write permission in the parent.
1627 if(!wstatallow && !writeallow && iaccess(file, d1, DWRITE)){
1634 * Check for permission to change owner - must be god.
1636 if(dir.uid != nil && *dir.uid != '\0'){
1637 uid = strtouid(dir.uid);
1648 if(dir.muid != nil && *dir.muid != '\0'){
1649 muid = strtouid(dir.muid);
1650 if(muid != d->muid){
1662 * Check for permission to change group, must be
1663 * either owner and in new group or leader of both groups.
1666 if(!(wstatallow || writeallow)
1667 && !(d->uid == file->uid && ingroup(file->uid, gid))
1676 * Checks all done, update if necessary.
1679 d->mode = mkmode9p1(dir.mode);
1680 file->qid.type = mktype9p2(d->mode);
1681 d->mtime = dir.mtime;
1682 if (dir.length < d->size) {
1683 err = dtrunclen(p, d, dir.length, uid);
1687 d->size = dir.length;
1688 if(dir.name != d->name)
1689 strncpy(d->name, dir.name, sizeof(d->name));
1695 accessdir(p, d, FREAD, file->uid);
1708 serve9p2(Msgbuf* mb)
1718 fmtinstall('F', fcallfmt);
1723 * 0 return means i don't understand this message,
1724 * 1 return means i dealt with it, including error
1727 if(convM2S(mb->data, mb->count, &f) != mb->count)
1729 print("didn't like %d byte message\n", mb->count);
1733 if(type < Tversion || type >= Tmax || (type & 1) || type == Terror)
1738 print("9p2: f %F\n", &f);
1748 snprint(ename, sizeof(ename), "unknown message: %F", &f);
1752 error = version(chan, &f, &r);
1755 error = auth(chan, &f, &r);
1758 error = attach(chan, &f, &r);
1761 error = flush(chan, &f, &r);
1764 error = walk(chan, &f, &r);
1767 error = fs_open(chan, &f, &r);
1770 error = fs_create(chan, &f, &r);
1773 data = mballoc(chan->msize, chan, Mbreply1);
1774 error = fs_read(chan, &f, &r, data->data);
1777 error = fs_write(chan, &f, &r);
1780 error = clunk(chan, &f, &r);
1783 error = fs_remove(chan, &f, &r);
1786 data = mballoc(chan->msize, chan, Mbreply1);
1787 error = fs_stat(chan, &f, &r, data->data);
1790 data = mballoc(chan->msize, chan, Mbreply1);
1791 error = fs_wstat(chan, &f, &r, (char*)data->data);
1798 r.ename = chan->err;
1799 else if(error >= MAXERR){
1800 snprint(ename, sizeof(ename), "error %d", error);
1803 r.ename = errstr9p[error];
1806 print("9p2: r %F\n", &r);
1808 rmb = mballoc(chan->msize, chan, Mbreply2);
1809 n = convS2M(&r, rmb->data, chan->msize);
1817 * If a Tversion has not been seen on the chan then
1818 * chan->msize will be 0. In that case craft a special
1819 * Rerror message. It's fortunate that the mballoc above
1820 * for rmb will have returned a Msgbuf of MAXMSG size
1821 * when given a request with count of 0...
1823 if(chan->msize == 0){
1824 r.ename = "Tversion not seen";
1825 n = convS2M(&r, rmb->data, MAXMSG);
1827 snprint(ename, sizeof(ename), "9p2: convS2M: type %d",
1830 n = convS2M(&r, rmb->data, chan->msize);
1832 print("%s\n", r.ename);
1835 * What to do here, the failure notification failed?
1842 rmb->param = mb->param;
1844 /* done 9P processing, write reply to network */
1845 fs_send(chan->reply, rmb);