| 419 | # Join two paths, normalizing and eliminating any symbolic links |
| 420 | # encountered in the second path. |
| 421 | def _joinrealpath(path, rest, strict, seen): |
| 422 | if isinstance(path, bytes): |
| 423 | sep = b'/' |
| 424 | curdir = b'.' |
| 425 | pardir = b'..' |
| 426 | else: |
| 427 | sep = '/' |
| 428 | curdir = '.' |
| 429 | pardir = '..' |
| 430 | |
| 431 | if isabs(rest): |
| 432 | rest = rest[1:] |
| 433 | path = sep |
| 434 | |
| 435 | while rest: |
| 436 | name, _, rest = rest.partition(sep) |
| 437 | if not name or name == curdir: |
| 438 | # current dir |
| 439 | continue |
| 440 | if name == pardir: |
| 441 | # parent dir |
| 442 | if path: |
| 443 | path, name = split(path) |
| 444 | if name == pardir: |
| 445 | path = join(path, pardir, pardir) |
| 446 | else: |
| 447 | path = pardir |
| 448 | continue |
| 449 | newpath = join(path, name) |
| 450 | try: |
| 451 | st = os.lstat(newpath) |
| 452 | except OSError: |
| 453 | if strict: |
| 454 | raise |
| 455 | is_link = False |
| 456 | else: |
| 457 | is_link = stat.S_ISLNK(st.st_mode) |
| 458 | if not is_link: |
| 459 | path = newpath |
| 460 | continue |
| 461 | # Resolve the symbolic link |
| 462 | if newpath in seen: |
| 463 | # Already seen this path |
| 464 | path = seen[newpath] |
| 465 | if path is not None: |
| 466 | # use cached value |
| 467 | continue |
| 468 | # The symlink is not resolved, so we must have a symlink loop. |
| 469 | if strict: |
| 470 | # Raise OSError(errno.ELOOP) |
| 471 | os.stat(newpath) |
| 472 | else: |
| 473 | # Return already resolved part + rest of the path unchanged. |
| 474 | return join(newpath, rest), False |
| 475 | seen[newpath] = None # not resolved symlink |
| 476 | path, ok = _joinrealpath(path, os.readlink(newpath), strict, seen) |
| 477 | if not ok: |
| 478 | return join(path, rest), False |