1 /* Copyright ©2006 Kris Maglione <fbsdaemon at gmail dot com>
2  * See LICENSE file for license details.
3  */
4 #include "dat.h"
5 #include <assert.h>
6 #include <ctype.h>
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include "fns.h"
13 
14 
15 /* Datatypes: */
16 typedef struct Dirtab Dirtab;
17 struct Dirtab {
18 	char		*name;
19 	uchar	qtype;
20 	uint	type;
21 	uint	perm;
22 };
23 
24 typedef struct FidLink FidLink;
25 struct FidLink {
26 	FidLink *next;
27 	Fid *fid;
28 };
29 
30 typedef struct FileId FileId;
31 struct FileId {
32 	FileId		*next;
33 	union {
34 		void	*ref;
35 		char	*buf;
36 		Bar	*bar;
37 		Bar	**bar_p;
38 		View	*view;
39 		Client	*client;
40 		Ruleset	*rule;
41 		CTuple	*col;
42 	} p;
43 	uint	id;
44 	uint	index;
45 	Dirtab		tab;
46 	ushort	nref;
47 	uchar	volatil;
48 };
49 
50 /* Constants */
51 enum {	/* Dirs */
52 	FsRoot, FsDClient, FsDClients, FsDBars,
53 	FsDTag, FsDTags,
54 	/* Files */
55 	FsFBar, FsFCctl, FsFColRules, FsFClabel,
56 	FsFCtags, FsFEvent, FsFKeys, FsFRctl,
57 	FsFTagRules, FsFTctl, FsFTindex,
58 	FsFprops
59 };
60 
61 /* Error messages */
62 static char
63 	Enoperm[] = "permission denied",
64 	Enofile[] = "file not found",
65 	Ebadvalue[] = "bad value",
66 	Einterrupted[] = "interrupted";
67 
68 /* Macros */
69 #define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF))
70 
71 /* Global Vars */
72 /***************/
73 FileId *free_fileid;
74 /* Pending, outgoing reads on /event */
75 Ixp9Req *peventread, *oeventread;
76 /* Fids for /event with pending reads */
77 FidLink *peventfid;
78 
79 Ixp9Srv p9srv = {
80 	.open=	fs_open,
81 	.walk=	fs_walk,
82 	.read=	fs_read,
83 	.stat=	fs_stat,
84 	.write=	fs_write,
85 	.clunk=	fs_clunk,
86 	.flush=	fs_flush,
87 	.attach=fs_attach,
88 	.create=fs_create,
89 	.remove=fs_remove,
90 	.freefid=fs_freefid
91 };
92 
93 /* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled
94  * in by lookup_file
95  */
96 static Dirtab
97 dirtab_root[]=	 {{".",		QTDIR,		FsRoot,		0500|DMDIR },
98 		  {"rbar",	QTDIR,		FsDBars,	0700|DMDIR },
99 		  {"lbar",	QTDIR,		FsDBars,	0700|DMDIR },
100 		  {"client",	QTDIR,		FsDClients,	0500|DMDIR },
101 		  {"tag",	QTDIR,		FsDTags,	0500|DMDIR },
102 		  {"ctl",	QTAPPEND,	FsFRctl,	0600|DMAPPEND },
103 		  {"colrules",	QTFILE,		FsFColRules,	0600 },
104 		  {"event",	QTFILE,		FsFEvent,	0600 },
105 		  {"keys",	QTFILE,		FsFKeys,	0600 },
106 		  {"tagrules",	QTFILE,		FsFTagRules,	0600 },
107 		  {nil}},
108 dirtab_clients[]={{".",		QTDIR,		FsDClients,	0500|DMDIR },
109 		  {"",		QTDIR,		FsDClient,	0500|DMDIR },
110 		  {nil}},
111 dirtab_client[]= {{".",		QTDIR,		FsDClient,	0500|DMDIR },
112 		  {"ctl",	QTAPPEND,	FsFCctl,	0600|DMAPPEND },
113 		  {"label",	QTFILE,	FsFClabel,	0600 },
114 		  {"tags",	QTFILE,		FsFCtags,	0600 },
115 		  {"props",	QTFILE,		FsFprops,	0400 },
116 		  {nil}},
117 dirtab_bars[]=	 {{".",		QTDIR,		FsDBars,	0700|DMDIR },
118 		  {"",		QTFILE,		FsFBar,		0600 },
119 		  {nil}},
120 dirtab_tags[]=	 {{".",		QTDIR,		FsDTags,	0500|DMDIR },
121 		  {"",		QTDIR,		FsDTag,		0500|DMDIR },
122 		  {nil}},
123 dirtab_tag[]=	 {{".",		QTDIR,		FsDTag,		0500|DMDIR },
124 		  {"ctl",	QTAPPEND,	FsFTctl,	0600|DMAPPEND },
125 		  {"index",	QTFILE,		FsFTindex,	0400 },
126 		  {nil}};
127 static Dirtab *dirtab[] = {
128 	[FsRoot] = dirtab_root,
129 	[FsDBars] = dirtab_bars,
130 	[FsDClients] = dirtab_clients,
131 	[FsDClient] = dirtab_client,
132 	[FsDTags] = dirtab_tags,
133 	[FsDTag] = dirtab_tag,
134 };
135 
136 /* Utility Functions */
137 static FileId *
get_file(void)138 get_file(void) {
139 	FileId *temp;
140 	if(!free_fileid) {
141 		uint i = 15;
142 		temp = emallocz(sizeof(FileId) * i);
143 		for(; i; i--) {
144 			temp->next = free_fileid;
145 			free_fileid = temp++;
146 		}
147 	}
148 	temp = free_fileid;
149 	free_fileid = temp->next;
150 	temp->volatil = 0;
151 	temp->nref = 1;
152 	temp->next = nil;
153 	return temp;
154 }
155 
156 static void
free_file(FileId * f)157 free_file(FileId *f) {
158 	if(--f->nref)
159 		return;
160 	free(f->tab.name);
161 	f->next = free_fileid;
162 	free_fileid = f;
163 }
164 
165 /* Increase the reference counts of the FileId list */
166 static void
clone_files(FileId * f)167 clone_files(FileId *f) {
168 	for(; f; f=f->next)
169 		assert(f->nref++);
170 }
171 
172 /* This should be moved to libixp */
173 static void
write_buf(Ixp9Req * r,char * buf,uint len)174 write_buf(Ixp9Req *r, char *buf, uint len) {
175 	if(r->ifcall.offset >= len)
176 		return;
177 
178 	len -= r->ifcall.offset;
179 	if(len > r->ifcall.count)
180 		len = r->ifcall.count;
181 	r->ofcall.data = emalloc(len);
182 	memcpy(r->ofcall.data, buf + r->ifcall.offset, len);
183 	r->ofcall.count = len;
184 }
185 
186 /* This should be moved to libixp */
187 static void
write_to_buf(Ixp9Req * r,void * buf,uint * len,uint max)188 write_to_buf(Ixp9Req *r, void *buf, uint *len, uint max) {
189 	uint offset, count;
190 
191 	offset = (r->fid->omode&OAPPEND) ? *len : r->ifcall.offset;
192 	if(offset > *len || r->ifcall.count == 0) {
193 		r->ofcall.count = 0;
194 		return;
195 	}
196 
197 	count = r->ifcall.count;
198 	if(max && (count > max - offset))
199 		count = max - offset;
200 
201 	*len = offset + count;
202 
203 	if(max == 0) {
204 		*(void **)buf = erealloc(*(void **)buf, *len + 1);
205 		buf = *(void **)buf;
206 	}
207 
208 	memcpy((uchar*)buf + offset, r->ifcall.data, count);
209 	r->ofcall.count = count;
210 	((char *)buf)[offset+count] = '\0';
211 }
212 
213 /* This should be moved to libixp */
214 static void
data_to_cstring(Ixp9Req * r)215 data_to_cstring(Ixp9Req *r) {
216 	char *p;
217 	uint i;
218 
219 	i = r->ifcall.count;
220 	p = r->ifcall.data;
221 	if(p[i - 1] == '\n')
222 		i--;
223 
224 	r->ifcall.data = toutf8n(p, i);
225 	assert(r->ifcall.data);
226 	free(p);
227 }
228 
229 typedef char* (*MsgFunc)(void*, IxpMsg*);
230 
231 static char *
message(Ixp9Req * r,MsgFunc fn)232 message(Ixp9Req *r, MsgFunc fn) {
233 	char *err, *s, *p, c;
234 	FileId *f;
235 	IxpMsg m;
236 
237 	f = r->fid->aux;
238 
239 	data_to_cstring(r);
240 	s = r->ifcall.data;
241 
242 	err = nil;
243 	c = *s;
244 	while(c != '\0') {
245 		while(*s == '\n')
246 			s++;
247 		p = s;
248 		while(*p != '\0' && *p != '\n')
249 			p++;
250 		c = *p;
251 		*p = '\0';
252 
253 		m = ixp_message((uchar*)s, p-s, 0);
254 		s = fn(f->p.ref, &m);
255 		if(s)
256 			err = s;
257 		s = p + 1;
258 	}
259 	return err;
260 }
261 
262 static void
respond_event(Ixp9Req * r)263 respond_event(Ixp9Req *r) {
264 	FileId *f = r->fid->aux;
265 	if(f->p.buf) {
266 		r->ofcall.data = (void *)f->p.buf;
267 		r->ofcall.count = strlen(f->p.buf);
268 		respond(r, nil);
269 		f->p.buf = nil;
270 	}else{
271 		r->aux = peventread;
272 		peventread = r;
273 	}
274 }
275 
276 void
write_event(char * format,...)277 write_event(char *format, ...) {
278 	uint len, slen;
279 	va_list ap;
280 	FidLink *f;
281 	FileId *fi;
282 	Ixp9Req *req;
283 
284 	va_start(ap, format);
285 	vsnprint(buffer, sizeof(buffer), format, ap);
286 	va_end(ap);
287 
288 	len = strlen(buffer);
289 	if(len == 0)
290 		return;
291 
292 	for(f=peventfid; f; f=f->next) {
293 		fi = f->fid->aux;
294 
295 		slen = 0;
296 		if(fi->p.buf)
297 			slen = strlen(fi->p.buf);
298 		fi->p.buf = erealloc(fi->p.buf, slen + len + 1);
299 		fi->p.buf[slen] = '\0';
300 		strcat(fi->p.buf, buffer);
301 	}
302 	oeventread = peventread;
303 	peventread = nil;
304 	while((req = oeventread)) {
305 		oeventread = oeventread->aux;
306 		respond_event(req);
307 	}
308 }
309 
310 static void
dostat(Stat * s,uint len,FileId * f)311 dostat(Stat *s, uint len, FileId *f) {
312 	s->type = 0;
313 	s->dev = 0;
314 	s->qid.path = QID(f->tab.type, f->id);
315 	s->qid.version = 0;
316 	s->qid.type = f->tab.qtype;
317 	s->mode = f->tab.perm;
318 	s->atime = time(nil);
319 	s->mtime = time(nil);
320 	s->length = len;
321 	s->name = f->tab.name;
322 	s->uid = user;
323 	s->gid = user;
324 	s->muid = user;
325 }
326 
327 /* All lookups and directory organization should be performed through
328  * lookup_file, mostly through the dirtabs[] tree. */
329 static FileId *
lookup_file(FileId * parent,char * name)330 lookup_file(FileId *parent, char *name)
331 {
332 	FileId *ret, *file, **last;
333 	Dirtab *dir;
334 	Client *c;
335 	View *v;
336 	Bar *b;
337 	uint id;
338 
339 	if(!(parent->tab.perm & DMDIR))
340 		return nil;
341 	dir = dirtab[parent->tab.type];
342 	last = &ret;
343 	ret = nil;
344 	for(; dir->name; dir++) {
345 		/* Dynamic dirs */
346 		if(!*dir->name) { /* strlen(dir->name) == 0 */
347 			switch(parent->tab.type) {
348 			case FsDClients:
349 				if(!name || !strcmp(name, "sel")) {
350 					if((c = selclient())) {
351 						file = get_file();
352 						*last = file;
353 						last = &file->next;
354 						file->volatil = True;
355 						file->p.client = c;
356 						file->id = c->w.w;
357 						file->index = c->w.w;
358 						file->tab = *dir;
359 						file->tab.name = estrdup("sel");
360 					}if(name) goto LastItem;
361 				}
362 				SET(id);
363 				if(name) {
364 					id = (uint)strtol(name, &name, 16);
365 					if(*name) goto NextItem;
366 				}
367 				for(c=client; c; c=c->next) {
368 					if(!name || c->w.w == id) {
369 						file = get_file();
370 						*last = file;
371 						last = &file->next;
372 						file->volatil = True;
373 						file->p.client = c;
374 						file->id = c->w.w;
375 						file->index = c->w.w;
376 						file->tab = *dir;
377 						file->tab.name = smprint("%C", c);
378 						assert(file->tab.name);
379 						if(name) goto LastItem;
380 					}
381 				}
382 				break;
383 			case FsDTags:
384 				if(!name || !strcmp(name, "sel")) {
385 					if(screen->sel) {
386 						file = get_file();
387 						*last = file;
388 						last = &file->next;
389 						file->volatil = True;
390 						file->p.view = screen->sel;
391 						file->id = screen->sel->id;
392 						file->tab = *dir;
393 						file->tab.name = estrdup("sel");
394 					}if(name) goto LastItem;
395 				}
396 				for(v=view; v; v=v->next) {
397 					if(!name || !strcmp(name, v->name)) {
398 						file = get_file();
399 						*last = file;
400 						last = &file->next;
401 						file->volatil = True;
402 						file->p.view = v;
403 						file->id = v->id;
404 						file->tab = *dir;
405 						file->tab.name = estrdup(v->name);
406 						if(name) goto LastItem;
407 					}
408 				}
409 				break;
410 			case FsDBars:
411 				for(b=*parent->p.bar_p; b; b=b->next) {
412 					if(!name || !strcmp(name, b->name)) {
413 						file = get_file();
414 						*last = file;
415 						last = &file->next;
416 						file->volatil = True;
417 						file->p.bar = b;
418 						file->id = b->id;
419 						file->tab = *dir;
420 						file->tab.name = estrdup(b->name);
421 						if(name) goto LastItem;
422 					}
423 				}
424 				break;
425 			}
426 		}else /* Static dirs */
427 		if(!name || !strcmp(name, dir->name)) {
428 			file = get_file();
429 			*last = file;
430 			last = &file->next;
431 			file->id = 0;
432 			file->p.ref = parent->p.ref;
433 			file->index = parent->index;
434 			file->tab = *dir;
435 			file->tab.name = estrdup(file->tab.name);
436 			/* Special considerations: */
437 			switch(file->tab.type) {
438 			case FsDBars:
439 				if(!strcmp(file->tab.name, "lbar"))
440 					file->p.bar_p = &screen[0].bar[BarLeft];
441 				else
442 					file->p.bar_p = &screen[0].bar[BarRight];
443 				break;
444 			case FsFColRules:
445 				file->p.rule = &def.colrules;
446 				break;
447 			case FsFTagRules:
448 				file->p.rule = &def.tagrules;
449 				break;
450 			}
451 			if(name) goto LastItem;
452 		}
453 	NextItem:
454 		continue;
455 	}
456 LastItem:
457 	*last = nil;
458 	return ret;
459 }
460 
461 static Bool
verify_file(FileId * f)462 verify_file(FileId *f) {
463 	FileId *nf;
464 	int ret;
465 
466 	if(!f->next)
467 		return True;
468 
469 	ret = False;
470 	if(verify_file(f->next)) {
471 		nf = lookup_file(f->next, f->tab.name);
472 		if(nf) {
473 			if(!nf->volatil || nf->p.ref == f->p.ref)
474 				ret = True;
475 			free_file(nf);
476 		}
477 	}
478 	return ret;
479 }
480 
481 /* Service Functions */
482 void
fs_attach(Ixp9Req * r)483 fs_attach(Ixp9Req *r) {
484 	FileId *f = get_file();
485 	f->tab = dirtab[FsRoot][0];
486 	f->tab.name = estrdup("/");
487 	f->p.ref = nil;
488 	r->fid->aux = f;
489 	r->fid->qid.type = f->tab.qtype;
490 	r->fid->qid.path = QID(f->tab.type, 0);
491 	r->ofcall.qid = r->fid->qid;
492 	respond(r, nil);
493 }
494 
495 void
fs_walk(Ixp9Req * r)496 fs_walk(Ixp9Req *r) {
497 	FileId *f, *nf;
498 	int i;
499 
500 	f = r->fid->aux;
501 	clone_files(f);
502 	for(i=0; i < r->ifcall.nwname; i++) {
503 		if(!strcmp(r->ifcall.wname[i], "..")) {
504 			if(f->next) {
505 				nf=f;
506 				f=f->next;
507 				free_file(nf);
508 			}
509 		}else{
510 			nf = lookup_file(f, r->ifcall.wname[i]);
511 			if(!nf)
512 				break;
513 			assert(!nf->next);
514 			if(strcmp(r->ifcall.wname[i], ".")) {
515 				nf->next = f;
516 				f = nf;
517 			}
518 		}
519 		r->ofcall.wqid[i].type = f->tab.qtype;
520 		r->ofcall.wqid[i].path = QID(f->tab.type, f->id);
521 	}
522 	/* There should be a way to do this on freefid() */
523 	if(i < r->ifcall.nwname) {
524 		while((nf = f)) {
525 			f=f->next;
526 			free_file(nf);
527 		}
528 		respond(r, Enofile);
529 		return;
530 	}
531 	/* Remove refs for r->fid if no new fid */
532 	if(r->ifcall.fid == r->ifcall.newfid) {
533 		nf = r->fid->aux;
534 		r->fid->aux = f;
535 		while((f = nf)) {
536 			nf = nf->next;
537 			free_file(f);
538 		}
539 	}else
540 		r->newfid->aux = f;
541 	r->ofcall.nwqid = i;
542 	respond(r, nil);
543 }
544 
545 static uint
fs_size(FileId * f)546 fs_size(FileId *f) {
547 	switch(f->tab.type) {
548 	default:
549 		return 0;
550 	case FsFColRules:
551 	case FsFTagRules:
552 		return f->p.rule->size;
553 	case FsFKeys:
554 		return def.keyssz;
555 	case FsFCtags:
556 		return strlen(f->p.client->tags);
557 	case FsFClabel:
558 		return strlen(f->p.client->name);
559 	case FsFprops:
560 		return strlen(f->p.client->props);
561 	}
562 }
563 
564 void
fs_stat(Ixp9Req * r)565 fs_stat(Ixp9Req *r) {
566 	IxpMsg m;
567 	Stat s;
568 	int size;
569 	uchar *buf;
570 	FileId *f;
571 
572 	f = r->fid->aux;
573 
574 	if(!verify_file(f)) {
575 		respond(r, Enofile);
576 		return;
577 	}
578 
579 	dostat(&s, fs_size(f), f);
580 	r->ofcall.nstat = size = ixp_sizeof_stat(&s);
581 	buf = emallocz(size);
582 
583 	m = ixp_message(buf, size, MsgPack);
584 	ixp_pstat(&m, &s);
585 
586 	r->ofcall.stat = m.data;
587 	respond(r, nil);
588 }
589 
590 void
fs_read(Ixp9Req * r)591 fs_read(Ixp9Req *r) {
592 	char *buf;
593 	FileId *f, *tf;
594 	int n, offset;
595 	int size;
596 
597 	f = r->fid->aux;
598 
599 	if(!verify_file(f)) {
600 		respond(r, Enofile);
601 		return;
602 	}
603 
604 	if(f->tab.perm & DMDIR && f->tab.perm & 0400) {
605 		Stat s;
606 		IxpMsg m;
607 
608 		offset = 0;
609 		size = r->ifcall.count;
610 		buf = emallocz(size);
611 		m = ixp_message((uchar*)buf, size, MsgPack);
612 
613 		tf = f = lookup_file(f, nil);
614 		/* Note: f->tab.name == "." so we skip it */
615 		for(f=f->next; f; f=f->next) {
616 			dostat(&s, fs_size(f), f);
617 			n = ixp_sizeof_stat(&s);
618 			if(offset >= r->ifcall.offset) {
619 				if(size < n)
620 					break;
621 				ixp_pstat(&m, &s);
622 				size -= n;
623 			}
624 			offset += n;
625 		}
626 		while((f = tf)) {
627 			tf=tf->next;
628 			free_file(f);
629 		}
630 		r->ofcall.count = r->ifcall.count - size;
631 		r->ofcall.data = (char*)m.data;
632 		respond(r, nil);
633 		return;
634 	}
635 	else{
636 		switch(f->tab.type) {
637 		case FsFprops:
638 			write_buf(r, f->p.client->props, strlen(f->p.client->props));
639 			respond(r, nil);
640 			return;
641 		case FsFColRules:
642 		case FsFTagRules:
643 			write_buf(r, f->p.rule->string, f->p.rule->size);
644 			respond(r, nil);
645 			return;
646 		case FsFKeys:
647 			write_buf(r, def.keys, def.keyssz);
648 			respond(r, nil);
649 			return;
650 		case FsFCtags:
651 			write_buf(r, f->p.client->tags, strlen(f->p.client->tags));
652 			respond(r, nil);
653 			return;
654 		case FsFClabel:
655 			write_buf(r, f->p.client->name, strlen(f->p.client->name));
656 			respond(r, nil);
657 			return;
658 		case FsFBar:
659 			write_buf(r, f->p.bar->buf, strlen(f->p.bar->buf));
660 			respond(r, nil);
661 			return;
662 		case FsFRctl:
663 			buf = read_root_ctl();
664 			write_buf(r, buf, strlen(buf));
665 			respond(r, nil);
666 			return;
667 		case FsFCctl:
668 			if(r->ifcall.offset) {
669 				respond(r, nil);
670 				return;
671 			}
672 			r->ofcall.data = smprint("%C", f->p.client);
673 			r->ofcall.count = strlen(r->ofcall.data); /* will die if nil */
674 			respond(r, nil);
675 			return;
676 		case FsFTindex:
677 			buf = (char*)view_index(f->p.view);
678 			n = strlen(buf);
679 			write_buf(r, buf, n);
680 			respond(r, nil);
681 			return;
682 		case FsFTctl:
683 			buf = (char*)view_ctl(f->p.view);
684 			n = strlen(buf);
685 			write_buf(r, buf, n);
686 			respond(r, nil);
687 			return;
688 		case FsFEvent:
689 			respond_event(r);
690 			return;
691 		}
692 	}
693 	/*
694 	 * This is an assert because this should this should not be called if
695 	 * the file is not open for reading.
696 	 */
697 	assert(!"Read called on an unreadable file");
698 }
699 
700 void
fs_write(Ixp9Req * r)701 fs_write(Ixp9Req *r) {
702 	FileId *f;
703 	char *errstr;
704 	uint i;
705 
706 	if(r->ifcall.count == 0) {
707 		respond(r, nil);
708 		return;
709 	}
710 	f = r->fid->aux;
711 
712 	if(!verify_file(f)) {
713 		respond(r, Enofile);
714 		return;
715 	}
716 
717 	switch(f->tab.type) {
718 	case FsFColRules:
719 	case FsFTagRules:
720 		write_to_buf(r, &f->p.rule->string, &f->p.rule->size, 0);
721 		respond(r, nil);
722 		return;
723 	case FsFKeys:
724 		write_to_buf(r, &def.keys, &def.keyssz, 0);
725 		respond(r, nil);
726 		return;
727 	case FsFClabel:
728 		data_to_cstring(r);
729 		utfecpy(f->p.client->name, f->p.client->name+sizeof(client->name), r->ifcall.data);
730 		draw_frame(f->p.client->sel);
731 		update_class(f->p.client);
732 		r->ofcall.count = r->ifcall.count;
733 		respond(r, nil);
734 		return;
735 	case FsFCtags:
736 		data_to_cstring(r);
737 		apply_tags(f->p.client, r->ifcall.data);
738 		r->ofcall.count = r->ifcall.count;
739 		respond(r, nil);
740 		return;
741 	case FsFBar:
742 		i = strlen(f->p.bar->buf);
743 		write_to_buf(r, f->p.bar->buf, &i, 279);
744 		r->ofcall.count = i - r->ifcall.offset;
745 		respond(r, nil);
746 		return;
747 	case FsFCctl:
748 		errstr = message(r, (MsgFunc)message_client);
749 		r->ofcall.count = r->ifcall.count;
750 		respond(r, errstr);
751 		return;
752 	case FsFTctl:
753 		errstr = message(r, (MsgFunc)message_view);
754 		r->ofcall.count = r->ifcall.count;
755 		respond(r, errstr);
756 		return;
757 	case FsFRctl:
758 		errstr = message(r, (MsgFunc)message_root);
759 		r->ofcall.count = r->ifcall.count;
760 		respond(r, errstr);
761 		return;
762 	case FsFEvent:
763 		if(r->ifcall.data[r->ifcall.count-1] == '\n')
764 			write_event("%.*s", r->ifcall.count, r->ifcall.data);
765 		else
766 			write_event("%.*s\n", r->ifcall.count, r->ifcall.data);
767 		r->ofcall.count = r->ifcall.count;
768 		respond(r, nil);
769 		return;
770 	}
771 	/*
772 	 * This is an assert because this function should not be called if
773 	 * the file is not open for writing.
774 	 */
775 	assert(!"Write called on an unwritable file");
776 }
777 
778 void
fs_open(Ixp9Req * r)779 fs_open(Ixp9Req *r) {
780 	FidLink *fl;
781 	FileId *f = r->fid->aux;
782 
783 	if(!verify_file(f)) {
784 		respond(r, Enofile);
785 		return;
786 	}
787 
788 	switch(f->tab.type) {
789 	case FsFEvent:
790 		fl = emallocz(sizeof(FidLink));
791 		fl->fid = r->fid;
792 		fl->next = peventfid;
793 		peventfid = fl;
794 		break;
795 	}
796 	if((r->ifcall.mode&3) == OEXEC) {
797 		respond(r, Enoperm);
798 		return;
799 	}
800 	if((r->ifcall.mode&3) != OREAD && !(f->tab.perm & 0200)) {
801 		respond(r, Enoperm);
802 		return;
803 	}
804 	if((r->ifcall.mode&3) != OWRITE && !(f->tab.perm & 0400)) {
805 		respond(r, Enoperm);
806 		return;
807 	}
808 	if((r->ifcall.mode&~(3|OAPPEND|OTRUNC))) {
809 		respond(r, Enoperm);
810 		return;
811 	}
812 	respond(r, nil);
813 }
814 
815 void
fs_create(Ixp9Req * r)816 fs_create(Ixp9Req *r) {
817 	FileId *f = r->fid->aux;
818 
819 	switch(f->tab.type) {
820 	default:
821 		respond(r, Enoperm);
822 		return;
823 	case FsDBars:
824 		if(!strlen(r->ifcall.name)) {
825 			respond(r, Ebadvalue);
826 			return;
827 		}
828 		create_bar(f->p.bar_p, r->ifcall.name);
829 		f = lookup_file(f, r->ifcall.name);
830 		if(!f) {
831 			respond(r, Enofile);
832 			return;
833 		}
834 		r->ofcall.qid.type = f->tab.qtype;
835 		r->ofcall.qid.path = QID(f->tab.type, f->id);
836 		f->next = r->fid->aux;
837 		r->fid->aux = f;
838 		respond(r, nil);
839 		break;
840 	}
841 }
842 
843 void
fs_remove(Ixp9Req * r)844 fs_remove(Ixp9Req *r) {
845 	FileId *f = r->fid->aux;
846 
847 	if(!verify_file(f)) {
848 		respond(r, Enofile);
849 		return;
850 	}
851 
852 
853 	switch(f->tab.type) {
854 	default:
855 		respond(r, Enoperm);
856 		return;
857 	case FsFBar:
858 		destroy_bar(f->next->p.bar_p, f->p.bar);
859 		draw_bar(screen);
860 		respond(r, nil);
861 		break;
862 	}
863 }
864 
865 void
fs_clunk(Ixp9Req * r)866 fs_clunk(Ixp9Req *r) {
867 	FidLink **fl, *ft;
868 	FileId *f;
869 	char *p, *q;
870 	Client *c;
871 	IxpMsg m;
872 
873 	f = r->fid->aux;
874 	if(!verify_file(f)) {
875 		respond(r, nil);
876 		return;
877 	}
878 
879 	switch(f->tab.type) {
880 	case FsFColRules:
881 		update_rules(&f->p.rule->rule, f->p.rule->string);
882 		break;
883 	case FsFTagRules:
884 		update_rules(&f->p.rule->rule, f->p.rule->string);
885 		for(c=client; c; c=c->next)
886 			apply_rules(c);
887 		update_views();
888 		break;
889 	case FsFKeys:
890 		update_keys();
891 		break;
892 	case FsFBar:
893 		p = toutf8(f->p.bar->buf);
894 
895 		m = ixp_message((uchar*)p, strlen(p), 0);
896 		parse_colors(&m, &f->p.bar->col);
897 
898 		q = (char*)m.end-1;
899 		while(q >= (char*)m.pos && *q == '\n')
900 			*q-- = '\0';
901 
902 		q = f->p.bar->text;
903 		utflcpy(q, (char*)m.pos, sizeof(((Bar*)0)->text));
904 
905 		free(p);
906 
907 		draw_bar(screen);
908 		break;
909 	case FsFEvent:
910 		for(fl=&peventfid; *fl; fl=&(*fl)->next)
911 			if((*fl)->fid == r->fid) {
912 				ft = *fl;
913 				*fl = (*fl)->next;
914 				f = ft->fid->aux;
915 				free(f->p.buf);
916 				free(ft);
917 				break;
918 			}
919 		break;
920 	}
921 	respond(r, nil);
922 }
923 
924 void
fs_flush(Ixp9Req * r)925 fs_flush(Ixp9Req *r) {
926 	Ixp9Req **i, **j;
927 
928 	for(i=&peventread; i != &oeventread; i=&oeventread)
929 		for(j=i; *j; j=(Ixp9Req**)&(*j)->aux)
930 			if(*j == r->oldreq) {
931 				*j = (*j)->aux;
932 				respond(r->oldreq, Einterrupted);
933 				goto done;
934 			}
935 done:
936 	respond(r, nil);
937 }
938 
939 void
fs_freefid(Fid * f)940 fs_freefid(Fid *f) {
941 	FileId *id, *tid;
942 
943 	tid = f->aux;
944 	while((id = tid)) {
945 		tid = id->next;
946 		free_file(id);
947 	}
948 }
949