Statistics
| Branch: | Tag: | Revision:

root / lib / storage / filestorage.py @ f3aebf6f

History | View | Annotate | Download (13 kB)

1 820bade9 Helga Velroyen
#
2 820bade9 Helga Velroyen
#
3 820bade9 Helga Velroyen
4 820bade9 Helga Velroyen
# Copyright (C) 2013 Google Inc.
5 820bade9 Helga Velroyen
#
6 820bade9 Helga Velroyen
# This program is free software; you can redistribute it and/or modify
7 820bade9 Helga Velroyen
# it under the terms of the GNU General Public License as published by
8 820bade9 Helga Velroyen
# the Free Software Foundation; either version 2 of the License, or
9 820bade9 Helga Velroyen
# (at your option) any later version.
10 820bade9 Helga Velroyen
#
11 820bade9 Helga Velroyen
# This program is distributed in the hope that it will be useful, but
12 820bade9 Helga Velroyen
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 820bade9 Helga Velroyen
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 820bade9 Helga Velroyen
# General Public License for more details.
15 820bade9 Helga Velroyen
#
16 820bade9 Helga Velroyen
# You should have received a copy of the GNU General Public License
17 820bade9 Helga Velroyen
# along with this program; if not, write to the Free Software
18 820bade9 Helga Velroyen
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 820bade9 Helga Velroyen
# 02110-1301, USA.
20 820bade9 Helga Velroyen
21 820bade9 Helga Velroyen
22 2656b017 Santi Raffa
"""Filesystem-based access functions and disk templates.
23 820bade9 Helga Velroyen

24 820bade9 Helga Velroyen
"""
25 820bade9 Helga Velroyen
26 13a6c760 Helga Velroyen
import logging
27 2656b017 Santi Raffa
import errno
28 5d94c034 Helga Velroyen
import os
29 820bade9 Helga Velroyen
30 13a6c760 Helga Velroyen
from ganeti import compat
31 13669ecd Helga Velroyen
from ganeti import constants
32 5d94c034 Helga Velroyen
from ganeti import errors
33 13a6c760 Helga Velroyen
from ganeti import pathutils
34 13a6c760 Helga Velroyen
from ganeti import utils
35 267520ba Santi Raffa
from ganeti.utils import io
36 2656b017 Santi Raffa
from ganeti.storage import base
37 2656b017 Santi Raffa
38 2656b017 Santi Raffa
39 267520ba Santi Raffa
class FileDeviceHelper(object):
40 267520ba Santi Raffa
41 267520ba Santi Raffa
  @classmethod
42 267520ba Santi Raffa
  def CreateFile(cls, path, size, create_folders=False,
43 267520ba Santi Raffa
                 _file_path_acceptance_fn=None):
44 267520ba Santi Raffa
    """Create a new file and its file device helper.
45 267520ba Santi Raffa

46 267520ba Santi Raffa
    @param size: the size in MiBs the file should be truncated to.
47 267520ba Santi Raffa
    @param create_folders: create the directories for the path if necessary
48 267520ba Santi Raffa
                           (using L{ganeti.utils.io.Makedirs})
49 267520ba Santi Raffa

50 267520ba Santi Raffa
    @rtype: FileDeviceHelper
51 267520ba Santi Raffa
    @return: The FileDeviceHelper object representing the object.
52 267520ba Santi Raffa
    @raise errors.FileStoragePathError: if the file path is disallowed by policy
53 267520ba Santi Raffa

54 267520ba Santi Raffa
    """
55 267520ba Santi Raffa
56 267520ba Santi Raffa
    if not _file_path_acceptance_fn:
57 267520ba Santi Raffa
      _file_path_acceptance_fn = CheckFileStoragePathAcceptance
58 267520ba Santi Raffa
    _file_path_acceptance_fn(path)
59 267520ba Santi Raffa
60 267520ba Santi Raffa
    if create_folders:
