domfs

Document Object Model as a filesystem for plan9 os
git clone git://nsmpr.xyz/domfs.git
Log | Files | Refs | README

domfs.c (12732B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <fcall.h>
      4 #include <thread.h>
      5 #include <9p.h>
      6 
      7 typedef struct Finf Finf; /* file info */
      8 typedef struct DTree DTree;
      9 typedef struct Node Node;
     10 typedef struct Fusr Fusr;
     11 
     12 enum { /* file types */
     13 	ROOT,
     14 	TREE,
     15 	NODE,
     16 	NCTL,
     17 	USER,
     18 	NNEW,
     19 	TNEW,
     20 
     21 	REMV = -1,
     22 };
     23 
     24 struct Finf {
     25 	uvlong id;
     26 	Qid qid;
     27 	int type;
     28 	void *aux;
     29 };
     30 
     31 struct DTree {
     32 	uvlong id;
     33 	Node **nodes;
     34 	uvlong nidcount;
     35 	Finf *finf;
     36 	Finf *fnew;
     37 };
     38 
     39 struct Node {
     40 	uvlong id;
     41 	DTree *tree;
     42 	Node *parent;
     43 	Node **children;
     44 	Fusr **files;
     45 	Finf *finf;
     46 	Finf *fctl;
     47 };
     48 
     49 struct Fusr {
     50 	Node *node;
     51 	char *name;
     52 	char *data;
     53 	uvlong nsize;
     54 	Finf *finf;
     55 };
     56 
     57 Finf **files, *rootf, *fnew;
     58 
     59 DTree **trees;
     60 static uvlong tcount;
     61 
     62 long
     63 stacksize(void **v)
     64 {
     65 	long n;
     66 	if (v == nil) return 0;
     67 	for (n = 0; *v != nil; v++) n++;
     68 	return n;
     69 }
     70 
     71 void
     72 stackpush(void ***v, void *new)
     73 {
     74 	long n;
     75 	n = stacksize(*v);
     76 	*v = realloc(*v, (n + 2) * sizeof(void*));
     77 	(*v)[n] = new;
     78 	(*v)[n+1] = nil;
     79 }
     80 
     81 void
     82 stackremv(void ***v, void *w)
     83 {
     84 	long i, n;
     85 	int remv;
     86 	remv = 0;
     87 	n = stacksize(*v);
     88 	for (i = 0; i < n; i++) {
     89 		if ((*v)[i] == w) remv = 1;
     90 		if (remv != 0) (*v)[i] = (*v)[i+1];
     91 	}
     92 	if (remv != 0) *v = realloc(*v, (n) * sizeof(void*));
     93 }
     94 
     95 Finf*
     96 newfinf(int type, void *aux)
     97 {
     98 	static uvlong id = 0;
     99 	Finf *new;
    100 	new = mallocz(sizeof(Finf), 1);
    101 	switch (type) {
    102 	case ROOT:
    103 	case TREE:
    104 	case NODE:
    105 			new->qid = (Qid){id, 0, QTDIR};
    106 			break;
    107 	case NNEW:
    108 	case TNEW:
    109 	case NCTL:
    110 	case USER:
    111 	case REMV:
    112 			new->qid = (Qid){id, 0, QTFILE};
    113 			break;
    114 	default:
    115 			sysfatal("newfinf: unknown file type %d", type);
    116 	}
    117 	new->id = id++;
    118 	new->type = type;
    119 	new->aux = aux;
    120 	return new;
    121 }
    122 
    123 DTree*
    124 newtree(void)
    125 {
    126 	static uvlong id;
    127 	DTree *new;
    128 	new = mallocz(sizeof(DTree), 1);
    129 	new->id = ++id;
    130 	new->finf = newfinf(TREE, new);
    131 	stackpush(&files, new->finf);
    132 
    133 	new->fnew = newfinf(NNEW, new);
    134 	stackpush(&files, new->fnew);
    135 
    136 	return new;
    137 }
    138 
    139 DTree*
    140 gettree(DTree **trees, uvlong id)
    141 {
    142 	DTree **tp;
    143 	if (trees == nil) return nil;
    144 	for (tp = trees; *tp != nil; tp++)
    145 		if ((*tp)->id == id) return *tp;
    146 	return nil;
    147 }
    148 
    149 Node*
    150 newnode(DTree *T)
    151 {
    152 	Node *new;
    153 	new = mallocz(sizeof(Node), 1);
    154 	new->id = ++T->nidcount;
    155 	new->finf = newfinf(NODE, new);
    156 	new->tree = T;
    157 	stackpush(&files, new->finf);
    158 	new->fctl = newfinf(NCTL, new);
    159 	stackpush(&files, new->fctl);
    160 	return new;
    161 }
    162 
    163 Node*
    164 getnode(DTree *T, uvlong n)
    165 {
    166 	Node **np;
    167 	if (T->nodes == nil) return nil;
    168 	for (np = T->nodes; *np != nil; np++)
    169 		if ((*np)->id == n) return *np;
    170 	return nil;
    171 }
    172 
    173 Fusr*
    174 newfile(char *name)
    175 {
    176 	Fusr *new;
    177 	new = mallocz(sizeof(Fusr), 1);
    178 	new->name = strdup(name);
    179 	new->finf = newfinf(USER, new);
    180 	stackpush(&files, new->finf);
    181 	return new;
    182 }
    183 
    184 Fusr*
    185 getfile(Node *node, char *name)
    186 {
    187 	Fusr **fp;
    188 	if (node->files == nil) return nil;
    189 	for (fp = node->files; *fp != nil; fp++)
    190 		if (strcmp(name, (*fp)->name) == 0) return *fp;
    191 	return nil;
    192 }
    193 
    194 void
    195 fsattach(Req *r)
    196 {
    197 	r->fid->qid = rootf->qid;
    198 	r->fid->aux = rootf;
    199 	r->ofcall.qid = r->fid->qid;
    200 	respond(r, nil);
    201 }
    202 
    203 int
    204 dirgenroot(int n, Dir *dir, void*)
    205 {
    206 	DTree *t;
    207 	if (n == 0) {
    208 		nulldir(dir);
    209 		dir->qid = fnew->qid;
    210 		dir->mode = 0666;
    211 		dir->atime = time(0);
    212 		dir->mtime = time(0);
    213 		dir->length = 0;
    214 		dir->name = strdup("new");
    215 		dir->uid = strdup("domfs");
    216 		dir->gid = strdup("domfs");
    217 		dir->muid = strdup("");
    218 		return 0;
    219 	}
    220 	n--;
    221 	if (trees == nil) return -1;
    222 	t = trees[n];
    223 	if (t == nil) return -1;
    224 	nulldir(dir);
    225 	dir->qid = t->finf->qid;
    226 	dir->mode = 0777 | DMDIR;
    227 	dir->atime = time(0);
    228 	dir->mtime = time(0);
    229 	dir->length = 0;
    230 	dir->name = smprint("%ulld", t->id);
    231 	dir->uid = strdup("domfs");
    232 	dir->gid = strdup("domfs");
    233 	dir->muid = strdup("");
    234 	return 0;
    235 }
    236 
    237 int
    238 dirgentree(int n, Dir *dir, void *aux)
    239 {
    240 	DTree *tree;
    241 	Node *node;
    242 	tree = aux;
    243 	if (tree == nil) return -1;
    244 	if (n == 0) {
    245 		nulldir(dir);
    246 		dir->qid = tree->fnew->qid;
    247 		dir->mode = 0666;
    248 		dir->atime = time(0);
    249 		dir->mtime = time(0);
    250 		dir->length = 0;
    251 		dir->name = strdup("new");
    252 		dir->uid = strdup("domfs");
    253 		dir->gid = strdup("domfs");
    254 		dir->muid = strdup("");
    255 		return 0;
    256 	}
    257 	n--;
    258 	if (tree->nodes == nil) return -1;
    259 	node = tree->nodes[n];
    260 	if (node == nil) return -1;
    261 	nulldir(dir);
    262 	dir->qid = node->finf->qid;
    263 	dir->mode = 0777 | DMDIR;
    264 	dir->atime = time(0);
    265 	dir->mtime = time(0);
    266 	dir->length = 0;
    267 	dir->name = smprint("%ulld", node->id);
    268 	dir->uid = strdup("domfs");
    269 	dir->gid = strdup("domfs");
    270 	dir->muid = strdup("");
    271 	return 0;
    272 }
    273 
    274 int
    275 dirgennode(int n, Dir *dir, void *aux)
    276 {
    277 	Node *node;
    278 	Fusr *file;
    279 	node = aux;
    280 	if (node == nil) return -1;
    281 
    282 	if (n == 0) {
    283 		nulldir(dir);
    284 		dir->qid = node->fctl->qid;
    285 		dir->mode = 0666;
    286 		dir->atime = time(0);
    287 		dir->mtime = time(0);
    288 		dir->length = 0;
    289 		dir->name = strdup("ctl");
    290 		dir->uid = strdup("domfs");
    291 		dir->gid = strdup("domfs");
    292 		dir->muid = strdup("");
    293 		return 0;
    294 	}
    295 	n--;
    296 
    297 	if (node->files == nil) return -1;
    298 	file = node->files[n];
    299 	if (file == nil) return -1;
    300 	nulldir(dir);
    301 	dir->qid = file->finf->qid;
    302 	dir->mode = 0666;
    303 	dir->atime = time(0);
    304 	dir->mtime = time(0);
    305 	dir->length = file->nsize;
    306 	dir->name = strdup(file->name);
    307 	dir->uid = strdup("domfs");
    308 	dir->gid = strdup("domfs");
    309 	dir->muid = strdup("");
    310 	return 0;
    311 }
    312 
    313 char *
    314 nctl2str(Finf *f)
    315 {
    316 	char *str;
    317 	Node *node, **cp;
    318 	uvlong parid;
    319 	long len, n;
    320 	if (f == nil) sysfatal("nctl2str: f == nil"); //return nil;
    321 	if (f->type != NCTL) sysfatal("nctl2str: f->type != NCTL"); //return nil;
    322 	node = f->aux;
    323 	if (node == nil) sysfatal("nctl2str: node == nil"); //return nil;
    324 	parid = (node->parent != nil) ? node->parent->id : 0;
    325 
    326 	str = smprint("parent %ulld\nchildren\n", parid);
    327 	len = strlen(str);
    328 	
    329 	if (node->children == nil) return str;
    330 	for (cp = node->children; *cp != nil; cp++) {
    331 		char *buf;
    332 		str[len - 1] = ' ';
    333 		buf = smprint("%ulld\n", (*cp)->id);
    334 		n = strlen(buf);
    335 		str = realloc(str, len + n + 1);
    336 		memmove(str + len, buf, n);
    337 		str[len + n] = 0;
    338 		free(buf);
    339 		len += n;
    340 	}
    341 	return str;
    342 }
    343 
    344 char *
    345 nctlparse(Finf *f, char *cmd)
    346 {
    347 	Node *node;
    348 	DTree *tree;
    349 	int argc;
    350 	char **argv;
    351 	if (f->type != NCTL) sysfatal("nctlparse: f->type != NCTL");
    352 	node = f->aux;
    353 	tree = node->tree;
    354 	argv = malloc(sizeof(char*) * 256);
    355 	argc = tokenize(cmd, argv, 256);
    356 	if (argc == 0) return nil;
    357 	if (strcmp(argv[0], "adopt") == 0) {
    358 		char *cpt;
    359 		Node *child;
    360 		uvlong id;
    361 		if (argc == 1) return "not enough args";
    362 		id = strtoull(argv[1], &cpt, 10);
    363 		// TODO: check if parsed correctly
    364 		child = getnode(tree, id);
    365 		if (child == nil) return "no such node";
    366 		if (child == node) return "can't adopt self";
    367 		if (child->parent != nil) stackremv(&child->parent->children, child);
    368 		stackpush(&(node->children), child);
    369 		child->parent = node;
    370 		return nil;
    371 	}
    372 	return "unknown cmd";
    373 }
    374 
    375 void
    376 fsread(Req *r)
    377 {
    378 	Finf *f;
    379 	char *buf;
    380 	Node *node;
    381 	DTree *tree;
    382 	f = r->fid->aux;
    383 	switch(f->type){
    384 	case ROOT:
    385 		dirread9p(r, dirgenroot, nil);
    386 		break;
    387 	case TREE:
    388 		dirread9p(r, dirgentree, f->aux);
    389 		break;
    390 	case NODE:
    391 		dirread9p(r, dirgennode, f->aux);
    392 		break;
    393 	case USER:
    394 		readbuf(r, ((Fusr*)f->aux)->data, ((Fusr*)f->aux)->nsize);
    395 		break;
    396 	case NCTL:
    397 		// TODO: first line - parent, sec line - children
    398 		buf = nctl2str(f);
    399 		if (buf == nil) break;
    400 		readstr(r, buf);
    401 		free(buf);
    402 		break;
    403 	case TNEW:
    404 		if (r->ifcall.offset == 0) {
    405 			tree  = newtree();
    406 			stackpush(&trees, tree);
    407 		} else tree = gettree(trees, stacksize(trees));
    408 		buf = smprint("%ulld\n", tree->id);
    409 		readstr(r, buf);
    410 		free(buf);
    411 		break;
    412 	case NNEW:
    413 		tree = f->aux;
    414 		if (r->ifcall.offset == 0) {
    415 			node  = newnode(tree);
    416 			stackpush(&(tree->nodes), node);
    417 		} else node = getnode(tree, stacksize(tree->nodes));
    418 		buf = smprint("%ulld\n", node->id);
    419 		readstr(r, buf);
    420 		free(buf);
    421 		break;
    422 	case REMV:
    423 		respond(r, "file does not exist");
    424 		return;
    425 	default:
    426 		respond(r, "fsread: unknown file type");
    427 		return;
    428 	}
    429 	respond(r, nil);
    430 }
    431 
    432 void
    433 fswrite(Req *r)
    434 {
    435 	char *buf, *rstr;
    436 	long off, nsize;
    437 	Finf *file;
    438 	Fusr *f;
    439 	file = r->fid->aux;
    440 	switch (file->type) {
    441 	case USER:
    442 		// TODO: finish this section
    443 		f = file->aux;
    444 		off = r->ifcall.offset;
    445 		if (r->d.mode & DMAPPEND) off = f->nsize;
    446 		nsize = off + r->ifcall.count;
    447 		if (nsize > f->nsize) f->nsize = nsize;
    448 		f->data = realloc(f->data, f->nsize);
    449 		memmove(f->data + off, r->ifcall.data, r->ifcall.count);
    450 		r->ofcall.count = r->ifcall.count;
    451 		rstr = nil;
    452 		break;
    453 	case NCTL:
    454 		buf = mallocz(r->ifcall.count + 1, 1);
    455 		memmove(buf, r->ifcall.data, r->ifcall.count);
    456 		rstr = nctlparse(file, buf);
    457 		free(buf);
    458 		r->ofcall.count = r->ifcall.count;
    459 		break;
    460 	default:
    461 		rstr = "permission denied";
    462 	}
    463 	respond(r, rstr);
    464 }
    465 
    466 void
    467 fscreate(Req *r)
    468 {
    469 	Node *node;
    470 	Finf *rf;
    471 	Fusr *f;
    472 	rf = r->fid->aux;
    473 	if (rf->type != NODE) {
    474 		respond(r, "permission denied");
    475 		return;
    476 	}
    477 	node = rf->aux;
    478 	f = newfile(r->ifcall.name);
    479 	f->node = node;
    480 	stackpush(&(node->files), f);
    481 	r->fid->qid = f->finf->qid;
    482 	r->ofcall.qid = r->fid->qid;
    483 	r->fid->aux = f->finf;
    484 	respond(r, nil);
    485 }
    486 
    487 void
    488 fsremove(Req *r)
    489 {
    490 	Finf *f;
    491 	f = r->fid->aux;
    492 	if (f->type != USER) {
    493 		respond(r, "remove prohibited");
    494 		return;
    495 	}
    496 	f->type = REMV;
    497 	
    498 	respond(r, nil);
    499 }
    500 
    501 char*
    502 fsclone(Fid *oldfid, Fid *newfid)
    503 {
    504 	newfid->aux = oldfid->aux;
    505 	return nil;
    506 }
    507 
    508 char*
    509 fswalk1(Fid *fid, char *name, Qid *qid)
    510 {
    511 	void *p;
    512 	char *chp;
    513 	uvlong id;
    514 	Finf *f, *nf;
    515 	f = fid->aux;
    516 	nf = nil;
    517 	switch (f->type){
    518 	case ROOT:
    519 		if (strcmp("new", name) == 0) {
    520 			nf = fnew;
    521 			break;
    522 		}
    523 		id = strtoull(name, &chp, 10);
    524 		// TODO: check if parsed correctly
    525 		p = gettree(trees, id);
    526 		if (p == nil) return "file does not exist";
    527 		nf = ((DTree*)p)->finf;
    528 		break;
    529 	case TREE:
    530 		if (strcmp("..", name) == 0) {
    531 			nf = rootf;
    532 			break;
    533 		}
    534 		if (strcmp("new", name) == 0) {
    535 			nf = ((DTree*)f->aux)->fnew;
    536 			break;
    537 		}
    538 		id = strtoull(name, &chp, 10);
    539 		// TODO: check if parsed correctly
    540 		p = getnode(f->aux, id);
    541 		if (p == nil) return "file does not exist";
    542 		nf = ((Node*)p)->finf;
    543 		break;
    544 	case NODE:
    545 		if (strcmp("..", name) == 0) {
    546 			nf = ((Node*)f->aux)->tree->finf;
    547 			break;
    548 		}
    549 		if (strcmp("ctl", name) == 0) {
    550 			nf = ((Node*)f->aux)->fctl;
    551 			break;
    552 		}
    553 		p = getfile(f->aux, name);
    554 		if (p == nil) return "file does not exist";
    555 		nf = ((Fusr*)p)->finf;
    556 		break;
    557 	case NNEW:
    558 	case TNEW:
    559 	case NCTL:
    560 	case USER:
    561 	case REMV:
    562 		return "walk on file, what?";
    563 		break;
    564 	default:
    565 		sysfatal("fswalk1: unknown file type %d", f->type);
    566 	}
    567 	if (nf == nil) return "fswalk1: nf = nil";
    568 	*qid = nf->qid;
    569 	fid->qid = *qid;
    570 	fid->aux = nf;
    571 
    572 	return nil;
    573 }
    574 
    575 void
    576 fsstat(Req *r)
    577 {
    578 	Finf *f;
    579 	f = r->fid->aux;
    580 	nulldir(&r->d);
    581 	r->d.type = L'M';
    582 	r->d.dev = 1;
    583 	r->d.length = 0;
    584 	r->d.atime = time(0);
    585 	r->d.mtime = time(0);
    586 	r->d.uid = strdup("domfs");
    587 	r->d.gid = strdup("domfs");
    588 	r->d.muid = strdup("");
    589 	r->d.qid = f->qid;
    590 	
    591 	switch (f->type) {
    592 	case ROOT:
    593 		r->d.name = strdup("/");
    594 		r->d.mode = 0777|DMDIR;
    595 		break;
    596 	case TREE:
    597 		r->d.name = smprint("%ulld", ((DTree*)f->aux)->id);
    598 		r->d.mode = 0777|DMDIR;
    599 		break;
    600 	case NODE:
    601 		r->d.name = smprint("%ulld", ((Node*)f->aux)->id);
    602 		r->d.mode = 0777|DMDIR;
    603 		break;
    604 	case USER:
    605 		r->d.name = strdup(((Fusr*)f->aux)->name);
    606 		r->d.mode = 0666;
    607 		break;
    608 	case REMV:
    609 		r->d.name = strdup(((Fusr*)f->aux)->name);
    610 		r->d.mode = -1;
    611 		break;
    612 	case NCTL:
    613 		r->d.name = strdup("ctl");
    614 		r->d.mode = 0666;
    615 		break;
    616 	case TNEW:
    617 		r->d.name = strdup("new");
    618 		r->d.mode = 0666;
    619 		break;
    620 	case NNEW:
    621 		r->d.name = strdup("new");
    622 		r->d.mode = 0666;
    623 		break;
    624 	default:
    625 		sysfatal("fsstat: unknown file type %d", f->type);
    626 	}
    627 	
    628 	respond(r, nil);
    629 }
    630 
    631 void
    632 fsdestroyfid(Fid *fid)
    633 {
    634 	/*
    635 	 * TODO: this whole func is probably not correct;
    636 	 * 9p(2) manpage's description of remove func recommends tracking fids that
    637 	 * access file to be deleted, but I am not sure how to do it properly.
    638 	 */
    639 
    640 	Finf *f;
    641 	Fusr *fu;
    642 	f = fid->aux;
    643 	if(f != nil && f->type == REMV) {
    644 		fu = f->aux;
    645 		free(fu->data);
    646 		free(fu->name);
    647 		fu->nsize = 0;
    648 		stackremv(&(fu->node->files), fu);
    649 		free(fu);
    650 		stackremv(&files, &f);
    651 		free(f);
    652 	}
    653 }
    654 
    655 void
    656 usage(void)
    657 {
    658 	fprint(2, "usage %s [-D][-m /mnt/dom][-s service]\n", argv0);
    659 	exits("usage");
    660 }
    661 
    662 void
    663 main(int argc, char **argv)
    664 {
    665 	char *srv, *mtpt;
    666 	srv = nil;
    667 	mtpt = "/mnt/dom";
    668 
    669 	ARGBEGIN {
    670 	case 'm':
    671 		mtpt = EARGF(usage());
    672 		break;
    673 	case 's':
    674 		srv = EARGF(usage());
    675 		break;
    676 	case 'D':
    677 		chatty9p++;
    678 		break;
    679 	default:
    680 		usage();
    681 	} ARGEND
    682 	
    683 	rootf = newfinf(ROOT, &trees);
    684 	stackpush(&files, rootf);
    685 	fnew = newfinf(TNEW, &rootf);
    686 	stackpush(&files, fnew);
    687 	
    688 	Srv fs = {
    689 		.attach = fsattach,
    690 		.read = fsread,
    691 		.write = fswrite,
    692 		.clone = fsclone,
    693 		.walk1 = fswalk1,
    694 		.stat = fsstat,
    695 		.create = fscreate,
    696 		.remove = fsremove,
    697 		.destroyfid = fsdestroyfid,
    698 	};
    699 
    700 	postmountsrv(&fs, srv, mtpt, MREPL);
    701 }