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