rsc | 57ccfb9 | 2003-12-11 17:50:50 +0000 | [diff] [blame] | 1 | #include <u.h> |
| 2 | #include <libc.h> |
| 3 | #include <fcall.h> |
| 4 | |
| 5 | int post9pservice(int, char*); |
| 6 | |
| 7 | /* |
| 8 | * Rather than reading /adm/users, which is a lot of work for |
| 9 | * a toy program, we assume all groups have the form |
| 10 | * NNN:user:user: |
| 11 | * meaning that each user is the leader of his own group. |
| 12 | */ |
| 13 | |
| 14 | enum |
| 15 | { |
| 16 | OPERM = 0x3, /* mask of all permission types in open mode */ |
| 17 | Nram = 2048, |
| 18 | Maxsize = 512*1024*1024, |
| 19 | Maxfdata = 8192, |
| 20 | }; |
| 21 | |
| 22 | typedef struct Fid Fid; |
| 23 | typedef struct Ram Ram; |
| 24 | |
| 25 | struct Fid |
| 26 | { |
| 27 | short busy; |
| 28 | short open; |
| 29 | short rclose; |
| 30 | int fid; |
| 31 | Fid *next; |
| 32 | char *user; |
| 33 | Ram *ram; |
| 34 | }; |
| 35 | |
| 36 | struct Ram |
| 37 | { |
| 38 | short busy; |
| 39 | short open; |
| 40 | long parent; /* index in Ram array */ |
| 41 | Qid qid; |
| 42 | long perm; |
| 43 | char *name; |
| 44 | ulong atime; |
| 45 | ulong mtime; |
| 46 | char *user; |
| 47 | char *group; |
| 48 | char *muid; |
| 49 | char *data; |
| 50 | long ndata; |
| 51 | }; |
| 52 | |
| 53 | enum |
| 54 | { |
| 55 | Pexec = 1, |
| 56 | Pwrite = 2, |
| 57 | Pread = 4, |
| 58 | Pother = 1, |
| 59 | Pgroup = 8, |
| 60 | Powner = 64, |
| 61 | }; |
| 62 | |
| 63 | ulong path; /* incremented for each new file */ |
| 64 | Fid *fids; |
| 65 | Ram ram[Nram]; |
| 66 | int nram; |
| 67 | int mfd[2]; |
| 68 | char *user; |
| 69 | uchar mdata[IOHDRSZ+Maxfdata]; |
| 70 | uchar rdata[Maxfdata]; /* buffer for data in reply */ |
| 71 | uchar statbuf[STATMAX]; |
| 72 | Fcall thdr; |
| 73 | Fcall rhdr; |
| 74 | int messagesize = sizeof mdata; |
| 75 | |
| 76 | Fid * newfid(int); |
| 77 | uint ramstat(Ram*, uchar*, uint); |
| 78 | void error(char*); |
| 79 | void io(void); |
| 80 | void *erealloc(void*, ulong); |
| 81 | void *emalloc(ulong); |
| 82 | char *estrdup(char*); |
| 83 | void usage(void); |
| 84 | int perm(Fid*, Ram*, int); |
| 85 | |
| 86 | char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*), |
| 87 | *rattach(Fid*), *rwalk(Fid*), |
| 88 | *ropen(Fid*), *rcreate(Fid*), |
| 89 | *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), |
| 90 | *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*); |
| 91 | |
rsc | be22ae2 | 2004-03-26 01:59:35 +0000 | [diff] [blame^] | 92 | char *(*fcalls[Tmax])(Fid*); |
| 93 | |
| 94 | static void |
| 95 | initfcalls(void) |
| 96 | { |
| 97 | fcalls[Tversion]= rversion; |
| 98 | fcalls[Tflush]= rflush; |
| 99 | fcalls[Tauth]= rauth; |
| 100 | fcalls[Tattach]= rattach; |
| 101 | fcalls[Twalk]= rwalk; |
| 102 | fcalls[Topen]= ropen; |
| 103 | fcalls[Tcreate]= rcreate; |
| 104 | fcalls[Tread]= rread; |
| 105 | fcalls[Twrite]= rwrite; |
| 106 | fcalls[Tclunk]= rclunk; |
| 107 | fcalls[Tremove]= rremove; |
| 108 | fcalls[Tstat]= rstat; |
| 109 | fcalls[Twstat]= rwstat; |
| 110 | } |
rsc | 57ccfb9 | 2003-12-11 17:50:50 +0000 | [diff] [blame] | 111 | |
| 112 | char Eperm[] = "permission denied"; |
| 113 | char Enotdir[] = "not a directory"; |
| 114 | char Enoauth[] = "ramfs: authentication not required"; |
| 115 | char Enotexist[] = "file does not exist"; |
| 116 | char Einuse[] = "file in use"; |
| 117 | char Eexist[] = "file exists"; |
| 118 | char Eisdir[] = "file is a directory"; |
| 119 | char Enotowner[] = "not owner"; |
| 120 | char Eisopen[] = "file already open for I/O"; |
| 121 | char Excl[] = "exclusive use file already open"; |
| 122 | char Ename[] = "illegal name"; |
| 123 | char Eversion[] = "unknown 9P version"; |
| 124 | char Enotempty[] = "directory not empty"; |
| 125 | char Ebadfid[] = "bad fid"; |
| 126 | |
| 127 | int debug; |
| 128 | int private; |
| 129 | |
| 130 | void |
| 131 | notifyf(void *a, char *s) |
| 132 | { |
| 133 | USED(a); |
| 134 | if(strncmp(s, "interrupt", 9) == 0) |
| 135 | noted(NCONT); |
| 136 | noted(NDFLT); |
| 137 | } |
| 138 | |
| 139 | void |
| 140 | main(int argc, char *argv[]) |
| 141 | { |
| 142 | Ram *r; |
| 143 | char *defmnt; |
| 144 | int p[2]; |
| 145 | int stdio = 0; |
| 146 | char *service; |
| 147 | |
rsc | be22ae2 | 2004-03-26 01:59:35 +0000 | [diff] [blame^] | 148 | initfcalls(); |
rsc | 57ccfb9 | 2003-12-11 17:50:50 +0000 | [diff] [blame] | 149 | service = "ramfs"; |
| 150 | defmnt = "/tmp"; |
| 151 | ARGBEGIN{ |
| 152 | case 'D': |
| 153 | debug = 1; |
| 154 | break; |
| 155 | case 'i': |
| 156 | defmnt = 0; |
| 157 | stdio = 1; |
| 158 | mfd[0] = 0; |
| 159 | mfd[1] = 1; |
| 160 | break; |
| 161 | case 's': |
| 162 | defmnt = 0; |
| 163 | break; |
| 164 | case 'm': |
| 165 | defmnt = ARGF(); |
| 166 | break; |
| 167 | case 'p': |
| 168 | private++; |
| 169 | break; |
| 170 | case 'S': |
| 171 | defmnt = 0; |
| 172 | service = EARGF(usage()); |
| 173 | break; |
| 174 | default: |
| 175 | usage(); |
| 176 | }ARGEND |
| 177 | |
| 178 | if(defmnt) |
| 179 | sysfatal("cannot mount -- not on plan 9"); |
| 180 | |
| 181 | if(pipe(p) < 0) |
| 182 | error("pipe failed"); |
| 183 | if(!stdio){ |
| 184 | mfd[0] = p[0]; |
| 185 | mfd[1] = p[0]; |
| 186 | if(post9pservice(p[1], service) < 0) |
| 187 | sysfatal("post9pservice %s: %r", service); |
| 188 | } |
| 189 | |
| 190 | user = getuser(); |
| 191 | notify(notifyf); |
| 192 | nram = 2; |
| 193 | r = &ram[0]; |
| 194 | r->busy = 1; |
| 195 | r->data = 0; |
| 196 | r->ndata = 0; |
| 197 | r->perm = DMDIR | 0775; |
| 198 | r->qid.type = QTDIR; |
| 199 | r->qid.path = 0; |
| 200 | r->qid.vers = 0; |
| 201 | r->parent = 0; |
| 202 | r->user = user; |
| 203 | r->group = user; |
| 204 | r->muid = user; |
| 205 | r->atime = time(0); |
| 206 | r->mtime = r->atime; |
| 207 | r->name = estrdup("."); |
| 208 | |
| 209 | r = &ram[1]; |
| 210 | r->busy = 1; |
| 211 | r->data = 0; |
| 212 | r->ndata = 0; |
| 213 | r->perm = 0666; |
| 214 | r->qid.type = 0; |
| 215 | r->qid.path = 1; |
| 216 | r->qid.vers = 0; |
| 217 | r->parent = 0; |
| 218 | r->user = user; |
| 219 | r->group = user; |
| 220 | r->muid = user; |
| 221 | r->atime = time(0); |
| 222 | r->mtime = r->atime; |
| 223 | r->name = estrdup("file"); |
| 224 | |
| 225 | if(debug) |
| 226 | fmtinstall('F', fcallfmt); |
| 227 | switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ |
| 228 | case -1: |
| 229 | error("fork"); |
| 230 | case 0: |
| 231 | close(p[1]); |
| 232 | io(); |
| 233 | break; |
| 234 | default: |
| 235 | close(p[0]); /* don't deadlock if child fails */ |
| 236 | } |
| 237 | exits(0); |
| 238 | } |
| 239 | |
| 240 | char* |
| 241 | rversion(Fid *x) |
| 242 | { |
| 243 | Fid *f; |
| 244 | |
| 245 | USED(x); |
| 246 | for(f = fids; f; f = f->next) |
| 247 | if(f->busy) |
| 248 | rclunk(f); |
| 249 | if(thdr.msize > sizeof mdata) |
| 250 | rhdr.msize = sizeof mdata; |
| 251 | else |
| 252 | rhdr.msize = thdr.msize; |
| 253 | messagesize = rhdr.msize; |
| 254 | if(strncmp(thdr.version, "9P2000", 6) != 0) |
| 255 | return Eversion; |
| 256 | rhdr.version = "9P2000"; |
| 257 | return 0; |
| 258 | } |
| 259 | |
| 260 | char* |
| 261 | rauth(Fid *x) |
| 262 | { |
| 263 | if(x->busy) |
| 264 | return Ebadfid; |
| 265 | return "ramfs: no authentication required"; |
| 266 | } |
| 267 | |
| 268 | char* |
| 269 | rflush(Fid *f) |
| 270 | { |
| 271 | USED(f); |
| 272 | return 0; |
| 273 | } |
| 274 | |
| 275 | char* |
| 276 | rattach(Fid *f) |
| 277 | { |
| 278 | /* no authentication! */ |
| 279 | if(f->busy) |
| 280 | return Ebadfid; |
| 281 | f->busy = 1; |
| 282 | f->rclose = 0; |
| 283 | f->ram = &ram[0]; |
| 284 | rhdr.qid = f->ram->qid; |
| 285 | if(thdr.uname[0]) |
| 286 | f->user = estrdup(thdr.uname); |
| 287 | else |
| 288 | f->user = "none"; |
| 289 | if(strcmp(user, "none") == 0) |
| 290 | user = f->user; |
| 291 | return 0; |
| 292 | } |
| 293 | |
| 294 | char* |
| 295 | clone(Fid *f, Fid **nf) |
| 296 | { |
| 297 | if(!f->busy) |
| 298 | return Ebadfid; |
| 299 | if(f->open) |
| 300 | return Eisopen; |
| 301 | if(f->ram->busy == 0) |
| 302 | return Enotexist; |
| 303 | *nf = newfid(thdr.newfid); |
| 304 | (*nf)->busy = 1; |
| 305 | (*nf)->open = 0; |
| 306 | (*nf)->rclose = 0; |
| 307 | (*nf)->ram = f->ram; |
| 308 | (*nf)->user = f->user; /* no ref count; the leakage is minor */ |
| 309 | return 0; |
| 310 | } |
| 311 | |
| 312 | char* |
| 313 | rwalk(Fid *f) |
| 314 | { |
| 315 | Ram *r, *fram; |
| 316 | char *name; |
| 317 | Ram *parent; |
| 318 | Fid *nf; |
| 319 | char *err; |
| 320 | ulong t; |
| 321 | int i; |
| 322 | |
| 323 | if(!f->busy) |
| 324 | return Ebadfid; |
| 325 | err = nil; |
| 326 | nf = nil; |
| 327 | rhdr.nwqid = 0; |
| 328 | if(thdr.newfid != thdr.fid){ |
| 329 | err = clone(f, &nf); |
| 330 | if(err) |
| 331 | return err; |
| 332 | f = nf; /* walk the new fid */ |
| 333 | } |
| 334 | fram = f->ram; |
| 335 | if(thdr.nwname > 0){ |
| 336 | t = time(0); |
| 337 | for(i=0; i<thdr.nwname && i<MAXWELEM; i++){ |
| 338 | if((fram->qid.type & QTDIR) == 0){ |
| 339 | err = Enotdir; |
| 340 | break; |
| 341 | } |
| 342 | if(fram->busy == 0){ |
| 343 | err = Enotexist; |
| 344 | break; |
| 345 | } |
| 346 | fram->atime = t; |
| 347 | name = thdr.wname[i]; |
| 348 | if(strcmp(name, ".") == 0){ |
| 349 | Found: |
| 350 | rhdr.nwqid++; |
| 351 | rhdr.wqid[i] = fram->qid; |
| 352 | continue; |
| 353 | } |
| 354 | parent = &ram[fram->parent]; |
| 355 | if(!perm(f, parent, Pexec)){ |
| 356 | err = Eperm; |
| 357 | break; |
| 358 | } |
| 359 | if(strcmp(name, "..") == 0){ |
| 360 | fram = parent; |
| 361 | goto Found; |
| 362 | } |
| 363 | for(r=ram; r < &ram[nram]; r++) |
| 364 | if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){ |
| 365 | fram = r; |
| 366 | goto Found; |
| 367 | } |
| 368 | break; |
| 369 | } |
| 370 | if(i==0 && err == nil) |
| 371 | err = Enotexist; |
| 372 | } |
| 373 | if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){ |
| 374 | /* clunk the new fid, which is the one we walked */ |
| 375 | fprint(2, "f %d zero busy\n", f->fid); |
| 376 | f->busy = 0; |
| 377 | f->ram = nil; |
| 378 | } |
| 379 | if(rhdr.nwqid == thdr.nwname) /* update the fid after a successful walk */ |
| 380 | f->ram = fram; |
| 381 | assert(f->busy); |
| 382 | return err; |
| 383 | } |
| 384 | |
| 385 | char * |
| 386 | ropen(Fid *f) |
| 387 | { |
| 388 | Ram *r; |
| 389 | int mode, trunc; |
| 390 | |
| 391 | if(!f->busy) |
| 392 | return Ebadfid; |
| 393 | if(f->open) |
| 394 | return Eisopen; |
| 395 | r = f->ram; |
| 396 | if(r->busy == 0) |
| 397 | return Enotexist; |
| 398 | if(r->perm & DMEXCL) |
| 399 | if(r->open) |
| 400 | return Excl; |
| 401 | mode = thdr.mode; |
| 402 | if(r->qid.type & QTDIR){ |
| 403 | if(mode != OREAD) |
| 404 | return Eperm; |
| 405 | rhdr.qid = r->qid; |
| 406 | return 0; |
| 407 | } |
| 408 | if(mode & ORCLOSE){ |
| 409 | /* can't remove root; must be able to write parent */ |
| 410 | if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite)) |
| 411 | return Eperm; |
| 412 | f->rclose = 1; |
| 413 | } |
| 414 | trunc = mode & OTRUNC; |
| 415 | mode &= OPERM; |
| 416 | if(mode==OWRITE || mode==ORDWR || trunc) |
| 417 | if(!perm(f, r, Pwrite)) |
| 418 | return Eperm; |
| 419 | if(mode==OREAD || mode==ORDWR) |
| 420 | if(!perm(f, r, Pread)) |
| 421 | return Eperm; |
| 422 | if(mode==OEXEC) |
| 423 | if(!perm(f, r, Pexec)) |
| 424 | return Eperm; |
| 425 | if(trunc && (r->perm&DMAPPEND)==0){ |
| 426 | r->ndata = 0; |
| 427 | if(r->data) |
| 428 | free(r->data); |
| 429 | r->data = 0; |
| 430 | r->qid.vers++; |
| 431 | } |
| 432 | rhdr.qid = r->qid; |
| 433 | rhdr.iounit = messagesize-IOHDRSZ; |
| 434 | f->open = 1; |
| 435 | r->open++; |
| 436 | return 0; |
| 437 | } |
| 438 | |
| 439 | char * |
| 440 | rcreate(Fid *f) |
| 441 | { |
| 442 | Ram *r; |
| 443 | char *name; |
| 444 | long parent, prm; |
| 445 | |
| 446 | if(!f->busy) |
| 447 | return Ebadfid; |
| 448 | if(f->open) |
| 449 | return Eisopen; |
| 450 | if(f->ram->busy == 0) |
| 451 | return Enotexist; |
| 452 | parent = f->ram - ram; |
| 453 | if((f->ram->qid.type&QTDIR) == 0) |
| 454 | return Enotdir; |
| 455 | /* must be able to write parent */ |
| 456 | if(!perm(f, f->ram, Pwrite)) |
| 457 | return Eperm; |
| 458 | prm = thdr.perm; |
| 459 | name = thdr.name; |
| 460 | if(strcmp(name, ".")==0 || strcmp(name, "..")==0) |
| 461 | return Ename; |
| 462 | for(r=ram; r<&ram[nram]; r++) |
| 463 | if(r->busy && parent==r->parent) |
| 464 | if(strcmp((char*)name, r->name)==0) |
| 465 | return Einuse; |
| 466 | for(r=ram; r->busy; r++) |
| 467 | if(r == &ram[Nram-1]) |
| 468 | return "no free ram resources"; |
| 469 | r->busy = 1; |
| 470 | r->qid.path = ++path; |
| 471 | r->qid.vers = 0; |
| 472 | if(prm & DMDIR) |
| 473 | r->qid.type |= QTDIR; |
| 474 | r->parent = parent; |
| 475 | free(r->name); |
| 476 | r->name = estrdup(name); |
| 477 | r->user = f->user; |
| 478 | r->group = f->ram->group; |
| 479 | r->muid = f->ram->muid; |
| 480 | if(prm & DMDIR) |
| 481 | prm = (prm&~0777) | (f->ram->perm&prm&0777); |
| 482 | else |
| 483 | prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666); |
| 484 | r->perm = prm; |
| 485 | r->ndata = 0; |
| 486 | if(r-ram >= nram) |
| 487 | nram = r - ram + 1; |
| 488 | r->atime = time(0); |
| 489 | r->mtime = r->atime; |
| 490 | f->ram->mtime = r->atime; |
| 491 | f->ram = r; |
| 492 | rhdr.qid = r->qid; |
| 493 | rhdr.iounit = messagesize-IOHDRSZ; |
| 494 | f->open = 1; |
| 495 | if(thdr.mode & ORCLOSE) |
| 496 | f->rclose = 1; |
| 497 | r->open++; |
| 498 | return 0; |
| 499 | } |
| 500 | |
| 501 | char* |
| 502 | rread(Fid *f) |
| 503 | { |
| 504 | Ram *r; |
| 505 | uchar *buf; |
| 506 | long off; |
| 507 | int n, m, cnt; |
| 508 | |
| 509 | if(!f->busy) |
| 510 | return Ebadfid; |
| 511 | if(f->ram->busy == 0) |
| 512 | return Enotexist; |
| 513 | n = 0; |
| 514 | rhdr.count = 0; |
| 515 | off = thdr.offset; |
| 516 | buf = rdata; |
| 517 | cnt = thdr.count; |
| 518 | if(cnt > messagesize) /* shouldn't happen, anyway */ |
| 519 | cnt = messagesize; |
| 520 | if(f->ram->qid.type & QTDIR){ |
| 521 | for(r=ram+1; off > 0; r++){ |
| 522 | if(r->busy && r->parent==f->ram-ram) |
| 523 | off -= ramstat(r, statbuf, sizeof statbuf); |
| 524 | if(r == &ram[nram-1]) |
| 525 | return 0; |
| 526 | } |
| 527 | for(; r<&ram[nram] && n < cnt; r++){ |
| 528 | if(!r->busy || r->parent!=f->ram-ram) |
| 529 | continue; |
| 530 | m = ramstat(r, buf+n, cnt-n); |
| 531 | if(m == 0) |
| 532 | break; |
| 533 | n += m; |
| 534 | } |
| 535 | rhdr.data = (char*)rdata; |
| 536 | rhdr.count = n; |
| 537 | return 0; |
| 538 | } |
| 539 | r = f->ram; |
| 540 | if(off >= r->ndata) |
| 541 | return 0; |
| 542 | r->atime = time(0); |
| 543 | n = cnt; |
| 544 | if(off+n > r->ndata) |
| 545 | n = r->ndata - off; |
| 546 | rhdr.data = r->data+off; |
| 547 | rhdr.count = n; |
| 548 | return 0; |
| 549 | } |
| 550 | |
| 551 | char* |
| 552 | rwrite(Fid *f) |
| 553 | { |
| 554 | Ram *r; |
| 555 | ulong off; |
| 556 | int cnt; |
| 557 | |
| 558 | r = f->ram; |
| 559 | if(!f->busy) |
| 560 | return Ebadfid; |
| 561 | if(r->busy == 0) |
| 562 | return Enotexist; |
| 563 | off = thdr.offset; |
| 564 | if(r->perm & DMAPPEND) |
| 565 | off = r->ndata; |
| 566 | cnt = thdr.count; |
| 567 | if(r->qid.type & QTDIR) |
| 568 | return Eisdir; |
| 569 | if(off+cnt >= Maxsize) /* sanity check */ |
| 570 | return "write too big"; |
| 571 | if(off+cnt > r->ndata) |
| 572 | r->data = erealloc(r->data, off+cnt); |
| 573 | if(off > r->ndata) |
| 574 | memset(r->data+r->ndata, 0, off-r->ndata); |
| 575 | if(off+cnt > r->ndata) |
| 576 | r->ndata = off+cnt; |
| 577 | memmove(r->data+off, thdr.data, cnt); |
| 578 | r->qid.vers++; |
| 579 | r->mtime = time(0); |
| 580 | rhdr.count = cnt; |
| 581 | return 0; |
| 582 | } |
| 583 | |
| 584 | static int |
| 585 | emptydir(Ram *dr) |
| 586 | { |
| 587 | long didx = dr - ram; |
| 588 | Ram *r; |
| 589 | |
| 590 | for(r=ram; r<&ram[nram]; r++) |
| 591 | if(r->busy && didx==r->parent) |
| 592 | return 0; |
| 593 | return 1; |
| 594 | } |
| 595 | |
| 596 | char * |
| 597 | realremove(Ram *r) |
| 598 | { |
| 599 | if(r->qid.type & QTDIR && !emptydir(r)) |
| 600 | return Enotempty; |
| 601 | r->ndata = 0; |
| 602 | if(r->data) |
| 603 | free(r->data); |
| 604 | r->data = 0; |
| 605 | r->parent = 0; |
| 606 | memset(&r->qid, 0, sizeof r->qid); |
| 607 | free(r->name); |
| 608 | r->name = nil; |
| 609 | r->busy = 0; |
| 610 | return nil; |
| 611 | } |
| 612 | |
| 613 | char * |
| 614 | rclunk(Fid *f) |
| 615 | { |
| 616 | char *e = nil; |
| 617 | |
| 618 | if(f->open) |
| 619 | f->ram->open--; |
| 620 | if(f->rclose) |
| 621 | e = realremove(f->ram); |
| 622 | fprint(2, "clunk fid %d busy=%d\n", f->fid, f->busy); |
| 623 | fprint(2, "f %d zero busy\n", f->fid); |
| 624 | f->busy = 0; |
| 625 | f->open = 0; |
| 626 | f->ram = 0; |
| 627 | return e; |
| 628 | } |
| 629 | |
| 630 | char * |
| 631 | rremove(Fid *f) |
| 632 | { |
| 633 | Ram *r; |
| 634 | |
| 635 | if(f->open) |
| 636 | f->ram->open--; |
| 637 | fprint(2, "f %d zero busy\n", f->fid); |
| 638 | f->busy = 0; |
| 639 | f->open = 0; |
| 640 | r = f->ram; |
| 641 | f->ram = 0; |
| 642 | if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite)) |
| 643 | return Eperm; |
| 644 | ram[r->parent].mtime = time(0); |
| 645 | return realremove(r); |
| 646 | } |
| 647 | |
| 648 | char * |
| 649 | rstat(Fid *f) |
| 650 | { |
| 651 | if(!f->busy) |
| 652 | return Ebadfid; |
| 653 | if(f->ram->busy == 0) |
| 654 | return Enotexist; |
| 655 | rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf); |
| 656 | rhdr.stat = statbuf; |
| 657 | return 0; |
| 658 | } |
| 659 | |
| 660 | char * |
| 661 | rwstat(Fid *f) |
| 662 | { |
| 663 | Ram *r, *s; |
| 664 | Dir dir; |
| 665 | |
| 666 | if(!f->busy) |
| 667 | return Ebadfid; |
| 668 | if(f->ram->busy == 0) |
| 669 | return Enotexist; |
| 670 | convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf); |
| 671 | r = f->ram; |
| 672 | |
| 673 | /* |
| 674 | * To change length, must have write permission on file. |
| 675 | */ |
| 676 | if(dir.length!=~0 && dir.length!=r->ndata){ |
| 677 | if(!perm(f, r, Pwrite)) |
| 678 | return Eperm; |
| 679 | } |
| 680 | |
| 681 | /* |
| 682 | * To change name, must have write permission in parent |
| 683 | * and name must be unique. |
| 684 | */ |
| 685 | if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){ |
| 686 | if(!perm(f, &ram[r->parent], Pwrite)) |
| 687 | return Eperm; |
| 688 | for(s=ram; s<&ram[nram]; s++) |
| 689 | if(s->busy && s->parent==r->parent) |
| 690 | if(strcmp(dir.name, s->name)==0) |
| 691 | return Eexist; |
| 692 | } |
| 693 | |
| 694 | /* |
| 695 | * To change mode, must be owner or group leader. |
| 696 | * Because of lack of users file, leader=>group itself. |
| 697 | */ |
| 698 | if(dir.mode!=~0 && r->perm!=dir.mode){ |
| 699 | if(strcmp(f->user, r->user) != 0) |
| 700 | if(strcmp(f->user, r->group) != 0) |
| 701 | return Enotowner; |
| 702 | } |
| 703 | |
| 704 | /* |
| 705 | * To change group, must be owner and member of new group, |
| 706 | * or leader of current group and leader of new group. |
| 707 | * Second case cannot happen, but we check anyway. |
| 708 | */ |
| 709 | if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){ |
| 710 | if(strcmp(f->user, r->user) == 0) |
| 711 | // if(strcmp(f->user, dir.gid) == 0) |
| 712 | goto ok; |
| 713 | if(strcmp(f->user, r->group) == 0) |
| 714 | if(strcmp(f->user, dir.gid) == 0) |
| 715 | goto ok; |
| 716 | return Enotowner; |
| 717 | ok:; |
| 718 | } |
| 719 | |
| 720 | /* all ok; do it */ |
| 721 | if(dir.mode != ~0){ |
| 722 | dir.mode &= ~DMDIR; /* cannot change dir bit */ |
| 723 | dir.mode |= r->perm&DMDIR; |
| 724 | r->perm = dir.mode; |
| 725 | } |
| 726 | if(dir.name[0] != '\0'){ |
| 727 | free(r->name); |
| 728 | r->name = estrdup(dir.name); |
| 729 | } |
| 730 | if(dir.gid[0] != '\0') |
| 731 | r->group = estrdup(dir.gid); |
| 732 | if(dir.length!=~0 && dir.length!=r->ndata){ |
| 733 | r->data = erealloc(r->data, dir.length); |
| 734 | if(r->ndata < dir.length) |
| 735 | memset(r->data+r->ndata, 0, dir.length-r->ndata); |
| 736 | r->ndata = dir.length; |
| 737 | } |
| 738 | ram[r->parent].mtime = time(0); |
| 739 | return 0; |
| 740 | } |
| 741 | |
| 742 | uint |
| 743 | ramstat(Ram *r, uchar *buf, uint nbuf) |
| 744 | { |
| 745 | int n; |
| 746 | Dir dir; |
| 747 | |
| 748 | dir.name = r->name; |
| 749 | dir.qid = r->qid; |
| 750 | dir.mode = r->perm; |
| 751 | dir.length = r->ndata; |
| 752 | dir.uid = r->user; |
| 753 | dir.gid = r->group; |
| 754 | dir.muid = r->muid; |
| 755 | dir.atime = r->atime; |
| 756 | dir.mtime = r->mtime; |
| 757 | n = convD2M(&dir, buf, nbuf); |
| 758 | if(n > 2) |
| 759 | return n; |
| 760 | return 0; |
| 761 | } |
| 762 | |
| 763 | Fid * |
| 764 | newfid(int fid) |
| 765 | { |
| 766 | Fid *f, *ff; |
| 767 | |
| 768 | ff = 0; |
| 769 | for(f = fids; f; f = f->next) |
| 770 | if(f->fid == fid){ |
| 771 | fprint(2, "got fid %d busy=%d\n", fid, f->busy); |
| 772 | return f; |
| 773 | } |
| 774 | else if(!ff && !f->busy) |
| 775 | ff = f; |
| 776 | if(ff){ |
| 777 | ff->fid = fid; |
| 778 | return ff; |
| 779 | } |
| 780 | f = emalloc(sizeof *f); |
| 781 | f->ram = nil; |
| 782 | f->fid = fid; |
| 783 | f->next = fids; |
| 784 | fids = f; |
| 785 | return f; |
| 786 | } |
| 787 | |
| 788 | void |
| 789 | io(void) |
| 790 | { |
| 791 | char *err, buf[20]; |
| 792 | int n, pid, ctl; |
| 793 | |
| 794 | pid = getpid(); |
| 795 | if(private){ |
| 796 | snprint(buf, sizeof buf, "/proc/%d/ctl", pid); |
| 797 | ctl = open(buf, OWRITE); |
| 798 | if(ctl < 0){ |
| 799 | fprint(2, "can't protect ramfs\n"); |
| 800 | }else{ |
| 801 | fprint(ctl, "noswap\n"); |
| 802 | fprint(ctl, "private\n"); |
| 803 | close(ctl); |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | for(;;){ |
| 808 | /* |
| 809 | * reading from a pipe or a network device |
| 810 | * will give an error after a few eof reads. |
| 811 | * however, we cannot tell the difference |
| 812 | * between a zero-length read and an interrupt |
| 813 | * on the processes writing to us, |
| 814 | * so we wait for the error. |
| 815 | */ |
| 816 | n = read9pmsg(mfd[0], mdata, messagesize); |
| 817 | if(n < 0) |
| 818 | error("mount read"); |
| 819 | if(n == 0) |
| 820 | error("mount eof"); |
| 821 | if(convM2S(mdata, n, &thdr) == 0) |
| 822 | continue; |
| 823 | |
| 824 | if(debug) |
| 825 | fprint(2, "ramfs %d:<-%F\n", pid, &thdr); |
| 826 | |
| 827 | if(!fcalls[thdr.type]) |
| 828 | err = "bad fcall type"; |
| 829 | else |
| 830 | err = (*fcalls[thdr.type])(newfid(thdr.fid)); |
| 831 | if(err){ |
| 832 | rhdr.type = Rerror; |
| 833 | rhdr.ename = err; |
| 834 | }else{ |
| 835 | rhdr.type = thdr.type + 1; |
| 836 | rhdr.fid = thdr.fid; |
| 837 | } |
| 838 | rhdr.tag = thdr.tag; |
| 839 | if(debug) |
| 840 | fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/ |
| 841 | n = convS2M(&rhdr, mdata, messagesize); |
| 842 | if(n == 0) |
| 843 | error("convS2M error on write"); |
| 844 | if(write(mfd[1], mdata, n) != n) |
| 845 | error("mount write"); |
| 846 | } |
| 847 | } |
| 848 | |
| 849 | int |
| 850 | perm(Fid *f, Ram *r, int p) |
| 851 | { |
| 852 | if((p*Pother) & r->perm) |
| 853 | return 1; |
| 854 | if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm)) |
| 855 | return 1; |
| 856 | if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm)) |
| 857 | return 1; |
| 858 | return 0; |
| 859 | } |
| 860 | |
| 861 | void |
| 862 | error(char *s) |
| 863 | { |
| 864 | fprint(2, "%s: %s: %r\n", argv0, s); |
| 865 | exits(s); |
| 866 | } |
| 867 | |
| 868 | void * |
| 869 | emalloc(ulong n) |
| 870 | { |
| 871 | void *p; |
| 872 | |
| 873 | p = malloc(n); |
| 874 | if(!p) |
| 875 | error("out of memory"); |
| 876 | memset(p, 0, n); |
| 877 | return p; |
| 878 | } |
| 879 | |
| 880 | void * |
| 881 | erealloc(void *p, ulong n) |
| 882 | { |
| 883 | p = realloc(p, n); |
| 884 | if(!p) |
| 885 | error("out of memory"); |
| 886 | return p; |
| 887 | } |
| 888 | |
| 889 | char * |
| 890 | estrdup(char *q) |
| 891 | { |
| 892 | char *p; |
| 893 | int n; |
| 894 | |
| 895 | n = strlen(q)+1; |
| 896 | p = malloc(n); |
| 897 | if(!p) |
| 898 | error("out of memory"); |
| 899 | memmove(p, q, n); |
| 900 | return p; |
| 901 | } |
| 902 | |
| 903 | void |
| 904 | usage(void) |
| 905 | { |
| 906 | fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0); |
| 907 | exits("usage"); |
| 908 | } |
| 909 | |