]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hjfs/fs2.c
hjfs: disable hjfs check until more functionality is complete
[plan9front.git] / sys / src / cmd / hjfs / fs2.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include "dat.h"
6 #include "fns.h"
7
8 Chan *
9 chanattach(Fs *fs, int flags)
10 {       
11         Chan *ch;
12
13         ch = emalloc(sizeof(*ch));
14         ch->fs = fs;
15         ch->flags = flags;
16         ch->loc = cloneloc(fs, (flags & CHFDUMP) != 0 ? fs->dumprootloc : fs->rootloc);
17         return ch;
18 }
19
20 Chan *
21 chanclone(Chan *ch)
22 {
23         Chan *d;
24
25         chbegin(ch);
26         d = emalloc(sizeof(*d));
27         d->fs = ch->fs;
28         d->flags = ch->flags;
29         d->uid = ch->uid;
30         d->loc = cloneloc(ch->fs, ch->loc);
31         chend(ch);
32         return d;
33 }
34
35 int
36 chanwalk(Chan *ch, char *name)
37 {
38         Buf *b;
39         Dentry *d;
40         Loc *l;
41         FLoc f;
42         
43         if(name == nil || name[0] == 0 || name[0] == '.' && name[1] == 0)
44                 return 1;
45         chbegin(ch);
46         if(ch->open != 0){
47                 werrstr(Einval);
48                 chend(ch);
49                 return -1;
50         }
51         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
52         if(b == nil){
53                 chend(ch);
54                 return -1;
55         }
56         d = getdent(ch->loc, b);
57         if(d == nil)
58                 goto error;
59         if((d->type & QTDIR) == 0){
60                 werrstr(Enotadir);
61                 goto error;
62         }
63         if(!permcheck(ch->fs, d, ch->uid, OEXEC)){
64                 werrstr(Eperm);
65                 goto error;
66         }
67         if(strcmp(name, "..") == 0){
68                 l = ch->loc->next;
69                 if(l == nil)
70                         goto done;
71                 putloc(ch->fs, ch->loc, 0);
72                 ch->loc = l;
73                 goto done;
74         }
75         if(findentry(ch->fs, ch->loc, b, name, &f, ch->flags & CHFDUMP) <= 0)
76                 goto error;
77         ch->loc = getloc(ch->fs, f, ch->loc);
78 done:
79         putbuf(b);
80         chend(ch);
81         return 1;
82 error:
83         putbuf(b);
84         chend(ch);
85         return -1;
86 }
87
88 int
89 namevalid(char *name)
90 {
91         char *p;
92         
93         if(name == nil || name[0] == 0)
94                 return 0;
95         if(name[0] == '.' && (name[1] == 0 || name[1] == '.' && name[2] == 0))
96                 return 0;
97         for(p = name; *p; p++)
98                 if((uchar) *p < ' ' || *p == '/')
99                         return 0;
100         return p - name < NAMELEN;
101 }
102
103 int
104 chancreat(Chan *ch, char *name, int perm, int mode)
105 {
106         Buf *b;
107         Dentry *d;
108         int isdir;
109         Loc *l;
110         FLoc f;
111
112         b = nil;
113         l = nil;
114         chbegin(ch);
115         if(!namevalid(name) || ch->open != 0)
116                 goto inval;
117         if((ch->flags & CHFRO) != 0)
118                 goto inval;
119         if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0)
120                 goto error;
121         if(isdir = ((perm & DMDIR) != 0))
122                 if((mode & (OWRITE | OEXEC | ORCLOSE | OTRUNC)) != 0)
123                         goto inval;
124         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
125         if(b == nil)
126                 goto error;
127         d = getdent(ch->loc, b);
128         if(d == nil)
129                 goto error;
130         if((d->type & QTDIR) == 0){
131                 werrstr(Enotadir);
132                 goto error;
133         }
134         if((ch->flags & CHFNOPERM) == 0){
135                 if(!permcheck(ch->fs, d, ch->uid, OWRITE)){
136                         werrstr(Eperm);
137                         goto error;
138                 }
139                 if(isdir)
140                         perm &= ~0777 | d->mode & 0777;
141                 else
142                         perm &= ~0666 | d->mode & 0666;
143         }
144         if(newentry(ch->fs, ch->loc, b, name, &f, 0) <= 0)
145                 goto error;
146
147         f.type = perm >> 24;
148         if(newqid(ch->fs, &f.path) < 0)
149                 goto error;
150         l = getloc(ch->fs, f, ch->loc);
151         modified(ch, d);
152         b->op |= BDELWRI;
153         putbuf(b);
154         b = nil;
155         if(willmodify(ch->fs, l, ch->flags & CHFNOLOCK) < 0)
156                 goto error;
157         b = getbuf(ch->fs->d, l->blk, TDENTRY, 0);
158         if(b == nil)
159                 goto error;
160         ch->loc = l;
161         d = &b->de[l->deind];
162         memset(d, 0, sizeof(*d));
163         d->Qid = l->Qid;
164         strcpy(d->name, name);
165         d->mtime = time(0);
166         d->atime = d->mtime;
167         d->gid = d->uid = d->muid = ch->uid;
168         d->mode = DALLOC | perm & 0777;
169         if((d->type & QTEXCL) != 0){
170                 qlock(&ch->loc->ex);
171                 ch->loc->exlock = ch;
172                 ch->loc->lwrite = d->atime;
173                 qunlock(&ch->loc->ex);
174         }
175         b->op |= BDELWRI;
176         putbuf(b);
177         switch(mode & OEXEC){
178         case ORDWR:
179                 ch->open |= CHREAD;
180         case OWRITE:
181                 ch->open |= CHWRITE;
182                 break;
183         case OEXEC:
184         case OREAD:
185                 ch->open |= CHREAD;
186                 break;
187         }
188         if((mode & ORCLOSE) != 0)
189                 ch->open |= CHRCLOSE;
190         chend(ch);
191         return 1;
192 inval:
193         werrstr(Einval);
194 error:
195         if(l != nil)
196                 putloc(ch->fs, l, 0);
197         if(b != nil)
198                 putbuf(b);
199         chend(ch);
200         return -1;
201 }
202
203 int
204 chanopen(Chan *ch, int mode)
205 {
206         Buf *b;
207         Dentry *d;
208
209         b = nil;
210         chbegin(ch);
211         if(ch->open != 0)
212                 goto inval;
213         if((ch->flags & CHFRO) != 0 && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0)
214                 goto inval;
215         if((mode & OTRUNC) != 0)
216                 if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0)
217                         goto error;
218         if((mode & ORCLOSE) != 0){
219                 if(ch->loc->next == nil)
220                         goto inval;
221                 b = getbuf(ch->fs->d, ch->loc->next->blk, TDENTRY, 0);
222                 if(b == nil)
223                         goto error;
224                 d = getdent(ch->loc->next, b);
225                 if(d == nil)
226                         goto error;
227                 if((ch->flags & CHFNOPERM) == 0)
228                         if(!permcheck(ch->fs, d, ch->uid, OWRITE))
229                                 goto perm;
230                 putbuf(b);
231         }
232         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
233         if(b == nil)
234                 goto error;
235         d = getdent(ch->loc, b);
236         if(d == nil)
237                 goto error;
238         if((d->type & QTAPPEND) != 0)
239                 mode &= ~OTRUNC;
240         if((d->type & QTDIR) != 0 && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0)
241                 goto inval;
242         if((ch->flags & CHFNOPERM) == 0){
243                 if(!permcheck(ch->fs, d, ch->uid, mode & OEXEC))
244                         goto perm;
245                 if((mode & OTRUNC) != 0 && !permcheck(ch->fs, d, ch->uid, OWRITE))
246                         goto perm;
247         }
248         if((ch->loc->type & QTEXCL) != 0){
249                 qlock(&ch->loc->ex);
250                 if(ch->loc->exlock == nil || ch->loc->lwrite < time(0) - EXCLDUR){
251                         ch->loc->exlock = ch;
252                         ch->loc->lwrite = time(0);
253                         qunlock(&ch->loc->ex);
254                 }else{
255                         qunlock(&ch->loc->ex);
256                         werrstr(Elocked);
257                         goto error;
258                 }
259         }
260         switch(mode & OEXEC){
261         case ORDWR:
262                 ch->open |= CHREAD;
263         case OWRITE:
264                 ch->open |= CHWRITE;
265                 break;
266         case OEXEC:
267         case OREAD:
268                 ch->open |= CHREAD;
269                 break;
270         }
271         if((mode & OTRUNC) != 0){
272                 trunc(ch->fs, ch->loc, b, 0);
273                 modified(ch, d);
274                 b->op |= BDELWRI;
275         }
276         if((mode & ORCLOSE) != 0)
277                 ch->open |= CHRCLOSE;
278         putbuf(b);
279         chend(ch);
280         return 1;
281 inval:
282         werrstr(Einval);
283         goto error;
284 perm:
285         werrstr(Eperm);
286 error:
287         if(b != nil)
288                 putbuf(b);
289         chend(ch);
290         return -1;
291 }
292
293 static int
294 checklock(Chan *ch)
295 {
296         int rc;
297
298         qlock(&ch->loc->ex);
299         rc = 1;
300         if(ch->loc->exlock == ch){
301                 if(ch->loc->lwrite < time(0) - EXCLDUR){
302                         ch->loc->exlock = nil;
303                         werrstr("lock broken");
304                         rc = -1;
305                 }else
306                         ch->loc->lwrite = time(0);
307         }else{
308                 werrstr(Elocked);
309                 rc = -1;
310         }
311         qunlock(&ch->loc->ex);
312         return rc;
313 }
314
315 int
316 chanwrite(Chan *ch, void *buf, ulong n, uvlong off)
317 {
318         uvlong i, e, bl;
319         int r, rn, rc;
320         Buf *b, *c;
321         Dentry *d;
322         uchar *p;
323
324         if(n == 0)
325                 return 0;
326         if((ch->flags & CHFRO) != 0){
327                 werrstr(Einval);
328                 return -1;
329         }
330         if((ch->open & CHWRITE) == 0){
331                 werrstr(Einval);
332                 return -1;
333         }
334         chbegin(ch);
335         if((ch->loc->type & QTEXCL) != 0 && checklock(ch) < 0){
336                 chend(ch);
337                 return -1;
338         }
339         if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0){
340                 chend(ch);
341                 return -1;
342         }
343         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
344         if(b == nil){
345                 chend(ch);
346                 return -1;
347         }
348         d = getdent(ch->loc, b);
349         if(d == nil){
350                 putbuf(b);
351                 chend(ch);
352                 return -1;
353         }
354         if((d->type & QTAPPEND) != 0)
355                 off = d->size;
356         e = off + n;
357         i = off;
358         p = buf;
359         while(i < e){
360                 bl = i / RBLOCK;
361                 r = i % RBLOCK;
362                 rn = RBLOCK - r;
363                 if(i + rn > e)
364                         rn = e - i;
365                 rc = getblk(ch->fs, ch->loc, b, bl, &bl, rn == RBLOCK ? GBOVERWR : GBCREATE);
366                 if(rc < 0)
367                         goto done;
368                 c = getbuf(ch->fs->d, bl, TRAW, rc == 0 || rn == RBLOCK);
369                 if(c == nil)
370                         goto done;
371                 if(rc == 0 && rn != RBLOCK)
372                         memset(c->data, 0, sizeof(c->data));
373                 memcpy(c->data + r, p, rn);
374                 i += rn;
375                 c->op |= i != e ? BWRIM : BDELWRI;
376                 putbuf(c);
377                 p += rn;
378         }
379 done:
380         modified(ch, d);
381         e = off + (p - (uchar *) buf);
382         if(e > d->size)
383                 d->size = e;
384         b->op |= BDELWRI;
385         putbuf(b);
386         chend(ch);
387         if(p == buf)
388                 return -1;
389         return p - (uchar *) buf;
390 }
391
392 static int chandirread(Chan *, void *, ulong, uvlong);
393
394 int
395 chanread(Chan *ch, void *buf, ulong n, uvlong off)
396 {
397         uvlong i, e, bl;
398         int r, rn, rc;
399         uchar *p;
400         Buf *b, *c;
401         Dentry *d;
402
403         if((ch->open & CHREAD) == 0){
404                 werrstr(Einval);
405                 return -1;
406         }
407         chbegin(ch);
408         if((ch->loc->type & QTEXCL) != 0 && checklock(ch) < 0){
409                 chend(ch);
410                 return -1;
411         }
412         if((ch->loc->Qid.type & QTDIR) != 0)
413                 return chandirread(ch, buf, n, off);
414         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
415         if(b == nil){
416                 chend(ch);
417                 return -1;
418         }
419         d = getdent(ch->loc, b);
420         if(d == nil)
421                 goto error;
422         if(off >= d->size)
423                 n = 0;
424         else if(off + n > d->size)
425                 n = d->size - off;
426         if(n == 0){
427                 putbuf(b);
428                 chend(ch);
429                 return 0;
430         }
431         e = off + n;
432         i = off;
433         p = buf;
434         while(i < e){
435                 bl = i / RBLOCK;
436                 r = i % RBLOCK;
437                 rn = RBLOCK - r;
438                 if(i + rn > e)
439                         rn = e - i;
440                 rc = getblk(ch->fs, ch->loc, b, bl, &bl, GBREAD);
441                 if(rc < 0)
442                         goto error;
443                 if(rc == 0)
444                         memset(p, 0, rn);
445                 else{
446                         c = getbuf(ch->fs->d, bl, TRAW, 0);
447                         if(c == nil)
448                                 goto error;
449                         memcpy(p, c->data + r, rn);
450                         putbuf(c);
451                 }
452                 i += rn;
453                 p += rn;
454         }
455         putbuf(b);
456         chend(ch);
457         return n;
458 error:
459         putbuf(b);
460         chend(ch);
461         return -1;
462 }
463
464 static void
465 statbuf(Fs *fs, Dentry *d, Dir *di, char *buf)
466 {
467         di->qid = d->Qid;
468         di->mode = (d->mode & 0777) | (d->Qid.type << 24);
469         di->mtime = d->mtime;
470         di->atime = d->atime;
471         di->length = d->size;
472         if(d->type & QTDIR)
473                 di->length = 0;
474         if(buf == nil){
475                 di->name = estrdup(d->name);
476                 di->uid = uid2name(fs, d->uid, nil);
477                 di->gid = uid2name(fs, d->gid, nil);
478                 di->muid = uid2name(fs, d->muid, nil);
479         }else{
480                 memset(buf, 0, NAMELEN + 3 * USERLEN);
481                 strncpy(buf, d->name, NAMELEN - 1);
482                 di->name = buf;
483                 di->uid = uid2name(fs, d->uid, buf + NAMELEN);
484                 di->gid = uid2name(fs, d->gid, buf + NAMELEN + USERLEN);
485                 di->muid = uid2name(fs, d->muid, buf + NAMELEN + 2 * USERLEN);
486         }
487 }
488
489 int
490 chanstat(Chan *ch, Dir *di)
491 {
492         Buf *b;
493         Dentry *d;
494         
495         chbegin(ch);
496         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
497         if(b == nil){
498                 chend(ch);
499                 return -1;
500         }
501         d = getdent(ch->loc, b);
502         if(d == nil){
503                 putbuf(b);
504                 chend(ch);
505                 return -1;
506         }
507         statbuf(ch->fs, d, di, nil);
508         putbuf(b);
509         chend(ch);
510         return 0;
511 }
512
513 static int
514 chandirread(Chan *ch, void *buf, ulong n, uvlong off)
515 {
516         Buf *b, *c;
517         Dentry *d;
518         uvlong i, blk;
519         int j;
520         int rc;
521         ulong wr;
522         Dir di;
523         char cbuf[NAMELEN + 3 * USERLEN];
524
525         if(off == 0){
526                 ch->dwloff = 0;
527                 ch->dwblk = 0;
528                 ch->dwind = 0;
529         }else if(ch->dwloff != off){
530                 werrstr(Einval);
531                 chend(ch);
532                 return -1;
533         }
534         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
535         if(b == nil){
536                 chend(ch);
537                 return -1;
538         }
539         d = getdent(ch->loc, b);
540         if(d == nil){
541                 putbuf(b);
542                 chend(ch);
543                 return -1;
544         }
545         if(ch->dwblk >= d->size){
546                 putbuf(b);
547                 chend(ch);
548                 return 0;
549         }
550         c = nil;
551         wr = 0;
552         i = ch->dwblk;
553         j = ch->dwind;
554         for(;;){
555                 if(c == nil){
556                         rc = getblk(ch->fs, ch->loc, b, i, &blk, GBREAD);
557                         if(rc < 0)
558                                 goto error;
559                         if(rc == 0){
560                                 j = 0;
561                                 if(++i >= d->size)
562                                         break;
563                                 continue;
564                         }
565                         c = getbuf(ch->fs->d, blk, TDENTRY, 0);
566                         if(c == nil)
567                                 goto error;
568                 }
569                 if((c->de[j].mode & DALLOC) == 0)
570                         goto next;
571                 if((ch->flags & CHFDUMP) != 0 && (c->de[j].type & QTTMP) != 0)
572                         goto next;
573                 statbuf(ch->fs, &c->de[j], &di, cbuf);
574                 rc = convD2M(&di, (uchar *) buf + wr, n - wr);
575                 if(rc <= BIT16SZ)
576                         break;
577                 wr += rc;
578         next:
579                 if(++j >= DEPERBLK){
580                         j = 0;
581                         if(c != nil)
582                                 putbuf(c);
583                         c = nil;
584                         if(++i >= d->size)
585                                 break;
586                 }
587         }
588         ch->dwblk = i;
589         ch->dwind = j;
590         ch->dwloff += wr;
591         if(c != nil)
592                 putbuf(c);
593         putbuf(b);
594         chend(ch);
595         return wr;
596 error:
597         putbuf(b);
598         chend(ch);
599         return -1;
600 }
601
602 int
603 chanclunk(Chan *ch)
604 {
605         Buf *b, *p;
606         int rc;
607         Dentry *d;
608
609         rc = 1;
610         b = p = nil;
611         chbegin(ch);
612         if(ch->open & CHRCLOSE){
613                 if((ch->flags & CHFRO) != 0)
614                         goto inval;
615                 if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0)
616                         goto error;
617                 if(ch->loc->next == nil)
618                         goto inval;
619                 p = getbuf(ch->fs->d, ch->loc->next->blk, TDENTRY, 0);
620                 if(p == nil)
621                         goto error;
622                 b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
623                 if(b == nil)
624                         goto error;
625                 d = getdent(ch->loc->next, p);
626                 if(d == nil)
627                         goto error;
628                 if((ch->flags & CHFNOPERM) == 0)
629                         if(!permcheck(ch->fs, d, ch->uid, OWRITE)){
630                                 werrstr(Eperm);
631                                 goto error;
632                         }
633                 d = getdent(ch->loc, b);
634                 if(d == nil)
635                         goto error;
636                 if((d->type & QTDIR) != 0 && findentry(ch->fs, ch->loc, b, nil, nil, ch->flags & CHFDUMP) != 0)
637                         goto inval;
638                 if((d->mode & DGONE) != 0)
639                         goto done;
640                 qlock(&ch->fs->loctree);
641                 if(ch->loc->ref > 1){
642                         d->mode &= ~DALLOC;
643                         d->mode |= DGONE; /* aaaaand it's gone */
644                         ch->loc->flags |= LGONE;
645                         qunlock(&ch->fs->loctree);
646                 }else{
647                         ch->loc->flags &= ~LGONE;
648                         qunlock(&ch->fs->loctree);
649                         rc = delete(ch->fs, ch->loc, b);
650                 }
651                 b->op |= BDELWRI;
652         }
653 done:
654         if(b != nil)
655                 putbuf(b);
656         if(p != nil)
657                 putbuf(p);
658         if((ch->loc->type & QTEXCL) != 0){
659                 qlock(&ch->loc->ex);
660                 if(ch->loc->exlock == ch)
661                         ch->loc->exlock = nil;
662                 qunlock(&ch->loc->ex);
663         }
664         putloc(ch->fs, ch->loc, 1);
665         chend(ch);
666         free(ch);
667         return rc;
668 inval:
669         werrstr(Einval);
670 error:
671         rc = -1;
672         goto done;
673 }
674
675 int
676 chanwstat(Chan *ch, Dir *di)
677 {
678         Buf *b, *pb;
679         Dentry *d;
680         int isdir, owner, rc;
681         short nuid, ngid;
682
683         b = pb = nil;
684         chbegin(ch);
685         if((ch->flags & CHFRO) != 0)
686                 goto inval;
687         if(willmodify(ch->fs, ch->loc, ch->flags & CHFNOLOCK) < 0)
688                 goto error;
689         if(*di->name){
690                 FLoc f;
691
692                 if(!namevalid(di->name) || ch->loc->next == nil)
693                         goto inval;
694                 pb = getbuf(ch->fs->d, ch->loc->next->blk, TDENTRY, 0);
695                 if(pb == nil)
696                         goto error;
697                 d = getdent(ch->loc->next, pb);
698                 if(d == nil)
699                         goto error;
700                 rc = findentry(ch->fs, ch->loc->next, pb, di->name, &f, ch->flags & CHFDUMP);
701                 if(rc < 0)
702                         goto error;
703                 else if(rc == 0){
704                         if((ch->flags & CHFNOPERM) == 0)
705                                 if(!permcheck(ch->fs, d, ch->uid, OWRITE))
706                                         goto perm;
707                 } else if(f.blk != ch->loc->blk || f.deind != ch->loc->deind){
708                         werrstr(Eexists);
709                         goto error;
710                 }
711         }
712         b = getbuf(ch->fs->d, ch->loc->blk, TDENTRY, 0);
713         if(b == nil)
714                 goto error;
715         d = getdent(ch->loc, b);
716         if(d == nil)
717                 goto error;
718         isdir = (d->type & QTDIR) != 0;
719         owner = ch->uid == d->uid ||
720                 ingroup(ch->fs, ch->uid, d->gid, 1) ||
721                 (ch->fs->flags & FSNOPERM) != 0 ||
722                 (ch->flags & CHFNOPERM) != 0;
723         if(di->length != ~0){
724                 if(isdir && di->length != 0)
725                         goto inval;
726                 if((ch->flags & CHFNOPERM) == 0)
727                         if(di->length != d->size && !permcheck(ch->fs, d, ch->uid, OWRITE))
728                                 goto perm;
729         }
730         if(di->mtime != ~0 && !owner)
731                 goto perm;
732         if(di->mode != ~0 && !owner)
733                 goto perm;
734         nuid = d->uid;
735         ngid = d->gid;
736         if(*di->uid != 0 && name2uid(ch->fs, di->uid, &nuid) < 0)
737                 goto inval;
738         if(*di->gid != 0 && name2uid(ch->fs, di->gid, &ngid) < 0)
739                 goto inval;
740         if(nuid != d->uid && (ch->fs->flags & FSCHOWN) == 0)
741                 goto perm;
742         if((nuid != d->uid || ngid != d->gid) && !owner)
743                 goto perm;
744         d->uid = nuid;
745         d->gid = ngid;
746         if(di->length != ~0 && di->length != d->size && !isdir){
747                 trunc(ch->fs, ch->loc, b, di->length);
748                 modified(ch, d);
749         }
750         if(di->mtime != ~0)
751                 d->mtime = di->mtime;
752         if(di->mode != ~0){
753                 d->mode = d->mode & ~0777 | di->mode & 0777;
754                 ch->loc->type = d->type = di->mode >> 24;
755         }
756         if(*di->name){
757                 memset(d->name, 0, NAMELEN);
758                 strcpy(d->name, di->name);
759         }
760         b->op |= BDELWRI;
761         if(pb != nil)
762                 putbuf(pb);
763         putbuf(b);
764         chend(ch);
765         return 1;
766 inval:
767         werrstr(Einval);
768         goto error;
769 perm:
770         werrstr(Eperm);
771 error:
772         if(pb != nil)
773                 putbuf(pb);
774         if(b != nil)
775                 putbuf(b);
776         chend(ch);
777         return -1;
778 }
779
780 int
781 chanremove(Chan *ch)
782 {
783         ch->open |= CHRCLOSE;
784         return chanclunk(ch);
785 }