]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devshr.c
fix kernel: pio()/mfreeseg() race
[plan9front.git] / sys / src / 9 / port / devshr.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 enum {
9         Qroot,
10         Qcroot,
11         Qshr,
12         Qcshr,
13         Qcmpt,
14 };
15
16 typedef struct Ent Ent;
17 typedef struct Shr Shr;
18 typedef struct Mpt Mpt;
19 typedef struct Sch Sch;
20
21 struct Ent
22 {
23         Ref;
24         int     id;
25         char    *name;
26         char    *owner;
27         ulong   perm;
28 };
29
30 struct Shr
31 {
32         Ent;
33         Mhead   umh;
34         Shr     *next;
35 };
36
37 struct Mpt
38 {
39         Ent;
40         Mount   m;
41 };
42
43 struct Sch
44 {
45         int     level;
46
47         Shr     *shr;
48         Mpt     *mpt;
49
50         Chan    *chan;
51 };
52
53 static QLock    shrslk;
54 static Shr      *shrs;
55
56 static int      shrid;
57 static int      mptid;
58
59 static Mpt*
60 tompt(Mount *m)
61 {
62         return (Mpt*)((char*)m - (char*)&((Mpt*)0)->m);
63 }
64
65 static Sch*
66 tosch(Chan *c)
67 {
68         Sch *sch;
69
70         if(c == nil)
71                 error("nil chan");
72         sch = c->aux;
73         if(sch == nil)
74                 error("nil chan aux");
75         if(sch->chan != c)
76                 error("bad chan");
77         return sch;
78 }
79
80 static void
81 shrinit(void)
82 {
83         shrid = 1;
84         mptid = 1;
85 }
86
87 static void
88 putmpt(Mpt *mpt)
89 {
90         if(decref(mpt))
91                 return;
92         if(mpt->m.to)
93                 cclose(mpt->m.to);
94         free(mpt->name);
95         free(mpt->owner);
96         free(mpt);
97 }
98
99 static void
100 putshr(Shr *shr)
101 {
102         if(decref(shr))
103                 return;
104         free(shr->name);
105         free(shr->owner);
106         free(shr);
107 }
108
109 static Qid
110 shrqid(int level, int id)
111 {
112         Qid q;
113
114         q.type = (level == Qcmpt) ? QTFILE : QTDIR;
115         q.path = (uvlong)id<<4 | level;
116         q.vers = 0;
117         return q;
118 }
119
120 static Chan*
121 shrattach(char *spec)
122 {
123         Sch *sch;
124         Chan *c;
125         
126         if(!(spec[0] == 'c' && spec[1] == 0 || spec[0] == 0))
127                 error(Enoattach);
128         c = devattach(L'σ', spec);
129
130         sch = smalloc(sizeof(*sch));
131         sch->level = spec[0] == 'c' ? Qcroot : Qroot;
132         sch->mpt = nil;
133         sch->shr = nil;
134         sch->chan = c;
135         c->aux = sch;
136         c->qid = shrqid(sch->level, 0);
137
138         return c;
139 }
140
141 static Chan*
142 shrclone(Chan *c)
143 {
144         Chan *nc;
145         Sch *sch, *och;
146
147         och = tosch(c);
148         nc = devclone(c);
149         sch = smalloc(sizeof(*sch));
150         memmove(sch, och, sizeof(*sch));
151         if(sch->shr)
152                 incref(sch->shr);
153         if(sch->mpt)
154                 incref(sch->mpt);
155         sch->chan = nc;
156         nc->aux = sch;
157         return nc;
158 }
159
160 static void
161 shrclunk(Chan *c)
162 {
163         Sch *sch;
164
165         sch = tosch(c);
166         c->aux = nil;
167         sch->chan = nil;
168         if(sch->mpt)
169                 putmpt(sch->mpt);
170         if(sch->shr)
171                 putshr(sch->shr);
172         free(sch);      
173 }
174
175 static Walkqid*
176 shrwalk(Chan *c, Chan *nc, char **name, int nname)
177 {
178         Walkqid *wq, *wq2;
179         int alloc, j;
180         char *nam;
181         Sch *sch;
182         Shr *shr;
183         Mpt *mpt;
184         Mount *m;
185         Mhead *h;
186         
187         alloc = 0;
188         if(nc == nil){
189                 nc = shrclone(c);
190                 alloc = 1;
191         }
192         wq = smalloc(sizeof(Walkqid) + (nname - 1) * sizeof(Qid));
193         wq->nqid = 0;
194         wq->clone = nc;
195         if(waserror()){
196                 if(alloc)
197                         cclose(wq->clone);
198                 if(wq->nqid > 0)
199                         wq->clone = nil;
200                 else {
201                         free(wq);
202                         wq = nil;
203                 }
204                 return wq;
205         }
206         sch = tosch(nc);
207         for(j = 0; j < nname; j++){
208                 if(nc->qid.type != QTDIR)
209                         error(Enotdir);
210
211                 nam = name[j];
212                 if(nam[0] == '.' && nam[1] == 0) {
213                         /* nop */
214                 } else if(nam[0] == '.' && nam[1] == '.' && nam[2] == 0) {
215                         switch(sch->level){
216                         default:
217                                 error(Egreg);
218                         case Qshr:
219                                 nc->qid = shrqid(sch->level = Qroot, 0);
220                                 break;
221                         case Qcshr:
222                                 nc->qid = shrqid(sch->level = Qcroot, 0);
223                                 break;
224                         }
225                         putshr(sch->shr);
226                         sch->shr = nil;
227                 } else if(sch->level == Qcroot || sch->level == Qroot) {
228                         qlock(&shrslk);
229                         for(shr = shrs; shr; shr = shr->next)
230                                 if(strcmp(nam, shr->name) == 0){
231                                         incref(shr);
232                                         break;
233                                 }
234                         qunlock(&shrslk);
235                         if(shr == nil)
236                                 error(Enonexist);
237                         sch->level = sch->level == Qcroot ? Qcshr : Qshr;
238                         sch->shr = shr;
239                         nc->qid = shrqid(sch->level, shr->id);
240                 } else if(sch->level == Qcshr) {
241                         mpt = nil;
242                         shr = sch->shr;
243                         h = &shr->umh;
244                         rlock(&h->lock);
245                         for(m = h->mount; m; m = m->next){
246                                 mpt = tompt(m);
247                                 if(strcmp(nam, mpt->name) == 0){
248                                         incref(mpt);
249                                         break;
250                                 }
251                         }
252                         runlock(&h->lock);
253                         if(m == nil)
254                                 error(Enonexist);
255                         sch->mpt = mpt;
256                         nc->qid = shrqid(sch->level = Qcmpt, mpt->id);
257                 } else if(sch->level == Qshr) {
258                         shr = sch->shr;
259                         h = &shr->umh;
260                         wq2 = nil;
261                         rlock(&h->lock);
262                         for(m = h->mount; m && wq2 == nil; m = m->next){
263                                 if(m->to == nil)
264                                         continue;
265                                 if(waserror())
266                                         continue;
267                                 wq2 = devtab[m->to->type]->walk(m->to, nil, name + j, nname - j);
268                                 poperror();
269                         }
270                         runlock(&h->lock);
271                         if(wq2 == nil)
272                                 error(Enonexist);
273                         memmove(wq->qid + wq->nqid, wq2->qid, wq2->nqid);
274                         wq->nqid += wq2->nqid;
275                         if(alloc)
276                                 cclose(wq->clone);
277                         wq->clone = wq2->clone;
278                         free(wq2);
279                         poperror();
280                         return wq;
281                 } else
282                         error(Egreg);
283                 wq->qid[wq->nqid++] = nc->qid;
284         }
285         poperror();
286         return wq;
287 }
288
289 static int
290 shrgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
291 {
292         Mpt *mpt;
293         Sch *sch;
294         Shr *shr;
295         Mhead *h;
296         Mount *m;
297
298         sch = tosch(c);
299         switch(sch->level){
300         default:
301                 error(Egreg);
302         case Qroot:
303         case Qcroot:
304                 qlock(&shrslk);
305                 for(shr = shrs; shr && s; shr = shr->next)
306                         s--;
307                 if(shr == nil){
308                         qunlock(&shrslk);
309                         return -1;
310                 }
311                 kstrcpy(up->genbuf, shr->name, sizeof up->genbuf);
312                 if(sch->level == Qroot)
313                         devdir(c, shrqid(Qshr, shr->id), up->genbuf, 0, shr->owner,
314                                 shr->perm & ~0222, dp);
315                 else
316                         devdir(c, shrqid(Qcshr, shr->id), up->genbuf, 0, shr->owner,
317                                 shr->perm, dp);
318                 qunlock(&shrslk);
319                 return 1;
320         case Qcshr:
321                 shr = sch->shr;
322                 h = &shr->umh;
323                 rlock(&h->lock);
324                 for(m = h->mount; m && s; m = m->next)
325                         s--;
326                 if(m == nil){
327                         runlock(&h->lock);
328                         return -1;
329                 }
330                 mpt = tompt(m);
331                 kstrcpy(up->genbuf, mpt->name, sizeof up->genbuf);
332                 devdir(c, shrqid(Qcmpt, mpt->id), up->genbuf, 0, mpt->owner, mpt->perm, dp);
333                 runlock(&h->lock);
334                 return 1;
335         }
336 }
337
338 static int
339 shrstat(Chan *c, uchar *db, int n)
340 {
341         Sch *sch;
342         Dir dir;
343         int rc;
344
345         sch = tosch(c);
346         switch(sch->level){
347         default:
348                 error(Egreg);
349         case Qroot:
350                 devdir(c, c->qid, "#σ", 0, eve, 0555, &dir);
351                 break;
352         case Qcroot:
353                 devdir(c, c->qid, "#σc", 0, eve, 0777, &dir);
354                 break;
355         case Qshr:
356                 devdir(c, c->qid, sch->shr->name, 0, sch->shr->owner, sch->shr->perm & ~0222, &dir);
357                 break;
358         case Qcshr:
359                 devdir(c, c->qid, sch->shr->name, 0, sch->shr->owner, sch->shr->perm, &dir);
360                 break;
361         case Qcmpt:
362                 devdir(c, c->qid, sch->mpt->name, 0, sch->mpt->owner, sch->mpt->perm, &dir);
363                 break;
364         }
365         rc = convD2M(&dir, db, n);
366         if(rc == 0)
367                 error(Ebadarg);
368         return rc;
369 }
370
371 static Chan*
372 shropen(Chan *c, int omode)
373 {
374         Chan *nc;
375         Sch *sch;
376         Shr *shr;
377         Mpt *mpt;
378
379         if(c->qid.type == QTDIR && omode != OREAD)
380                 error(Eisdir);
381
382         sch = tosch(c);
383         switch(sch->level){
384         default:
385                 error(Egreg);
386         case Qroot:
387         case Qcroot:
388                 break;
389         case Qshr:
390         case Qcshr:
391                 shr = sch->shr;
392                 devpermcheck(shr->owner, shr->perm, openmode(omode));
393                 break;
394         case Qcmpt:
395                 if(omode&OTRUNC)
396                         error(Eexist);
397                 shr = sch->shr;
398                 mpt = sch->mpt;
399                 devpermcheck(mpt->owner, mpt->perm, openmode(omode));
400                 rlock(&shr->umh.lock);
401                 if(mpt->m.to == nil || mpt->m.to->mchan == nil){
402                         runlock(&shr->umh.lock);
403                         error(Eshutdown);
404                 }
405                 nc = mpt->m.to->mchan;
406                 incref(nc);
407                 runlock(&shr->umh.lock);
408                 if(openmode(omode) != nc->mode){
409                         cclose(nc);
410                         error(Eperm);
411                 }
412                 cclose(c);
413                 return nc;
414         }
415         c->mode = openmode(omode);
416         c->flag |= COPEN;
417         c->offset = 0;
418         return c;
419 }
420
421 /* chan.c */
422 Chan* createdir(Chan *c, Mhead *m);
423
424 static Chan*
425 shrcreate(Chan *c, char *name, int omode, ulong perm)
426 {
427         Sch *sch;
428         Shr *shr;
429         Mpt *mpt;
430         Mhead *h;
431         Mount *m;
432         Chan *nc;
433
434         sch = tosch(c);
435         switch(sch->level){
436         case Qcroot:
437         case Qcshr:
438                 if(strcmp(up->user, "none") == 0)
439                         error(Eperm);
440         }
441         switch(sch->level){
442         default:
443                 error(Eperm);
444         case Qshr:
445                 incref(c);
446                 if(waserror()){
447                         cclose(c);
448                         nexterror();
449                 }
450                 nc = createdir(c, &sch->shr->umh);
451                 poperror();
452                 if(waserror()){
453                         cclose(nc);
454                         nexterror();
455                 }
456                 nc = devtab[nc->type]->create(nc, name, omode, perm);
457                 poperror();
458                 cclose(c);
459                 return nc;      
460         case Qcroot:
461                 if((perm & DMDIR) == 0 || openmode(omode) != OREAD)
462                         error(Eperm);
463
464                 qlock(&shrslk);
465                 if(waserror()){
466                         qunlock(&shrslk);
467                         nexterror();
468                 }
469                 for(shr = shrs; shr; shr = shr->next)
470                         if(strcmp(name, shr->name) == 0)
471                                 error(Eexist);
472
473                 shr = smalloc(sizeof(*shr));
474                 incref(shr);
475                 shr->id = shrid++;
476
477                 kstrdup(&shr->name, name);
478                 kstrdup(&shr->owner, up->user);
479                 shr->perm = perm;
480
481                 incref(shr);
482                 shr->next = shrs;
483                 shrs = shr;
484
485                 poperror();
486                 qunlock(&shrslk);
487
488                 c->qid = shrqid(sch->level = Qcshr, shr->id);
489                 sch->shr = shr;
490                 break;
491         case Qcshr:
492                 if((perm & DMDIR) || openmode(omode) != OWRITE)
493                         error(Eperm);
494
495                 shr = sch->shr;
496                 if(strcmp(shr->owner, eve) == 0 && !iseve())
497                         error(Eperm);
498                 devpermcheck(shr->owner, shr->perm, ORDWR);
499
500                 h = &shr->umh;
501                 wlock(&h->lock);
502                 if(waserror()){
503                         wunlock(&h->lock);
504                         nexterror();
505                 }
506                 for(m = h->mount; m; m = m->next){
507                         mpt = tompt(m);
508                         if(strcmp(name, mpt->name) == 0)
509                                 error(Eexist);
510                 }
511
512                 mpt = smalloc(sizeof(*mpt));
513                 incref(mpt);
514                 mpt->id = mptid++;
515
516                 kstrdup(&mpt->name, name);
517                 kstrdup(&mpt->owner, up->user);
518                 mpt->perm = perm;
519
520                 incref(mpt);
521                 mpt->m.mflag = (h->mount == nil) ? MCREATE : 0;
522                 mpt->m.head = h;
523                 mpt->m.next = h->mount;
524                 h->mount = &mpt->m;
525
526                 poperror();
527                 wunlock(&h->lock);
528
529                 c->qid = shrqid(sch->level = Qcmpt, mpt->id);
530                 sch->mpt = mpt;
531                 break;
532         }
533         c->flag |= COPEN;
534         c->mode = openmode(omode);
535         return c;
536 }
537
538 static void
539 shrremove(Chan *c)
540 {
541         Mount *m, **ml;
542         Shr *shr, **sl;
543         Sch *sch;
544         Mpt *mpt;
545         Mhead *h;
546
547         sch = tosch(c);
548         if(waserror()){
549                 shrclunk(c);
550                 nexterror();
551         }
552         switch(sch->level){
553         default:
554                 error(Eperm);
555         case Qcshr:
556         case Qcmpt:
557                 shr = sch->shr;
558                 if(!iseve()){
559                         if(strcmp(shr->owner, eve) == 0)
560                                 error(Eperm);
561                         devpermcheck(shr->owner, shr->perm, ORDWR);
562                 }
563         }
564         switch(sch->level){
565         case Qcshr:
566                 h = &shr->umh;
567                 qlock(&shrslk);
568                 rlock(&h->lock);
569                 if(h->mount){
570                         runlock(&h->lock);
571                         qunlock(&shrslk);
572                         error("directory not empty");
573                 }
574                 runlock(&h->lock);
575                 for(sl = &shrs; *sl; sl = &((*sl)->next))
576                         if(*sl == shr){
577                                 *sl = shr->next;
578                                 shr->next = nil;
579                                 putshr(shr);
580                                 break;
581                         }
582                 qunlock(&shrslk);
583                 break;
584         case Qcmpt:
585                 mpt = sch->mpt;
586                 m = &mpt->m;
587                 h = &shr->umh;
588                 wlock(&h->lock);
589                 for(ml = &h->mount; *ml; ml = &((*ml)->next))
590                         if(*ml == m){
591                                 *ml = m->next;
592                                 m->next = nil;
593                                 m->head = nil;
594                                 putmpt(mpt);
595                                 break;
596                         }
597                 wunlock(&h->lock);
598                 break;
599         }
600         poperror();
601         shrclunk(c);
602 }
603
604 static int
605 shrwstat(Chan *c, uchar *dp, int n)
606 {
607         char *strs;
608         Mhead *h;
609         Sch *sch;
610         Ent *ent;
611         Dir d;
612
613         h = nil;
614         sch = tosch(c);
615         switch(sch->level){
616         default:
617                 error(Eperm);
618         case Qcshr:
619                 ent = sch->shr;
620                 qlock(&shrslk);
621                 if(waserror()){
622                         qunlock(&shrslk);
623                         nexterror();
624                 }
625                 break;
626         case Qcmpt:
627                 ent = sch->mpt;
628                 h = &sch->shr->umh;
629                 wlock(&h->lock);
630                 if(waserror()){
631                         wunlock(&h->lock);
632                         nexterror();
633                 }
634                 break;
635         }
636
637         if(strcmp(ent->owner, up->user) && !iseve())
638                 error(Eperm);
639
640         strs = smalloc(n);
641         if(waserror()){
642                 free(strs);
643                 nexterror();
644         }
645         n = convM2D(dp, n, &d, strs);
646         if(n == 0)
647                 error(Eshortstat);
648         if(d.mode != ~0UL)
649                 ent->perm = d.mode & 0777;
650         if(d.uid && *d.uid)
651                 kstrdup(&ent->owner, d.uid);
652         if(d.name && *d.name && strcmp(ent->name, d.name) != 0) {
653                 if(strchr(d.name, '/') != nil)
654                         error(Ebadchar);
655                 kstrdup(&ent->name, d.name);
656         }
657         poperror();
658         free(strs);
659
660         switch(sch->level){
661         default:
662                 error(Egreg);
663         case Qcshr:
664                 poperror();
665                 qunlock(&shrslk);
666                 break;
667         case Qcmpt:
668                 poperror();
669                 wunlock(&h->lock);
670                 break;
671         }
672         return n;
673 }
674
675 static long
676 shrread(Chan *c, void *va, long n, vlong)
677 {
678         Mhead *omh;
679         Sch *sch;
680
681         sch = tosch(c);
682         switch(sch->level){
683         default:
684                 error(Egreg);
685         case Qroot:
686         case Qcroot:
687         case Qcshr:
688                 return devdirread(c, va, n, 0, 0, shrgen);
689         case Qshr:
690                 omh = c->umh;
691                 c->umh = &sch->shr->umh;
692                 if(waserror()){
693                         c->umh = omh;
694                         nexterror();
695                 }
696                 n = unionread(c, va, n);
697                 poperror();
698                 c->umh = omh;
699                 return n;
700         }
701 }
702
703 static long
704 shrwrite(Chan *c, void *va, long n, vlong)
705 {
706         Sch *sch;
707         char *buf, *p, *aname;
708         int fd;
709         Chan *bc, *c0;
710         Mhead *h;
711         Mount *m;
712         struct{
713                 Chan    *chan;
714                 Chan    *authchan;
715                 char    *spec;
716                 int     flags;
717         }bogus;
718
719         sch = tosch(c);
720         if(sch->level != Qcmpt)
721                 error(Egreg);
722
723         buf = smalloc(n+1);
724         if(waserror()){
725                 free(buf);
726                 nexterror();
727         }
728         memmove(buf, va, n);
729         buf[n] = 0;
730         
731         fd = strtol(buf, &p, 10);
732         if(p == buf || (*p != 0 && *p != '\n'))
733                 error(Ebadarg);
734         if(*p == '\n' && *(p+1) != 0)
735                 aname = p + 1;
736         else
737                 aname = nil;
738         
739         bc = fdtochan(fd, ORDWR, 0, 1);
740         if(waserror()) {
741                 cclose(bc);
742                 nexterror();
743         }
744         bogus.flags = 0;
745         bogus.chan = bc;
746         bogus.authchan = nil;
747         bogus.spec = aname;
748         c0 = devtab[devno('M', 0)]->attach((char*)&bogus);
749         poperror();
750         cclose(bc);
751         poperror();
752         free(buf);
753
754         if(c0 == nil)
755                 error(Egreg);
756
757         m = &sch->mpt->m;
758         h = &sch->shr->umh;
759         wlock(&h->lock);
760         bc = m->to;
761         m->to = c0;
762         wunlock(&h->lock);
763
764         if(bc)
765                 cclose(bc);
766
767         return n;
768 }
769
770 static void
771 shrclose(Chan *c)
772 {
773         if(c->flag & CRCLOSE)
774                 shrremove(c);
775         else
776                 shrclunk(c);
777 }
778
779 Dev shrdevtab = {
780         L'σ',
781         "shr",
782
783         devreset,
784         shrinit,        
785         devshutdown,
786         shrattach,
787         shrwalk,
788         shrstat,
789         shropen,
790         shrcreate,
791         shrclose,
792         shrread,
793         devbread,
794         shrwrite,
795         devbwrite,
796         shrremove,
797         shrwstat,
798 };
799
800 static void
801 chowner(Ent *ent, char *old, char *new)
802 {
803         if(ent->owner!=nil && strcmp(old, ent->owner)==0)
804                 kstrdup(&ent->owner, new);
805 }
806
807 void
808 shrrenameuser(char *old, char *new)
809 {
810         Shr *shr;
811         Mount *m;
812
813         qlock(&shrslk);
814         for(shr = shrs; shr; shr = shr->next){
815                 wlock(&shr->umh.lock);
816                 for(m = shr->umh.mount; m; m = m->next)
817                         chowner(tompt(m), old, new);
818                 wunlock(&shr->umh.lock);
819                 chowner(shr, old, new);
820         }
821         qunlock(&shrslk);
822 }