61 267520ba Santi Raffa
      folder = os.path.dirname(path)
62 267520ba Santi Raffa
      io.Makedirs(folder)
63 267520ba Santi Raffa
64 267520ba Santi Raffa
    try:
65 267520ba Santi Raffa
      fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
66 267520ba Santi Raffa
      f = os.fdopen(fd, "w")
67 267520ba Santi Raffa
      f.truncate(size * 1024 * 1024)
68 267520ba Santi Raffa
      f.close()
69 267520ba Santi Raffa
    except EnvironmentError as err:
70 267520ba Santi Raffa
      base.ThrowError("%s: can't create: %s", path, str(err))
71 267520ba Santi Raffa
72 267520ba Santi Raffa
    return FileDeviceHelper(path,
73 267520ba Santi Raffa
                            _file_path_acceptance_fn=_file_path_acceptance_fn)
74 267520ba Santi Raffa
75 267520ba Santi Raffa
  def __init__(self, path, _file_path_acceptance_fn=None):
76 267520ba Santi Raffa
    """Create a new file device helper.
77 267520ba Santi Raffa

78 267520ba Santi Raffa
    @raise errors.FileStoragePathError: if the file path is disallowed by policy
79 267520ba Santi Raffa

80 267520ba Santi Raffa
    """
81 267520ba Santi Raffa
    if not _file_path_acceptance_fn:
82 267520ba Santi Raffa
      _file_path_acceptance_fn = CheckFileStoragePathAcceptance
83 267520ba Santi Raffa
    _file_path_acceptance_fn(path)
84 267520ba Santi Raffa
85 267520ba Santi Raffa
    self.path = path
86 267520ba Santi Raffa
87 267520ba Santi Raffa
  def Exists(self, assert_exists=None):
88 267520ba Santi Raffa
    """Check for the existence of the given file.
89 267520ba Santi Raffa

90 267520ba Santi Raffa
    @param assert_exists: creates an assertion on the result value:
91 267520ba Santi Raffa
      - if true, raise errors.BlockDeviceError if the file doesn't exist
92 267520ba Santi Raffa
      - if false, raise errors.BlockDeviceError if the file does exist
93 267520ba Santi Raffa
    @rtype: boolean
94 267520ba Santi Raffa
    @return: True if the file exists
95 267520ba Santi Raffa

96 267520ba Santi Raffa
    """
97 267520ba Santi Raffa
98 267520ba Santi Raffa
    exists = os.path.isfile(self.path)
99 267520ba Santi Raffa
100 267520ba Santi Raffa
    if not exists and assert_exists is True:
101 267520ba Santi Raffa
      raise base.ThrowError("%s: No such file", self.path)
102 267520ba Santi Raffa
    if exists and assert_exists is False:
103 267520ba Santi Raffa
      raise base.ThrowError("%s: File exists", self.path)
104 267520ba Santi Raffa
105 267520ba Santi Raffa
    return exists
106 267520ba Santi Raffa
107 267520ba Santi Raffa
  def Remove(self):
108 267520ba Santi Raffa
    """Remove the file backing the block device.
109 267520ba Santi Raffa

110 267520ba Santi Raffa
    @rtype: boolean
111 267520ba Santi Raffa
    @return: True if the removal was successful
112 267520ba Santi Raffa

113 267520ba Santi Raffa
    """
114 267520ba Santi Raffa
    try:
115 267520ba Santi Raffa
      os.remove(self.path)
116 267520ba Santi Raffa
      return True
117 267520ba Santi Raffa
    except OSError as err:
118 267520ba Santi Raffa
      if err.errno != errno.ENOENT:
119 267520ba Santi Raffa
        base.ThrowError("%s: can't remove: %s", self.path, err)
120 267520ba Santi Raffa
      return False
121 267520ba Santi Raffa
122 267520ba Santi Raffa
  def Size(self):
