Revision 4bb678e9

b/lib/utils.py
1936 1936
  return os.path.normpath(path) == path and os.path.isabs(path)
1937 1937

  
1938 1938

  
1939
def PathJoin(*args):
1940
  """Safe-join a list of path components.
1941

  
1942
  Requirements:
1943
      - the first argument must be an absolute path
1944
      - no component in the path must have backtracking (e.g. /../),
1945
        since we check for normalization at the end
1946

  
1947
  @param args: the path components to be joined
1948
  @raise ValueError: for invalid paths
1949

  
1950
  """
1951
  # ensure we're having at least one path passed in
1952
  assert args
1953
  # ensure the first component is an absolute and normalized path name
1954
  root = args[0]
1955
  if not IsNormAbsPath(root):
1956
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
1957
  result = os.path.join(*args)
1958
  # ensure that the whole path is normalized
1959
  if not IsNormAbsPath(result):
1960
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
1961
  # check that we're still under the original prefix
1962
  prefix = os.path.commonprefix([root, result])
1963
  if prefix != root:
1964
    raise ValueError("Error: path joining resulted in different prefix"
1965
                     " (%s != %s)" % (prefix, root))
1966
  return result
1967

  
1968

  
1939 1969
def TailFile(fname, lines=20):
1940 1970
  """Return the last lines from a file.
1941 1971

  
b/test/ganeti.utils_unittest.py
47 47
     ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
48 48
     SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
49 49
     TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \
50
     UnescapeAndSplit, RunParts
50
     UnescapeAndSplit, RunParts, PathJoin
51 51

  
52 52
from ganeti.errors import LockError, UnitParseError, GenericError, \
53 53
     ProgrammerError
......
1260 1260
      self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b)
1261 1261

  
1262 1262

  
1263
class TestPathJoin(unittest.TestCase):
1264
  """Testing case for PathJoin"""
1265

  
1266
  def testBasicItems(self):
1267
    mlist = ["/a", "b", "c"]
1268
    self.failUnlessEqual(PathJoin(*mlist), "/".join(mlist))
1269

  
1270
  def testNonAbsPrefix(self):
1271
    self.failUnlessRaises(ValueError, PathJoin, "a", "b")
1272

  
1273
  def testBackTrack(self):
1274
    self.failUnlessRaises(ValueError, PathJoin, "/a", "b/../c")
1275

  
1276
  def testMultiAbs(self):
1277
    self.failUnlessRaises(ValueError, PathJoin, "/a", "/b")
1278

  
1279

  
1263 1280
if __name__ == '__main__':
1264 1281
  testutils.GanetiTestProgram()

Also available in: Unified diff