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