123 267520ba Santi Raffa
    """Return the actual disk size in bytes.
124 267520ba Santi Raffa

125 267520ba Santi Raffa
    @rtype: int
126 267520ba Santi Raffa
    @return: The file size in bytes.
127 267520ba Santi Raffa

128 267520ba Santi Raffa
    """
129 267520ba Santi Raffa
    self.Exists(assert_exists=True)
130 267520ba Santi Raffa
    try:
131 267520ba Santi Raffa
      return os.stat(self.path).st_size
132 267520ba Santi Raffa
    except OSError as err:
133 267520ba Santi Raffa
      base.ThrowError("%s: can't stat: %s", self.path, err)
134 267520ba Santi Raffa
135 267520ba Santi Raffa
  def Grow(self, amount, dryrun, backingstore, _excl_stor):
136 267520ba Santi Raffa
    """Grow the file
137 267520ba Santi Raffa

138 267520ba Santi Raffa
    @param amount: the amount (in mebibytes) to grow by.
139 267520ba Santi Raffa

140 267520ba Santi Raffa
    """
141 267520ba Santi Raffa
    # Check that the file exists
142 267520ba Santi Raffa
    self.Exists(assert_exists=True)
143 267520ba Santi Raffa
144 267520ba Santi Raffa
    if amount < 0:
145 267520ba Santi Raffa
      base.ThrowError("%s: can't grow by negative amount", self.path)
146 267520ba Santi Raffa
147 267520ba Santi Raffa
    if dryrun:
148 267520ba Santi Raffa
      return
149 267520ba Santi Raffa
    if not backingstore:
150 267520ba Santi Raffa
      return
151 267520ba Santi Raffa
152 267520ba Santi Raffa
    current_size = self.Size()
153 267520ba Santi Raffa
    new_size = current_size + amount * 1024 * 1024
154 267520ba Santi Raffa
    try:
155 267520ba Santi Raffa
      f = open(self.path, "a+")
156 267520ba Santi Raffa
      f.truncate(new_size)
157 267520ba Santi Raffa
      f.close()
158 267520ba Santi Raffa
    except EnvironmentError, err:
159 267520ba Santi Raffa
      base.ThrowError("%s: can't grow: ", self.path, str(err))
160 267520ba Santi Raffa
161 267520ba Santi Raffa
162 2656b017 Santi Raffa
class FileStorage(base.BlockDev):
163 2656b017 Santi Raffa
  """File device.
164 2656b017 Santi Raffa

165 2656b017 Santi Raffa
  This class represents a file storage backend device.
166 2656b017 Santi Raffa

167 2656b017 Santi Raffa
  The unique_id for the file device is a (file_driver, file_path) tuple.
168 2656b017 Santi Raffa

169 2656b017 Santi Raffa
  """
170 c032b2ce Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
171 2656b017 Santi Raffa
    """Initalizes a file device backend.
172 2656b017 Santi Raffa

173 2656b017 Santi Raffa
    """
174 2656b017 Santi Raffa
    if children:
175 2656b017 Santi Raffa
      raise errors.BlockDeviceError("Invalid setup for file device")
176 2656b017 Santi Raffa
    super(FileStorage, self).__init__(unique_id, children, size, params,
177 c032b2ce Klaus Aehlig
                                      dyn_params, *args)
178 2656b017 Santi Raffa
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
179 2656b017 Santi Raffa
      raise ValueError("Invalid configuration data %s" % str(unique_id))
180 2656b017 Santi Raffa
    self.driver = unique_id[0]
181 2656b017 Santi Raffa
    self.dev_path = unique_id[1]
182 267520ba Santi Raffa
    self.file = FileDeviceHelper(self.dev_path)
183 2656b017 Santi Raffa
    self.Attach()
184 2656b017 Santi Raffa
185 2656b017 Santi Raffa
  def Assemble(self):
