1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1989-2011 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 mounted fs support
26  *
27  * NOTE: be vewwwy careful with errno
28  */
29 
30 #include "3d.h"
31 
32 #if FS
33 
34 #include <cs.h>
35 
36 #define DEVFD		"/dev/fd"
37 #define MNTFD		"path\n"
38 #define MNTNAM		"/dev/tcp/*"
39 
40 /*
41  * initialize mount channel and return fs fd for mp
42  */
43 
44 int
fschannel(register Mount_t * mp)45 fschannel(register Mount_t* mp)
46 {
47 	register Fs_t*	fs;
48 	register int	fd;
49 
50 	if (mp->channel == -1)
51 		return -1;
52 	fs = mp->fs;
53 	if (mp->channel && fs->fd)
54 		return fs->fd;
55 	if ((fd = fsfd(fs)) <= 0)
56 		return -1;
57 	if (mp->channel)
58 		return fs->fd;
59 	mp->channel = 1;
60 	mp->flags |= MOUNT_PRIMARY;
61 	return fd;
62 }
63 
64 /*
65  * generate phony monitor open for fd inherited from parent
66  */
67 
68 static void
fsphony(register Mount_t * mp,register File_t * fp,int fd)69 fsphony(register Mount_t* mp, register File_t* fp, int fd)
70 {
71 	register ssize_t	n;
72 	int			nd;
73 	Mount_t*		np;
74 	char			buf[64];
75 
76 	state.kernel++;
77 	fp->flags |= FILE_LOCK;
78 	*buf = 0;
79 	if (!(np = getmount(MNTNAM, NiL)) || (nd = fsfd(np->fs)) < 0)
80 		sfsprintf(buf, sizeof(buf), "%s/%d", DEVFD, fd);
81 	else if (!(np->fs->flags & FS_LOCK) && WRITE(nd, MNTFD, sizeof(MNTFD) - 1) == sizeof(MNTFD) - 1)
82 	{
83 		np->fs->flags |= FS_LOCK;
84 		if (!cssend(&cs, nd, &fd, 1) && (n = READ(nd, buf, sizeof(buf))) > 1)
85 			buf[n - 1] = 0;
86 		np->fs->flags &= ~FS_LOCK;
87 	}
88 	state.kernel--;
89 	if (*buf)
90 	{
91 		fp->open |= (1<<(mp-state.mount));
92 		message((-3, "fs: phony: %s", buf));
93 		fscall(mp, MSG_open, fd, buf, fp->oflag, 0, 0);
94 	}
95 	fp->flags &= ~FILE_LOCK;
96 }
97 
98 /*
99  * return real fd for up under mount mp to be accessed by call
100  * return value stuck in tail portion of state.path.name
101  */
102 
103 char*
fsreal(register Mount_t * mp,long call,const char * up)104 fsreal(register Mount_t* mp, long call, const char* up)
105 {
106 	char		dev[2 * PATH_MAX + 3];
107 	register char*	s = dev;
108 	register Fs_t*	fs = mp->fs;
109 	register int	n;
110 	register int	fd;
111 	int		lz;
112 	int		pz;
113 
114 	if (!(lz = mp->logicalsize))
115 		lz = strlen(mp->logical);
116 	if (!(pz = mp->physicalsize) && mp->physical)
117 		pz = strlen(mp->physical);
118 	n = sfsprintf(s, sizeof(dev) - 1, "%s %-*s %s%s%-*s pwd=%s%s%s", msgname(call), lz, mp->logical, up, pz ? " physical=" : "", pz, mp->physical ? mp->physical : "", state.pwd, mp->fs->attr, mp->attr);
119 	s[n] = 0;
120 	message((-2, "fs: %s: real: service=%-*s request=\"%s\"", fs->special, fs->servicesize, fs->service, s));
121 	s[n++] = '\n';
122 	if ((fd = fsfd(fs)) <= 0)
123 		return 0;
124 	s = state.path.name + PATH_MAX;
125 	if (write(fd, dev, n) != n || (n = csread(&cs, fd, s, PATH_MAX, CS_LINE)) <= 0)
126 	{
127 		fsdrop(fs, 0);
128 		return 0;
129 	}
130 	if (n <= 1)
131 		return 0;
132 	s[n - 1] = 0;
133 	if (s[0] >= 'A' && s[0] <= 'Z' && s[1] == ' ')
134 	{
135 		message((-2, "fs: %s: real: %s", fs->special, s + 2));
136 		return 0;
137 	}
138 	message((-2, "fs: %s: real: path=%s", fs->special, s));
139 	return s;
140 }
141 
142 /*
143  * do fs call
144  * -1 returned if not mounted
145  * 0 returned if mounted with state.ret call return value
146  * state.path.monitor set if monitor or name service mount
147  */
148 
149 int
fscall(register Mount_t * mp,long call,int ret,...)150 fscall(register Mount_t* mp, long call, int ret, ...)
151 {
152 	register Fs_t*		fs;
153 	register int		retry;
154 	register int		tries;
155 	const char*		up;
156 	const char*		sp;
157 	int			cd;
158 	int			fd;
159 	int			oerrno;
160 	int			m;
161 	long			n;
162 	File_t*			fp;
163 	Handler_t		handler;
164 	Msg_return_t*		rp;
165 	Msg_return_t		rv;
166 	void**			xp;
167 	void*			xv[2];
168 	int*			ip;
169 	Msg_file_t		iv[2];
170 	va_list			ap;
171 
172 	oerrno = errno;
173 	initialize();
174 	state.ret = -1;
175 	if (state.in_2d)
176 		return -1;
177 	up = 0;
178 	/* proto workaround */
179 	va_start(ap, ret); va_end(ap);
180 	if (!mp)
181 	{
182 		state.path.monitor = 0;
183 		state.path.mount = 0;
184 		sp = 0;
185 		va_start(ap, ret);
186 		switch (MSG_ARG(call, 1))
187 		{
188 		case MSG_ARG_file:
189 			fd = va_arg(ap, int);
190 			if (fd < 0 || fd >= elementsof(state.file))
191 				goto nope;
192 			if (!state.kernel && (mp = state.global) && !((fp = &state.file[fd])->flags & FILE_LOCK)) do
193 			{
194 				if (fssys(mp, MSG_open) && ((fp->flags & FILE_OPEN) || !fileinit(fd, NiL, NiL, 0)))
195 				{
196 					fs = mp->fs;
197 					if ((!(fs->flags & FS_REGULAR) || (fp->flags & FILE_REGULAR)) && (!(fs->flags & FS_WRITE) || (fp->flags & FILE_WRITE)) && ((mp->flags & MOUNT_PRIMARY) || fd > 2) && !(fp->open & (1<<(mp-state.mount))))
198 						fsphony(mp, fp, fd);
199 				}
200 			} while (mp = mp->global);
201 			mp = fgetmount(fd);
202 			n = FS_ERROR|FS_INIT|FS_LOCK|FS_NAME|FS_ON;
203 			break;
204 		case MSG_ARG_string:
205 			sp = va_arg(ap, const char*);
206 			if (sp != state.path.name)
207 				sp = pathreal(sp, P_PATHONLY|P_ABSOLUTE, NiL);
208 			if (sp) mp = getmount(sp, &up);
209 			n = FS_ERROR|FS_INIT|FS_LOCK|FS_ON;
210 			break;
211 		}
212 		va_end(ap);
213 		if (!mp || ((fs = mp->fs)->flags & n) != FS_ON)
214 			goto nope;
215 		if (fs->flags & FS_MONITOR)
216 		{
217 			if (!state.kernel && (fs->call & MSG_MASK(call)))
218 			{
219 				if (sp && fs->match)
220 				{
221 					if (fs->matchsize)
222 					{
223 						cd = fs->match[fs->matchsize];
224 						fs->match[fs->matchsize] = 0;
225 					}
226 					if (strmatch(sp, fs->match))
227 						state.path.monitor = mp;
228 					if (fs->matchsize)
229 						fs->match[fs->matchsize] = cd;
230 				}
231 				else state.path.monitor = mp;
232 			}
233 			goto nope;
234 		}
235 	}
236 	else if (((fs = mp->fs)->flags & (FS_ERROR|FS_INIT|FS_LOCK|FS_ON)) != FS_ON)
237 		goto nope;
238 	if (!(fs->call & MSG_MASK(call)))
239 		goto nope;
240 	if (fs->flags & FS_MONITOR)
241 	{
242 		if (state.kernel)
243 			goto nope;
244 		if (MSG_ARG(call, 1) == MSG_ARG_file)
245 		{
246 			va_start(ap, ret);
247 			fd = va_arg(ap, int);
248 			va_end(ap);
249 			if (fd < 0 || fd >= elementsof(state.file))
250 				goto nope;
251 			fp = &state.file[fd];
252 			if (fp->flags & FILE_LOCK)
253 				goto nope;
254 			if (!(fp->flags & FILE_OPEN) && fileinit(fd, NiL, NiL, 0))
255 				goto nope;
256 			if ((fs->flags & FS_REGULAR) && !(fp->flags & FILE_REGULAR) || (fs->flags & FS_WRITE) && !(fp->flags & FILE_WRITE))
257 				goto nope;
258 			if (fssys(mp, MSG_open) && ((mp->flags & MOUNT_PRIMARY) || fd > 2) && !(fp->open & (1<<(mp-state.mount))))
259 				fsphony(mp, fp, fd);
260 		}
261 		else if (call == MSG_open)
262 		{
263 			if (fsmount(mp) < 0)
264 				goto nope;
265 			if (fs->flags & FS_WRITE)
266 			{
267 				va_start(ap, ret);
268 				va_arg(ap, const char*);
269 				n = va_arg(ap, int);
270 				va_end(ap);
271 				if ((n & O_ACCMODE) == O_RDONLY)
272 					goto nope;
273 			}
274 		}
275 		if (MSG_ARG(call, 0) == MSG_ARG_file)
276 		{
277 			fp = &state.file[ret];
278 			fp->open |= (1<<(mp-state.mount));
279 			rv.file = fp->id;
280 		}
281 		else rv.number = ret;
282 		rp = &rv;
283 	}
284 	else if (call == MSG_open)
285 	{
286 		int	oflag;
287 		int	mode;
288 		int	level;
289 
290 		fs->flags |= FS_LOCK;
291 		va_start(ap, ret);
292 		sp = va_arg(ap, const char*);
293 		oflag = va_arg(ap, int);
294 		mode = va_arg(ap, int);
295 		level = va_arg(ap, int);
296 		va_end(ap);
297 		message((-3, "fs: %s: open: path=%s", fs->special, sp));
298 		if (fs == &state.fs[FS_fd])
299 		{
300 			const char*	ep;
301 
302 			if ((fd = OPEN(sp, oflag, mode)) >= 0)
303 			{
304 				state.ret = fd;
305 				goto unlock;
306 			}
307 			fd = strtol(up, (char**)&ep, 0);
308 			if (*ep)
309 			{
310 				oerrno = ENOENT;
311 				goto unlock;
312 			}
313 			if ((n = FCNTL(fd, F_GETFL, 0)) < 0)
314 			{
315 				oerrno = errno;
316 				goto unlock;
317 			}
318 			n &= O_ACCMODE;
319 			oflag &= O_ACCMODE;
320 			if (n == O_RDONLY && oflag == O_WRONLY || n == O_WRONLY && oflag == O_RDONLY)
321 			{
322 				oerrno = EPERM;
323 				goto unlock;
324 			}
325 			if ((state.ret = FCNTL(fd, F_DUPFD, 0)) < 0)
326 				oerrno = errno;
327 		}
328 		else if ((sp = (const char*)fsreal(mp, MSG_open, up)) && (fd = fsfd(mp->fs)) > 0)
329 		{
330 			/*
331 			 * /#<id>/[#]<path> for active fd's
332 			 * /#<id>\n written back to initialize
333 			 */
334 
335 			if (sp[0] == '/' && sp[1] == '#')
336 			{
337 				up = sp;
338 				if (!(sp = strchr(sp + 2, '/')))
339 				{
340 					sp = up;
341 					up = 0;
342 				}
343 				else
344 				{
345 					m = sp - up;
346 					if (sp[1] == '#')
347 						sp += 2;
348 				}
349 			}
350 			else up = 0;
351 			if (streq(sp, DEVFD))
352 			{
353 				cd = -1;
354 				while (csrecv(&cs, fd, NiL, &cd, 1) != 1 && errno == EINTR);
355 				fd = cd;
356 			}
357 			else if ((fd = fs3d_open(sp, oflag, mode)) == -1)
358 				oerrno = errno;
359 			if (fd >= 0 && up)
360 			{
361 				*((char*)up + m++) = '\n';
362 				if (write(fd, up, m) != m)
363 					fd = -1;
364 				else
365 				{
366 					if (fd > state.cache)
367 						state.cache = fd;
368 					fp = &state.file[fd];
369 					fp->open = ~0;
370 					fp->flags = FILE_OPEN;
371 					fp->mount = mp;
372 				}
373 			}
374 			state.ret = fd;
375 		}
376 		goto unlock;
377 	}
378 	else
379 		rp = 0;
380 	if (fs->flags & FS_NAME)
381 	{
382 		if (up && fs != &state.fs[FS_fd])
383 		{
384 			state.path.monitor = mp;
385 			state.path.mount = (char*)up;
386 		}
387 		goto nope;
388 	}
389 	if (MSG_MASK(call) & (MSG_MASK(MSG_close)|MSG_MASK(MSG_dup)))
390 		goto nope;
391 	message((-3, "fs: %s: %s: call", fs->special, msgname(call)));
392 	fs->flags |= FS_LOCK;
393 	if (fs->terse & MSG_MASK(call))
394 	{
395 		tries = MSG_ARG_CALL;
396 		for (;;)
397 		{
398 			tries += MSG_ARG_TYPE;
399 			switch ((call >> tries) & ((1 << MSG_ARG_TYPE) - 1))
400 			{
401 			case 0:
402 				break;
403 			case MSG_ARG_output:
404 				if (!(fs->flags & FS_MONITOR)) break;
405 				/*FALLTHROUGH*/
406 			case MSG_ARG_input:
407 			case MSG_ARG_vector:
408 				call = (call & ~(((1 << MSG_ARG_TYPE) - 1) << tries)) | (MSG_ARG_number << tries);
409 				continue;
410 			default:
411 				continue;
412 			}
413 			break;
414 		}
415 	}
416 	if (fs->flags & FS_ACTIVE)
417 	{
418 		if (!(fs->flags & FS_MONITOR))
419 			call |= MSG_RETURN;
420 		else if (fs->ack & MSG_MASK(call))
421 			call |= (fs->flags & FS_INTERACTIVE) ? MSG_RETURN : MSG_ACK;
422 		retry = fs->retry;
423 	}
424 	else retry = 0;
425 	if ((fs->flags & FS_FLUSH) && (call |= MSG_FLUSH) || retry)
426 		handler = signal(SIGPIPE, SIG_IGN);
427 	tries = 1;
428 	for (;;)
429 	{
430 		if ((cd = fsmount(mp)) < 0)
431 		{
432 			message((-2, "fs: %s: %s: connect error on try %d", fs->special, msgname(call), tries));
433 			goto unlock;
434 		}
435 		va_start(ap, ret);
436 		xp = xv;
437 		switch (MSG_ARG(call, 1))
438 		{
439 		case MSG_ARG_file:
440 			fd = va_arg(ap, int);
441 			if (!(fs->flags & FS_MONITOR))
442 				cd = fd;
443 			*xp++ = (void*)&state.file[fd].id;
444 			break;
445 		case MSG_ARG_string:
446 			sp = va_arg(ap, const char*);
447 			if (MSG_VAR(call) == MSG_VAR_FILE)
448 			{
449 				if (!(fs->flags & FS_MONITOR))
450 					sp = up;
451 				else if (sp != state.path.name && !(sp = (const char*)pathreal(sp, P_PATHONLY|P_ABSOLUTE, NiL)))
452 					goto unlock;
453 			}
454 			*xp++ = (void*)sp;
455 			break;
456 		case MSG_ARG_output:
457 			if (call == MSG_pipe)
458 			{
459 				ip = va_arg(ap, int*);
460 				for (n = 0; n < 2; n++)
461 				{
462 					fp = &state.file[ip[n]];
463 					if (!(fp->flags & FILE_OPEN))
464 						fileinit(ip[n], NiL, NiL, 0);
465 					fp->open |= (1<<(mp-state.mount));
466 					iv[n] = fp->id;
467 					*xp++ = (void*)iv;
468 				}
469 			}
470 			break;
471 		default:
472 			xp = 0;
473 			break;
474 		}
475 		if (xp)
476 		{
477 			*xp = 0;
478 			xp = xv;
479 		}
480 		n = msgvcall(cd, MSG_CHANNEL(state.pid, mp->channel), call, rp, xp, ap);
481 		va_end(ap);
482 		if (n != -1)
483 			break;
484 		if (errno != EMSGIO)
485 		{
486 			if (!(fs->flags & FS_MONITOR))
487 				oerrno = errno;
488 			break;
489 		}
490 		message((-2, "fs: %s: %s: error on try %d", fs->special, msgname(call), tries));
491 		if (tries++ > retry)
492 			break;
493 		fsdrop(fs, 0);
494 	}
495 	if ((fs->flags & FS_FLUSH) || retry)
496 		signal(SIGPIPE, handler);
497 	if (fs->flags & FS_ACTIVE)
498 	{
499 		if ((state.ret = n) > 0 && (fs->flags & (FS_ACTIVE|FS_INTERACTIVE|FS_MONITOR)) == (FS_ACTIVE|FS_INTERACTIVE|FS_MONITOR) && (fs->ack & MSG_MASK(call)))
500 		{
501 			char	buf[TABLE_MAX];
502 
503 			if ((n = READ(cd, buf, sizeof(buf))) > 1)
504 			{
505 				buf[n - 1] = 0;
506 				mapinit(buf, 0);
507 			}
508 			else message((-3, "fs: %s: %s: interactive ack failed", fs->special, msgname(call)));
509 		}
510 	}
511 	else if (!(fs->flags & FS_MONITOR))
512 	{
513 		oerrno = errno = ENODEV;
514 		message((-3, "fs: %s: %s: return: passive fs", fs->special, msgname(call)));
515 	}
516 	else if (fs->ack & MSG_MASK(call))
517 	{
518 		oerrno = errno = ENODEV;
519 		message((-3, "fs: %s: %s: ack: passive fs", fs->special, msgname(call)));
520 	}
521 	else state.ret = 0;
522  unlock:
523 	fs->flags &= ~FS_LOCK;
524 	errno = oerrno;
525 	return 0;
526  nope:
527 	errno = oerrno;
528 	return -1;
529 }
530 
531 #endif
532 
533 /*
534  * initialize mounted fs and return device service fd
535  */
536 
537 int
fsinit(register Fs_t * fs,int fd)538 fsinit(register Fs_t* fs, int fd)
539 {
540 	int	n;
541 	int	oerrno;
542 
543 	if (fd < 0 && (fs->flags & (FS_BOUND|FS_ERROR|FS_INIT|FS_ON)) != (FS_BOUND|FS_ON) || state.kernel && (fs->flags & FS_GLOBAL))
544 		return -1;
545 	oerrno = errno;
546 	fs->flags |= FS_INIT;
547 	if ((fs->flags & (FS_ON|FS_OPEN)) != (FS_ON|FS_OPEN))
548 	{
549 		state.kernel++;
550 		if ((fs->fd = fd) < 0)
551 		{
552 			char*	svc;
553 			char	buf[PATH_MAX];
554 
555 			if (n = fs->servicesize)
556 			{
557 				if (n >= sizeof(buf)) n = sizeof(buf) - 1;
558 				svc = (char*)memcpy(buf, fs->service, n);
559 				svc[n] = 0;
560 			}
561 			else svc = fs->service;
562 			message((-3, "fs: %s: init#1: service=%s", fs->special, svc));
563 #if FS
564 			fs->fd = cslocal(&cs, svc);
565 			message((-3, "fs: %s: init#2: service=%s cslocal=%d", fs->special, svc, fs->fd));
566 			if (fs->fd >= 0)
567 			{
568 				if (fs->flags & FS_RECEIVE)
569 				{
570 					n = csrecv(&cs, fs->fd, NiL, &fd, 1);
571 					CLOSE(fs->fd);
572 					fs->fd = n == 1 ? fd : -1;
573 				}
574 			}
575 			else if (errno == ENOENT)
576 #endif
577 			fs->fd = fs3d_open(svc, O_CREAT|O_RDWR|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
578 		}
579 		if (fs->fd < 0 || FSTAT(fs->fd, &fs->st))
580 		{
581 			fs->fd = -1;
582 			fs->flags |= FS_ERROR;
583 		}
584 		else
585 		{
586 			if (fs->flags & FS_CLOSE)
587 				FCNTL(fs->fd, F_SETFD, FD_CLOEXEC);
588 			if (S_ISREG(fs->st.st_mode))
589 				fs->flags &= ~FS_ACTIVE;
590 			fs->flags |= FS_ON|FS_OPEN;
591 			reserve(&fs->fd);
592 			if (fd < 0)
593 				message((-3, "fs: %s: init#3: service=%-*s fd=%d cache=%d", fs->special, fs->servicesize ? fs->servicesize : strlen(fs->service), fs->service, fs->fd, state.cache));
594 		}
595 		state.kernel--;
596 	}
597 	fs->flags &= ~FS_INIT;
598 	errno = oerrno;
599 	return fs->fd;
600 }
601 
602 /*
603  * drop internal 3d mount
604  * if clear!=0 then path binding also cleared
605  */
606 
607 void
fsdrop(register Fs_t * fs,int clear)608 fsdrop(register Fs_t* fs, int clear)
609 {
610 	int	oerrno;
611 
612 	state.kernel++;
613 	oerrno = errno;
614 	message((-3, "fs: %s: drop:%s", fs->special, clear ? " clear" : state.null));
615 	if (fs->flags & FS_OPEN)
616 	{
617 		fs->flags &= ~FS_OPEN;
618 		cancel(&fs->fd);
619 	}
620 	fs->flags &= ~FS_ERROR;
621 	if (clear)
622 	{
623 #if FS
624 		if (fs->flags & FS_FS)
625 		{
626 			register int		n;
627 			register int		m;
628 			register Mount_t*	mp;
629 
630 			for (n = 0; n <= state.cache; n++)
631 				if ((mp = state.file[n].mount) && mp->fs == fs)
632 					state.file[n].mount = 0;
633 			for (n = m = 0; n < state.vmount.size; n++)
634 			{
635 				if (((Mount_t*)state.vmount.table[n].val)->fs != fs)
636 				{
637 					if (n != m)
638 						state.vmount.table[m] = state.vmount.table[n];
639 					m++;
640 				}
641 				else if (state.vmount.table[n].valsize & T_ALLOCATE)
642 					free(state.vmount.table[n].key);
643 			}
644 			state.vmount.size = m;
645 			for (n = 0; n < elementsof(state.mount); n++)
646 				if (state.mount[n].fs == fs)
647 				{
648 					state.mount[n].fs = 0;
649 					if (state.mount[n].physical && !state.mount[n].physicalsize)
650 					{
651 						free(state.mount[n].physical);
652 						state.mount[n].physical = 0;
653 					}
654 				}
655 		}
656 #endif
657 		if (fs->flags & FS_INTERNAL) fs->flags &= ~(FS_BOUND|FS_ON);
658 		else
659 		{
660 			fs->flags = 0;
661 			if (fs->service && !fs->servicesize)
662 			{
663 				free(fs->service);
664 				fs->service = 0;
665 			}
666 		}
667 	}
668 	errno = oerrno;
669 	state.kernel--;
670 }
671