]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devenv.c
devenv: prevent non-hostowner from creating or removing variables in '#ec', cleanup
[plan9front.git] / sys / src / 9 / port / devenv.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 {
10         Maxenvsize = 16300,
11 };
12
13 static Egrp     *envgrp(Chan *c);
14 static int      envwriteable(Chan *c);
15
16 static Egrp     confegrp;       /* global environment group containing the kernel configuration */
17
18 static Evalue*
19 envlookup(Egrp *eg, char *name, ulong qidpath)
20 {
21         Evalue *e;
22         int i;
23
24         for(i=0; i<eg->nent; i++){
25                 e = eg->ent[i];
26                 if(e->qid.path == qidpath || (name != nil && name[0] == e->name[0] && strcmp(e->name, name) == 0))
27                         return e;
28         }
29         return nil;
30 }
31
32 static int
33 envgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp)
34 {
35         Egrp *eg;
36         Evalue *e;
37
38         if(s == DEVDOTDOT){
39                 devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp);
40                 return 1;
41         }
42
43         eg = envgrp(c);
44         rlock(eg);
45         e = nil;
46         if(name != nil)
47                 e = envlookup(eg, name, -1);
48         else if(s < eg->nent)
49                 e = eg->ent[s];
50
51         if(e == nil || name != nil && (strlen(e->name) >= sizeof(up->genbuf))) {
52                 runlock(eg);
53                 return -1;
54         }
55
56         /* make sure name string continues to exist after we release lock */
57         kstrcpy(up->genbuf, e->name, sizeof up->genbuf);
58         devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp);
59         runlock(eg);
60         return 1;
61 }
62
63 static Chan*
64 envattach(char *spec)
65 {
66         Chan *c;
67         Egrp *egrp = nil;
68
69         if(spec != nil && *spec != '\0') {
70                 if(strcmp(spec, "c") != 0)
71                         error(Ebadarg);
72                 egrp = &confegrp;
73         }
74
75         c = devattach('e', spec);
76         c->aux = egrp;
77         return c;
78 }
79
80 static Walkqid*
81 envwalk(Chan *c, Chan *nc, char **name, int nname)
82 {
83         return devwalk(c, nc, name, nname, 0, 0, envgen);
84 }
85
86 static int
87 envstat(Chan *c, uchar *db, int n)
88 {
89         if(c->qid.type & QTDIR)
90                 c->qid.vers = envgrp(c)->vers;
91         return devstat(c, db, n, 0, 0, envgen);
92 }
93
94 static Chan*
95 envopen(Chan *c, int omode)
96 {
97         Egrp *eg;
98         Evalue *e;
99         int trunc;
100
101         eg = envgrp(c);
102         if(c->qid.type & QTDIR) {
103                 if(omode != OREAD)
104                         error(Eperm);
105         }
106         else {
107                 trunc = omode & OTRUNC;
108                 if(omode != OREAD && !envwriteable(c))
109                         error(Eperm);
110                 if(trunc)
111                         wlock(eg);
112                 else
113                         rlock(eg);
114                 e = envlookup(eg, nil, c->qid.path);
115                 if(e == nil) {
116                         if(trunc)
117                                 wunlock(eg);
118                         else
119                                 runlock(eg);
120                         error(Enonexist);
121                 }
122                 if(trunc && e->value != nil) {
123                         e->qid.vers++;
124                         free(e->value);
125                         e->value = nil;
126                         e->len = 0;
127                 }
128                 if(trunc)
129                         wunlock(eg);
130                 else
131                         runlock(eg);
132         }
133         c->mode = openmode(omode);
134         c->flag |= COPEN;
135         c->offset = 0;
136         return c;
137 }
138
139 static Chan*
140 envcreate(Chan *c, char *name, int omode, ulong)
141 {
142         Egrp *eg;
143         Evalue *e;
144         Evalue **ent;
145
146         if(c->qid.type != QTDIR || !envwriteable(c))
147                 error(Eperm);
148
149         if(strlen(name) >= sizeof(up->genbuf))
150                 error(Etoolong);
151
152         omode = openmode(omode);
153         eg = envgrp(c);
154
155         wlock(eg);
156         if(waserror()) {
157                 wunlock(eg);
158                 nexterror();
159         }
160
161         if(envlookup(eg, name, -1) != nil)
162                 error(Eexist);
163
164         e = smalloc(sizeof(Evalue));
165         e->name = smalloc(strlen(name)+1);
166         strcpy(e->name, name);
167
168         if(eg->nent == eg->ment){
169                 eg->ment += 32;
170                 ent = smalloc(sizeof(eg->ent[0])*eg->ment);
171                 if(eg->nent)
172                         memmove(ent, eg->ent, sizeof(eg->ent[0])*eg->nent);
173                 free(eg->ent);
174                 eg->ent = ent;
175         }
176         e->qid.path = ++eg->path;
177         e->qid.vers = 0;
178         eg->vers++;
179         eg->ent[eg->nent++] = e;
180         c->qid = e->qid;
181
182         wunlock(eg);
183         poperror();
184
185         c->offset = 0;
186         c->mode = omode;
187         c->flag |= COPEN;
188         return c;
189 }
190
191 static void
192 envremove(Chan *c)
193 {
194         int i;
195         Egrp *eg;
196         Evalue *e;
197
198         if(c->qid.type & QTDIR || !envwriteable(c))
199                 error(Eperm);
200
201         eg = envgrp(c);
202         wlock(eg);
203         e = nil;
204         for(i=0; i<eg->nent; i++){
205                 if(eg->ent[i]->qid.path == c->qid.path){
206                         e = eg->ent[i];
207                         eg->nent--;
208                         eg->ent[i] = eg->ent[eg->nent];
209                         eg->vers++;
210                         break;
211                 }
212         }
213         wunlock(eg);
214         if(e == nil)
215                 error(Enonexist);
216         free(e->name);
217         free(e->value);
218         free(e);
219 }
220
221 static void
222 envclose(Chan *c)
223 {
224         /*
225          * cclose can't fail, so errors from remove will be ignored.
226          * since permissions aren't checked,
227          * envremove can't not remove it if its there.
228          */
229         if(c->flag & CRCLOSE)
230                 envremove(c);
231 }
232
233 static long
234 envread(Chan *c, void *a, long n, vlong off)
235 {
236         Egrp *eg;
237         Evalue *e;
238         ulong offset = off;
239
240         if(c->qid.type & QTDIR)
241                 return devdirread(c, a, n, 0, 0, envgen);
242
243         eg = envgrp(c);
244         rlock(eg);
245         e = envlookup(eg, nil, c->qid.path);
246         if(e == nil) {
247                 runlock(eg);
248                 error(Enonexist);
249         }
250
251         if(offset >= e->len || e->value == nil)
252                 n = 0;
253         else if(offset + n > e->len)
254                 n = e->len - offset;
255         if(n <= 0)
256                 n = 0;
257         else
258                 memmove(a, e->value+offset, n);
259         runlock(eg);
260         return n;
261 }
262
263 static long
264 envwrite(Chan *c, void *a, long n, vlong off)
265 {
266         char *s;
267         ulong len;
268         Egrp *eg;
269         Evalue *e;
270         ulong offset = off;
271
272         if(n <= 0)
273                 return 0;
274         if(offset > Maxenvsize || n > (Maxenvsize - offset))
275                 error(Etoobig);
276
277         eg = envgrp(c);
278         wlock(eg);
279         e = envlookup(eg, nil, c->qid.path);
280         if(e == nil) {
281                 wunlock(eg);
282                 error(Enonexist);
283         }
284
285         len = offset+n;
286         if(len > e->len) {
287                 s = malloc(len);
288                 if(s == nil){
289                         wunlock(eg);
290                         error(Enomem);
291                 }
292                 if(e->value != nil){
293                         memmove(s, e->value, e->len);
294                         free(e->value);
295                 }
296                 e->value = s;
297                 e->len = len;
298         }
299         memmove(e->value+offset, a, n);
300         e->qid.vers++;
301         eg->vers++;
302         wunlock(eg);
303         return n;
304 }
305
306 Dev envdevtab = {
307         'e',
308         "env",
309
310         devreset,
311         devinit,
312         devshutdown,
313         envattach,
314         envwalk,
315         envstat,
316         envopen,
317         envcreate,
318         envclose,
319         envread,
320         devbread,
321         envwrite,
322         devbwrite,
323         envremove,
324         devwstat,
325 };
326
327 void
328 envcpy(Egrp *to, Egrp *from)
329 {
330         int i;
331         Evalue *ne, *e;
332
333         rlock(from);
334         to->ment = (from->nent+31)&~31;
335         to->ent = smalloc(to->ment*sizeof(to->ent[0]));
336         for(i=0; i<from->nent; i++){
337                 e = from->ent[i];
338                 ne = smalloc(sizeof(Evalue));
339                 ne->name = smalloc(strlen(e->name)+1);
340                 strcpy(ne->name, e->name);
341                 if(e->value != nil){
342                         ne->value = smalloc(e->len);
343                         memmove(ne->value, e->value, e->len);
344                         ne->len = e->len;
345                 }
346                 ne->qid.path = ++to->path;
347                 to->ent[i] = ne;
348         }
349         to->nent = from->nent;
350         runlock(from);
351 }
352
353 void
354 closeegrp(Egrp *eg)
355 {
356         int i;
357         Evalue *e;
358
359         if(decref(eg) == 0){
360                 for(i=0; i<eg->nent; i++){
361                         e = eg->ent[i];
362                         free(e->name);
363                         free(e->value);
364                         free(e);
365                 }
366                 free(eg->ent);
367                 free(eg);
368         }
369 }
370
371 static Egrp*
372 envgrp(Chan *c)
373 {
374         if(c->aux == nil)
375                 return up->egrp;
376         return c->aux;
377 }
378
379 static int
380 envwriteable(Chan *c)
381 {
382         return c->aux == nil || c->aux == up->egrp || iseve();
383 }
384
385 /*
386  *  to let the kernel set environment variables
387  */
388 void
389 ksetenv(char *ename, char *eval, int conf)
390 {
391         Chan *c;
392         char buf[2*KNAMELEN];
393         
394         snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename);
395         c = namec(buf, Acreate, OWRITE, 0600);
396         devtab[c->type]->write(c, eval, strlen(eval), 0);
397         cclose(c);
398 }
399
400 /*
401  * Return a copy of configuration environment as a sequence of strings.
402  * The strings alternate between name and value.  A zero length name string
403  * indicates the end of the list
404  */
405 char *
406 getconfenv(void)
407 {
408         Egrp *eg = &confegrp;
409         Evalue *e;
410         char *p, *q;
411         int i, n;
412
413         rlock(eg);
414         if(waserror()) {
415                 runlock(eg);
416                 nexterror();
417         }
418         
419         /* determine size */
420         n = 0;
421         for(i=0; i<eg->nent; i++){
422                 e = eg->ent[i];
423                 n += strlen(e->name) + e->len + 2;
424         }
425         p = malloc(n + 1);
426         if(p == nil)
427                 error(Enomem);
428         q = p;
429         for(i=0; i<eg->nent; i++){
430                 e = eg->ent[i];
431                 strcpy(q, e->name);
432                 q += strlen(q) + 1;
433                 memmove(q, e->value, e->len);
434                 q[e->len] = 0;
435                 /* move up to the first null */
436                 q += strlen(q) + 1;
437         }
438         *q = '\0';
439         
440         poperror();
441         runlock(eg);
442         return p;
443 }