1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1989-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Eduardo Krell <ekrell@adexus.cl>                   *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 
24 /*
25  * 3d file system initialization
26  */
27 
28 #include "3d.h"
29 
30 static const char id[] =
31 #if defined(__STDC__) || defined(__STDPP__)
32 "\n@(#)$Id: 3d [ "
33 #if DEBUG
34 	"debug "
35 #endif
36 #if FS
37 	"msg "
38 #endif
39 "safe "
40 #if VCS
41 	"vcs "
42 #endif
43 "] (AT&T Research) 2012-06-25 $\0\n"
44 #else
45 "\n@(#)$Id: 3d (AT&T Research) 2011-06-25 $\0\n"
46 #endif
47 ;
48 
49 /*
50  * if _3d_2d!=0 && getenv(_3d_2d)==0 then 2d
51  */
52 
53 char*	_3d_2d = 0;
54 
55 /*
56  * 3d mount get and set access functions
57  */
58 
59 static int
get_fs(Fs_t * gs,register char * buf,const char * op,int flags)60 get_fs(Fs_t* gs, register char* buf, const char* op, int flags)
61 {
62 	register Fs_t*		fs;
63 	register int		n;
64 	register int		sum = 0;
65 	register unsigned long	x;
66 	int			m;
67 #if FS
68 	Mount_t*		mp;
69 #endif
70 
71 	x = op ? getkey(op, op + strlen(op), 0) : 0;
72 	for (fs = state.fs; fs < state.fs + elementsof(state.fs); fs++)
73 		if (!(fs->flags & (FS_ERROR|FS_INIT)) && (fs->flags & (FS_BOUND|FS_OPEN)) && (!x || x == fs->key))
74 		{
75 			if (buf)
76 			{
77 				if (fs->flags & FS_BOUND) n = sfsprintf(buf, 0, "%-*s", fs->servicesize ? fs->servicesize : strlen(fs->service), fs->service);
78 				else n = sfsprintf(buf, 0, "-");
79 				n += sfsprintf(buf + n, 0, " /#%s/%s", gs->special, fs->special);
80 				m = n;
81 #if FS
82 				if (fs->ack)
83 				{
84 					n += sfsprintf(buf + n, 0, "/ack=");
85 					n += msggetmask(buf + n, SHRT_MAX, fs->ack);
86 				}
87 				if (fs->call != ~0)
88 				{
89 					n += sfsprintf(buf + n, 0, "/call=");
90 					n += msggetmask(buf + n, SHRT_MAX, fs->call);
91 				}
92 				if (fs->flags & FS_CLOSE)
93 					n += sfsprintf(buf + n, 0, "/close");
94 				if (fs->flags & FS_FLUSH)
95 					n += sfsprintf(buf + n, 0, "/flush");
96 				if (fs->flags & FS_GLOBAL)
97 				{
98 					n += sfsprintf(buf + n, 0, "/global");
99 					if (!(fs->flags & FS_ACTIVE))
100 						for (mp = state.global; mp; mp = mp->global)
101 							if (mp->fs == fs && mp->channel && mp->channel != -1)
102 							{
103 								n += sfsprintf(buf + n, 0, "=%d.%d", state.pid, MSG_CHANNEL_SYS(mp->channel));
104 								break;
105 							}
106 				}
107 				if (fs->flags & FS_INTERACTIVE)
108 					n += sfsprintf(buf + n, 0, "/interactive");
109 				if (fs->match)
110 					n += sfsprintf(buf + n, 0, "/match=%-*s", fs->matchsize ? fs->matchsize : strlen(fs->match), fs->match);
111 				if (fs->flags & FS_MONITOR)
112 					n += sfsprintf(buf + n, 0, "/monitor");
113 				if (fs->flags & FS_NAME)
114 					n += sfsprintf(buf + n, 0, "/name");
115 				if (!(fs->flags & FS_ON))
116 					n += sfsprintf(buf + n, 0, "/off");
117 				if (fs->flags & FS_RECEIVE)
118 					n += sfsprintf(buf + n, 0, "/receive");
119 				if (fs->flags & FS_REGULAR)
120 					n += sfsprintf(buf + n, 0, "/regular");
121 				if (fs->retry)
122 					n += sfsprintf(buf + n, 0, "/retry=%d", fs->retry);
123 				if (fs->terse)
124 				{
125 					n += sfsprintf(buf + n, 0, "/terse=");
126 					n += msggetmask(buf + n, SHRT_MAX, fs->terse);
127 				}
128 				if (fs->flags & FS_UNIQUE)
129 					n += sfsprintf(buf + n, 0, "/unique");
130 				if (fs->flags & FS_WRITE)
131 					n += sfsprintf(buf + n, 0, "/write");
132 				n += getattr(fs->attr, buf + n);
133 #endif
134 				if ((flags & (MAP_EXEC|MAP_INIT)) && (fs->flags & FS_OPEN))
135 				{
136 					if (!(fs->flags & FS_CLOSE))
137 					{
138 						if (fs->fd >= RESERVED_FD)
139 							n += sfsprintf(buf + n, 0, "/fd=%d", fs->fd);
140 					}
141 					else if ((flags & MAP_EXEC) && !(fs->flags & FS_BOUND))
142 						n = 0;
143 				}
144 				if (n > m || (fs->flags & FS_BOUND))
145 				{
146 					buf += n++;
147 					*buf++ = ' ';
148 				}
149 				else n = 0;
150 			}
151 			else n = ((fs->flags & FS_BOUND) ? (fs->servicesize ? fs->servicesize : strlen(fs->service)) : 0) +
152 				gs->specialsize +
153 				fs->specialsize +
154 #if FS
155 				(fs->ack ? (msggetmask(NiL, 0, fs->ack) + 6) : 0) +
156 				(fs->call != ~0 ? (msggetmask(NiL, 0, fs->call) + 6) : 0) +
157 				((fs->flags & FS_CLOSE) ? 6 : 0) +
158 				((fs->flags & FS_FLUSH) ? 6 : 0) +
159 				((fs->flags & FS_GLOBAL) ? ((fs->flags & (FS_ACTIVE|FS_CLOSE)) ? 7 : 22) : 0) +
160 				((fs->flags & FS_INTERACTIVE) ? 11 : 0) +
161 				(fs->match ? ((fs->matchsize ? fs->matchsize : strlen(fs->match)) + 7) : 0) +
162 				((fs->flags & FS_MONITOR) ? 0 : 8) +
163 				((fs->flags & FS_NAME) ? 5 : 0) +
164 				((fs->flags & FS_ON) ? 0 : 3) +
165 				((fs->flags & FS_RECEIVE) ? 0 : 8) +
166 				((fs->flags & FS_REGULAR) ? 0 : 8) +
167 				(fs->retry ? 12 : 0) +
168 				(fs->terse ? (msggetmask(NiL, 0, fs->terse) + 8) : 0) +
169 				((fs->flags & FS_UNIQUE) ? 0 : 7) +
170 				((fs->flags & FS_WRITE) ? 0 : 6) +
171 				getattr(fs->attr, NiL) +
172 #endif
173 				((flags & (MAP_EXEC|MAP_INIT)) && (fs->flags & (FS_CLOSE|FS_OPEN)) == FS_OPEN ? 10 : 0) +
174 				6;
175 			sum += n;
176 			if (op)
177 			{
178 				state.visit.fs = fs;
179 				break;
180 			}
181 		}
182 	n = iterate(&state.vmount, mapget, buf, flags);
183 	if (buf) buf += n;
184 	sum += n;
185 	state.visit.fs = 0;
186 	return(sum);
187 }
188 
189 /*
190  * return mask for msg calls in state.key.*
191  */
192 
193 static unsigned long
getmsgmask(const char * e)194 getmsgmask(const char* e)
195 {
196 	register char*		s;
197 	register char*		t;
198 	register int		c;
199 	register unsigned long	x = ~0;
200 
201 	if (!(s = state.key.invert)) s = state.key.value;
202 	if (s != state.one)
203 	{
204 		for (t = s; t < (char*)e; t++)
205 			if (*t == '/')
206 				break;
207 		if (c = *t) *t = 0;
208 		x = msgsetmask(s);
209 		if (c) *t = c;
210 	}
211 	return(state.key.invert ? ~x : x);
212 }
213 
214 static int
set_fs(register Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)215 set_fs(register Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
216 {
217 	register Fs_t*		ff;
218 	register unsigned long	x;
219 	int			n;
220 	int			old;
221 	const char*		oe;
222 #if FS
223 	Mount_t*		mp;
224 	Mount_t*		pm;
225 #endif
226 
227 	oe = op + (opsize ? opsize : strlen(op));
228 	if (!(x = getkey(op, oe, 0)))
229 	{
230 		if (!*arg)
231 			while (state.vmount.table->key)
232 				search(&state.vmount, state.vmount.table->key, state.vmount.table->keysize, NiL, T_DELETE);
233 		return(0);
234 	}
235 	for (ff = 0, fs = state.fs; fs < state.fs + elementsof(state.fs); fs++)
236 		if ((fs->flags & (FS_BOUND|FS_INTERNAL)) && fs->key == x) break;
237 		else if (!ff && !fs->flags) ff = fs;
238 	if (fs >= state.fs + elementsof(state.fs))
239 	{
240 		if (!*arg && !state.key.next) return(0);
241 		if (!(fs = ff)) return(-1);
242 		fs->specialsize = state.key.next ? (state.key.next - (char*)op - 1) : opsize ? opsize : strlen(op);
243 		if (fs->specialsize >= elementsof(fs->special))
244 			fs->specialsize = elementsof(fs->special) - 1;
245 		strncpy(fs->special, op, fs->specialsize);
246 		fs->special[fs->specialsize] = 0;
247 		fs->key = x;
248 		fs->call = ~0;
249 		old = 0;
250 	}
251 	else if (!*arg && !state.key.next)
252 	{
253 		fsdrop(fs, 1);
254 		return(0);
255 	}
256 	else old = 1;
257 	if (*arg)
258 	{
259 		n = argsize ? argsize : strlen(arg);
260 		if (!fs->service || (fs->servicesize ? fs->servicesize : strlen(fs->service)) != n || strncmp(arg, fs->service, n))
261 		{
262 			fsdrop(fs, 1);
263 			if (fs->servicesize = argsize) fs->service = (char*)arg;
264 			else fs->service = strcpy(newof(0, char, n, 1), arg);
265 			fs->flags |= FS_BOUND|FS_ON;
266 			if (!(fs->flags & FS_INTERNAL)) fs->flags |= FS_FS;
267 			if (fs == &state.fs[FS_safe] && !(fs->flags & FS_INIT))
268 				state.safe = fs;
269 		}
270 	}
271 	while ((op = (const char*)state.key.next) && (x = getkey(op, oe, 0)))
272 		switch (x)
273 		{
274 		case HASHKEY2('f','d'):
275 			fsdrop(fs, 0);
276 			if ((fs->fd = strtol(state.key.value, NiL, 0)) > 0)
277 				fsinit(fs, fs->fd);
278 			break;
279 		case HASHKEY3('o','f','f'):
280 			if (state.key.invert) fs->flags |= FS_ON;
281 			else fs->flags &= ~FS_ON;
282 			break;
283 		case HASHKEY2('o','n'):
284 			if (state.key.invert) fs->flags &= ~FS_ON;
285 			else fs->flags |= FS_ON;
286 			break;
287 #if FS
288 		case HASHKEY3('a','c','k'):
289 			fs->ack = getmsgmask(oe);
290 			break;
291 		case HASHKEY6('a','c','t','i','v','e'):
292 			if (!(fs->flags & FS_INTERNAL))
293 			{
294 				if (state.key.invert) fs->flags &= ~FS_ACTIVE;
295 				else fs->flags |= FS_ACTIVE;
296 			}
297 			break;
298 		case HASHKEY4('c','a','l','l'):
299 			fs->call = getmsgmask(oe);
300 			break;
301 		case HASHKEY5('c','l','o','s','e'):
302 			if (!(fs->flags & FS_INTERNAL))
303 			{
304 				if (state.key.invert) fs->flags &= ~FS_CLOSE;
305 				else fs->flags |= FS_CLOSE;
306 			}
307 			break;
308 		case HASHKEY5('f','l','u','s','h'):
309 			if (state.key.invert) fs->flags &= ~FS_FLUSH;
310 			else fs->flags |= FS_FLUSH;
311 			break;
312 		case HASHKEY4('f','o','r','k'):
313 			if (!(fs->flags & FS_INTERNAL))
314 			{
315 				if (state.key.invert) fs->flags &= ~FS_FORK;
316 				else fs->flags |= FS_FORK;
317 			}
318 			break;
319 		case HASHKEY6('g','l','o','b','a','l'):
320 			if (state.key.invert) fs->flags &= ~FS_GLOBAL;
321 			else fs->flags |= FS_GLOBAL;
322 			for (mp = state.global, pm = 0; mp && mp->fs != fs; pm = mp, mp = mp->global);
323 			if (!mp)
324 			{
325 				if (!state.key.invert)
326 				{
327 					for (mp = state.mount; mp < state.mount + elementsof(state.mount) && mp->fs; mp++);
328 					if (mp < state.mount + elementsof(state.mount))
329 					{
330 						mp->fs = fs;
331 						mp->global = state.global;
332 						state.global = mp;
333 						if (state.key.value != state.one)
334 						{
335 							int	n;
336 							char*	e;
337 
338 							n = strtol(state.key.value, &e, 0);
339 							if (*e == '.' && n == state.pid && (n = strtol(e + 1, NiL, 0)))
340 								mp->channel = MSG_CHANNEL(state.pid, n);
341 						}
342 					}
343 				}
344 			}
345 			else if (state.key.invert)
346 			{
347 				mp->fs = 0;
348 				if (pm) pm->global = mp->global;
349 				else state.global = 0;
350 			}
351 			if (state.global && !state.cache) state.cache = 1;
352 			break;
353 		case HASHKEY6('i','n','t','e','r','a'):
354 			if (state.key.invert) fs->flags &= ~FS_INTERACTIVE;
355 			else fs->flags |= FS_INTERACTIVE;
356 			break;
357 		case HASHKEY4('l','o','a','d'):
358 			if (!(fs->flags & (FS_INTERNAL|FS_LOAD)) && fs->service)
359 			{
360 				void*		dll;
361 				Fs_get_t	get;
362 				Fs_set_t	set;
363 
364 				if ((dll = dlopen(fs->service, RTLD_LAZY)) && (set = (Fs_set_t)dlsym(dll, "set")))
365 				{
366 					fs->flags |= FS_LOAD|FS_INIT|FS_ON;
367 					fs->set = set;
368 					(*fs->set)(fs, state.null, 0, "init", 4);
369 					if (get = (Fs_get_t)dlsym(dll, "get"))
370 						fs->get = get;
371 				}
372 			}
373 			break;
374 		case HASHKEY5('m','a','t','c','h'):
375 			if (fs->match)
376 			{
377 				if (fs->matchsize) fs->matchsize = 0;
378 				else free(fs->match);
379 				fs->match = 0;
380 			}
381 			if (!state.key.invert)
382 			{
383 				if (opsize)
384 				{
385 					fs->match = state.key.value;
386 					fs->matchsize = state.key.valsize;
387 				}
388 				else fs->match = strcpy(newof(0, char, state.key.valsize, 1), state.key.value);
389 			}
390 			break;
391 		case HASHKEY6('m','o','n','i','t','o'):
392 			if (!(fs->flags & FS_INTERNAL))
393 			{
394 				if (state.key.invert) fs->flags &= ~FS_MONITOR;
395 				else fs->flags |= FS_MONITOR;
396 			}
397 			break;
398 		case HASHKEY4('n','a','m','e'):
399 			if (!(fs->flags & FS_INTERNAL))
400 			{
401 				if (state.key.invert) fs->flags &= ~FS_NAME;
402 				else fs->flags |= FS_NAME;
403 			}
404 			break;
405 		case HASHKEY6('r','e','c','e','i','v'):
406 			if (!(fs->flags & FS_INTERNAL))
407 			{
408 				if (state.key.invert) fs->flags &= ~FS_RECEIVE;
409 				else fs->flags |= FS_RECEIVE;
410 			}
411 			break;
412 		case HASHKEY6('r','e','g','u','l','a'):
413 			if (state.key.invert) fs->flags &= ~FS_REGULAR;
414 			else fs->flags |= FS_REGULAR;
415 			break;
416 		case HASHKEY5('r','e','t','r','y'):
417 			fs->retry = strtol(state.key.value, NiL, 0);
418 			break;
419 		case HASHKEY5('t','e','r','s','e'):
420 			fs->terse = getmsgmask(oe);
421 			break;
422 		case HASHKEY6('u','n','i','q','u','e'):
423 			if (state.key.invert) fs->flags &= ~FS_UNIQUE;
424 			else fs->flags |= FS_UNIQUE;
425 			break;
426 		case HASHKEY5('w','r','i','t','e'):
427 			if (state.key.invert) fs->flags &= ~FS_WRITE;
428 			else fs->flags |= FS_WRITE;
429 			break;
430 		default:
431 			setattr(fs->attr, op, oe);
432 			break;
433 #endif
434 		}
435 	if (!old)
436 	{
437 #if FS
438 		if ((fs->flags & (FS_ACTIVE|FS_MONITOR)) == FS_ACTIVE)
439 			fs->call |= MSG_MASK(MSG_fork);
440 		if (fs->flags & FS_NAME) state.call.name++;
441 		else state.call.monitor++;
442 #endif
443 	}
444 	return(0);
445 }
446 
447 static int
get_map(Fs_t * fs,register char * buf,const char * op,int flags)448 get_map(Fs_t* fs, register char* buf, const char* op, int flags)
449 {
450 	register int	n;
451 
452 	if (op) return(-1);
453 	state.visit.prefix = fs->special;
454 	state.visit.prelen = fs->specialsize + 4;
455 	n = iterate(&state.vmap, mapget, buf, flags);
456 	state.visit.prelen = 0;
457 	state.visit.prefix = 0;
458 	return(n);
459 }
460 
461 static int
set_map(Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)462 set_map(Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
463 {
464 	NoP(fs);
465 	return(mapset(&state.vmap, arg, argsize, op, opsize));
466 }
467 
468 static int
get_safe(Fs_t * fs,register char * buf,const char * op,int flags)469 get_safe(Fs_t* fs, register char* buf, const char* op, int flags)
470 {
471 	register int	n;
472 
473 	if (op) return(-1);
474 	state.visit.prefix = fs->special;
475 	state.visit.prelen = fs->specialsize + 4;
476 	n = iterate(&state.vsafe, mapget, buf, flags);
477 	state.visit.prelen = 0;
478 	state.visit.prefix = 0;
479 	return(n);
480 }
481 
482 static int
set_safe(Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)483 set_safe(Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
484 {
485 	NoP(fs);
486 	return(mapset(&state.vsafe, arg, argsize, op, opsize));
487 }
488 
489 static int
get_intercept(Fs_t * fs,register char * buf,const char * op,int flags)490 get_intercept(Fs_t* fs, register char* buf, const char* op, int flags)
491 {
492 	register int	n;
493 
494 	if (op)
495 		return -1;
496 	state.visit.prefix = fs->special;
497 	state.visit.prelen = fs->specialsize + 4;
498 	n = iterate(&state.vintercept, mapget, buf, flags);
499 	state.visit.prelen = 0;
500 	state.visit.prefix = 0;
501 	return n;
502 }
503 
504 typedef int (*Init_f)(int, const char*, int);
505 
506 static int
set_intercept(Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)507 set_intercept(Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
508 {
509 	void*			dll;
510 	Init_f			init;
511 	char			buf[PATH_MAX + 1];
512 
513 	static const char	sym[] = "_3d_init";
514 
515 	NoP(fs);
516 	if (!*arg || mapset(&state.vintercept, arg, argsize, op, opsize))
517 		return -1;
518 	if (argsize)
519 	{
520 		if (argsize > PATH_MAX)
521 			return -1;
522 		strncpy(buf, arg, argsize);
523 		buf[argsize] = 0;
524 		arg = (const char*)buf;
525 	}
526 	if (!(dll = dlopen(arg, RTLD_LAZY)))
527 	{
528 		error(2, "%s: %s", arg, dlerror());
529 		return -1;
530 	}
531 	if (!(init = (Init_f)dlsym(dll, sym)))
532 	{
533 		error(2, "%s: %s: initialization function not found", arg, sym);
534 		dlclose(dll);
535 		return -1;
536 	}
537 	return (*init)(0, op, opsize);
538 }
539 
540 #if FS
541 
542 static void
bencode(char ** b,char * e,register unsigned long n,int a,int r,int x)543 bencode(char** b, char* e, register unsigned long n, int a, int r, int x)
544 {
545 	register char*	s;
546 	register char*	t;
547 	register int	m;
548 	register int	z;
549 	char		buf[16];
550 
551 	s = buf;
552 	z = (1 << (r - (x != 0)));
553 	do
554 	{
555 		m = n & ((1 << r) - 1);
556 		*s++ = m + ((m >= z) ? x : a);
557 	} while ((n >>= r) && s < &buf[sizeof(buf)]);
558 	t = *b;
559 	while (s > buf && t < e)
560 		*t++ = *--s;
561 }
562 
563 #endif
564 
565 static int
get_option(register Fs_t * fs,register char * buf,const char * op,int flags)566 get_option(register Fs_t* fs, register char* buf, const char* op, int flags)
567 {
568 	register int		c;
569 	register int		n;
570 	register int		sum = 0;
571 	register unsigned long	x;
572 	char*			b;
573 	char*			e;
574 	int			m;
575 
576 	x = op ? getkey(op, op + strlen(op), 0) : 0;
577 
578 	/*
579 	 * table version
580 	 */
581 
582 	if (!x && (flags & (MAP_EXEC|MAP_INIT)) || x == HASHKEY6('v','e','r','s','i','o'))
583 	{
584 		if (buf)
585 		{
586 			n = sfsprintf(buf, 0, "- /#%s/version=%d ", fs->special, TABLE_VERSION);
587 			buf += n;
588 		}
589 		else n = fs->specialsize + 18;
590 		sum += n;
591 	}
592 
593 	/*
594 	 * trace output -- special case to capture redirection early
595 	 */
596 
597 	if (!x && (fs->flags & FS_BOUND))
598 	{
599 		if (buf)
600 		{
601 			n = sfsprintf(buf, 0, "%-*s /#%s/%s ", fs->servicesize ? fs->servicesize : strlen(fs->service), fs->service, state.fs[FS_fs].special, fs->special);
602 			buf += n;
603 		}
604 		else n = (fs->servicesize ? fs->servicesize : strlen(fs->service)) + state.fs[FS_fs].specialsize + fs->specialsize + 4;
605 		sum += n;
606 	}
607 
608 	/*
609 	 * test mask
610 	 */
611 
612 	if (!x && state.test || x == HASHKEY4('t','e','s','t'))
613 	{
614 		if (buf)
615 		{
616 			n = sfsprintf(buf, 0, "- /#%s/test=0%lo ", fs->special, state.test);
617 			buf += n;
618 		}
619 		else n = fs->specialsize + 23;
620 		sum += n;
621 	}
622 
623 	/*
624 	 * readdir() view boundaries
625 	 */
626 
627 	if (x == HASHKEY6('b','o','u','n','d','a'))
628 	{
629 		if (buf)
630 		{
631 			n = sfsprintf(buf, 0, "- /#%s/%sboundary ", fs->special, state.boundary ? state.null : "no");
632 			buf += n;
633 		}
634 		else n = fs->specialsize + 16;
635 		sum += n;
636 	}
637 
638 #if DEBUG
639 	/*
640 	 * debug level
641 	 */
642 
643 	if (!x && error_info.trace || x == HASHKEY5('d','e','b','u','g'))
644 	{
645 		if (buf)
646 		{
647 			n = sfsprintf(buf, 0, "- /#%s/debug=%d ", fs->special, -error_info.trace);
648 			buf += n;
649 		}
650 		else n = fs->specialsize + 18;
651 		sum += n;
652 	}
653 #endif
654 
655 	/*
656 	 * license features
657 	 */
658 
659 	if (!x && *state.license || x == HASHKEY6('l','i','c','e','n','s'))
660 	{
661 		if (buf)
662 		{
663 			n = sfsprintf(buf, 0, "- /#%s/license=%s ", fs->special, state.license);
664 			buf += n;
665 		}
666 		else n = fs->specialsize + strlen(state.license) + 14;
667 		sum += n;
668 	}
669 
670 	/*
671 	 * 2d && 3d
672 	 */
673 
674 	if (!x && (state.in_2d || state.limit < TABSIZE) || x == HASHKEY2(HASHKEYN('2'),'d') || x == HASHKEY2(HASHKEYN('3'),'d'))
675 	{
676 		if (buf)
677 		{
678 			n = (state.limit == TABSIZE) ? sfsprintf(buf, 0, "- /#%s/%cd ", fs->special, state.in_2d ? '2' : '3') : sfsprintf(buf, 0, "- /#%s/2d=%d ", fs->special, state.limit);
679 			buf += n;
680 		}
681 		else n = fs->specialsize + (state.limit == TABSIZE) ? 8 : 11;
682 		sum += n;
683 	}
684 
685 #if FS
686 
687 	/*
688 	 * file table
689 	 */
690 
691 	if (state.cache && (!x && (flags & (MAP_EXEC|MAP_INIT)) || x == HASHKEY4('f','i','l','e')))
692 	{
693 		b = state.path.name;
694 		e = b + sizeof(state.path.name) - 1;
695 		c = -1;
696 		for (n = m = 0; n <= state.cache; n++)
697 			if (state.file[n].flags & FILE_CLOEXEC)
698 			{
699 				if ((flags & MAP_EXEC) && state.file[n].mount && fssys(state.file[n].mount, MSG_close))
700 					fscall(state.file[n].mount, MSG_close, 0, n);
701 			}
702 			else if (state.file[n].flags & FILE_OPEN)
703 			{
704 				if ((x = n - m - 1) > 0)
705 					bencode(&b, e, x, '0', 3, 0);
706 				if (x = state.file[n].id.fid[0])
707 					bencode(&b, e, x, 'a', 4, 0);
708 				bencode(&b, e, state.file[n].id.fid[1], 'A', 4, 0);
709 				if (state.file[n].mount && (x = state.file[n].mount - state.mount) != c)
710 					bencode(&b, e, c = x, 'Q', 4, 'q');
711 				m = n;
712 			}
713 		n = b - state.path.name;
714 		*b = 0;
715 		b = state.path.name;
716 		if (n)
717 		{
718 			if (buf)
719 			{
720 				n = sfsprintf(buf, 0, "- /#%s/file=%s ", fs->special, b);
721  				buf += n;
722  			}
723  			else n += fs->specialsize + 11;
724  			sum += n;
725 		}
726 	}
727 
728 #endif
729 
730 	/*
731 	 * fd table
732 	 */
733 
734 	if (!x && (flags & (MAP_EXEC|MAP_INIT)) && state.table.fd != TABLE_FD || x == HASHKEY5('t','a','b','l','e'))
735 	{
736 		if (buf)
737 		{
738 			if ((n = FCNTL(TABLE_FD, F_GETFD, 0)) >= 0)
739 			{
740 				n = n ? -1 : FCNTL(TABLE_FD, F_DUPFD, RESERVED_FD);
741 				CLOSE(TABLE_FD);
742 			}
743 			if (state.table.fd <= 0 || FCNTL(state.table.fd, F_DUPFD, TABLE_FD) < 0)
744 			{
745 				if (state.table.fd > 0)
746 					cancel(&state.table.fd);
747 				n = -1;
748 			}
749 			else
750 			{
751 				cancel(&state.table.fd);
752 				state.table.fd = TABLE_FD;
753 				reserve(&state.table.fd);
754 			}
755 			if (n < 0) n = 0;
756 			else
757 			{
758 				n = sfsprintf(buf, 0, "- /#%s/table=%d ", fs->special, n);
759 				buf += n;
760 			}
761 		}
762 		else n = fs->specialsize + 18;
763 		sum += n;
764 	}
765 
766 	/*
767 	 * syscall count
768 	 */
769 
770 	if (!x && state.trace.count || x == HASHKEY5('c','o','u','n','t'))
771 	{
772 		if (buf)
773 		{
774 			n = sfsprintf(buf, 0, "- /#%s/%scount ", fs->special, state.trace.count ? state.null : "no");
775 			buf += n;
776 		}
777 		else n = fs->specialsize + 13;
778 		sum += n;
779 	}
780 
781 	/*
782 	 * syscall trace
783 	 */
784 
785 	if (!x && state.trace.pid || x == HASHKEY5('t','r','a','c','e'))
786 	{
787 		if (buf)
788 		{
789 			n = sfsprintf(buf, 0, "- /#%s/trace=%u ", fs->special, state.trace.pid + ((flags & (MAP_EXEC|MAP_INIT)) && state.trace.pid <= 2));
790 			buf += n;
791 		}
792 		else n = fs->specialsize + 18;
793 		sum += n;
794 	}
795 
796 	/*
797 	 * syscall calls
798 	 */
799 
800 	if (state.trace.call != ~0 && (!x || x == HASHKEY4('c','a','l','l')))
801 	{
802 		if (buf)
803 		{
804 			n = sfsprintf(buf, 0, "- /#%s/call=", fs->special);
805 			n += msggetmask(buf + n, SHRT_MAX, state.trace.call);
806 			buf += n++;
807 			*buf++ = ' ';
808 		}
809 		else n = fs->specialsize + msggetmask(NiL, 0, state.trace.call) + 12;
810 		sum += n;
811 	}
812 
813 #if FS
814 
815 	/*
816 	 * message timeout
817 	 */
818 
819 	if (!x && msg_info.timeout != MSG_TIMEOUT || x == HASHKEY6('t','i','m','e','o','u'))
820 	{
821 		if (buf)
822 		{
823 			n = sfsprintf(buf, 0, "- /#%s/timeout=%d ", fs->special, msg_info.timeout);
824 			buf += n;
825 		}
826 		else n = fs->specialsize + 20;
827 		sum += n;
828 	}
829 
830 #endif
831 
832 	return(sum);
833 }
834 
835 #if DEBUG && FS
836 
837 #define DUMP_call	(1<<0)
838 #define DUMP_file	(1<<1)
839 #define DUMP_fs		(1<<2)
840 #define DUMP_map	(1<<3)
841 #define DUMP_mount	(1<<4)
842 #define DUMP_safe	(1<<5)
843 #define DUMP_state	(1<<6)
844 #define DUMP_view	(1<<7)
845 
846 /*
847  * dump Table_t
848  */
849 
850 static void
dumptable(char ** b,char * e,Table_t * tab,const char * name)851 dumptable(char** b, char* e, Table_t* tab, const char* name)
852 {
853 	register Map_t*	cp;
854 	register Map_t*	ep;
855 	register int	n;
856 
857 	if (tab->size)
858 	{
859 		bprintf(b, e, "\n%s table\n\n", name);
860 		for (ep = (cp = tab->table) + tab->size; cp < ep; cp++)
861 		{
862 			bprintf(b, e, "  [%d]  %-*s", cp - tab->table, cp->keysize, cp->key);
863 			if ((n = 32 - cp->keysize) > 0)
864 				bprintf(b, e, "%*s", n, state.null);
865 			bprintf(b, e, " %-*s\n", T_VALSIZE(cp), cp->val);
866 		}
867 	}
868 }
869 
870 /*
871  * dump internal state to option output
872  */
873 
874 static void
dump(const char * op,const char * oe)875 dump(const char* op, const char* oe)
876 {
877 	register char*		e;
878 	register File_t*	fp;
879 	register Fs_t*		fs;
880 	register Mount_t*	mp;
881 	register int		list;
882 	register int		n;
883 	int			on;
884 	char*			b;
885 
886 	if ((on = fsfd(&state.fs[FS_option])) <= 0) return;
887 	if (op == (char*)state.one) list = ~DUMP_call;
888 	else
889 	{
890 		list = 0;
891 		e = state.key.next;
892 		state.key.next = (char*)op;
893 		for (;;)
894 		{
895 			switch (getkey(state.key.next, oe, ','))
896 			{
897 			case 0:
898 				break;
899 			case HASHKEY4('c','a','l','l'):
900 				list |= DUMP_call;
901 				continue;
902 			case HASHKEY4('f','i','l','e'):
903 				list |= DUMP_file;
904 				continue;
905 			case HASHKEY2('f','s'):
906 				list |= DUMP_fs;
907 				continue;
908 			case HASHKEY3('m','a','p'):
909 				list |= DUMP_map;
910 				continue;
911 			case HASHKEY5('m','o','u','n','t'):
912 				list |= DUMP_mount;
913 				continue;
914 			case HASHKEY4('s','a','f','e'):
915 				list |= DUMP_safe;
916 				continue;
917 			case HASHKEY5('s','t','a','t','e'):
918 				list |= DUMP_state;
919 				continue;
920 			case HASHKEY4('v','i','e','w'):
921 				list |= DUMP_view;
922 				continue;
923 			}
924 			break;
925 		}
926 		state.key.next = e;
927 		if (!list) return;
928 	}
929 	e = (b = state.path.name) + sizeof(state.path.name) - 1;
930 	if (list & DUMP_state)
931 	{
932 		bprintf(&b, e, "\nstate %s\n\n", id + 10);
933 		if (state.limit == TABSIZE) bprintf(&b, e, "           %cd  on\n", state.in_2d ? '2' : '3');
934 		else bprintf(&b, e, "           2d  %d\n", state.limit);
935 		bprintf(&b, e, "     boundary  %s\n", state.boundary ? "on" : "off");
936 		bprintf(&b, e, "        cache  %u\n", state.cache);
937 		bprintf(&b, e, "         call  %u.%u", state.call.monitor, state.call.name);
938 		if (state.trace.call != ~0)
939 		{
940 			bprintf(&b, e, " ");
941 			b += msggetmask(b, e - b, state.trace.call);
942 		}
943 		bprintf(&b, e, "\n        count  %u\n", state.trace.count);
944 #if DEBUG
945 		bprintf(&b, e, "        debug  %d\n", -error_info.trace);
946 #endif
947 		bprintf(&b, e, "        level  %d\n", state.level);
948 #if LICENSED
949 		bprintf(&b, e, "      license  %s\n", state.license);
950 #endif
951 		bprintf(&b, e, "          pid  %u\n", state.pid);
952 		bprintf(&b, e, "          pwd  %s\n", state.pwd);
953 		bprintf(&b, e, "        table  %d\n", state.table.fd);
954 		bprintf(&b, e, "         test  %08o\n", state.test);
955 		bprintf(&b, e, "        trace  %u\n", state.trace.pid);
956 		bprintf(&b, e, "      version  %u\n", TABLE_VERSION);
957 	}
958 	if (list & DUMP_fs)
959 	{
960 		bprintf(&b, e, "\nfs table\n\n");
961 		for (fs = state.fs; fs < state.fs + elementsof(state.fs); fs++)
962 			if (fs->flags)
963 			{
964 				if ((n = fs - state.fs) < 10) n += '0';
965 				else n -= 10 - 'a';
966 				bprintf(&b, e, "  [%c]  %-*s", n, sizeof(fs->special), fs->special);
967 				if (fs->flags & FS_BOUND) bprintf(&b, e, " service=%-*s", fs->servicesize ? fs->servicesize : strlen(fs->service), fs->service);
968 				if (fs->flags & FS_ACTIVE) bprintf(&b, e, " active");
969 				if (fs->flags & FS_CLOSE) bprintf(&b, e, " close");
970 				if (fs->flags & FS_ERROR) bprintf(&b, e, " error");
971 				if (fs->flags & FS_FLUSH) bprintf(&b, e, " flush");
972 				if (fs->flags & FS_FORK) bprintf(&b, e, " fork");
973 				if (fs->flags & FS_FS) bprintf(&b, e, " fs");
974 				if (fs->flags & FS_GLOBAL) bprintf(&b, e, " global");
975 				if (fs->flags & FS_INIT) bprintf(&b, e, " init");
976 				if (fs->flags & FS_INTERACTIVE) bprintf(&b, e, " interactive");
977 				if (fs->flags & FS_INTERNAL) bprintf(&b, e, " internal");
978 #if LICENSED
979 				if (fs->flags & FS_LICENSED) bprintf(&b, e, " licensed");
980 #endif
981 				if (fs->flags & FS_LOAD) bprintf(&b, e, " load");
982 				if (fs->flags & FS_LOCK) bprintf(&b, e, " lock");
983 				if (fs->flags & FS_MAGIC) bprintf(&b, e, " magic");
984 				if (fs->flags & FS_MONITOR) bprintf(&b, e, " monitor");
985 				if (fs->flags & FS_NAME) bprintf(&b, e, " name");
986 				if (!(fs->flags & FS_ON)) bprintf(&b, e, " off");
987 				if (fs->flags & FS_OPEN) bprintf(&b, e, " open=%d", fs->fd);
988 				if (fs->flags & FS_RAW) bprintf(&b, e, " raw");
989 				if (fs->flags & FS_RECEIVE) bprintf(&b, e, " receive");
990 				if (fs->flags & FS_REFERENCED) bprintf(&b, e, " referenced");
991 				if (fs->flags & FS_REGULAR) bprintf(&b, e, " regular");
992 				if (fs->flags & FS_UNIQUE) bprintf(&b, e, " unique");
993 				if (fs->flags & FS_VALIDATED) bprintf(&b, e, " validated");
994 				if (fs->flags & FS_WRITE) bprintf(&b, e, " write");
995 				if (fs->call != ~0)
996 				{
997 					bprintf(&b, e, " call=");
998 					b += msggetmask(b, e - b, fs->call);
999 				}
1000 				if (fs->ack)
1001 				{
1002 					bprintf(&b, e, " ack=");
1003 					b += msggetmask(b, e - b, fs->ack);
1004 				}
1005 				if (fs->terse)
1006 				{
1007 					bprintf(&b, e, " terse=");
1008 					b += msggetmask(b, e - b, fs->terse);
1009 				}
1010 				bprintf(&b, e, "%s", fs->attr);
1011 				bprintf(&b, e, "\n");
1012 			}
1013 	}
1014 	if (list & DUMP_mount)
1015 	{
1016 		bprintf(&b, e, "\nmount table\n\n");
1017 		for (mp = state.mount; mp < state.mount + elementsof(state.mount); mp++)
1018 			if (mp->fs)
1019 			{
1020 				if ((n = mp - state.mount) < 10) n += '0';
1021 				else n -= 10 - 'a';
1022 				bprintf(&b, e, "  [%c]  %-*s", n, sizeof(mp->fs->special), mp->fs->special);
1023 				if (mp->logical) bprintf(&b, e, " logical=%-*s", mp->logicalsize ? mp->logicalsize : strlen(mp->logical), mp->logical);
1024 				else if (mp->fs->flags & FS_GLOBAL) bprintf(&b, e, " global");
1025 				if (mp->physical) bprintf(&b, e, " physical=%-*s", mp->physicalsize ? mp->physicalsize : strlen(mp->physical), mp->physical);
1026 				if (mp->channel) bprintf(&b, e, " channel=%u", MSG_CHANNEL_SYS(mp->channel));
1027 				bprintf(&b, e, "%s", mp->attr);
1028 				bprintf(&b, e, "\n");
1029 			}
1030 	}
1031 	if (list & DUMP_file)
1032 	{
1033 		bprintf(&b, e, "\nfile table\n\n");
1034 		for (fp = state.file; fp < state.file + elementsof(state.file); fp++)
1035 			if ((mp = fp->mount) || (fp->flags & (FILE_ERROR|FILE_OPEN)) || fp->reserved)
1036 			{
1037 				bprintf(&b, e, "  [%02d]", fp - state.file);
1038 				if (mp)
1039 				{
1040 					if ((n = mp - state.mount) < 10) n += '0';
1041 					else n -= 10 - 'a';
1042 					bprintf(&b, e, " mount=%s[%c]", mp->fs->special, n);
1043 				}
1044 				if (fp->flags & FILE_CLOEXEC) bprintf(&b, e, " cloexec");
1045 				if (fp->flags & FILE_ERROR) bprintf(&b, e, " error");
1046 				if (fp->flags & FILE_LOCK) bprintf(&b, e, " lock");
1047 				if (fp->id.fid[0] || fp->id.fid[1]) bprintf(&b, e, " fid=%ld%s%ld", fp->id.fid[0], fp->id.fid[1] >= 0 ? "+" : state.null, fp->id.fid[1]);
1048 				if (fp->flags & FILE_OPEN) bprintf(&b, e, " open");
1049 				if (fp->flags & FILE_REGULAR) bprintf(&b, e, " regular");
1050 				if (fp->reserved) bprintf(&b, e, " reserved");
1051 				if (fp->flags & FILE_VIRTUAL) bprintf(&b, e, " virtual");
1052 				if (fp->flags & FILE_WRITE) bprintf(&b, e, " write");
1053 				if (n = fp->flags & ~(FILE_LOCAL - 1)) bprintf(&b, e, " local=%08o", n);
1054 				bprintf(&b, e, "\n");
1055 			}
1056 	}
1057 	if (list & DUMP_view) dumptable(&b, e, &state.vpath, "view");
1058 	if (list & DUMP_map) dumptable(&b, e, &state.vmap, "map");
1059 	if (list & DUMP_safe) dumptable(&b, e, &state.vsafe, "safe");
1060 	if (list & DUMP_call) calldump(&b, e);
1061 	bprintf(&b, e + 1, "\n");
1062 	WRITE(on, state.path.name, b - state.path.name);
1063 }
1064 
1065 #endif
1066 
1067 static int
set_option(Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)1068 set_option(Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
1069 {
1070 	register int		c;
1071 	register const char*	oe;
1072 	register char*		s;
1073 	int			i;
1074 	int			m;
1075 	long			n;
1076 #if FS
1077 	Mount_t*		mp;
1078 #endif
1079 
1080 	NoP(argsize);
1081 	oe = op + (opsize ? opsize : strlen(op));
1082 	do switch (n = getkey(op, oe, 0))
1083 	{
1084 	case HASHKEY2(HASHKEYN('2'),'d'):
1085 		state.limit = state.key.value == state.one ? 0 : strtol(state.key.value, NiL, 0);
1086 		if (state.limit > 0) state.in_2d = 0;
1087 		else
1088 		{
1089 			if (state.limit < 0)
1090 				cancel(&state.table.fd);
1091 			state.limit = TABSIZE;
1092 			state.in_2d = 1;
1093 		}
1094 		break;
1095 	case HASHKEY2(HASHKEYN('3'),'d'):
1096 		state.in_2d = strtol(state.key.value, NiL, 0) <= 0;
1097 		state.limit = TABSIZE;
1098 		break;
1099 	case HASHKEY6('b','o','u','n','d','a'):
1100 		state.boundary = strtol(state.key.value, NiL, 0) > 0;
1101 		break;
1102 	case HASHKEY4('c','a','l','l'):
1103 		state.trace.call = getmsgmask(oe);
1104 		if (state.trace.count)
1105 			state.trace.call |= MSG_MASK(MSG_exit);
1106 		break;
1107 	case HASHKEY5('c','o','u','n','t'):
1108 		if (state.trace.count = strtol(state.key.value, NiL, 0))
1109 		{
1110 			state.trace.call |= MSG_MASK(MSG_exit);
1111 			if (!state.trace.pid)
1112 			{
1113 				state.trace.pid = 1;
1114 				goto setout;
1115 			}
1116 		}
1117 		break;
1118 
1119 #if DEBUG
1120 	case HASHKEY5('d','e','b','u','g'):
1121 		c = error_info.trace;
1122 		if (error_info.trace = -strtol(state.key.value, NiL, 0));
1123 		{
1124 			if (!c)
1125 			{
1126 				errno = 0;
1127 				message((error_info.trace, "%s [%d]", state.id, state.pid));
1128 			}
1129 			goto setout;
1130 		}
1131 		break;
1132 #endif
1133 
1134 #if DEBUG && FS
1135 	case HASHKEY4('d','u','m','p'):
1136 		dump(state.key.value, oe);
1137 		break;
1138 #endif
1139 
1140 #if FS
1141 	case HASHKEY4('f','i','l','e'):
1142 		s = state.key.value;
1143 		n = -1;
1144 		m = 0;
1145 		while (s < oe)
1146 		{
1147 			long	fid[2];
1148 			long	off;
1149 
1150 			if ((c = *s++) >= '0' && c < '0' + 8)
1151 			{
1152 				i = c - '0';
1153 				while (s < oe && (c = *s++) >= '0' && c < '0' + 8)
1154 					i = (i << 3) + c - '0';
1155 				if (s >= oe) break;
1156 				n += i;
1157 			}
1158 			else n++;
1159 			fid[0] = 0;
1160 			while (c >= 'a' && c < 'a' + 16)
1161 			{
1162 				fid[0] = (fid[0] << 4) + c - 'a';
1163 				if (s >= oe) break;
1164 				c = *s++;
1165 			}
1166 			fid[1] = 0;
1167 			while (c >= 'A' && c < 'A' + 16)
1168 			{
1169 				fid[1] = (fid[1] << 4) + c - 'A';
1170 				if (s >= oe) break;
1171 				c = *s++;
1172 			}
1173 			off = 0;
1174 			while (c >= 'a' && c < 'a' + 16)
1175 			{
1176 				off = (off << 4) + c - 'a';
1177 				if (s >= oe) break;
1178 				c = *s++;
1179 			}
1180 			i = 0;
1181 			for (;;)
1182 			{
1183 				if (c >= 'Q' && c < 'Q' + 8)
1184 					i = (i << 4) + c - 'Q';
1185 				else if (c >= 'q' && c < 'q' + 8)
1186 					i = (i << 4) + c - 'q' + 8;
1187 				else break;
1188 				if (s >= oe) break;
1189 				c = *s++;
1190 			}
1191 			if (i) m = i;
1192 			if (m >= 0 && m < elementsof(state.mount) && !fileinit(n, NiL, state.mount + m, 0))
1193 			{
1194 				state.file[n].id.fid[0] = fid[0];
1195 				state.file[n].id.fid[1] = fid[1];
1196 			}
1197 		}
1198 		break;
1199 #endif
1200 
1201 	case HASHKEY4('f','o','r','k'):
1202 		if (state.trace.pid > 2)
1203 			state.trace.pid = state.pid;
1204 		break;
1205 	case HASHKEY4('i','n','i','t'):
1206 		if (!(fs->flags & FS_OPEN) && !FSTAT(2, &fs->st))
1207 		{
1208 			fs->flags |= FS_OPEN;
1209 			fs->fd = 2;
1210 		}
1211 		state.trace.call = ~0;
1212 		break;
1213 	case HASHKEY6('l','i','c','e','n','s'):
1214 		if ((char*)op >= state.table.buf && (char*)oe < state.table.buf + sizeof(state.table.buf))
1215 		{
1216 			if (state.key.valsize >= sizeof(state.license))
1217 				state.key.valsize = sizeof(state.license) - 1;
1218 			memcpy(state.license, state.key.value, state.key.valsize);
1219 			state.license[state.key.valsize] = 0;
1220 		}
1221 		break;
1222 
1223 #if DEBUG && FS
1224 	case HASHKEY5('m','o','u','n','t'):
1225 		if (pathreal(arg, P_PATHONLY|P_ABSOLUTE|P_NOSLASH, NiL) && (mp = getmount(state.path.name, &arg)))
1226 			error(0, "getmount: %s: %s + %s", state.path.name, mp->fs->special, *arg ? arg : state.dot);
1227 		break;
1228 #endif
1229 
1230 	case HASHKEY5('t','a','b','l','e'):
1231 		if (state.table.fd == TABLE_FD && (i = strtol(state.key.value, NiL, 0)) > 0 && i != TABLE_FD)
1232 		{
1233 			CLOSE(state.table.fd);
1234 			state.table.fd = FCNTL(i, F_DUPFD, TABLE_FD);
1235 			CLOSE(i);
1236 		}
1237 		break;
1238 	case HASHKEY4('t','e','s','t'):
1239 		if (state.key.invert)
1240 		{
1241 			if (*state.key.invert >= '0' && *state.key.invert <= '9') state.key.value = state.key.invert;
1242 			if ((n = strtol(state.key.value, NiL, 0)) <= 0) state.test = 0;
1243 			else state.test &= ~n;
1244 		}
1245 		else state.test |= strtol(state.key.value, NiL, 0);
1246 		break;
1247 
1248 #if FS
1249 	case HASHKEY6('t','i','m','e','o','u'):
1250 		msg_info.timeout = strtol(state.key.value, NiL, 0);
1251 		break;
1252 	case HASHKEY6('t','i','m','e','s','t'):
1253 		msg_info.timestamp = !state.key.invert;
1254 		break;
1255 #endif
1256 
1257 	case HASHKEY5('t','r','a','c','e'):
1258 		if (state.trace.pid = strtol(state.key.value, NiL, 0))
1259 		{
1260 			if (state.trace.pid > 2)
1261 				state.trace.pid = state.pid;
1262 		setout:
1263 			if (state.fs[FS_option].fd == 2 && (i = FCNTL(2, F_DUPFD, RESERVED_FD)) >= 0)
1264 			{
1265 				state.fs[FS_option].fd = i;
1266 				reserve(&state.fs[FS_option].fd);
1267 			}
1268 		}
1269 		break;
1270 	case HASHKEY6('v','e','r','s','i','o'):
1271 		if ((state.table.version = strtol(state.key.value, NiL, 0)) != TABLE_VERSION)
1272 			return(-1);
1273 		break;
1274 	} while (op = (const char*)state.key.next);
1275 	return(0);
1276 }
1277 
1278 static int
get_pwd(register Fs_t * fs,register char * buf,const char * op,int flags)1279 get_pwd(register Fs_t* fs, register char* buf, const char* op, int flags)
1280 {
1281 	register int	n = 0;
1282 
1283 	NoP(flags);
1284 	if (op) return(-1);
1285 	if (state.pwd)
1286 	{
1287 		if (buf) n = sfsprintf(buf, 0, "%s /#%s ", state.pwd, fs->special);
1288 		else n = state.pwdsize + fs->specialsize + 4;
1289 	}
1290 	return(n);
1291 }
1292 
1293 /*
1294  * set state.pwd from s
1295  */
1296 
1297 static int
setpwd(register const char * s)1298 setpwd(register const char* s)
1299 {
1300 	int		osiz;
1301 	int		olev;
1302 	struct stat	dot;
1303 	struct stat	pwd;
1304 
1305 	if (*s != '/' || *state.pwd == '/')
1306 		return(-1);
1307 	if (STAT(state.dot, &dot))
1308 	{
1309 		message((-1, "%s: cannot stat", state.dot));
1310 		return(-1);
1311 	}
1312 	osiz = state.pwdsize;
1313 	if ((state.pwdsize = strlen(s)) >= sizeof(state.pwdbuf))
1314 		state.pwdsize = sizeof(state.pwdbuf) - 1;
1315 	strncpy(state.pwd, s, state.pwdsize);
1316 	state.pwd[state.pwdsize] = 0;
1317 	state.pwdsize = pathcanon(state.pwd, sizeof(state.pwdbuf), 0) - state.pwd;
1318 	olev = state.level;
1319 	state.level = -1;
1320 	state.path.linkname = 0;
1321 	if ((s = pathreal(state.pwd, 0, &pwd)) && (dot.st_ino == pwd.st_ino && dot.st_dev == pwd.st_dev || state.path.linkname && !STAT(state.path.name, &pwd) && dot.st_ino == pwd.st_ino && dot.st_dev == pwd.st_dev))
1322 	{
1323 		state.level = state.path.level;
1324 		memcpy(state.envpwd + sizeof(var_pwd) - 1, state.pwd, state.pwdsize);
1325 		message((-1, "setpwd: state.pwd=%s state.level=%d state.path.level=%d", state.pwd, state.level, state.path.level));
1326 		return(0);
1327 	}
1328 	message((-1, "%s: cannot set PWD", state.pwd));
1329 	*state.pwd = '.';
1330 	*(state.pwd + 1) = 0;
1331 	state.pwdsize = osiz;
1332 	state.level = olev;
1333 	return(-1);
1334 }
1335 
1336 static int
set_pwd(Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)1337 set_pwd(Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
1338 {
1339 	int	c;
1340 	int	r;
1341 
1342 	NoP(fs);
1343 	NoP(op);
1344 	NoP(opsize);
1345 	if (!*arg || *state.pwd == '/')
1346 		return(0);
1347 	if (argsize)
1348 	{
1349 		c = *((char*)arg + argsize);
1350 		*((char*)arg + argsize) = 0;
1351 	}
1352 	else c = 0;
1353 	if (op) message((-1, "set_pwd arg=%s op=%-*s", arg, opsize ? opsize : strlen(op), op));
1354 	r = setpwd(arg);
1355 	if (c) *((char*)arg + argsize) = c;
1356 	return(r);
1357 }
1358 
1359 static int
get_view(Fs_t * fs,register char * buf,const char * op,int flags)1360 get_view(Fs_t* fs, register char* buf, const char* op, int flags)
1361 {
1362 	NoP(fs);
1363 	if (op) return(-1);
1364 	return(iterate(&state.vpath, mapget, buf, flags));
1365 }
1366 
1367 static int
set_view(Fs_t * fs,const char * arg,int argsize,const char * op,int opsize)1368 set_view(Fs_t* fs, const char* arg, int argsize, const char* op, int opsize)
1369 {
1370 	NoP(fs);
1371 	return(mapset(&state.vpath, arg, argsize, op, opsize));
1372 }
1373 
1374 /*
1375  * set state.shell from s
1376  */
1377 
1378 static int
setshell(register const char * s)1379 setshell(register const char* s)
1380 {
1381 	if (ACCESS(s, 1))
1382 	{
1383 		message((-1, "%s: cannot access SHELL", s));
1384 		return(-1);
1385 	}
1386 	strncpy(state.shell, s, PATH_MAX);
1387 	return(0);
1388 }
1389 
1390 /*
1391  * static data initialization
1392  */
1393 
1394 #define FSINIT(n,g,s,f,k)	{FS_INTERNAL|f,0,0,sizeof(n)-1,g,s,k,0,~0,0,n}
1395 
1396 State_t	state =
1397 {
1398 	id + 10,			/* id			*/
1399 	IDNAME,				/* cmd			*/
1400 	".",				/* dot			*/
1401 	"",				/* null			*/
1402 	"1",				/* one			*/
1403 	"/bin/sh",			/* binsh		*/
1404 	var_3d,				/* env3d		*/
1405 	var_pwd,			/* envpwd		*/
1406 	var_shell,			/* envshell		*/
1407 	var_view,			/* envview		*/
1408 	{
1409 	FSINIT("null",	0,		0,		FS_LICENSED,
1410 		HASHKEY4('n','u','l','l')),
1411 	FSINIT("option",get_option,	set_option,	FS_FORK|FS_LICENSED,
1412 		HASHKEY6('o','p','t','i','o','n')),
1413 	FSINIT("view",	get_view,	set_view,	0,
1414 		HASHKEY4('v','i','e','w')),
1415 	FSINIT("pwd",	get_pwd,	set_pwd,	FS_LICENSED,
1416 		HASHKEY3('p','w','d')),
1417 	FSINIT("fs",	get_fs,		set_fs,		0,
1418 		HASHKEY2('f','s')),
1419 	FSINIT("map",	get_map,	set_map,	0,
1420 		HASHKEY3('m','a','p')),
1421 	FSINIT("safe",	get_safe,	set_safe,	0,
1422 		HASHKEY4('s','a','f','e')),
1423 #if FS
1424 	FSINIT("fd",	0,		0,		FS_FS|FS_NAME,
1425 		HASHKEY2('f','d')),
1426 #endif
1427 	FSINIT("intercept",	get_intercept,	set_intercept,	FS_RAW,
1428 		HASHKEY6('i','n','t','e','r','c')),
1429 
1430 	/* NOTE: add internal mounts here */
1431 
1432 #if VCS && defined(VCS_FS)
1433 	VCS_FS,
1434 #endif
1435 	},
1436 	"default",			/* default instance name*/
1437 	".../...",			/* opaque		*/
1438 	TABSIZE,			/* limit		*/
1439 };
1440 
1441 /*
1442  * note external control interrupt
1443  */
1444 
1445 static void
note(int sig)1446 note(int sig)
1447 {
1448 	state.control.note++;
1449 	signal(sig, note);
1450 }
1451 
1452 /*
1453  * handle external control interrupt
1454  */
1455 
1456 void
control(void)1457 control(void)
1458 {
1459 	char*	s;
1460 	int	fd;
1461 	ssize_t	n;
1462 	char	buf[PATH_MAX];
1463 
1464 	if (state.control.note)
1465 	{
1466 		message((-2, "external control interrupt"));
1467 		if (s = state.control.path) n = state.control.pathsize;
1468 		else
1469 		{
1470 			s = "/tmp/3d";
1471 			n = 0;
1472 		}
1473 		if (!n) n = strlen(s);
1474 		sfsprintf(buf, sizeof(buf), "%-*s#%d", n, s, state.pid);
1475 		if ((fd = OPEN(buf, O_RDONLY, 0)) >= 0)
1476 		{
1477 			if ((n = READ(fd, buf, sizeof(buf) - 1)) > 0)
1478 			{
1479 				buf[n] = 0;
1480 				mapinit(buf, 0);
1481 			}
1482 			CLOSE(fd);
1483 			if ((fd = OPEN(buf, O_RDWR|O_TRUNC, 0)) >= 0)
1484 				CLOSE(fd);
1485 		}
1486 		state.control.note = 0;
1487 	}
1488 }
1489 
1490 /*
1491  * push system call intercept
1492  */
1493 
1494 int
intercept(Intercept_f call,unsigned long mask)1495 intercept(Intercept_f call, unsigned long mask)
1496 {
1497 	register int	i;
1498 
1499 	for (i = 0;; i++)
1500 		if (i >= state.trap.size)
1501 		{
1502 			if (i >= elementsof(state.trap.intercept))
1503 				return(-1);
1504 			state.trap.size++;
1505 			break;
1506 		}
1507 		else if (state.trap.intercept[i].call == call)
1508 			break;
1509 	state.trap.intercept[i].call = call;
1510 	state.trap.intercept[i].mask = mask;
1511 	return(0);
1512 }
1513 
1514 /*
1515  * 3d initialization
1516  */
1517 
1518 #define env_2d		(1<<0)
1519 #define env_3d		(1<<1)
1520 #define env_cmd		(1<<2)
1521 #define env_path	(1<<3)
1522 #define env_pwd		(1<<4)
1523 #define env_shell	(1<<5)
1524 #define env_view	(1<<6)
1525 #define env_must	(env_2d|env_3d|env_cmd|env_path|env_pwd|env_shell|env_view)
1526 #define env_home	(1<<7)
1527 
1528 #define var_cmd		"_="
1529 #define var_disable	"_3D_DISABLE_="
1530 #define var_home	"HOME="
1531 #define var_path	"PATH="
1532 
1533 int
init(int force,const char * opt,int opsize)1534 init(int force, const char* opt, int opsize)
1535 {
1536 	register char**	ep = environ;
1537 	register char*	cp;
1538 	register int	i;
1539 	Fs_t*		fs;
1540 	char*		home = 0;
1541 	Handler_t	handler;
1542 	int		n;
1543 	int		oerrno;
1544 
1545 	/*
1546 	 * initialize the 3d state
1547 	 */
1548 
1549 	if (!force && state.pid) return(0);
1550 	oerrno = errno;
1551 #if DEBUG
1552 	error_info.id = state.cmd;
1553 #endif
1554 #if defined(SIGIO) || defined(SIGPWR)
1555 #if defined(SIGIO)
1556 	n = SIGIO;
1557 #else
1558 	n = SIGPWR;
1559 #endif
1560 	if ((handler = signal(n, note)) != SIG_DFL)
1561 		signal(n, handler);
1562 #endif
1563 	state.pid = getpid();
1564 	state.uid = geteuid();
1565 	state.gid = getegid();
1566 	state.pwd = state.pwdbuf;
1567 	*state.pwd = '.';
1568 	state.pwdsize = 1;
1569 	state.shell = state.envshell + sizeof(var_shell) - 1;
1570 	callinit();
1571 	state.fs[FS_safe].flags |= FS_INIT;
1572 	for (fs = state.fs; fs < state.fs + elementsof(state.fs) && fs->specialsize; fs++)
1573 	{
1574 		fs->flags |= FS_ON;
1575 		if (fs->set) (*fs->set)(fs, state.null, 0, "init", 4);
1576 	}
1577 
1578 	/*
1579 	 * extract the 3d tables from table.fd or the top of the environment
1580 	 */
1581 
1582 	cp = *(state.env = ep);
1583 	i = 0;
1584 	if ((n = peek(TABLE_FD, state.table.buf, sizeof(state.table.buf) - 1)) > 0 && !mapinit(state.table.buf, 1))
1585 	{
1586 		state.table.size = n + 1;
1587 		state.table.fd = TABLE_FD;
1588 		reserve(&state.table.fd);
1589 		i |= env_view|env_3d;
1590 	}
1591 	else
1592 	{
1593 		state.table.version = TABLE_VERSION;
1594 		if (cp && strneq(cp, state.env3d, sizeof(var_3d) - 1))
1595 		{
1596 			mapinit(cp + sizeof(var_3d) - 1, 1);
1597 			ep++;
1598 			environ++;
1599 			i |= env_view|env_3d;
1600 		}
1601 	}
1602 	if (_3d_2d) n = strlen(_3d_2d);
1603 	else i |= env_2d;
1604 
1605 	/*
1606 	 * look for remaining var_* not in env_* mask i
1607 	 */
1608 
1609 	while (cp = *ep)
1610 	{
1611 		if (strneq(cp, var_disable, sizeof(var_disable) - 1))
1612 		{
1613 			state.pid = 0;
1614 			errno = oerrno;
1615 			return(0);
1616 		}
1617 		else if (!(i & env_cmd) && strneq(cp, var_cmd, sizeof(var_cmd) - 1))
1618 		{
1619 			state.cmd = cp + sizeof(var_cmd) - 1;
1620 			if ((i |= env_cmd) == env_must) break;
1621 		}
1622 		else if (!(i & env_home) && strneq(cp, var_home, sizeof(var_home) - 1))
1623 		{
1624 			home = cp + sizeof(var_home) - 1;
1625 			if ((i |= env_home) == env_must) break;
1626 		}
1627 		else if (!(i & env_path) && strneq(cp, var_path, sizeof(var_path) - 1))
1628 		{
1629 			state.envpath = cp + sizeof(var_path) - 1;
1630 			if ((i |= env_path) == env_must) break;
1631 		}
1632 		else if (!(i & env_pwd) && strneq(cp, state.envpwd, sizeof(var_pwd) - 1))
1633 		{
1634 			if (geteuid())
1635 				*ep = state.envpwd;
1636 			cp += sizeof(var_pwd) - 1;
1637 			if (!setpwd(cp) && ((i |= env_pwd) == env_must)) break;
1638 		}
1639 		else if (!(i & env_shell) && strneq(cp, state.envshell, sizeof(var_shell) - 1))
1640 		{
1641 			*ep = state.envshell;
1642 			cp += sizeof(var_shell) - 1;
1643 			if (!setshell(cp) && ((i |= env_shell) == env_must)) break;
1644 		}
1645 		else if (!(i & env_view) && strneq(cp, state.envview, sizeof(var_view) - 1))
1646 		{
1647 			char*	mp;
1648 			char*	zp;
1649 
1650 			cp += sizeof(var_view) - 1;
1651 			if (mp = strchr(cp, ':')) do
1652 			{
1653 				if (!(zp = strchr(++mp, ':'))) zp = mp + strlen(mp);
1654 				mapset(&state.vpath, cp, mp - cp - 1, mp, zp - mp);
1655 				cp = mp;
1656 			} while (*(mp = zp));
1657 			if ((i |= env_view) == env_must) break;
1658 		}
1659 		else if (!(i & env_3d) && strneq(cp, state.env3d, sizeof(var_3d) - 1))
1660 		{
1661 			mapinit(cp + sizeof(var_3d) - 1, 1);
1662 			if ((i |= env_3d) == env_must) break;
1663 		}
1664 		else if (!(i & env_2d) && strneq(cp, _3d_2d, n) && cp[n] == '=')
1665 		{
1666 			if ((i |= env_2d) == env_must) break;
1667 		}
1668 		ep++;
1669 	}
1670 	if (!(i & env_2d)) state.in_2d = 1;
1671 	if (!(i & env_pwd) && *state.pwd != '/' && setpwd("/") && (!home || setpwd(home)))
1672 	{
1673 
1674 		n = state.in_2d;
1675 		state.in_2d = 2;
1676 		if (!getcwd(state.path.name, sizeof(state.path.name)) || setpwd(state.path.name))
1677 		{
1678 			state.pwd = 0;
1679 			if (!n)
1680 			{
1681 				static char	msg[] = "3d: invalid PWD -- falling back to 2d\n";
1682 
1683 				write(2, msg, sizeof(msg) - 1);
1684 			}
1685 		}
1686 		else state.in_2d = n;
1687 	}
1688 	if (!(i & env_shell)) strcpy(state.shell, state.binsh);
1689 	if (state.table.fd <= 0 && mapdump(NiL, NiL, MAP_INIT) < sizeof(state.table.buf))
1690 	{
1691 		n = mapdump(NiL, state.table.buf, MAP_INIT);
1692 		keep(state.table.buf, n, 0);
1693 	}
1694 	if (state.table.fd <= 0 && (state.channel.fd = open("/dev/null", O_RDONLY)) >= 0)
1695 		reserve(&state.channel.fd);
1696 	if (state.fs[FS_safe].flags & FS_BOUND)
1697 	{
1698 		state.safe = &state.fs[FS_safe];
1699 		if (!state.pwd || !pathreal(state.pwd, P_PATHONLY, NiL))
1700 		{
1701 #if DEBUG
1702 			error(4, ". is not safe");
1703 #else
1704 			static char	msg[] = "3d: . is not safe\n";
1705 
1706 			write(2, msg, sizeof(msg) - 1);
1707 			_exit(2);
1708 #endif
1709 		}
1710 	}
1711 	state.fs[FS_safe].flags &= ~FS_INIT;
1712 	errno = oerrno;
1713 	return(0);
1714 }
1715