186 2656b017 Santi Raffa
    """Assemble the device.
187 2656b017 Santi Raffa

188 2656b017 Santi Raffa
    Checks whether the file device exists, raises BlockDeviceError otherwise.
189 2656b017 Santi Raffa

190 2656b017 Santi Raffa
    """
191 267520ba Santi Raffa
    self.file.Exists(assert_exists=True)
192 2656b017 Santi Raffa
193 2656b017 Santi Raffa
  def Shutdown(self):
194 2656b017 Santi Raffa
    """Shutdown the device.
195 2656b017 Santi Raffa

196 2656b017 Santi Raffa
    This is a no-op for the file type, as we don't deactivate
197 2656b017 Santi Raffa
    the file on shutdown.
198 2656b017 Santi Raffa

199 2656b017 Santi Raffa
    """
200 2656b017 Santi Raffa
    pass
201 2656b017 Santi Raffa
202 2656b017 Santi Raffa
  def Open(self, force=False):
203 2656b017 Santi Raffa
    """Make the device ready for I/O.
204 2656b017 Santi Raffa

205 2656b017 Santi Raffa
    This is a no-op for the file type.
206 2656b017 Santi Raffa

207 2656b017 Santi Raffa
    """
208 2656b017 Santi Raffa
    pass
209 2656b017 Santi Raffa
210 2656b017 Santi Raffa
  def Close(self):
211 2656b017 Santi Raffa
    """Notifies that the device will no longer be used for I/O.
212 2656b017 Santi Raffa

213 2656b017 Santi Raffa
    This is a no-op for the file type.
214 2656b017 Santi Raffa

215 2656b017 Santi Raffa
    """
216 2656b017 Santi Raffa
    pass
217 2656b017 Santi Raffa
218 2656b017 Santi Raffa
  def Remove(self):
219 2656b017 Santi Raffa
    """Remove the file backing the block device.
220 2656b017 Santi Raffa

221 2656b017 Santi Raffa
    @rtype: boolean
222 2656b017 Santi Raffa
    @return: True if the removal was successful
223 2656b017 Santi Raffa

224 2656b017 Santi Raffa
    """
225 267520ba Santi Raffa
    return self.file.Remove()
226 2656b017 Santi Raffa
227 2656b017 Santi Raffa
  def Rename(self, new_id):
228 2656b017 Santi Raffa
    """Renames the file.
229 2656b017 Santi Raffa

230 2656b017 Santi Raffa
    """
231 2656b017 Santi Raffa
    # TODO: implement rename for file-based storage
232 2656b017 Santi Raffa
    base.ThrowError("Rename is not supported for file-based storage")
233 2656b017 Santi Raffa
234 2656b017 Santi Raffa
  def Grow(self, amount, dryrun, backingstore, excl_stor):
235 2656b017 Santi Raffa
    """Grow the file
236 2656b017 Santi Raffa

237 2656b017 Santi Raffa
    @param amount: the amount (in mebibytes) to grow with
238 2656b017 Santi Raffa

239 2656b017 Santi Raffa
    """
240 2656b017 Santi Raffa
    if not backingstore:
241 2656b017 Santi Raffa
      return
242 2656b017 Santi Raffa
    if dryrun:
243 2656b017 Santi Raffa
      return
244 267520ba Santi Raffa
    self.file.Grow(amount, dryrun, backingstore, excl_stor)
245 2656b017 Santi Raffa
246 2656b017 Santi Raffa
  def Attach(self):
247 2656b017 Santi Raffa
    """Attach to an existing file.
248 2656b017 Santi Raffa

249 2656b017 Santi Raffa
    Check if this file already exists.
250 2656b017 Santi Raffa

251 2656b017 Santi Raffa
    @rtype: boolean
252 2656b017 Santi Raffa
    @return: True if file exists
253 2656b017 Santi Raffa

254 2656b017 Santi Raffa
    """
255 267520ba Santi Raffa
    self.attached = self.file.Exists()
256 2656b017 Santi Raffa
    return self.attached
