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