aboutsummaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/path.py
diff options
context:
space:
mode:
authorEnrico Scholz <enrico.scholz@sigma-chemnitz.de>2013-02-10 13:41:46 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-02-11 14:45:37 +0000
commit76e0bd7f8e3a3bd052a6e329f88e2d8099e899c4 (patch)
tree00dccd24ac141a8121eb16caca73e8fe3146df30 /meta/lib/oe/path.py
parentec2aab09769f4b6817d74d2175afa2b7c7598750 (diff)
downloadopenembedded-core-76e0bd7f8e3a3bd052a6e329f88e2d8099e899c4.tar.gz
lib: implemented oe.path.realpath()
Various parts of the buildsystem have to work with symlinks. Resolving them is not trivial because they are always relative to a sysroot directory. Patch adds a function which returns the destination of a symlink by assuming a given path as the / toplevel directory. A testsuite was added too. Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oe/path.py')
-rw-r--r--meta/lib/oe/path.py87
1 files changed, 87 insertions, 0 deletions
diff --git a/meta/lib/oe/path.py b/meta/lib/oe/path.py
index ea58bedc8b..0b34cebdab 100644
--- a/meta/lib/oe/path.py
+++ b/meta/lib/oe/path.py
@@ -2,6 +2,7 @@ import errno
import glob
import shutil
import subprocess
+import os.path
def join(*paths):
"""Like os.path.join but doesn't treat absolute RHS specially"""
@@ -161,3 +162,89 @@ def find(dir, **walkoptions):
for root, dirs, files in os.walk(dir, **walkoptions):
for file in files:
yield os.path.join(root, file)
+
+
+## realpath() related functions
+def __is_path_below(file, root):
+ return (file + os.path.sep).startswith(root)
+
+def __realpath_rel(start, rel_path, root, loop_cnt):
+ """Calculates real path of symlink 'start' + 'rel_path' below
+ 'root'; no part of 'start' below 'root' must contain symlinks. """
+ have_dir = True
+
+ for d in rel_path.split(os.path.sep):
+ if not have_dir:
+ raise OSError(errno.ENOENT, "no such directory %s" % start)
+
+ if d == os.path.pardir: # '..'
+ if len(start) >= len(root):
+ # do not follow '..' before root
+ start = os.path.dirname(start)
+ else:
+ # emit warning?
+ pass
+ else:
+ (start, have_dir) = __realpath(os.path.join(start, d),
+ root, loop_cnt)
+
+ assert(__is_path_below(start, root))
+
+ return start
+
+def __realpath(file, root, loop_cnt):
+ while os.path.islink(file) and len(file) >= len(root):
+ if loop_cnt == 0:
+ raise OSError(errno.ELOOP, file)
+
+ loop_cnt -= 1
+ target = os.path.normpath(os.readlink(file))
+
+ if not os.path.isabs(target):
+ tdir = os.path.dirname(file)
+ assert(__is_path_below(tdir, root))
+ else:
+ tdir = root
+
+ file = __realpath_rel(tdir, target, root, loop_cnt)
+
+ try:
+ is_dir = os.path.isdir(file)
+ except:
+ is_dir = false
+
+ return (file, is_dir)
+
+def realpath(file, root, use_physdir = True, loop_cnt = 100):
+ """ Returns the canonical path of 'file' with assuming a toplevel
+ 'root' directory. When 'use_physdir' is set, all preceding path
+ components of 'file' will be resolved first; this flag should be
+ set unless it is guaranteed that there is no symlink in the path."""
+
+ root = os.path.normpath(root)
+ file = os.path.normpath(file)
+
+ if not root.endswith(os.path.sep):
+ # letting root end with '/' makes some things easier
+ root = root + os.path.sep
+
+ if not __is_path_below(file, root):
+ raise OSError(errno.EINVAL, "file '%s' is not below root" % file)
+
+ try:
+ if use_physdir:
+ file = __realpath_rel(root, file[(len(root) - 1):], root, loop_cnt)
+ else:
+ file = __realpath(file, root, loop_cnt)[0]
+ except OSError, e:
+ if e.errno == errno.ELOOP:
+ # make ELOOP more readable; without catching it, there will
+ # be printed a backtrace with 100s of OSError exceptions
+ # else
+ raise OSError(errno.ELOOP,
+ "too much recursions while resolving '%s'; loop in '%s'" %
+ (file, e.strerror))
+
+ raise
+
+ return file