257 2656b017 Santi Raffa
258 2656b017 Santi Raffa
  def GetActualSize(self):
259 2656b017 Santi Raffa
    """Return the actual disk size.
260 2656b017 Santi Raffa

261 2656b017 Santi Raffa
    @note: the device needs to be active when this is called
262 2656b017 Santi Raffa

263 2656b017 Santi Raffa
    """
264 267520ba Santi Raffa
    return self.file.Size()
265 2656b017 Santi Raffa
266 2656b017 Santi Raffa
  @classmethod
267 2656b017 Santi Raffa
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
268 bddc92ee Klaus Aehlig
             dyn_params, *args):
269 2656b017 Santi Raffa
    """Create a new file.
270 2656b017 Santi Raffa

271 2656b017 Santi Raffa
    @type size: int
272 2656b017 Santi Raffa
    @param size: the size of file in MiB
273 2656b017 Santi Raffa

274 2656b017 Santi Raffa
    @rtype: L{bdev.FileStorage}
275 2656b017 Santi Raffa
    @return: an instance of FileStorage
276 2656b017 Santi Raffa

277 2656b017 Santi Raffa
    """
278 2656b017 Santi Raffa
    if excl_stor:
279 2656b017 Santi Raffa
      raise errors.ProgrammerError("FileStorage device requested with"
280 2656b017 Santi Raffa
                                   " exclusive_storage")
281 2656b017 Santi Raffa
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
282 2656b017 Santi Raffa
      raise ValueError("Invalid configuration data %s" % str(unique_id))
283 2656b017 Santi Raffa
284 2656b017 Santi Raffa
    dev_path = unique_id[1]
285 2656b017 Santi Raffa
286 267520ba Santi Raffa
    FileDeviceHelper.CreateFile(dev_path, size)
287 bddc92ee Klaus Aehlig
    return FileStorage(unique_id, children, size, params, dyn_params,
288 bddc92ee Klaus Aehlig
                       *args)
289 820bade9 Helga Velroyen
290 820bade9 Helga Velroyen
291 e798d484 Helga Velroyen
def GetFileStorageSpaceInfo(path):
292 820bade9 Helga Velroyen
  """Retrieves the free and total space of the device where the file is
293 820bade9 Helga Velroyen
     located.
294 820bade9 Helga Velroyen

295 820bade9 Helga Velroyen
     @type path: string
296 820bade9 Helga Velroyen
     @param path: Path of the file whose embracing device's capacity is
297 820bade9 Helga Velroyen
       reported.
298 5d94c034 Helga Velroyen
     @return: a dictionary containing 'vg_size' and 'vg_free' given in MebiBytes
299 5d94c034 Helga Velroyen

300 820bade9 Helga Velroyen
  """
301 5d94c034 Helga Velroyen
  try:
302 5d94c034 Helga Velroyen
    result = os.statvfs(path)
303 5d94c034 Helga Velroyen
    free = (result.f_frsize * result.f_bavail) / (1024 * 1024)
304 5d94c034 Helga Velroyen
    size = (result.f_frsize * result.f_blocks) / (1024 * 1024)
305 13669ecd Helga Velroyen
    return {"type": constants.ST_FILE,
306 13669ecd Helga Velroyen
            "name": path,
307 32389d91 Helga Velroyen
            "storage_size": size,
308 32389d91 Helga Velroyen
            "storage_free": free}
309 5d94c034 Helga Velroyen
  except OSError, e:
310 5d94c034 Helga Velroyen
    raise errors.CommandError("Failed to retrieve file system information about"
311 5d94c034 Helga Velroyen
                              " path: %s - %s" % (path, e.strerror))
