Revision fbdac0d9

b/lib/bdev.py
36 36
from ganeti import objects
37 37
from ganeti import compat
38 38
from ganeti import netutils
39
from ganeti import pathutils
39 40

  
40 41

  
41 42
# Size of reads in _CanReadDevice
......
88 89
    return False
89 90

  
90 91

  
92
def _CheckFileStoragePath(path, allowed):
93
  """Checks if a path is in a list of allowed paths for file storage.
94

  
95
  @type path: string
96
  @param path: Path to check
97
  @type allowed: list
98
  @param allowed: List of allowed paths
99
  @raise errors.FileStoragePathError: If the path is not allowed
100

  
101
  """
102
  if not os.path.isabs(path):
103
    raise errors.FileStoragePathError("File storage path must be absolute,"
104
                                      " got '%s'" % path)
105

  
106
  for i in allowed:
107
    if not os.path.isabs(i):
108
      logging.info("Ignoring relative path '%s' for file storage", i)
109
      continue
110

  
111
    if utils.IsBelowDir(i, path):
112
      break
113
  else:
114
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
115
                                      " storage" % path)
116

  
117

  
118
def LoadAllowedFileStoragePaths(filename):
119
  """Loads file containing allowed file storage paths.
120

  
121
  @rtype: list
122
  @return: List of allowed paths (can be an empty list)
123

  
124
  """
125
  try:
126
    contents = utils.ReadFile(filename)
127
  except EnvironmentError:
128
    return []
129
  else:
130
    return utils.FilterEmptyLinesAndComments(contents)
131

  
132

  
133
def CheckFileStoragePath(path, _filename=pathutils.FILE_STORAGE_PATHS_FILE):
134
  """Checks if a path is allowed for file storage.
135

  
136
  @type path: string
137
  @param path: Path to check
138
  @raise errors.FileStoragePathError: If the path is not allowed
139

  
140
  """
141
  _CheckFileStoragePath(path, LoadAllowedFileStoragePaths(_filename))
142

  
143

  
144

  
91 145
class BlockDev(object):
92 146
  """Block device abstract class.
93 147

  
b/lib/errors.py
430 430
  """
431 431

  
432 432

  
433
class FileStoragePathError(GenericError):
434
  """Error from file storage path validation.
435

  
436
  """
437

  
438

  
433 439
# errors should be added above
434 440

  
435 441

  
b/lib/pathutils.py
86 86
USER_SCRIPTS_DIR = CONF_DIR + "/scripts"
87 87
VNC_PASSWORD_FILE = CONF_DIR + "/vnc-cluster-password"
88 88
HOOKS_BASE_DIR = CONF_DIR + "/hooks"
89
FILE_STORAGE_PATHS_FILE = CONF_DIR + "/file-storage-paths"
89 90

  
90 91
#: Lock file for watcher, locked in shared mode by watcher; lock in exclusive
91 92
# mode to block watcher (see L{cli._RunWhileClusterStoppedHelper.Call}
b/test/ganeti.bdev_unittest.py
28 28
from ganeti import bdev
29 29
from ganeti import errors
30 30
from ganeti import constants
31
from ganeti import utils
31 32

  
32 33
import testutils
33 34

  
......
372 373
                      output_extra_matches, volume_name)
373 374

  
374 375

  
375
if __name__ == '__main__':
376
class TestCheckFileStoragePath(unittest.TestCase):
377
  def testNonAbsolute(self):
378
    for i in ["", "tmp", "foo/bar/baz"]:
379
      self.assertRaises(errors.FileStoragePathError,
380
                        bdev._CheckFileStoragePath, i, ["/tmp"])
381

  
382
    self.assertRaises(errors.FileStoragePathError,
383
                      bdev._CheckFileStoragePath, "/tmp", ["tmp", "xyz"])
384

  
385
  def testNoAllowed(self):
386
    self.assertRaises(errors.FileStoragePathError,
387
                      bdev._CheckFileStoragePath, "/tmp", [])
388

  
389
  def testNoAdditionalPathComponent(self):
390
    self.assertRaises(errors.FileStoragePathError,
391
                      bdev._CheckFileStoragePath, "/tmp/foo", ["/tmp/foo"])
392

  
393
  def testAllowed(self):
394
    bdev._CheckFileStoragePath("/tmp/foo/a", ["/tmp/foo"])
395
    bdev._CheckFileStoragePath("/tmp/foo/a/x", ["/tmp/foo"])
396

  
397

  
398
class TestLoadAllowedFileStoragePaths(testutils.GanetiTestCase):
399
  def testDevNull(self):
400
    self.assertEqual(bdev.LoadAllowedFileStoragePaths("/dev/null"), [])
401

  
402
  def testNonExistantFile(self):
403
    filename = "/tmp/this/file/does/not/exist"
404
    assert not os.path.exists(filename)
405
    self.assertEqual(bdev.LoadAllowedFileStoragePaths(filename), [])
406

  
407
  def test(self):
408
    tmpfile = self._CreateTempFile()
409

  
410
    utils.WriteFile(tmpfile, data="""
411
      # This is a test file
412
      /tmp
413
      /srv/storage
414
      relative/path
415
      """)
416

  
417
    self.assertEqual(bdev.LoadAllowedFileStoragePaths(tmpfile), [
418
      "/tmp",
419
      "/srv/storage",
420
      "relative/path",
421
      ])
422

  
423

  
424
if __name__ == "__main__":
376 425
  testutils.GanetiTestProgram()
b/test/ganeti.utils.io_unittest.py
646 646
class TestIsBelowDir(unittest.TestCase):
647 647
  """Testing case for IsBelowDir"""
648 648

  
649
  def testExactlyTheSame(self):
650
    self.assertFalse(utils.IsBelowDir("/a/b", "/a/b"))
651
    self.assertFalse(utils.IsBelowDir("/a/b", "/a/b/"))
652
    self.assertFalse(utils.IsBelowDir("/a/b/", "/a/b"))
653
    self.assertFalse(utils.IsBelowDir("/a/b/", "/a/b/"))
654

  
649 655
  def testSamePrefix(self):
650 656
    self.assertTrue(utils.IsBelowDir("/a/b", "/a/b/c"))
651 657
    self.assertTrue(utils.IsBelowDir("/a/b/", "/a/b/e"))

Also available in: Unified diff