]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devsrv.c
kernel: make allocb() wait instead of panic() when possible
[plan9front.git] / sys / src / 9 / port / devsrv.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
9 typedef struct Srv Srv;
10 struct Srv
11 {
12         char    *name;
13         char    *owner;
14         ulong   perm;
15         Chan    *chan;
16         Srv     *link;
17         ulong   path;
18 };
19
20 static QLock    srvlk;
21 static Srv      *srv;
22 static int      qidpath;
23
24 static Srv*
25 srvlookup(char *name, ulong qidpath)
26 {
27         Srv *sp;
28         for(sp = srv; sp; sp = sp->link)
29                 if(sp->path == qidpath || (name && strcmp(sp->name, name) == 0))
30                         return sp;
31         return nil;
32 }
33
34 static int
35 srvgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp)
36 {
37         Srv *sp;
38         Qid q;
39
40         if(s == DEVDOTDOT){
41                 devdir(c, c->qid, "#s", 0, eve, 0555, dp);
42                 return 1;
43         }
44
45         qlock(&srvlk);
46         if(name)
47                 sp = srvlookup(name, -1);
48         else {
49                 for(sp = srv; sp && s; sp = sp->link)
50                         s--;
51         }
52         if(sp == 0 || name && (strlen(sp->name) >= sizeof(up->genbuf))) {
53                 qunlock(&srvlk);
54                 return -1;
55         }
56         mkqid(&q, sp->path, 0, QTFILE);
57         /* make sure name string continues to exist after we release lock */
58         kstrcpy(up->genbuf, sp->name, sizeof up->genbuf);
59         devdir(c, q, up->genbuf, 0, sp->owner, sp->perm, dp);
60         qunlock(&srvlk);
61         return 1;
62 }
63
64 static void
65 srvinit(void)
66 {
67         qidpath = 1;
68 }
69
70 static Chan*
71 srvattach(char *spec)
72 {
73         return devattach('s', spec);
74 }
75
76 static Walkqid*
77 srvwalk(Chan *c, Chan *nc, char **name, int nname)
78 {
79         return devwalk(c, nc, name, nname, 0, 0, srvgen);
80 }
81
82 static int
83 srvstat(Chan *c, uchar *db, int n)
84 {
85         return devstat(c, db, n, 0, 0, srvgen);
86 }
87
88 char*
89 srvname(Chan *c)
90 {
91         Srv *sp;
92         char *s;
93
94         for(sp = srv; sp; sp = sp->link)
95                 if(sp->chan == c){
96                         s = smalloc(3+strlen(sp->name)+1);
97                         sprint(s, "#s/%s", sp->name);
98                         return s;
99                 }
100         return nil;
101 }
102
103 static Chan*
104 srvopen(Chan *c, int omode)
105 {
106         Srv *sp;
107
108         if(c->qid.type == QTDIR){
109                 if(omode & ORCLOSE)
110                         error(Eperm);
111                 if(omode != OREAD)
112                         error(Eisdir);
113                 c->mode = omode;
114                 c->flag |= COPEN;
115                 c->offset = 0;
116                 return c;
117         }
118         qlock(&srvlk);
119         if(waserror()){
120                 qunlock(&srvlk);
121                 nexterror();
122         }
123
124         sp = srvlookup(nil, c->qid.path);
125         if(sp == 0 || sp->chan == 0)
126                 error(Eshutdown);
127
128         if(omode&OTRUNC)
129                 error(Eexist);
130         if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR)
131                 error(Eperm);
132         devpermcheck(sp->owner, sp->perm, omode);
133
134         cclose(c);
135         incref(sp->chan);
136         qunlock(&srvlk);
137         poperror();
138         return sp->chan;
139 }
140
141 static Chan*
142 srvcreate(Chan *c, char *name, int omode, ulong perm)
143 {
144         char *sname;
145         Srv *sp;
146
147         if(openmode(omode) != OWRITE)
148                 error(Eperm);
149
150         if(strlen(name) >= sizeof(up->genbuf))
151                 error(Etoolong);
152
153         sp = smalloc(sizeof *sp);
154         sname = smalloc(strlen(name)+1);
155
156         qlock(&srvlk);
157         if(waserror()){
158                 free(sp);
159                 free(sname);
160                 qunlock(&srvlk);
161                 nexterror();
162         }
163         if(sp == nil || sname == nil)
164                 error(Enomem);
165         if(srvlookup(name, -1))
166                 error(Eexist);
167
168         sp->path = qidpath++;
169         sp->link = srv;
170         strcpy(sname, name);
171         sp->name = sname;
172         c->qid.type = QTFILE;
173         c->qid.path = sp->path;
174         srv = sp;
175         qunlock(&srvlk);
176         poperror();
177
178         kstrdup(&sp->owner, up->user);
179         sp->perm = perm&0777;
180
181         c->flag |= COPEN;
182         c->mode = OWRITE;
183         return c;
184 }
185
186 static void
187 srvremove(Chan *c)
188 {
189         Srv *sp, **l;
190
191         if(c->qid.type == QTDIR)
192                 error(Eperm);
193
194         qlock(&srvlk);
195         if(waserror()){
196                 qunlock(&srvlk);
197                 nexterror();
198         }
199         l = &srv;
200         for(sp = *l; sp; sp = sp->link) {
201                 if(sp->path == c->qid.path)
202                         break;
203
204                 l = &sp->link;
205         }
206         if(sp == 0)
207                 error(Enonexist);
208
209         /*
210          * Only eve can remove system services.
211          */
212         if(strcmp(sp->owner, eve) == 0 && !iseve())
213                 error(Eperm);
214
215         /*
216          * No removing personal services.
217          */
218         if((sp->perm&7) != 7 && strcmp(sp->owner, up->user) && !iseve())
219                 error(Eperm);
220
221         *l = sp->link;
222         qunlock(&srvlk);
223         poperror();
224
225         if(sp->chan)
226                 cclose(sp->chan);
227         free(sp->owner);
228         free(sp->name);
229         free(sp);
230 }
231
232 static int
233 srvwstat(Chan *c, uchar *dp, int n)
234 {
235         char *strs;
236         Dir d;
237         Srv *sp;
238
239         if(c->qid.type & QTDIR)
240                 error(Eperm);
241
242         strs = nil;
243         qlock(&srvlk);
244         if(waserror()){
245                 qunlock(&srvlk);
246                 free(strs);
247                 nexterror();
248         }
249
250         sp = srvlookup(nil, c->qid.path);
251         if(sp == 0)
252                 error(Enonexist);
253
254         if(strcmp(sp->owner, up->user) != 0 && !iseve())
255                 error(Eperm);
256
257         strs = smalloc(n);
258         n = convM2D(dp, n, &d, strs);
259         if(n == 0)
260                 error(Eshortstat);
261         if(d.mode != ~0UL)
262                 sp->perm = d.mode & 0777;
263         if(d.uid && *d.uid)
264                 kstrdup(&sp->owner, d.uid);
265         if(d.name && *d.name && strcmp(sp->name, d.name) != 0) {
266                 if(strchr(d.name, '/') != nil)
267                         error(Ebadchar);
268                 if(strlen(d.name) >= sizeof(up->genbuf))
269                         error(Etoolong);
270                 kstrdup(&sp->name, d.name);
271         }
272         qunlock(&srvlk);
273         free(strs);
274         poperror();
275         return n;
276 }
277
278 static void
279 srvclose(Chan *c)
280 {
281         /*
282          * in theory we need to override any changes in removability
283          * since open, but since all that's checked is the owner,
284          * which is immutable, all is well.
285          */
286         if(c->flag & CRCLOSE){
287                 if(waserror())
288                         return;
289                 srvremove(c);
290                 poperror();
291         }
292 }
293
294 static long
295 srvread(Chan *c, void *va, long n, vlong)
296 {
297         isdir(c);
298         return devdirread(c, va, n, 0, 0, srvgen);
299 }
300
301 static long
302 srvwrite(Chan *c, void *va, long n, vlong)
303 {
304         Srv *sp;
305         Chan *c1;
306         int fd;
307         char buf[32];
308
309         if(n >= sizeof buf)
310                 error(Etoobig);
311         memmove(buf, va, n);    /* so we can NUL-terminate */
312         buf[n] = 0;
313         fd = strtoul(buf, 0, 0);
314
315         c1 = fdtochan(fd, -1, 0, 1);    /* error check and inc ref */
316
317         qlock(&srvlk);
318         if(waserror()) {
319                 qunlock(&srvlk);
320                 cclose(c1);
321                 nexterror();
322         }
323         if(c1->flag & (CCEXEC|CRCLOSE))
324                 error("posted fd has remove-on-close or close-on-exec");
325         if(c1->qid.type & QTAUTH)
326                 error("cannot post auth file in srv");
327         sp = srvlookup(nil, c->qid.path);
328         if(sp == 0)
329                 error(Enonexist);
330
331         if(sp->chan)
332                 error(Ebadusefd);
333
334         sp->chan = c1;
335         qunlock(&srvlk);
336         poperror();
337         return n;
338 }
339
340 Dev srvdevtab = {
341         's',
342         "srv",
343
344         devreset,
345         srvinit,        
346         devshutdown,
347         srvattach,
348         srvwalk,
349         srvstat,
350         srvopen,
351         srvcreate,
352         srvclose,
353         srvread,
354         devbread,
355         srvwrite,
356         devbwrite,
357         srvremove,
358         srvwstat,
359 };
360
361 void
362 srvrenameuser(char *old, char *new)
363 {
364         Srv *sp;
365
366         qlock(&srvlk);
367         for(sp = srv; sp; sp = sp->link)
368                 if(sp->owner!=nil && strcmp(old, sp->owner)==0)
369                         kstrdup(&sp->owner, new);
370         qunlock(&srvlk);
371 }