utils: Introduce IsBelowDir
authorRené Nussbaumer <rn@google.com>
Mon, 5 Sep 2011 11:59:06 +0000 (13:59 +0200)
committerRené Nussbaumer <rn@google.com>
Mon, 5 Sep 2011 14:30:52 +0000 (16:30 +0200)
This is mainly a wrapper to overcome the limitation of commonprefix
which makes a string by string comparisation and reports the common
prefix in both strings. However this is bad for directory handling.

IsBelowDir works around this limitation and should be used in favour of
commonprefix.

Signed-off-by: René Nussbaumer <rn@google.com>
Reviewed-by: Michael Hanselmann <hansmi@google.com>

lib/utils/io.py
test/ganeti.utils.io_unittest.py

index c64c125..fc1be39 100644 (file)
@@ -465,6 +465,20 @@ def IsNormAbsPath(path):
   return os.path.normpath(path) == path and os.path.isabs(path)
 
 
+def IsBelowDir(root, other_path):
+  """Check whether a path is below a root dir.
+
+  This works around the nasty byte-byte comparisation of commonprefix.
+
+  """
+  if not (os.path.isabs(root) and os.path.isabs(other_path)):
+    raise ValueError("Provided paths '%s' and '%s' are not absolute" %
+                     (root, other_path))
+  prepared_root = "%s%s" % (os.path.normpath(root), os.sep)
+  return os.path.commonprefix([prepared_root,
+                               os.path.normpath(other_path)]) == prepared_root
+
+
 def PathJoin(*args):
   """Safe-join a list of path components.
 
index be1b4ab..8b2cc8b 100755 (executable)
@@ -586,6 +586,32 @@ class TestIsNormAbsPath(unittest.TestCase):
     self._pathTestHelper("/etc/", False)
 
 
+class TestIsBelowDir(unittest.TestCase):
+  """Testing case for IsBelowDir"""
+
+  def testSamePrefix(self):
+    self.assertTrue(utils.IsBelowDir("/a/b", "/a/b/c"))
+    self.assertTrue(utils.IsBelowDir("/a/b/", "/a/b/e"))
+
+  def testSamePrefixButDifferentDir(self):
+    self.assertFalse(utils.IsBelowDir("/a/b", "/a/bc/d"))
+    self.assertFalse(utils.IsBelowDir("/a/b/", "/a/bc/e"))
+
+  def testSamePrefixButDirTraversal(self):
+    self.assertFalse(utils.IsBelowDir("/a/b", "/a/b/../c"))
+    self.assertFalse(utils.IsBelowDir("/a/b/", "/a/b/../d"))
+
+  def testSamePrefixAndTraversal(self):
+    self.assertTrue(utils.IsBelowDir("/a/b", "/a/b/c/../d"))
+    self.assertTrue(utils.IsBelowDir("/a/b", "/a/b/c/./e"))
+    self.assertTrue(utils.IsBelowDir("/a/b", "/a/b/../b/./e"))
+
+  def testBothAbsPath(self):
+    self.assertRaises(ValueError, utils.IsBelowDir, "/a/b/c", "d")
+    self.assertRaises(ValueError, utils.IsBelowDir, "a/b/c", "/d")
+    self.assertRaises(ValueError, utils.IsBelowDir, "a/b/c", "d")
+
+
 class TestPathJoin(unittest.TestCase):
   """Testing case for PathJoin"""