mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2024-11-14 17:40:24 -08:00
108 lines
4.2 KiB
Python
108 lines
4.2 KiB
Python
import os.path
|
|
|
|
|
|
# realpath taken from https://bugs.python.org/file38057/issue9949-v4.patch
|
|
def realpath(path):
|
|
if isinstance(path, str):
|
|
prefix = '\\\\?\\'
|
|
unc_prefix = prefix + 'UNC'
|
|
new_unc_prefix = '\\'
|
|
cwd = os.getcwd()
|
|
else:
|
|
prefix = b'\\\\?\\'
|
|
unc_prefix = prefix + b'UNC'
|
|
new_unc_prefix = b'\\'
|
|
cwd = os.getcwdb()
|
|
had_prefix = path.startswith(prefix)
|
|
path, ok = _resolve_path(cwd, path, {})
|
|
# The path returned by _getfinalpathname will always start with \\?\ -
|
|
# strip off that prefix unless it was already provided on the original
|
|
# path.
|
|
if not had_prefix:
|
|
# For UNC paths, the prefix will actually be \\?\UNC - handle that
|
|
# case as well.
|
|
if path.startswith(unc_prefix):
|
|
path = new_unc_prefix + path[len(unc_prefix) :]
|
|
elif path.startswith(prefix):
|
|
path = path[len(prefix) :]
|
|
return path
|
|
|
|
|
|
def _resolve_path(path, rest, seen): # noqa: C901
|
|
# Windows normalizes the path before resolving symlinks; be sure to
|
|
# follow the same behavior.
|
|
rest = os.path.normpath(rest)
|
|
|
|
if isinstance(rest, str):
|
|
sep = '\\'
|
|
else:
|
|
sep = b'\\'
|
|
|
|
if os.path.isabs(rest):
|
|
drive, rest = os.path.splitdrive(rest)
|
|
path = drive + sep
|
|
rest = rest[1:]
|
|
|
|
while rest:
|
|
name, _, rest = rest.partition(sep)
|
|
new_path = os.path.join(path, name) if path else name
|
|
if os.path.exists(new_path):
|
|
if not rest:
|
|
# The whole path exists. Resolve it using the OS.
|
|
path = os.path._getfinalpathname(new_path)
|
|
else:
|
|
# The OS can resolve `new_path`; keep traversing the path.
|
|
path = new_path
|
|
elif not os.path.lexists(new_path):
|
|
# `new_path` does not exist on the filesystem at all. Use the
|
|
# OS to resolve `path`, if it exists, and then append the
|
|
# remainder.
|
|
if os.path.exists(path):
|
|
path = os.path._getfinalpathname(path)
|
|
rest = os.path.join(name, rest) if rest else name
|
|
return os.path.join(path, rest), True
|
|
else:
|
|
# We have a symbolic link that the OS cannot resolve. Try to
|
|
# resolve it ourselves.
|
|
|
|
# On Windows, symbolic link resolution can be partially or
|
|
# fully disabled [1]. The end result of a disabled symlink
|
|
# appears the same as a broken symlink (lexists() returns True
|
|
# but exists() returns False). And in both cases, the link can
|
|
# still be read using readlink(). Call stat() and check the
|
|
# resulting error code to ensure we don't circumvent the
|
|
# Windows symbolic link restrictions.
|
|
# [1] https://technet.microsoft.com/en-us/library/cc754077.aspx
|
|
try:
|
|
os.stat(new_path)
|
|
except OSError as e:
|
|
# WinError 1463: The symbolic link cannot be followed
|
|
# because its type is disabled.
|
|
if e.winerror == 1463:
|
|
raise
|
|
|
|
key = os.path.normcase(new_path)
|
|
if key in seen:
|
|
# This link has already been seen; try to use the
|
|
# previously resolved value.
|
|
path = seen[key]
|
|
if path is None:
|
|
# It has not yet been resolved, which means we must
|
|
# have a symbolic link loop. Return what we have
|
|
# resolved so far plus the remainder of the path (who
|
|
# cares about the Zen of Python?).
|
|
path = os.path.join(new_path, rest) if rest else new_path
|
|
return path, False
|
|
else:
|
|
# Mark this link as in the process of being resolved.
|
|
seen[key] = None
|
|
# Try to resolve it.
|
|
path, ok = _resolve_path(path, os.readlink(new_path), seen)
|
|
if ok:
|
|
# Resolution succeded; store the resolved value.
|
|
seen[key] = path
|
|
else:
|
|
# Resolution failed; punt.
|
|
return (os.path.join(path, rest) if rest else path), False
|
|
return path, True
|