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