lib/oe/path.py: support missing directory components in realpath()

Some use cases in OE operate on symlinks which dangling path components.
Assume that these are directories instead of raising ENOENT.

(From OE-Core rev: a96e2c84f24c15b77ee1fbc1f998b8b4796b8664)

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Enrico Scholz
2013-02-11 20:21:52 +01:00
committed by Richard Purdie
parent d518019d6c
commit 8c22531e49
2 changed files with 19 additions and 17 deletions

View File

@@ -168,13 +168,13 @@ def find(dir, **walkoptions):
def __is_path_below(file, root): def __is_path_below(file, root):
return (file + os.path.sep).startswith(root) return (file + os.path.sep).startswith(root)
def __realpath_rel(start, rel_path, root, loop_cnt): def __realpath_rel(start, rel_path, root, loop_cnt, assume_dir):
"""Calculates real path of symlink 'start' + 'rel_path' below """Calculates real path of symlink 'start' + 'rel_path' below
'root'; no part of 'start' below 'root' must contain symlinks. """ 'root'; no part of 'start' below 'root' must contain symlinks. """
have_dir = True have_dir = True
for d in rel_path.split(os.path.sep): for d in rel_path.split(os.path.sep):
if not have_dir: if not have_dir and not assume_dir:
raise OSError(errno.ENOENT, "no such directory %s" % start) raise OSError(errno.ENOENT, "no such directory %s" % start)
if d == os.path.pardir: # '..' if d == os.path.pardir: # '..'
@@ -186,13 +186,13 @@ def __realpath_rel(start, rel_path, root, loop_cnt):
pass pass
else: else:
(start, have_dir) = __realpath(os.path.join(start, d), (start, have_dir) = __realpath(os.path.join(start, d),
root, loop_cnt) root, loop_cnt, assume_dir)
assert(__is_path_below(start, root)) assert(__is_path_below(start, root))
return start return start
def __realpath(file, root, loop_cnt): def __realpath(file, root, loop_cnt, assume_dir):
while os.path.islink(file) and len(file) >= len(root): while os.path.islink(file) and len(file) >= len(root):
if loop_cnt == 0: if loop_cnt == 0:
raise OSError(errno.ELOOP, file) raise OSError(errno.ELOOP, file)
@@ -206,7 +206,7 @@ def __realpath(file, root, loop_cnt):
else: else:
tdir = root tdir = root
file = __realpath_rel(tdir, target, root, loop_cnt) file = __realpath_rel(tdir, target, root, loop_cnt, assume_dir)
try: try:
is_dir = os.path.isdir(file) is_dir = os.path.isdir(file)
@@ -215,11 +215,13 @@ def __realpath(file, root, loop_cnt):
return (file, is_dir) return (file, is_dir)
def realpath(file, root, use_physdir = True, loop_cnt = 100): def realpath(file, root, use_physdir = True, loop_cnt = 100, assume_dir = False):
""" Returns the canonical path of 'file' with assuming a toplevel """ Returns the canonical path of 'file' with assuming a
'root' directory. When 'use_physdir' is set, all preceding path toplevel 'root' directory. When 'use_physdir' is set, all
components of 'file' will be resolved first; this flag should be preceding path components of 'file' will be resolved first;
set unless it is guaranteed that there is no symlink in the path.""" this flag should be set unless it is guaranteed that there is
no symlink in the path. When 'assume_dir' is not set, missing
path components will raise an ENOENT error"""
root = os.path.normpath(root) root = os.path.normpath(root)
file = os.path.normpath(file) file = os.path.normpath(file)
@@ -233,9 +235,9 @@ def realpath(file, root, use_physdir = True, loop_cnt = 100):
try: try:
if use_physdir: if use_physdir:
file = __realpath_rel(root, file[(len(root) - 1):], root, loop_cnt) file = __realpath_rel(root, file[(len(root) - 1):], root, loop_cnt, assume_dir)
else: else:
file = __realpath(file, root, loop_cnt)[0] file = __realpath(file, root, loop_cnt, assume_dir)[0]
except OSError, e: except OSError, e:
if e.errno == errno.ELOOP: if e.errno == errno.ELOOP:
# make ELOOP more readable; without catching it, there will # make ELOOP more readable; without catching it, there will

View File

@@ -25,7 +25,7 @@ class TestRealPath(unittest.TestCase):
( "usr/bin/prog-F", "../../../sbin/prog-F", "/sbin/prog-F" ), ( "usr/bin/prog-F", "../../../sbin/prog-F", "/sbin/prog-F" ),
( "loop", "a/loop", None ), ( "loop", "a/loop", None ),
( "a/loop", "../loop", None ), ( "a/loop", "../loop", None ),
( "b/test", "file/foo", None ), ( "b/test", "file/foo", "/b/file/foo" ),
] ]
LINKS_PHYS = [ LINKS_PHYS = [
@@ -59,8 +59,9 @@ class TestRealPath(unittest.TestCase):
for l in self.LINKS: for l in self.LINKS:
os.symlink(l[1], os.path.join(self.root, l[0])) os.symlink(l[1], os.path.join(self.root, l[0]))
def __realpath(self, file, use_physdir): def __realpath(self, file, use_physdir, assume_dir = True):
return oe.path.realpath(os.path.join(self.root, file), self.root, use_physdir) return oe.path.realpath(os.path.join(self.root, file), self.root,
use_physdir, assume_dir = assume_dir)
def test_norm(self): def test_norm(self):
for l in self.LINKS: for l in self.LINKS:
@@ -85,5 +86,4 @@ class TestRealPath(unittest.TestCase):
def test_loop(self): def test_loop(self):
for e in self.EXCEPTIONS: for e in self.EXCEPTIONS:
self.assertRaisesRegexp(OSError, r'\[Errno %u\]' % e[1], self.assertRaisesRegexp(OSError, r'\[Errno %u\]' % e[1],
self.__realpath, e[0], False) self.__realpath, e[0], False, False)