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 #include "3d.h"
25 
26 #ifndef MAXSYMLINKS
27 #define MAXSYMLINKS	20
28 #endif
29 
30 /*
31  * if <sp> is a hard link to state.opaque in the same directory then the file
32  * is opaque and the errno should be ENOENT
33  */
34 
35 static int
checkopaque(register char * path,struct stat * st)36 checkopaque(register char* path, struct stat* st)
37 {
38 	register char*	basesp;
39 	int		oerrno = errno;
40 	struct stat	statb;
41 	char		savebuf[sizeof(state.opaque)];
42 	register int	r = 0;
43 
44 	if (st->st_nlink <= 1)
45 		return 0;
46 	if (st->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))
47 		return 0;
48 
49 	/*
50 	 * change the basename to state.opaque
51 	 */
52 
53 	if (basesp = strrchr(path, '/'))
54 		basesp++;
55 	else
56 		basesp = path;
57 	memcpy(savebuf, basesp, sizeof(state.opaque));
58 	strcpy(basesp, state.opaque);
59 	if (LSTAT(path, &statb))
60 	{
61 		/*
62 		 * for backward compatability
63 		 */
64 
65 		basesp[3] = 0;
66 		if (LSTAT(path, &statb))
67 			goto not_opaque;
68 	}
69 	if (statb.st_ino == st->st_ino && statb.st_dev == st->st_dev)
70 	{
71 		errno = ENOENT;
72 		r = statb.st_ino;
73 	}
74 	else
75 		errno = oerrno;
76  not_opaque:
77 	memcpy(basesp, savebuf, sizeof(state.opaque));
78 	return r;
79 }
80 
81 /*
82  * return real path name for path
83  */
84 
85 #if DEBUG
86 
87 static char*	_pathreal(const char*, int, struct stat*);
88 
89 char*
pathreal(const char * apath,register int type,struct stat * st)90 pathreal(const char* apath, register int type, struct stat* st)
91 {
92 	char*	path = (char*)apath;
93 
94 	initialize();
95 	message((-5, "pathreal: ++ %s type=|%s%s%s%s%s%s%s%s%s%s%s", path
96 		, (type & P_ABSOLUTE) ? "ABSOLUTE|" : state.null
97 		, (type & P_DOTDOT) ? "DOTDOT|" : state.null
98 		, (type & P_LSTAT) ? "LSTAT|" : state.null
99 		, st ? "MYSTAT|" : state.null
100 		, (type & P_NOOPAQUE) ? "NOOPAQUE|" : state.null
101 		, (type & P_NOSLASH) ? "NOSLASH|" : state.null
102 		, (type & P_PATHONLY) ? "PATHONLY|" : state.null
103 		, (type & P_READLINK) ? "READLINK|" : state.null
104 		, (type & P_SAFE) ? "SAFE|" : state.null
105 		, (type & P_SLASH) ? "SLASH|" : state.null
106 		, (type & P_TOP) ? "TOP|" : state.null
107 		));
108 	path = _pathreal(path, type, st);
109 	message((-5, "pathreal: -- %s level=%d links=%d", path, state.path.level, state.path.nlinks));
110 	return path;
111 }
112 
113 #undef	initialize
114 #define initialize()
115 #undef	pathreal
116 #define pathreal _pathreal
117 static
118 
119 #endif
120 
121 char*
pathreal(const char * apath,register int type,struct stat * st)122 pathreal(const char* apath, register int type, struct stat* st)
123 {
124 	char*			path = (char*)apath;
125 	register char*		sp;
126 	register char*		cp;
127 	register char*		ip;
128 	Table_t*		safe;
129 	int			oerrno = errno;
130 	int			opaqued = 0;
131 	int			len;
132 	int			vir;
133 	int			safesize;
134 	int			safe_dir;
135 	long			visits;
136 	char			buf[PATH_MAX + 1];
137 
138 	static struct stat	stbuf;
139 	static struct stat	tsbuf;
140 
141 	state.path.level = state.path.synthesize = state.path.nlinks = 0;
142 	if (!path)
143 	{
144 		errno = EFAULT;
145 		return 0;
146 	}
147 	initialize();
148 	if (state.in_2d)
149 	{
150 		if (!st || (!state.level || *path == '/') && !LSTAT(path, st))
151 			return path;
152 		if (state.level && streq(path, ".") && !CHDIR(state.pwd))
153 		{
154 			state.level = 0;
155 			return path;
156 		}
157 		return 0;
158 	}
159 #if FS
160 	if (mounted() && (sp = fsreal(state.path.monitor, MSG_stat, state.path.mount)))
161 		apath = (char*)(path = sp);
162 #endif
163 
164 	/*
165 	 * handle null path, . and / separately
166 	 */
167 
168 	if (safe = state.safe ? &state.vsafe : (Table_t*)0)
169 	{
170 		type |= P_ABSOLUTE;
171 		if (!(safesize = state.safe->servicesize))
172 			safesize = strlen(state.safe->service);
173 	}
174 	else
175 		type &= ~P_SAFE;
176  again:
177 	if (!*path)
178 	{
179 		errno = ENOENT;
180 		return 0;
181 	}
182 	cp = sp = path;
183 	state.path.synthesize = state.path.linksize = 0;
184 	if (!st)
185 		st = &stbuf;
186 
187 	/*
188 	 * check if virtual dir has been created by another process
189 	 * only P_PATHONLY|P_TOP calls (usually create or modify link) and
190 	 * references to "." are checked for performance
191 	 */
192 
193 	if (state.level > 0 && state.pwd && ((type & (P_PATHONLY|P_TOP)) && *sp != '/' || *sp == '.' && sp[1] == 0))
194 	{
195 		if (!CHDIR(state.pwd))
196 			state.level = 0;
197 		else if (!(type & (P_PATHONLY|P_TOP)))
198 		{
199 			len = 0;
200 			state.path.level += (state.path.synthesize = state.level);
201 			sp = strcpy(state.path.name, state.pwd);
202 			goto skip;
203 		}
204 	}
205 	if (!state.pwd || sp[1] == 0 && (*sp == '.' || *sp == '/' && !safe))
206 	{
207 		if (st != &stbuf && LSTAT(sp, st))
208 			return 0;
209 		if (*sp == '/' || !state.pwd && (type & P_PATHONLY))
210 			strncpy(state.path.name, sp, PATH_MAX);
211 		else if (!state.pwd)
212 		{
213 			/*
214 			 * treat the current directory as if were empty
215 			 */
216 
217 			errno = ENOENT;
218 			return 0;
219 		}
220 		else
221 			strncpy(state.path.name, state.pwd, PATH_MAX);
222 		errno = oerrno;
223 		return state.path.name;
224 	}
225 
226 	/*
227 	 * put absolute pathname into state.path
228 	 */
229 
230 	safe_dir = 0;
231 	if (*path != '/')
232 	{
233 		strcpy(state.path.name, state.pwd);
234 		sp = state.path.name + state.pwdsize;
235 		*sp++ = '/';
236 		if (safe && state.pwdsize >= safesize && !strncmp(state.pwd, state.safe->service, safesize) && (!state.pwd[safesize] || state.pwd[safesize] == '/'))
237 			safe_dir = safesize;
238 	}
239 	else
240 		sp = state.path.name;
241 	ip = state.path.name + elementsof(state.path.name);
242 	while (sp < ip && (*sp = *cp++))
243 		sp++;
244 	if (type & P_DOTDOT)
245 		strcpy(sp, "/..");
246 	sp = state.path.name;
247 	if (!(ip = pathcanon(sp + safe_dir, sizeof(state.path.name) - safe_dir, 0)))
248 	{
249 		errno = ENOENT;
250 		return 0;
251 	}
252 	if (type & (P_DOTDOT|P_NOSLASH))
253 	{
254 		/*
255 		 * remove trailing slashes
256 		 */
257 
258 		while (*--ip == '/');
259 		*++ip = 0;
260 	}
261 	else if ((type & P_SLASH) && *(ip - 1) != '/')
262 		*ip++ = '/';
263 	if (*(ip - 1) == '/' && ip - sp > 1)
264 	{
265 		/*
266 		 * trailing slash is equivalent to trailing slash-dot
267 		 * this forces the common-sense interpretation
268 		 */
269 #if DEBUG
270 		if (!(state.test & 010))
271 #endif
272 		*ip++ = '.';
273 		*ip = 0;
274 	}
275 	len = ip - sp;
276 
277 	/*
278 	 * try to use relative path
279 	 */
280 
281 	if (!(type & (P_LSTAT|P_READLINK)))
282 	{
283 		for (ip = state.pwd; *ip && *ip == *sp++; ip++);
284 		if (*ip != 0 || *sp && *sp != '/' || state.level < 0)
285 			sp = state.path.name;
286 		else
287 		{
288 			state.path.level += (state.path.synthesize = state.level);
289 			if (state.level && !(type & P_PATHONLY) && st == &stbuf)
290 			{
291 				sp = state.path.name;
292 				len -= state.pwdsize;
293 			}
294 			else if (type & P_ABSOLUTE)
295 				sp = state.path.name;
296 			else if (*sp == '/')
297 				sp++;
298 		}
299 		if (*sp == 0)
300 			sp = state.dot;
301 	}
302  skip:
303 	if ((type & P_NOOPAQUE) && !LSTAT(sp, st) && checkopaque(sp, st))
304 	{
305 		message((-1, "%s: remove opaque", sp));
306 		UNLINK(sp);
307 		opaqued = 1;
308 	}
309 	if (safe && *sp == '/')
310 	{
311 		state.path.table = safe;
312 		cp = pathnext(sp, NiL, NiL);
313 		state.path.table = safe = 0;
314 		if (cp)
315 		{
316 			state.path.level = 0;
317 			path = strcpy(buf, sp);
318 			message((-5, "pathreal: == safe map %s", path));
319 			type &= ~(P_DOTDOT|P_SAFE);
320 			goto again;
321 		}
322 		if (!*(sp + 1))
323 		{
324 			strncpy(sp, state.safe->service, safesize);
325 			sp[safesize] = 0;
326 		}
327 		else if (strncmp(sp, state.safe->service, safesize) || sp[safesize] && sp[safesize] != '/')
328 		{
329 			if (*path != '/' && safe_dir)
330 			{
331 				errno = EPERM;
332 				return 0;
333 			}
334 			if (sp[1])
335 				strcpy(buf, sp);
336 			else
337 				*buf = 0;
338 			len = sfsprintf(sp, sizeof(state.path.name), "%-*s%s", safesize, state.safe->service, buf);
339 			message((-5, "pathreal: == safe next %s", sp));
340 			if (!pathnext(sp, NiL, NiL))
341 			{
342 				errno = EPERM;
343 				return 0;
344 			}
345 		}
346 		else
347 			type &= ~P_SAFE;
348 	}
349 	if ((type & P_SAFE) && state.path.level)
350 	{
351 		errno = EPERM;
352 		return 0;
353 	}
354 	if (type & P_PATHONLY)
355 	{
356 		errno = oerrno;
357 		return sp;
358 	}
359 	visits = 0;
360 	vir = 1;
361 	while (LSTAT(sp, st))
362 	{
363 		if (vir)
364 		{
365 			if (apath[0] == '.' && apath[1] == '.' && apath[2] == '.' && !apath[3])
366 			{
367 				if (state.level > 0)
368 				{
369 					message((-1, "pathreal: %s => %s", apath, sp));
370 					LSTAT(".", st);
371 					return sp;
372 				}
373 				errno = ENOENT;
374 				return 0;
375 			}
376 			vir = 0;
377 		}
378 		if (errno == ENOTDIR)
379 		{
380 			/*
381 			 * check for version instance
382 			 */
383 
384 			cp = ip = sp + strlen(sp);
385 			while (ip > sp && *--ip != '/');
386 			if (ip < sp)
387 				return 0;
388 			while (ip > sp && *--ip == '/');
389 			if (ip < sp)
390 				return 0;
391 			while (ip > sp && *--ip != '/');
392 			if (*ip == '/')
393 				ip++;
394 			while (cp >= ip)
395 			{
396 				cp[4] = *cp;
397 				cp--;
398 			}
399 			memcpy(ip, state.opaque, 4);
400 			if (!LSTAT(sp, st))
401 				break;
402 			errno = ENOTDIR;
403 			return 0;
404 		}
405 
406 		if (errno != ENOENT || opaqued)
407 			return 0;
408 #if FS
409 		/*
410 		 * check user mount
411 		 */
412 
413 		if (visits)
414 		{
415 			Mount_t*	mp;
416 			const char*	up;
417 
418 			if ((mp = getmount(sp, &up)) && (mp->fs->flags & FS_NAME) && (sp = fsreal(mp, MSG_open, (char*)up)) && !LSTAT(sp, st))
419 				break;
420 		}
421 #endif
422 
423 		/*
424 		 * search down the viewpath
425 		 */
426 
427 		if (type & P_SAFE)
428 		{
429 			errno = EPERM;
430 			return 0;
431 		}
432 		if (!pathnext(state.path.name, NiL, &visits))
433 			return 0;
434 		sp = state.path.name;
435 		if (!(type & P_ABSOLUTE))
436 		{
437 			/*
438 			 * try to use relative path
439 			 */
440 
441 			for (ip = state.pwd; *ip && *ip == *sp++; ip++);
442 			if (*ip == 0 && *sp == '/')
443 				sp++;
444 			else
445 				sp = state.path.name;
446 		}
447 		if (*sp == 0)
448 			sp = state.dot;
449 	}
450 	if (st->st_nlink > 1 && checkopaque(sp, st))
451 		return 0;
452 	if ((type & P_TOP) && state.path.level)
453 	{
454 		int	rfd;
455 		int	wfd;
456 
457 		if ((rfd = OPEN(sp, O_RDONLY, 0)) < 0)
458 			sp = 0;
459 		else
460 		{
461 			tsbuf = *st;
462 			wfd = open(apath, O_WRONLY|O_CREAT|O_TRUNC|O_cloexec, st->st_mode & S_IPERM);
463 			*st = tsbuf;
464 			if (wfd < 0)
465 				sp = 0;
466 			else
467 			{
468 				if (fs3d_copy(rfd, wfd, st))
469 					sp = 0;
470 				CLOSE(wfd);
471 			}
472 			CLOSE(rfd);
473 		}
474 		if (!sp)
475 		{
476 			errno = EROFS;
477 			return 0;
478 		}
479 		if (st == &stbuf)
480 			st = 0;
481 		return pathreal(apath, P_PATHONLY, st);
482 	}
483 	IVIEW(st, state.path.level);
484 	if (state.path.synthesize)
485 	{
486 		if (state.path.level < state.level)
487 		{
488 			if (len)
489 			{
490 				ip  = state.path.name + strlen(state.path.name) - len;
491 				len = *ip;
492 				*ip = 0;
493 			}
494 			if (!CHDIR(state.path.name))
495 				state.level = state.path.level;
496 			message((-1, "chdir=%s level=%d", state.path.name, state.level));
497 			*ip = len;
498 		}
499 		else if (S_ISDIR(st->st_mode))
500 		{
501 			int		mask;
502 			static int	uid = -1;
503 			static int	gid;
504 
505 			umask(mask = umask(0));
506 			st->st_mode = (st->st_mode | (S_IRWXU|S_IRWXG|S_IRWXO)) & ~(mask & (S_IRWXU|S_IRWXG|S_IRWXO));
507 			if (uid == -1)
508 			{
509 				uid = geteuid();
510 				gid = getegid();
511 			}
512 			st->st_uid = uid;
513 			st->st_gid = gid;
514 		}
515 	}
516 	ip = sp;
517 
518 	/*
519 	 * symbolic links handled specially
520 	 * get filename from pathname
521 	 */
522 
523 	if (S_ISLNK(st->st_mode) && (len = checklink(sp, st, type)) > 1 && !(type & (P_LSTAT|P_READLINK)) && state.path.nlinks++ < MAXSYMLINKS)
524 	{
525 		path = strcpy(buf, state.path.name);
526 		message((-1, "pathreal: == again %s", path));
527 		if (*path != '/')
528 			state.path.level = 0;
529 		type &= ~(P_DOTDOT|P_SAFE);
530 		goto again;
531 	}
532 #if VCS && defined(VCS_REAL)
533 	VCS_REAL(state.path.name, st);
534 #endif
535 	errno = oerrno;
536 	return sp;
537 }
538 
539 /*
540  * check whether sp points to a version object and find version instance
541  * sp is canonicalized and points into state.path
542  * when called from unlink, (type & P_PATHONLY) is set
543  *     -1 for non-existent link
544  *     length of path for a relative link that is not a version object
545  *     0  otherwise
546  *    state.path.linkname and state.path.linksize are set for version object
547  */
548 
549 int
checklink(const char * asp,struct stat * st,int type)550 checklink(const char* asp, struct stat* st, int type)
551 {
552 	register char*	sp = (char*)asp;
553 	register char*	ip;
554 	register int	len;
555 	register int	n;
556 	register char*	bp;
557 
558 	char		buf[PATH_MAX + 1];
559 
560 	if (sp < state.path.name || sp >= state.path.name + sizeof(state.path.name))
561 	{
562 		message((-1, "AHA#%d checklink bounds sp=%p state.path.name=%p sp=%s", __LINE__, sp, state.path.name, sp));
563 		sp = strncpy(state.path.name, sp, sizeof(state.path.name) - 1);
564 	}
565 	while (S_ISLNK(st->st_mode))
566 	{
567 		/*
568 		 * go to the last component
569 		 */
570 
571 		if (ip = strrchr(sp, '/'))
572 			ip++;
573 		else
574 			ip = sp;
575 		strcpy(buf, ip);
576 		len = (state.path.name + sizeof(state.path.name) - 1) - ip;
577 		if ((len = READLINK(sp, ip, len)) < 0)
578 		{
579 			message((-1, "%s: cannot readlink", sp));
580 			return 0;
581 		}
582 		state.path.linkname = ip;
583 		state.path.linksize = len;
584 
585 		/*
586 		 * check for relative link
587 		 */
588 
589 		if (*ip != '/')
590 		{
591 			ip[len] = 0;
592 			if (*ip == *state.opaque && !memcmp(ip, state.opaque, 4) && !memcmp(ip + 4, buf, n = strlen(buf)))
593 			{
594 				/*
595 				 * version object
596 				 */
597 
598 				ip += n + 4;
599 				if (instance(state.path.name, ip, st, 0))
600 				{
601 					state.path.linksize = strlen(state.path.linkname);
602 					if (type & P_LSTAT)
603 					{
604 						st->st_size = state.path.linksize;
605 						st->st_mode &= S_IPERM;
606 #ifdef S_IFLNK
607 						st->st_mode |= S_IFLNK;
608 #endif
609 						return 0;
610 					}
611 					continue;
612 				}
613 				errno = ENOENT;
614 				return -1;
615 			}
616 			else if (!(type & (P_LSTAT|P_PATHONLY|P_READLINK)) && *ip == '.' && *(ip + 1) == '.' && (*(ip + 2) == '/' || *(ip + 2) == 0))
617 			{
618 				memcpy(buf, ip, len + 1);
619 				bp = state.path.name;
620 				while (ip > bp && *(ip - 1) == '/')
621 					ip--;
622 				for (;;)
623 				{
624 					*(sp = ip) = 0;
625 					while (ip > bp && *--ip != '/');
626 					while (ip > bp && *(ip - 1) == '/')
627 						ip--;
628 					if (*ip == '/')
629 						ip++;
630 					if ((n = READLINK(state.path.name, ip, PATH_MAX - (ip - state.path.name))) <= 0)
631 					{
632 						*sp++ = '/';
633 						state.path.linkname = (char*)memcpy(sp, buf, len + 1);
634 						return sp + len - state.path.name;
635 					}
636 					if (*ip == '/')
637 						ip = (char*)memcpy(bp = state.path.name, ip, n);
638 					else if (ip > bp)
639 						*(ip - 1) = '/';
640 					ip += n;
641 				}
642 			}
643 		}
644 
645 		/*
646 		 * restore last component
647 		 */
648 
649 		if (!(type & P_READLINK))
650 			strcpy(ip, buf);
651 		break;
652 	}
653 	return 0;
654 }
655