312 13a6c760 Helga Velroyen
313 13a6c760 Helga Velroyen
314 13a6c760 Helga Velroyen
def _GetForbiddenFileStoragePaths():
315 13a6c760 Helga Velroyen
  """Builds a list of path prefixes which shouldn't be used for file storage.
316 13a6c760 Helga Velroyen

317 13a6c760 Helga Velroyen
  @rtype: frozenset
318 13a6c760 Helga Velroyen

319 13a6c760 Helga Velroyen
  """
320 13a6c760 Helga Velroyen
  paths = set([
321 13a6c760 Helga Velroyen
    "/boot",
322 13a6c760 Helga Velroyen
    "/dev",
323 13a6c760 Helga Velroyen
    "/etc",
324 13a6c760 Helga Velroyen
    "/home",
325 13a6c760 Helga Velroyen
    "/proc",
326 13a6c760 Helga Velroyen
    "/root",
327 13a6c760 Helga Velroyen
    "/sys",
328 13a6c760 Helga Velroyen
    ])
329 13a6c760 Helga Velroyen
330 13a6c760 Helga Velroyen
  for prefix in ["", "/usr", "/usr/local"]:
331 13a6c760 Helga Velroyen
    paths.update(map(lambda s: "%s/%s" % (prefix, s),
332 13a6c760 Helga Velroyen
                     ["bin", "lib", "lib32", "lib64", "sbin"]))
333 13a6c760 Helga Velroyen
334 13a6c760 Helga Velroyen
  return compat.UniqueFrozenset(map(os.path.normpath, paths))
335 13a6c760 Helga Velroyen
336 13a6c760 Helga Velroyen
337 13a6c760 Helga Velroyen
def _ComputeWrongFileStoragePaths(paths,
338 13a6c760 Helga Velroyen
                                  _forbidden=_GetForbiddenFileStoragePaths()):
339 13a6c760 Helga Velroyen
  """Cross-checks a list of paths for prefixes considered bad.
340 13a6c760 Helga Velroyen

341 13a6c760 Helga Velroyen
  Some paths, e.g. "/bin", should not be used for file storage.
342 13a6c760 Helga Velroyen

343 13a6c760 Helga Velroyen
  @type paths: list
344 13a6c760 Helga Velroyen
  @param paths: List of paths to be checked
345 13a6c760 Helga Velroyen
  @rtype: list
346 13a6c760 Helga Velroyen
  @return: Sorted list of paths for which the user should be warned
347 13a6c760 Helga Velroyen

348 13a6c760 Helga Velroyen
  """
349 13a6c760 Helga Velroyen
  def _Check(path):
350 13a6c760 Helga Velroyen
    return (not os.path.isabs(path) or
351 13a6c760 Helga Velroyen
            path in _forbidden or
352 13a6c760 Helga Velroyen
            filter(lambda p: utils.IsBelowDir(p, path), _forbidden))
353 13a6c760 Helga Velroyen
354 13a6c760 Helga Velroyen
  return utils.NiceSort(filter(_Check, map(os.path.normpath, paths)))
355 13a6c760 Helga Velroyen
356 13a6c760 Helga Velroyen
357 13a6c760 Helga Velroyen
def ComputeWrongFileStoragePaths(_filename=pathutils.FILE_STORAGE_PATHS_FILE):
358 13a6c760 Helga Velroyen
  """Returns a list of file storage paths whose prefix is considered bad.
359 13a6c760 Helga Velroyen

360 13a6c760 Helga Velroyen
  See L{_ComputeWrongFileStoragePaths}.
361 13a6c760 Helga Velroyen

362 13a6c760 Helga Velroyen
  """
363 13a6c760 Helga Velroyen
  return _ComputeWrongFileStoragePaths(_LoadAllowedFileStoragePaths(_filename))
