| @@ -0,0 +1,426 @@ |
| | 1 | #include "u.h" |
| | 2 | #include "lib.h" |
| | 3 | #include "dat.h" |
| | 4 | #include "fns.h" |
| | 5 | #include "error.h" |
| | 6 | |
| | 7 | enum |
| | 8 | { |
| | 9 | Maxenvsize = 16300, |
| | 10 | }; |
| | 11 | |
| | 12 | static Egrp *envgrp(Chan *c); |
| | 13 | static int envwriteable(Chan *c); |
| | 14 | static void initunix(); |
| | 15 | |
| | 16 | static Egrp unixegrp; /* unix environment group */ |
| | 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 && e->name[0]==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 *_1, int _2, 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->lk); |
| | 45 | e = 0; |
| | 46 | if(name) |
| | 47 | e = envlookup(eg, name, -1); |
| | 48 | else if(s < eg->nent) |
| | 49 | e = eg->ent[s]; |
| | 50 | |
| | 51 | if(e == 0) { |
| | 52 | runlock(&eg->lk); |
| | 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->lk); |
| | 60 | return 1; |
| | 61 | } |
| | 62 | |
| | 63 | static Chan* |
| | 64 | envattach(char *spec) |
| | 65 | { |
| | 66 | Chan *c; |
| | 67 | |
| | 68 | if(spec && *spec) { |
| | 69 | error(Ebadarg); |
| | 70 | } |
| | 71 | initunix(); |
| | 72 | c = devattach('e', spec); |
| | 73 | c->aux = &unixegrp; |
| | 74 | return c; |
| | 75 | } |
| | 76 | |
| | 77 | static Walkqid* |
| | 78 | envwalk(Chan *c, Chan *nc, char **name, int nname) |
| | 79 | { |
| | 80 | return devwalk(c, nc, name, nname, 0, 0, envgen); |
| | 81 | } |
| | 82 | |
| | 83 | static int |
| | 84 | envstat(Chan *c, uchar *db, int n) |
| | 85 | { |
| | 86 | if(c->qid.type & QTDIR) |
| | 87 | c->qid.vers = envgrp(c)->vers; |
| | 88 | return devstat(c, db, n, 0, 0, envgen); |
| | 89 | } |
| | 90 | |
| | 91 | static Chan* |
| | 92 | envopen(Chan *c, int omode) |
| | 93 | { |
| | 94 | Egrp *eg; |
| | 95 | Evalue *e; |
| | 96 | int trunc; |
| | 97 | |
| | 98 | eg = envgrp(c); |
| | 99 | if(c->qid.type & QTDIR) { |
| | 100 | if(omode != OREAD) |
| | 101 | error(Eperm); |
| | 102 | } |
| | 103 | else { |
| | 104 | trunc = omode & OTRUNC; |
| | 105 | if(omode != OREAD && !envwriteable(c)) |
| | 106 | error(Eperm); |
| | 107 | if(trunc) |
| | 108 | wlock(&eg->lk); |
| | 109 | else |
| | 110 | rlock(&eg->lk); |
| | 111 | e = envlookup(eg, nil, c->qid.path); |
| | 112 | if(e == 0) { |
| | 113 | if(trunc) |
| | 114 | wunlock(&eg->lk); |
| | 115 | else |
| | 116 | runlock(&eg->lk); |
| | 117 | error(Enonexist); |
| | 118 | } |
| | 119 | if(trunc && e->value) { |
| | 120 | e->qid.vers++; |
| | 121 | free(e->value); |
| | 122 | e->value = 0; |
| | 123 | e->len = 0; |
| | 124 | } |
| | 125 | if(trunc) |
| | 126 | wunlock(&eg->lk); |
| | 127 | else |
| | 128 | runlock(&eg->lk); |
| | 129 | } |
| | 130 | c->mode = openmode(omode); |
| | 131 | c->flag |= COPEN; |
| | 132 | c->offset = 0; |
| | 133 | return c; |
| | 134 | } |
| | 135 | |
| | 136 | static Chan* |
| | 137 | envcreate(Chan *c, char *name, int omode, ulong _) |
| | 138 | { |
| | 139 | Egrp *eg; |
| | 140 | Evalue *e; |
| | 141 | Evalue **ent; |
| | 142 | |
| | 143 | if(c->qid.type != QTDIR) |
| | 144 | error(Eperm); |
| | 145 | if(strlen(name) >= sizeof up->genbuf) |
| | 146 | error("name too long"); /* protect envgen */ |
| | 147 | |
| | 148 | omode = openmode(omode); |
| | 149 | eg = envgrp(c); |
| | 150 | |
| | 151 | wlock(&eg->lk); |
| | 152 | if(waserror()) { |
| | 153 | wunlock(&eg->lk); |
| | 154 | nexterror(); |
| | 155 | } |
| | 156 | |
| | 157 | if(envlookup(eg, name, -1)) |
| | 158 | error(Eexist); |
| | 159 | |
| | 160 | e = smalloc(sizeof(Evalue)); |
| | 161 | e->name = smalloc(strlen(name)+1); |
| | 162 | strcpy(e->name, name); |
| | 163 | |
| | 164 | if(eg->nent == eg->ment){ |
| | 165 | eg->ment += 32; |
| | 166 | ent = smalloc(sizeof(eg->ent[0])*eg->ment); |
| | 167 | if(eg->nent) |
| | 168 | memmove(ent, eg->ent, sizeof(eg->ent[0])*eg->nent); |
| | 169 | free(eg->ent); |
| | 170 | eg->ent = ent; |
| | 171 | } |
| | 172 | e->qid.path = ++eg->path; |
| | 173 | e->qid.vers = 0; |
| | 174 | eg->vers++; |
| | 175 | eg->ent[eg->nent++] = e; |
| | 176 | c->qid = e->qid; |
| | 177 | |
| | 178 | wunlock(&eg->lk); |
| | 179 | poperror(); |
| | 180 | |
| | 181 | c->offset = 0; |
| | 182 | c->mode = omode; |
| | 183 | c->flag |= COPEN; |
| | 184 | return c; |
| | 185 | } |
| | 186 | |
| | 187 | static void |
| | 188 | envremove(Chan *c) |
| | 189 | { |
| | 190 | int i; |
| | 191 | Egrp *eg; |
| | 192 | Evalue *e; |
| | 193 | |
| | 194 | if(c->qid.type & QTDIR) |
| | 195 | error(Eperm); |
| | 196 | |
| | 197 | eg = envgrp(c); |
| | 198 | wlock(&eg->lk); |
| | 199 | e = 0; |
| | 200 | for(i=0; i<eg->nent; i++){ |
| | 201 | if(eg->ent[i]->qid.path == c->qid.path){ |
| | 202 | e = eg->ent[i]; |
| | 203 | eg->nent--; |
| | 204 | eg->ent[i] = eg->ent[eg->nent]; |
| | 205 | eg->vers++; |
| | 206 | break; |
| | 207 | } |
| | 208 | } |
| | 209 | wunlock(&eg->lk); |
| | 210 | if(e == 0) |
| | 211 | error(Enonexist); |
| | 212 | free(e->name); |
| | 213 | if(e->value) |
| | 214 | free(e->value); |
| | 215 | free(e); |
| | 216 | } |
| | 217 | |
| | 218 | static void |
| | 219 | envclose(Chan *c) |
| | 220 | { |
| | 221 | /* |
| | 222 | * cclose can't fail, so errors from remove will be ignored. |
| | 223 | * since permissions aren't checked, |
| | 224 | * envremove can't not remove it if its there. |
| | 225 | */ |
| | 226 | if(c->flag & CRCLOSE) |
| | 227 | envremove(c); |
| | 228 | } |
| | 229 | |
| | 230 | static long |
| | 231 | envread(Chan *c, void *a, long n, vlong off) |
| | 232 | { |
| | 233 | Egrp *eg; |
| | 234 | Evalue *e; |
| | 235 | ulong offset = off; |
| | 236 | |
| | 237 | if(c->qid.type & QTDIR) |
| | 238 | return devdirread(c, a, n, 0, 0, envgen); |
| | 239 | |
| | 240 | eg = envgrp(c); |
| | 241 | rlock(&eg->lk); |
| | 242 | e = envlookup(eg, nil, c->qid.path); |
| | 243 | if(e == 0) { |
| | 244 | runlock(&eg->lk); |
| | 245 | error(Enonexist); |
| | 246 | } |
| | 247 | |
| | 248 | if(offset > e->len) /* protects against overflow converting vlong to ulong */ |
| | 249 | n = 0; |
| | 250 | else if(offset + n > e->len) |
| | 251 | n = e->len - offset; |
| | 252 | if(n <= 0) |
| | 253 | n = 0; |
| | 254 | else |
| | 255 | memmove(a, e->value+offset, n); |
| | 256 | runlock(&eg->lk); |
| | 257 | return n; |
| | 258 | } |
| | 259 | |
| | 260 | static long |
| | 261 | envwrite(Chan *c, void *a, long n, vlong off) |
| | 262 | { |
| | 263 | char *s; |
| | 264 | ulong len; |
| | 265 | Egrp *eg; |
| | 266 | Evalue *e; |
| | 267 | ulong offset = off; |
| | 268 | |
| | 269 | if(n <= 0) |
| | 270 | return 0; |
| | 271 | if(offset > Maxenvsize || n > (Maxenvsize - offset)) |
| | 272 | error(Etoobig); |
| | 273 | |
| | 274 | eg = envgrp(c); |
| | 275 | wlock(&eg->lk); |
| | 276 | e = envlookup(eg, nil, c->qid.path); |
| | 277 | if(e == 0) { |
| | 278 | wunlock(&eg->lk); |
| | 279 | error(Enonexist); |
| | 280 | } |
| | 281 | |
| | 282 | len = offset+n; |
| | 283 | if(len > e->len) { |
| | 284 | s = smalloc(len); |
| | 285 | if(e->value){ |
| | 286 | memmove(s, e->value, e->len); |
| | 287 | free(e->value); |
| | 288 | } |
| | 289 | e->value = s; |
| | 290 | e->len = len; |
| | 291 | } |
| | 292 | memmove(e->value+offset, a, n); |
| | 293 | e->qid.vers++; |
| | 294 | eg->vers++; |
| | 295 | wunlock(&eg->lk); |
| | 296 | return n; |
| | 297 | } |
| | 298 | |
| | 299 | Dev envdevtab = { |
| | 300 | 'e', |
| | 301 | "env", |
| | 302 | |
| | 303 | devreset, |
| | 304 | devinit, |
| | 305 | devshutdown, |
| | 306 | envattach, |
| | 307 | envwalk, |
| | 308 | envstat, |
| | 309 | envopen, |
| | 310 | envcreate, |
| | 311 | envclose, |
| | 312 | envread, |
| | 313 | devbread, |
| | 314 | envwrite, |
| | 315 | devbwrite, |
| | 316 | envremove, |
| | 317 | devwstat, |
| | 318 | }; |
| | 319 | |
| | 320 | extern char **environ; |
| | 321 | |
| | 322 | static void |
| | 323 | initunix() |
| | 324 | { |
| | 325 | Egrp *eg = &unixegrp; |
| | 326 | Evalue **ent, *e; |
| | 327 | char *eq, **envp, *line; |
| | 328 | int n; |
| | 329 | |
| | 330 | wlock(&eg->lk); |
| | 331 | |
| | 332 | if(eg->path > 0 || eg->ment > 0 || !environ){ |
| | 333 | // already initialized or nothing in environent |
| | 334 | wunlock(&eg->lk); |
| | 335 | return; |
| | 336 | } |
| | 337 | |
| | 338 | for(envp = environ; *envp != nil; envp++) |
| | 339 | eg->ment++; |
| | 340 | ent = smalloc(sizeof(eg->ent[0])*eg->ment); |
| | 341 | eg->ent = ent; |
| | 342 | |
| | 343 | for(envp = environ; *envp != nil; envp++){ |
| | 344 | line = *envp; |
| | 345 | n = strlen(line); |
| | 346 | |
| | 347 | eq = strchr(line, '='); |
| | 348 | if(eq == nil) |
| | 349 | eq = &line[n]; |
| | 350 | e = smalloc(sizeof(Evalue)); |
| | 351 | e->name = smalloc(eq-line+1); |
| | 352 | strncpy(e->name, line, eq-line); |
| | 353 | |
| | 354 | if(eq[0] != '\0') |
| | 355 | eq++; |
| | 356 | e->len = line+n-eq; |
| | 357 | e->value = smalloc(e->len); |
| | 358 | memmove(e->value, eq, e->len); |
| | 359 | |
| | 360 | e->qid.path = ++eg->path; |
| | 361 | e->qid.vers = 0; |
| | 362 | eg->vers++; |
| | 363 | eg->ent[eg->nent++] = e; |
| | 364 | } |
| | 365 | |
| | 366 | wunlock(&eg->lk); |
| | 367 | } |
| | 368 | |
| | 369 | void |
| | 370 | envcpy(Egrp *to, Egrp *from) |
| | 371 | { |
| | 372 | int i; |
| | 373 | Evalue *ne, *e; |
| | 374 | |
| | 375 | rlock(&from->lk); |
| | 376 | to->ment = (from->nent+31)&~31; |
| | 377 | to->ent = smalloc(to->ment*sizeof(to->ent[0])); |
| | 378 | for(i=0; i<from->nent; i++){ |
| | 379 | e = from->ent[i]; |
| | 380 | ne = smalloc(sizeof(Evalue)); |
| | 381 | ne->name = smalloc(strlen(e->name)+1); |
| | 382 | strcpy(ne->name, e->name); |
| | 383 | if(e->value){ |
| | 384 | ne->value = smalloc(e->len); |
| | 385 | memmove(ne->value, e->value, e->len); |
| | 386 | ne->len = e->len; |
| | 387 | } |
| | 388 | ne->qid.path = ++to->path; |
| | 389 | to->ent[i] = ne; |
| | 390 | } |
| | 391 | to->nent = from->nent; |
| | 392 | runlock(&from->lk); |
| | 393 | } |
| | 394 | |
| | 395 | void |
| | 396 | closeegrp(Egrp *eg) |
| | 397 | { |
| | 398 | int i; |
| | 399 | Evalue *e; |
| | 400 | |
| | 401 | if(decref(&eg->ref) == 0){ |
| | 402 | for(i=0; i<eg->nent; i++){ |
| | 403 | e = eg->ent[i]; |
| | 404 | free(e->name); |
| | 405 | if(e->value) |
| | 406 | free(e->value); |
| | 407 | free(e); |
| | 408 | } |
| | 409 | free(eg->ent); |
| | 410 | free(eg); |
| | 411 | } |
| | 412 | } |
| | 413 | |
| | 414 | static Egrp* |
| | 415 | envgrp(Chan *c) |
| | 416 | { |
| | 417 | if(c->aux == nil) |
| | 418 | return &unixegrp; |
| | 419 | return c->aux; |
| | 420 | } |
| | 421 | |
| | 422 | static int |
| | 423 | envwriteable(Chan *c) |
| | 424 | { |
| | 425 | return iseve() || c->aux == nil; |
| | 426 | } |