diff --git a/lib/utils.py b/lib/utils.py
index 6877dc704d6a44f389b2e166df47839bc6a846cb..393f4f4f396058596dcd9a5fff723be03f74e324 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -1936,6 +1936,36 @@ def IsNormAbsPath(path):
   return os.path.normpath(path) == path and os.path.isabs(path)
 
 
+def PathJoin(*args):
+  """Safe-join a list of path components.
+
+  Requirements:
+      - the first argument must be an absolute path
+      - no component in the path must have backtracking (e.g. /../),
+        since we check for normalization at the end
+
+  @param args: the path components to be joined
+  @raise ValueError: for invalid paths
+
+  """
+  # ensure we're having at least one path passed in
+  assert args
+  # ensure the first component is an absolute and normalized path name
+  root = args[0]
+  if not IsNormAbsPath(root):
+    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
+  result = os.path.join(*args)
+  # ensure that the whole path is normalized
+  if not IsNormAbsPath(result):
+    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
+  # check that we're still under the original prefix
+  prefix = os.path.commonprefix([root, result])
+  if prefix != root:
+    raise ValueError("Error: path joining resulted in different prefix"
+                     " (%s != %s)" % (prefix, root))
+  return result
+
+
 def TailFile(fname, lines=20):
   """Return the last lines from a file.
 
diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py
index 4d19f859985419a5ef19c7beacc20fa18348c26e..550bf25034ea68885d0cb9c543c5f7ee93e8062a 100755
--- a/test/ganeti.utils_unittest.py
+++ b/test/ganeti.utils_unittest.py
@@ -47,7 +47,7 @@ from ganeti.utils import IsProcessAlive, RunCmd, \
      ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
      SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
      TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \
-     UnescapeAndSplit, RunParts
+     UnescapeAndSplit, RunParts, PathJoin
 
 from ganeti.errors import LockError, UnitParseError, GenericError, \
      ProgrammerError
@@ -1260,5 +1260,22 @@ class TestUnescapeAndSplit(unittest.TestCase):
       self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b)
 
 
+class TestPathJoin(unittest.TestCase):
+  """Testing case for PathJoin"""
+
+  def testBasicItems(self):
+    mlist = ["/a", "b", "c"]
+    self.failUnlessEqual(PathJoin(*mlist), "/".join(mlist))
+
+  def testNonAbsPrefix(self):
+    self.failUnlessRaises(ValueError, PathJoin, "a", "b")
+
+  def testBackTrack(self):
+    self.failUnlessRaises(ValueError, PathJoin, "/a", "b/../c")
+
+  def testMultiAbs(self):
+    self.failUnlessRaises(ValueError, PathJoin, "/a", "/b")
+
+
 if __name__ == '__main__':
   testutils.GanetiTestProgram()