364 13a6c760 Helga Velroyen
365 13a6c760 Helga Velroyen
366 9c1c3c19 Helga Velroyen
def _CheckFileStoragePath(path, allowed, exact_match_ok=False):
367 13a6c760 Helga Velroyen
  """Checks if a path is in a list of allowed paths for file storage.
368 13a6c760 Helga Velroyen

369 13a6c760 Helga Velroyen
  @type path: string
370 13a6c760 Helga Velroyen
  @param path: Path to check
371 13a6c760 Helga Velroyen
  @type allowed: list
372 13a6c760 Helga Velroyen
  @param allowed: List of allowed paths
373 9c1c3c19 Helga Velroyen
  @type exact_match_ok: bool
374 9c1c3c19 Helga Velroyen
  @param exact_match_ok: whether or not it is okay when the path is exactly
375 9c1c3c19 Helga Velroyen
      equal to an allowed path and not a subdir of it
376 13a6c760 Helga Velroyen
  @raise errors.FileStoragePathError: If the path is not allowed
377 13a6c760 Helga Velroyen

378 13a6c760 Helga Velroyen
  """
379 13a6c760 Helga Velroyen
  if not os.path.isabs(path):
380 13a6c760 Helga Velroyen
    raise errors.FileStoragePathError("File storage path must be absolute,"
381 13a6c760 Helga Velroyen
                                      " got '%s'" % path)
382 13a6c760 Helga Velroyen
383 13a6c760 Helga Velroyen
  for i in allowed:
384 13a6c760 Helga Velroyen
    if not os.path.isabs(i):
385 13a6c760 Helga Velroyen
      logging.info("Ignoring relative path '%s' for file storage", i)
386 13a6c760 Helga Velroyen
      continue
387 13a6c760 Helga Velroyen
388 9c1c3c19 Helga Velroyen
    if exact_match_ok:
389 9c1c3c19 Helga Velroyen
      if os.path.normpath(i) == os.path.normpath(path):
390 9c1c3c19 Helga Velroyen
        break
391 9c1c3c19 Helga Velroyen
392 13a6c760 Helga Velroyen
    if utils.IsBelowDir(i, path):
393 13a6c760 Helga Velroyen
      break
394 13a6c760 Helga Velroyen
  else:
395 13a6c760 Helga Velroyen
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
396 13a6c760 Helga Velroyen
                                      " storage" % path)
397 13a6c760 Helga Velroyen
398 13a6c760 Helga Velroyen
399 13a6c760 Helga Velroyen
def _LoadAllowedFileStoragePaths(filename):
400 13a6c760 Helga Velroyen
  """Loads file containing allowed file storage paths.
401 13a6c760 Helga Velroyen

402 13a6c760 Helga Velroyen
  @rtype: list
403 13a6c760 Helga Velroyen
  @return: List of allowed paths (can be an empty list)
404 13a6c760 Helga Velroyen

405 13a6c760 Helga Velroyen
  """
406 13a6c760 Helga Velroyen
  try:
407 13a6c760 Helga Velroyen
    contents = utils.ReadFile(filename)
408 13a6c760 Helga Velroyen
  except EnvironmentError:
409 13a6c760 Helga Velroyen
    return []
410 13a6c760 Helga Velroyen
  else:
411 13a6c760 Helga Velroyen
    return utils.FilterEmptyLinesAndComments(contents)
412 13a6c760 Helga Velroyen
413 13a6c760 Helga Velroyen
414 13a6c760 Helga Velroyen
def CheckFileStoragePathAcceptance(
415 9c1c3c19 Helga Velroyen
    path, _filename=pathutils.FILE_STORAGE_PATHS_FILE,
416 9c1c3c19 Helga Velroyen
    exact_match_ok=False):
417 13a6c760 Helga Velroyen
  """Checks if a path is allowed for file storage.
418 13a6c760 Helga Velroyen

419 13a6c760 Helga Velroyen
  @type path: string
420 13a6c760 Helga Velroyen
  @param path: Path to check
421 13a6c760 Helga Velroyen
  @raise errors.FileStoragePathError: If the path is not allowed
422 13a6c760 Helga Velroyen

423 13a6c760 Helga Velroyen
  """
424 13a6c760 Helga Velroyen
  allowed = _LoadAllowedFileStoragePaths(_filename)
425 f3ebe73e Helga Velroyen
  if not allowed:
426 f3ebe73e Helga Velroyen
    raise errors.FileStoragePathError("No paths are valid or path file '%s'"
427 f3ebe73e Helga Velroyen
                                      " was not accessible." % _filename)
428 13a6c760 Helga Velroyen
429 13a6c760 Helga Velroyen
  if _ComputeWrongFileStoragePaths([path]):
430 13a6c760 Helga Velroyen
    raise errors.FileStoragePathError("Path '%s' uses a forbidden prefix" %
431 13a6c760 Helga Velroyen
                                      path)
432 13a6c760 Helga Velroyen
433 9c1c3c19 Helga Velroyen
  _CheckFileStoragePath(path, allowed, exact_match_ok=exact_match_ok)
434 9c1c3c19 Helga Velroyen
435 9c1c3c19 Helga Velroyen
436 9c1c3c19 Helga Velroyen
def _CheckFileStoragePathExistance(path):
437 9c1c3c19 Helga Velroyen
  """Checks whether the given path is usable on the file system.
438 9c1c3c19 Helga Velroyen

439 9c1c3c19 Helga Velroyen
  This checks wether the path is existing, a directory and writable.
440 9c1c3c19 Helga Velroyen

441 9c1c3c19 Helga Velroyen
  @type path: string
442 9c1c3c19 Helga Velroyen
  @param path: path to check
443 9c1c3c19 Helga Velroyen

444 9c1c3c19 Helga Velroyen
  """
445 9c1c3c19 Helga Velroyen
  if not os.path.isdir(path):
446 f3ebe73e Helga Velroyen
    raise errors.FileStoragePathError("Path '%s' is not existing or not a"
447 9c1c3c19 Helga Velroyen
                                      " directory." % path)
448 9c1c3c19 Helga Velroyen
  if not os.access(path, os.W_OK):
449 9c1c3c19 Helga Velroyen
    raise errors.FileStoragePathError("Path '%s' is not writable" % path)
450 9c1c3c19 Helga Velroyen
451 9c1c3c19 Helga Velroyen
452 9c1c3c19 Helga Velroyen
def CheckFileStoragePath(
453 9c1c3c19 Helga Velroyen
    path, _allowed_paths_file=pathutils.FILE_STORAGE_PATHS_FILE):
454 9c1c3c19 Helga Velroyen
  """Checks whether the path exists and is acceptable to use.
455 9c1c3c19 Helga Velroyen

456 4b322a76 Helga Velroyen
  Can be used for any file-based storage, for example shared-file storage.
457 4b322a76 Helga Velroyen

458 9c1c3c19 Helga Velroyen
  @type path: string
459 9c1c3c19 Helga Velroyen
  @param path: path to check
460 9c1c3c19 Helga Velroyen
  @rtype: string
461 9c1c3c19 Helga Velroyen
  @returns: error message if the path is not ready to use
462 9c1c3c19 Helga Velroyen

463 9c1c3c19 Helga Velroyen
  """
464 9c1c3c19 Helga Velroyen
  try:
465 9c1c3c19 Helga Velroyen
    CheckFileStoragePathAcceptance(path, _filename=_allowed_paths_file,
466 9c1c3c19 Helga Velroyen
                                   exact_match_ok=True)
467 2650ea6b Helga Velroyen
  except errors.FileStoragePathError as e:
468 2650ea6b Helga Velroyen
    return str(e)
469 9c1c3c19 Helga Velroyen
  if not os.path.isdir(path):
470 9c1c3c19 Helga Velroyen
    return "Path '%s' is not exisiting or not a directory." % path
471 9c1c3c19 Helga Velroyen
  if not os.access(path, os.W_OK):
472 9c1c3c19 Helga Velroyen
    return "Path '%s' is not writable" % path