Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 0232b768

History | View | Annotate | Download (28.9 kB)

1 3865ca48 Michael Hanselmann
#
2 3865ca48 Michael Hanselmann
#
3 3865ca48 Michael Hanselmann
4 5ae4945a Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
5 3865ca48 Michael Hanselmann
#
6 3865ca48 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 3865ca48 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 3865ca48 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 3865ca48 Michael Hanselmann
# (at your option) any later version.
10 3865ca48 Michael Hanselmann
#
11 3865ca48 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 3865ca48 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 3865ca48 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 3865ca48 Michael Hanselmann
# General Public License for more details.
15 3865ca48 Michael Hanselmann
#
16 3865ca48 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 3865ca48 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 3865ca48 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 3865ca48 Michael Hanselmann
# 02110-1301, USA.
20 3865ca48 Michael Hanselmann
21 3865ca48 Michael Hanselmann
"""Utility functions for I/O.
22 3865ca48 Michael Hanselmann

23 3865ca48 Michael Hanselmann
"""
24 3865ca48 Michael Hanselmann
25 3865ca48 Michael Hanselmann
import os
26 3865ca48 Michael Hanselmann
import logging
27 3865ca48 Michael Hanselmann
import shutil
28 3865ca48 Michael Hanselmann
import tempfile
29 3865ca48 Michael Hanselmann
import errno
30 3865ca48 Michael Hanselmann
import time
31 b81b3c96 René Nussbaumer
import stat
32 3865ca48 Michael Hanselmann
33 3865ca48 Michael Hanselmann
from ganeti import errors
34 3865ca48 Michael Hanselmann
from ganeti import constants
35 111a7d04 Michael Hanselmann
from ganeti import pathutils
36 3865ca48 Michael Hanselmann
from ganeti.utils import filelock
37 3865ca48 Michael Hanselmann
38 3865ca48 Michael Hanselmann
39 90e234a6 Michael Hanselmann
#: Path generating random UUID
40 90e234a6 Michael Hanselmann
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
41 90e234a6 Michael Hanselmann
42 2dbc6857 Michael Hanselmann
#: Directory used by fsck(8) to store recovered data, usually at a file
43 2dbc6857 Michael Hanselmann
#: system's root directory
44 2dbc6857 Michael Hanselmann
_LOST_AND_FOUND = "lost+found"
45 2dbc6857 Michael Hanselmann
46 c47eddb8 Bernardo Dal Seno
# Possible values for keep_perms in WriteFile()
47 c47eddb8 Bernardo Dal Seno
KP_NEVER = 0
48 c47eddb8 Bernardo Dal Seno
KP_ALWAYS = 1
49 c47eddb8 Bernardo Dal Seno
KP_IF_EXISTS = 2
50 c47eddb8 Bernardo Dal Seno
51 c47eddb8 Bernardo Dal Seno
KEEP_PERMS_VALUES = [
52 c47eddb8 Bernardo Dal Seno
  KP_NEVER,
53 c47eddb8 Bernardo Dal Seno
  KP_ALWAYS,
54 c47eddb8 Bernardo Dal Seno
  KP_IF_EXISTS,
55 c47eddb8 Bernardo Dal Seno
  ]
56 c47eddb8 Bernardo Dal Seno
57 90e234a6 Michael Hanselmann
58 eb93b673 Guido Trotter
def ErrnoOrStr(err):
59 eb93b673 Guido Trotter
  """Format an EnvironmentError exception.
60 eb93b673 Guido Trotter

61 eb93b673 Guido Trotter
  If the L{err} argument has an errno attribute, it will be looked up
62 eb93b673 Guido Trotter
  and converted into a textual C{E...} description. Otherwise the
63 eb93b673 Guido Trotter
  string representation of the error will be returned.
64 eb93b673 Guido Trotter

65 eb93b673 Guido Trotter
  @type err: L{EnvironmentError}
66 eb93b673 Guido Trotter
  @param err: the exception to format
67 eb93b673 Guido Trotter

68 eb93b673 Guido Trotter
  """
69 eb93b673 Guido Trotter
  if hasattr(err, "errno"):
70 eb93b673 Guido Trotter
    detail = errno.errorcode[err.errno]
71 eb93b673 Guido Trotter
  else:
72 eb93b673 Guido Trotter
    detail = str(err)
73 eb93b673 Guido Trotter
  return detail
74 eb93b673 Guido Trotter
75 eb93b673 Guido Trotter
76 2635bb04 Michael Hanselmann
class FileStatHelper:
77 2635bb04 Michael Hanselmann
  """Helper to store file handle's C{fstat}.
78 2635bb04 Michael Hanselmann

79 2635bb04 Michael Hanselmann
  Useful in combination with L{ReadFile}'s C{preread} parameter.
80 2635bb04 Michael Hanselmann

81 2635bb04 Michael Hanselmann
  """
82 2635bb04 Michael Hanselmann
  def __init__(self):
83 2635bb04 Michael Hanselmann
    """Initializes this class.
84 2635bb04 Michael Hanselmann

85 2635bb04 Michael Hanselmann
    """
86 2635bb04 Michael Hanselmann
    self.st = None
87 2635bb04 Michael Hanselmann
88 2635bb04 Michael Hanselmann
  def __call__(self, fh):
89 2635bb04 Michael Hanselmann
    """Calls C{fstat} on file handle.
90 2635bb04 Michael Hanselmann

91 2635bb04 Michael Hanselmann
    """
92 2635bb04 Michael Hanselmann
    self.st = os.fstat(fh.fileno())
93 2635bb04 Michael Hanselmann
94 2635bb04 Michael Hanselmann
95 0e5084ee Michael Hanselmann
def ReadFile(file_name, size=-1, preread=None):
96 3865ca48 Michael Hanselmann
  """Reads a file.
97 3865ca48 Michael Hanselmann

98 3865ca48 Michael Hanselmann
  @type size: int
99 3865ca48 Michael Hanselmann
  @param size: Read at most size bytes (if negative, entire file)
100 0e5084ee Michael Hanselmann
  @type preread: callable receiving file handle as single parameter
101 0e5084ee Michael Hanselmann
  @param preread: Function called before file is read
102 3865ca48 Michael Hanselmann
  @rtype: str
103 3865ca48 Michael Hanselmann
  @return: the (possibly partial) content of the file
104 3865ca48 Michael Hanselmann

105 3865ca48 Michael Hanselmann
  """
106 3865ca48 Michael Hanselmann
  f = open(file_name, "r")
107 3865ca48 Michael Hanselmann
  try:
108 0e5084ee Michael Hanselmann
    if preread:
109 0e5084ee Michael Hanselmann
      preread(f)
110 0e5084ee Michael Hanselmann
111 3865ca48 Michael Hanselmann
    return f.read(size)
112 3865ca48 Michael Hanselmann
  finally:
113 3865ca48 Michael Hanselmann
    f.close()
114 3865ca48 Michael Hanselmann
115 3865ca48 Michael Hanselmann
116 3865ca48 Michael Hanselmann
def WriteFile(file_name, fn=None, data=None,
117 3865ca48 Michael Hanselmann
              mode=None, uid=-1, gid=-1,
118 3865ca48 Michael Hanselmann
              atime=None, mtime=None, close=True,
119 3865ca48 Michael Hanselmann
              dry_run=False, backup=False,
120 c47eddb8 Bernardo Dal Seno
              prewrite=None, postwrite=None, keep_perms=KP_NEVER):
121 3865ca48 Michael Hanselmann
  """(Over)write a file atomically.
122 3865ca48 Michael Hanselmann

123 3865ca48 Michael Hanselmann
  The file_name and either fn (a function taking one argument, the
124 3865ca48 Michael Hanselmann
  file descriptor, and which should write the data to it) or data (the
125 3865ca48 Michael Hanselmann
  contents of the file) must be passed. The other arguments are
126 3865ca48 Michael Hanselmann
  optional and allow setting the file mode, owner and group, and the
127 3865ca48 Michael Hanselmann
  mtime/atime of the file.
128 3865ca48 Michael Hanselmann

129 3865ca48 Michael Hanselmann
  If the function doesn't raise an exception, it has succeeded and the
130 3865ca48 Michael Hanselmann
  target file has the new contents. If the function has raised an
131 3865ca48 Michael Hanselmann
  exception, an existing target file should be unmodified and the
132 3865ca48 Michael Hanselmann
  temporary file should be removed.
133 3865ca48 Michael Hanselmann

134 3865ca48 Michael Hanselmann
  @type file_name: str
135 3865ca48 Michael Hanselmann
  @param file_name: the target filename
136 3865ca48 Michael Hanselmann
  @type fn: callable
137 3865ca48 Michael Hanselmann
  @param fn: content writing function, called with
138 3865ca48 Michael Hanselmann
      file descriptor as parameter
139 3865ca48 Michael Hanselmann
  @type data: str
140 3865ca48 Michael Hanselmann
  @param data: contents of the file
141 3865ca48 Michael Hanselmann
  @type mode: int
142 3865ca48 Michael Hanselmann
  @param mode: file mode
143 3865ca48 Michael Hanselmann
  @type uid: int
144 3865ca48 Michael Hanselmann
  @param uid: the owner of the file
145 3865ca48 Michael Hanselmann
  @type gid: int
146 3865ca48 Michael Hanselmann
  @param gid: the group of the file
147 3865ca48 Michael Hanselmann
  @type atime: int
148 3865ca48 Michael Hanselmann
  @param atime: a custom access time to be set on the file
149 3865ca48 Michael Hanselmann
  @type mtime: int
150 3865ca48 Michael Hanselmann
  @param mtime: a custom modification time to be set on the file
151 3865ca48 Michael Hanselmann
  @type close: boolean
152 3865ca48 Michael Hanselmann
  @param close: whether to close file after writing it
153 3865ca48 Michael Hanselmann
  @type prewrite: callable
154 3865ca48 Michael Hanselmann
  @param prewrite: function to be called before writing content
155 3865ca48 Michael Hanselmann
  @type postwrite: callable
156 3865ca48 Michael Hanselmann
  @param postwrite: function to be called after writing content
157 c47eddb8 Bernardo Dal Seno
  @type keep_perms: members of L{KEEP_PERMS_VALUES}
158 c47eddb8 Bernardo Dal Seno
  @param keep_perms: if L{KP_NEVER} (default), owner, group, and mode are
159 c47eddb8 Bernardo Dal Seno
      taken from the other parameters; if L{KP_ALWAYS}, owner, group, and
160 c47eddb8 Bernardo Dal Seno
      mode are copied from the existing file; if L{KP_IF_EXISTS}, owner,
161 c47eddb8 Bernardo Dal Seno
      group, and mode are taken from the file, and if the file doesn't
162 c47eddb8 Bernardo Dal Seno
      exist, they are taken from the other parameters. It is an error to
163 c47eddb8 Bernardo Dal Seno
      pass L{KP_ALWAYS} when the file doesn't exist or when C{uid}, C{gid},
164 c47eddb8 Bernardo Dal Seno
      or C{mode} are set to non-default values.
165 3865ca48 Michael Hanselmann

166 3865ca48 Michael Hanselmann
  @rtype: None or int
167 3865ca48 Michael Hanselmann
  @return: None if the 'close' parameter evaluates to True,
168 3865ca48 Michael Hanselmann
      otherwise the file descriptor
169 3865ca48 Michael Hanselmann

170 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: if any of the arguments are not valid
171 3865ca48 Michael Hanselmann

172 3865ca48 Michael Hanselmann
  """
173 3865ca48 Michael Hanselmann
  if not os.path.isabs(file_name):
174 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to WriteFile is not"
175 3865ca48 Michael Hanselmann
                                 " absolute: '%s'" % file_name)
176 3865ca48 Michael Hanselmann
177 3865ca48 Michael Hanselmann
  if [fn, data].count(None) != 1:
178 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("fn or data required")
179 3865ca48 Michael Hanselmann
180 3865ca48 Michael Hanselmann
  if [atime, mtime].count(None) == 1:
181 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Both atime and mtime must be either"
182 3865ca48 Michael Hanselmann
                                 " set or None")
183 3865ca48 Michael Hanselmann
184 c47eddb8 Bernardo Dal Seno
  if not keep_perms in KEEP_PERMS_VALUES:
185 c47eddb8 Bernardo Dal Seno
    raise errors.ProgrammerError("Invalid value for keep_perms: %s" %
186 c47eddb8 Bernardo Dal Seno
                                 keep_perms)
187 c47eddb8 Bernardo Dal Seno
  if keep_perms == KP_ALWAYS and (uid != -1 or gid != -1 or mode is not None):
188 c47eddb8 Bernardo Dal Seno
    raise errors.ProgrammerError("When keep_perms==KP_ALWAYS, 'uid', 'gid',"
189 c47eddb8 Bernardo Dal Seno
                                 " and 'mode' cannot be set")
190 c47eddb8 Bernardo Dal Seno
191 3865ca48 Michael Hanselmann
  if backup and not dry_run and os.path.isfile(file_name):
192 3865ca48 Michael Hanselmann
    CreateBackup(file_name)
193 3865ca48 Michael Hanselmann
194 c47eddb8 Bernardo Dal Seno
  if keep_perms == KP_ALWAYS or keep_perms == KP_IF_EXISTS:
195 c47eddb8 Bernardo Dal Seno
    # os.stat() raises an exception if the file doesn't exist
196 c47eddb8 Bernardo Dal Seno
    try:
197 c47eddb8 Bernardo Dal Seno
      file_stat = os.stat(file_name)
198 c47eddb8 Bernardo Dal Seno
      mode = stat.S_IMODE(file_stat.st_mode)
199 c47eddb8 Bernardo Dal Seno
      uid = file_stat.st_uid
200 c47eddb8 Bernardo Dal Seno
      gid = file_stat.st_gid
201 c47eddb8 Bernardo Dal Seno
    except OSError:
202 c47eddb8 Bernardo Dal Seno
      if keep_perms == KP_ALWAYS:
203 c47eddb8 Bernardo Dal Seno
        raise
204 c47eddb8 Bernardo Dal Seno
      # else: if keeep_perms == KP_IF_EXISTS it's ok if the file doesn't exist
205 c47eddb8 Bernardo Dal Seno
206 a9d68e40 Michael Hanselmann
  # Whether temporary file needs to be removed (e.g. if any error occurs)
207 3865ca48 Michael Hanselmann
  do_remove = True
208 a9d68e40 Michael Hanselmann
209 a9d68e40 Michael Hanselmann
  # Function result
210 a9d68e40 Michael Hanselmann
  result = None
211 a9d68e40 Michael Hanselmann
212 a9d68e40 Michael Hanselmann
  (dir_name, base_name) = os.path.split(file_name)
213 a9d68e40 Michael Hanselmann
  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
214 a9d68e40 Michael Hanselmann
                                    dir=dir_name)
215 3865ca48 Michael Hanselmann
  try:
216 a9d68e40 Michael Hanselmann
    try:
217 a9d68e40 Michael Hanselmann
      if uid != -1 or gid != -1:
218 a9d68e40 Michael Hanselmann
        os.chown(new_name, uid, gid)
219 a9d68e40 Michael Hanselmann
      if mode:
220 a9d68e40 Michael Hanselmann
        os.chmod(new_name, mode)
221 a9d68e40 Michael Hanselmann
      if callable(prewrite):
222 a9d68e40 Michael Hanselmann
        prewrite(fd)
223 a9d68e40 Michael Hanselmann
      if data is not None:
224 1d39e245 Iustin Pop
        if isinstance(data, unicode):
225 1d39e245 Iustin Pop
          data = data.encode()
226 1d39e245 Iustin Pop
        assert isinstance(data, str)
227 437c3e77 Iustin Pop
        to_write = len(data)
228 437c3e77 Iustin Pop
        offset = 0
229 437c3e77 Iustin Pop
        while offset < to_write:
230 437c3e77 Iustin Pop
          written = os.write(fd, buffer(data, offset))
231 437c3e77 Iustin Pop
          assert written >= 0
232 1d39e245 Iustin Pop
          assert written <= to_write - offset
233 437c3e77 Iustin Pop
          offset += written
234 437c3e77 Iustin Pop
        assert offset == to_write
235 a9d68e40 Michael Hanselmann
      else:
236 a9d68e40 Michael Hanselmann
        fn(fd)
237 a9d68e40 Michael Hanselmann
      if callable(postwrite):
238 a9d68e40 Michael Hanselmann
        postwrite(fd)
239 a9d68e40 Michael Hanselmann
      os.fsync(fd)
240 a9d68e40 Michael Hanselmann
      if atime is not None and mtime is not None:
241 a9d68e40 Michael Hanselmann
        os.utime(new_name, (atime, mtime))
242 a9d68e40 Michael Hanselmann
    finally:
243 a9d68e40 Michael Hanselmann
      # Close file unless the file descriptor should be returned
244 a9d68e40 Michael Hanselmann
      if close:
245 a9d68e40 Michael Hanselmann
        os.close(fd)
246 a9d68e40 Michael Hanselmann
      else:
247 a9d68e40 Michael Hanselmann
        result = fd
248 a9d68e40 Michael Hanselmann
249 a9d68e40 Michael Hanselmann
    # Rename file to destination name
250 3865ca48 Michael Hanselmann
    if not dry_run:
251 3865ca48 Michael Hanselmann
      os.rename(new_name, file_name)
252 a9d68e40 Michael Hanselmann
      # Successful, no need to remove anymore
253 3865ca48 Michael Hanselmann
      do_remove = False
254 3865ca48 Michael Hanselmann
  finally:
255 3865ca48 Michael Hanselmann
    if do_remove:
256 3865ca48 Michael Hanselmann
      RemoveFile(new_name)
257 3865ca48 Michael Hanselmann
258 3865ca48 Michael Hanselmann
  return result
259 3865ca48 Michael Hanselmann
260 3865ca48 Michael Hanselmann
261 3865ca48 Michael Hanselmann
def GetFileID(path=None, fd=None):
262 3865ca48 Michael Hanselmann
  """Returns the file 'id', i.e. the dev/inode and mtime information.
263 3865ca48 Michael Hanselmann

264 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
265 3865ca48 Michael Hanselmann

266 3865ca48 Michael Hanselmann
  @param path: the file path
267 3865ca48 Michael Hanselmann
  @param fd: a file descriptor
268 3865ca48 Michael Hanselmann
  @return: a tuple of (device number, inode number, mtime)
269 3865ca48 Michael Hanselmann

270 3865ca48 Michael Hanselmann
  """
271 3865ca48 Michael Hanselmann
  if [path, fd].count(None) != 1:
272 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("One and only one of fd/path must be given")
273 3865ca48 Michael Hanselmann
274 3865ca48 Michael Hanselmann
  if fd is None:
275 3865ca48 Michael Hanselmann
    st = os.stat(path)
276 3865ca48 Michael Hanselmann
  else:
277 3865ca48 Michael Hanselmann
    st = os.fstat(fd)
278 3865ca48 Michael Hanselmann
279 3865ca48 Michael Hanselmann
  return (st.st_dev, st.st_ino, st.st_mtime)
280 3865ca48 Michael Hanselmann
281 3865ca48 Michael Hanselmann
282 3865ca48 Michael Hanselmann
def VerifyFileID(fi_disk, fi_ours):
283 3865ca48 Michael Hanselmann
  """Verifies that two file IDs are matching.
284 3865ca48 Michael Hanselmann

285 3865ca48 Michael Hanselmann
  Differences in the inode/device are not accepted, but and older
286 3865ca48 Michael Hanselmann
  timestamp for fi_disk is accepted.
287 3865ca48 Michael Hanselmann

288 3865ca48 Michael Hanselmann
  @param fi_disk: tuple (dev, inode, mtime) representing the actual
289 3865ca48 Michael Hanselmann
      file data
290 3865ca48 Michael Hanselmann
  @param fi_ours: tuple (dev, inode, mtime) representing the last
291 3865ca48 Michael Hanselmann
      written file data
292 3865ca48 Michael Hanselmann
  @rtype: boolean
293 3865ca48 Michael Hanselmann

294 3865ca48 Michael Hanselmann
  """
295 3865ca48 Michael Hanselmann
  (d1, i1, m1) = fi_disk
296 3865ca48 Michael Hanselmann
  (d2, i2, m2) = fi_ours
297 3865ca48 Michael Hanselmann
298 3865ca48 Michael Hanselmann
  return (d1, i1) == (d2, i2) and m1 <= m2
299 3865ca48 Michael Hanselmann
300 3865ca48 Michael Hanselmann
301 3865ca48 Michael Hanselmann
def SafeWriteFile(file_name, file_id, **kwargs):
302 3865ca48 Michael Hanselmann
  """Wraper over L{WriteFile} that locks the target file.
303 3865ca48 Michael Hanselmann

304 3865ca48 Michael Hanselmann
  By keeping the target file locked during WriteFile, we ensure that
305 3865ca48 Michael Hanselmann
  cooperating writers will safely serialise access to the file.
306 3865ca48 Michael Hanselmann

307 3865ca48 Michael Hanselmann
  @type file_name: str
308 3865ca48 Michael Hanselmann
  @param file_name: the target filename
309 3865ca48 Michael Hanselmann
  @type file_id: tuple
310 3865ca48 Michael Hanselmann
  @param file_id: a result from L{GetFileID}
311 3865ca48 Michael Hanselmann

312 3865ca48 Michael Hanselmann
  """
313 3865ca48 Michael Hanselmann
  fd = os.open(file_name, os.O_RDONLY | os.O_CREAT)
314 3865ca48 Michael Hanselmann
  try:
315 3865ca48 Michael Hanselmann
    filelock.LockFile(fd)
316 3865ca48 Michael Hanselmann
    if file_id is not None:
317 3865ca48 Michael Hanselmann
      disk_id = GetFileID(fd=fd)
318 3865ca48 Michael Hanselmann
      if not VerifyFileID(disk_id, file_id):
319 3865ca48 Michael Hanselmann
        raise errors.LockError("Cannot overwrite file %s, it has been modified"
320 3865ca48 Michael Hanselmann
                               " since last written" % file_name)
321 3865ca48 Michael Hanselmann
    return WriteFile(file_name, **kwargs)
322 3865ca48 Michael Hanselmann
  finally:
323 3865ca48 Michael Hanselmann
    os.close(fd)
324 3865ca48 Michael Hanselmann
325 3865ca48 Michael Hanselmann
326 3865ca48 Michael Hanselmann
def ReadOneLineFile(file_name, strict=False):
327 3865ca48 Michael Hanselmann
  """Return the first non-empty line from a file.
328 3865ca48 Michael Hanselmann

329 3865ca48 Michael Hanselmann
  @type strict: boolean
330 3865ca48 Michael Hanselmann
  @param strict: if True, abort if the file has more than one
331 3865ca48 Michael Hanselmann
      non-empty line
332 3865ca48 Michael Hanselmann

333 3865ca48 Michael Hanselmann
  """
334 3865ca48 Michael Hanselmann
  file_lines = ReadFile(file_name).splitlines()
335 3865ca48 Michael Hanselmann
  full_lines = filter(bool, file_lines)
336 3865ca48 Michael Hanselmann
  if not file_lines or not full_lines:
337 3865ca48 Michael Hanselmann
    raise errors.GenericError("No data in one-liner file %s" % file_name)
338 3865ca48 Michael Hanselmann
  elif strict and len(full_lines) > 1:
339 3865ca48 Michael Hanselmann
    raise errors.GenericError("Too many lines in one-liner file %s" %
340 3865ca48 Michael Hanselmann
                              file_name)
341 3865ca48 Michael Hanselmann
  return full_lines[0]
342 3865ca48 Michael Hanselmann
343 3865ca48 Michael Hanselmann
344 3865ca48 Michael Hanselmann
def RemoveFile(filename):
345 3865ca48 Michael Hanselmann
  """Remove a file ignoring some errors.
346 3865ca48 Michael Hanselmann

347 3865ca48 Michael Hanselmann
  Remove a file, ignoring non-existing ones or directories. Other
348 3865ca48 Michael Hanselmann
  errors are passed.
349 3865ca48 Michael Hanselmann

350 3865ca48 Michael Hanselmann
  @type filename: str
351 3865ca48 Michael Hanselmann
  @param filename: the file to be removed
352 3865ca48 Michael Hanselmann

353 3865ca48 Michael Hanselmann
  """
354 3865ca48 Michael Hanselmann
  try:
355 3865ca48 Michael Hanselmann
    os.unlink(filename)
356 3865ca48 Michael Hanselmann
  except OSError, err:
357 3865ca48 Michael Hanselmann
    if err.errno not in (errno.ENOENT, errno.EISDIR):
358 3865ca48 Michael Hanselmann
      raise
359 3865ca48 Michael Hanselmann
360 3865ca48 Michael Hanselmann
361 3865ca48 Michael Hanselmann
def RemoveDir(dirname):
362 3865ca48 Michael Hanselmann
  """Remove an empty directory.
363 3865ca48 Michael Hanselmann

364 3865ca48 Michael Hanselmann
  Remove a directory, ignoring non-existing ones.
365 3865ca48 Michael Hanselmann
  Other errors are passed. This includes the case,
366 3865ca48 Michael Hanselmann
  where the directory is not empty, so it can't be removed.
367 3865ca48 Michael Hanselmann

368 3865ca48 Michael Hanselmann
  @type dirname: str
369 3865ca48 Michael Hanselmann
  @param dirname: the empty directory to be removed
370 3865ca48 Michael Hanselmann

371 3865ca48 Michael Hanselmann
  """
372 3865ca48 Michael Hanselmann
  try:
373 3865ca48 Michael Hanselmann
    os.rmdir(dirname)
374 3865ca48 Michael Hanselmann
  except OSError, err:
375 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
376 3865ca48 Michael Hanselmann
      raise
377 3865ca48 Michael Hanselmann
378 3865ca48 Michael Hanselmann
379 8e5a705d René Nussbaumer
def RenameFile(old, new, mkdir=False, mkdir_mode=0750, dir_uid=None,
380 8e5a705d René Nussbaumer
               dir_gid=None):
381 3865ca48 Michael Hanselmann
  """Renames a file.
382 3865ca48 Michael Hanselmann

383 9c2b3a70 René Nussbaumer
  This just creates the very least directory if it does not exist and C{mkdir}
384 9c2b3a70 René Nussbaumer
  is set to true.
385 9c2b3a70 René Nussbaumer

386 3865ca48 Michael Hanselmann
  @type old: string
387 3865ca48 Michael Hanselmann
  @param old: Original path
388 3865ca48 Michael Hanselmann
  @type new: string
389 3865ca48 Michael Hanselmann
  @param new: New path
390 3865ca48 Michael Hanselmann
  @type mkdir: bool
391 3865ca48 Michael Hanselmann
  @param mkdir: Whether to create target directory if it doesn't exist
392 3865ca48 Michael Hanselmann
  @type mkdir_mode: int
393 3865ca48 Michael Hanselmann
  @param mkdir_mode: Mode for newly created directories
394 8e5a705d René Nussbaumer
  @type dir_uid: int
395 8e5a705d René Nussbaumer
  @param dir_uid: The uid for the (if fresh created) dir
396 8e5a705d René Nussbaumer
  @type dir_gid: int
397 8e5a705d René Nussbaumer
  @param dir_gid: The gid for the (if fresh created) dir
398 3865ca48 Michael Hanselmann

399 3865ca48 Michael Hanselmann
  """
400 3865ca48 Michael Hanselmann
  try:
401 3865ca48 Michael Hanselmann
    return os.rename(old, new)
402 3865ca48 Michael Hanselmann
  except OSError, err:
403 3865ca48 Michael Hanselmann
    # In at least one use case of this function, the job queue, directory
404 3865ca48 Michael Hanselmann
    # creation is very rare. Checking for the directory before renaming is not
405 3865ca48 Michael Hanselmann
    # as efficient.
406 3865ca48 Michael Hanselmann
    if mkdir and err.errno == errno.ENOENT:
407 3865ca48 Michael Hanselmann
      # Create directory and try again
408 8e5a705d René Nussbaumer
      dir_path = os.path.dirname(new)
409 9c2b3a70 René Nussbaumer
      MakeDirWithPerm(dir_path, mkdir_mode, dir_uid, dir_gid)
410 3865ca48 Michael Hanselmann
411 3865ca48 Michael Hanselmann
      return os.rename(old, new)
412 3865ca48 Michael Hanselmann
413 3865ca48 Michael Hanselmann
    raise
414 3865ca48 Michael Hanselmann
415 3865ca48 Michael Hanselmann
416 9c2b3a70 René Nussbaumer
def EnforcePermission(path, mode, uid=None, gid=None, must_exist=True,
417 b81b3c96 René Nussbaumer
                      _chmod_fn=os.chmod, _chown_fn=os.chown, _stat_fn=os.stat):
418 b81b3c96 René Nussbaumer
  """Enforces that given path has given permissions.
419 b81b3c96 René Nussbaumer

420 b81b3c96 René Nussbaumer
  @param path: The path to the file
421 b81b3c96 René Nussbaumer
  @param mode: The mode of the file
422 b81b3c96 René Nussbaumer
  @param uid: The uid of the owner of this file
423 b81b3c96 René Nussbaumer
  @param gid: The gid of the owner of this file
424 b81b3c96 René Nussbaumer
  @param must_exist: Specifies if non-existance of path will be an error
425 b81b3c96 René Nussbaumer
  @param _chmod_fn: chmod function to use (unittest only)
426 b81b3c96 René Nussbaumer
  @param _chown_fn: chown function to use (unittest only)
427 b81b3c96 René Nussbaumer

428 b81b3c96 René Nussbaumer
  """
429 b81b3c96 René Nussbaumer
  logging.debug("Checking %s", path)
430 9c2b3a70 René Nussbaumer
431 9c2b3a70 René Nussbaumer
  # chown takes -1 if you want to keep one part of the ownership, however
432 9c2b3a70 René Nussbaumer
  # None is Python standard for that. So we remap them here.
433 9c2b3a70 René Nussbaumer
  if uid is None:
434 9c2b3a70 René Nussbaumer
    uid = -1
435 9c2b3a70 René Nussbaumer
  if gid is None:
436 9c2b3a70 René Nussbaumer
    gid = -1
437 9c2b3a70 René Nussbaumer
438 b81b3c96 René Nussbaumer
  try:
439 b81b3c96 René Nussbaumer
    st = _stat_fn(path)
440 b81b3c96 René Nussbaumer
441 b81b3c96 René Nussbaumer
    fmode = stat.S_IMODE(st[stat.ST_MODE])
442 b81b3c96 René Nussbaumer
    if fmode != mode:
443 b81b3c96 René Nussbaumer
      logging.debug("Changing mode of %s from %#o to %#o", path, fmode, mode)
444 b81b3c96 René Nussbaumer
      _chmod_fn(path, mode)
445 b81b3c96 René Nussbaumer
446 b81b3c96 René Nussbaumer
    if max(uid, gid) > -1:
447 b81b3c96 René Nussbaumer
      fuid = st[stat.ST_UID]
448 b81b3c96 René Nussbaumer
      fgid = st[stat.ST_GID]
449 b81b3c96 René Nussbaumer
      if fuid != uid or fgid != gid:
450 b81b3c96 René Nussbaumer
        logging.debug("Changing owner of %s from UID %s/GID %s to"
451 b81b3c96 René Nussbaumer
                      " UID %s/GID %s", path, fuid, fgid, uid, gid)
452 b81b3c96 René Nussbaumer
        _chown_fn(path, uid, gid)
453 b81b3c96 René Nussbaumer
  except EnvironmentError, err:
454 b81b3c96 René Nussbaumer
    if err.errno == errno.ENOENT:
455 b81b3c96 René Nussbaumer
      if must_exist:
456 b81b3c96 René Nussbaumer
        raise errors.GenericError("Path %s should exist, but does not" % path)
457 b81b3c96 René Nussbaumer
    else:
458 b81b3c96 René Nussbaumer
      raise errors.GenericError("Error while changing permissions on %s: %s" %
459 b81b3c96 René Nussbaumer
                                (path, err))
460 b81b3c96 René Nussbaumer
461 b81b3c96 René Nussbaumer
462 b81b3c96 René Nussbaumer
def MakeDirWithPerm(path, mode, uid, gid, _lstat_fn=os.lstat,
463 b81b3c96 René Nussbaumer
                    _mkdir_fn=os.mkdir, _perm_fn=EnforcePermission):
464 b81b3c96 René Nussbaumer
  """Enforces that given path is a dir and has given mode, uid and gid set.
465 b81b3c96 René Nussbaumer

466 b81b3c96 René Nussbaumer
  @param path: The path to the file
467 b81b3c96 René Nussbaumer
  @param mode: The mode of the file
468 b81b3c96 René Nussbaumer
  @param uid: The uid of the owner of this file
469 b81b3c96 René Nussbaumer
  @param gid: The gid of the owner of this file
470 b81b3c96 René Nussbaumer
  @param _lstat_fn: Stat function to use (unittest only)
471 b81b3c96 René Nussbaumer
  @param _mkdir_fn: mkdir function to use (unittest only)
472 b81b3c96 René Nussbaumer
  @param _perm_fn: permission setter function to use (unittest only)
473 b81b3c96 René Nussbaumer

474 b81b3c96 René Nussbaumer
  """
475 b81b3c96 René Nussbaumer
  logging.debug("Checking directory %s", path)
476 b81b3c96 René Nussbaumer
  try:
477 b81b3c96 René Nussbaumer
    # We don't want to follow symlinks
478 b81b3c96 René Nussbaumer
    st = _lstat_fn(path)
479 b81b3c96 René Nussbaumer
  except EnvironmentError, err:
480 b81b3c96 René Nussbaumer
    if err.errno != errno.ENOENT:
481 b81b3c96 René Nussbaumer
      raise errors.GenericError("stat(2) on %s failed: %s" % (path, err))
482 b81b3c96 René Nussbaumer
    _mkdir_fn(path)
483 b81b3c96 René Nussbaumer
  else:
484 b81b3c96 René Nussbaumer
    if not stat.S_ISDIR(st[stat.ST_MODE]):
485 b81b3c96 René Nussbaumer
      raise errors.GenericError(("Path %s is expected to be a directory, but "
486 b81b3c96 René Nussbaumer
                                 "isn't") % path)
487 b81b3c96 René Nussbaumer
488 b81b3c96 René Nussbaumer
  _perm_fn(path, mode, uid=uid, gid=gid)
489 b81b3c96 René Nussbaumer
490 b81b3c96 René Nussbaumer
491 3865ca48 Michael Hanselmann
def Makedirs(path, mode=0750):
492 3865ca48 Michael Hanselmann
  """Super-mkdir; create a leaf directory and all intermediate ones.
493 3865ca48 Michael Hanselmann

494 3865ca48 Michael Hanselmann
  This is a wrapper around C{os.makedirs} adding error handling not implemented
495 3865ca48 Michael Hanselmann
  before Python 2.5.
496 3865ca48 Michael Hanselmann

497 3865ca48 Michael Hanselmann
  """
498 3865ca48 Michael Hanselmann
  try:
499 3865ca48 Michael Hanselmann
    os.makedirs(path, mode)
500 3865ca48 Michael Hanselmann
  except OSError, err:
501 3865ca48 Michael Hanselmann
    # Ignore EEXIST. This is only handled in os.makedirs as included in
502 3865ca48 Michael Hanselmann
    # Python 2.5 and above.
503 3865ca48 Michael Hanselmann
    if err.errno != errno.EEXIST or not os.path.exists(path):
504 3865ca48 Michael Hanselmann
      raise
505 3865ca48 Michael Hanselmann
506 3865ca48 Michael Hanselmann
507 3865ca48 Michael Hanselmann
def TimestampForFilename():
508 3865ca48 Michael Hanselmann
  """Returns the current time formatted for filenames.
509 3865ca48 Michael Hanselmann

510 3865ca48 Michael Hanselmann
  The format doesn't contain colons as some shells and applications treat them
511 3865ca48 Michael Hanselmann
  as separators. Uses the local timezone.
512 3865ca48 Michael Hanselmann

513 3865ca48 Michael Hanselmann
  """
514 3865ca48 Michael Hanselmann
  return time.strftime("%Y-%m-%d_%H_%M_%S")
515 3865ca48 Michael Hanselmann
516 3865ca48 Michael Hanselmann
517 3865ca48 Michael Hanselmann
def CreateBackup(file_name):
518 3865ca48 Michael Hanselmann
  """Creates a backup of a file.
519 3865ca48 Michael Hanselmann

520 3865ca48 Michael Hanselmann
  @type file_name: str
521 3865ca48 Michael Hanselmann
  @param file_name: file to be backed up
522 3865ca48 Michael Hanselmann
  @rtype: str
523 3865ca48 Michael Hanselmann
  @return: the path to the newly created backup
524 3865ca48 Michael Hanselmann
  @raise errors.ProgrammerError: for invalid file names
525 3865ca48 Michael Hanselmann

526 3865ca48 Michael Hanselmann
  """
527 3865ca48 Michael Hanselmann
  if not os.path.isfile(file_name):
528 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" %
529 5ae4945a Iustin Pop
                                 file_name)
530 3865ca48 Michael Hanselmann
531 3865ca48 Michael Hanselmann
  prefix = ("%s.backup-%s." %
532 3865ca48 Michael Hanselmann
            (os.path.basename(file_name), TimestampForFilename()))
533 3865ca48 Michael Hanselmann
  dir_name = os.path.dirname(file_name)
534 3865ca48 Michael Hanselmann
535 d0c8c01d Iustin Pop
  fsrc = open(file_name, "rb")
536 3865ca48 Michael Hanselmann
  try:
537 3865ca48 Michael Hanselmann
    (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
538 d0c8c01d Iustin Pop
    fdst = os.fdopen(fd, "wb")
539 3865ca48 Michael Hanselmann
    try:
540 3865ca48 Michael Hanselmann
      logging.debug("Backing up %s at %s", file_name, backup_name)
541 3865ca48 Michael Hanselmann
      shutil.copyfileobj(fsrc, fdst)
542 3865ca48 Michael Hanselmann
    finally:
543 3865ca48 Michael Hanselmann
      fdst.close()
544 3865ca48 Michael Hanselmann
  finally:
545 3865ca48 Michael Hanselmann
    fsrc.close()
546 3865ca48 Michael Hanselmann
547 3865ca48 Michael Hanselmann
  return backup_name
548 3865ca48 Michael Hanselmann
549 3865ca48 Michael Hanselmann
550 2dbc6857 Michael Hanselmann
def ListVisibleFiles(path, _is_mountpoint=os.path.ismount):
551 3865ca48 Michael Hanselmann
  """Returns a list of visible files in a directory.
552 3865ca48 Michael Hanselmann

553 3865ca48 Michael Hanselmann
  @type path: str
554 3865ca48 Michael Hanselmann
  @param path: the directory to enumerate
555 3865ca48 Michael Hanselmann
  @rtype: list
556 3865ca48 Michael Hanselmann
  @return: the list of all files not starting with a dot
557 3865ca48 Michael Hanselmann
  @raise ProgrammerError: if L{path} is not an absolue and normalized path
558 3865ca48 Michael Hanselmann

559 3865ca48 Michael Hanselmann
  """
560 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(path):
561 3865ca48 Michael Hanselmann
    raise errors.ProgrammerError("Path passed to ListVisibleFiles is not"
562 3865ca48 Michael Hanselmann
                                 " absolute/normalized: '%s'" % path)
563 2dbc6857 Michael Hanselmann
564 2dbc6857 Michael Hanselmann
  mountpoint = _is_mountpoint(path)
565 2dbc6857 Michael Hanselmann
566 2dbc6857 Michael Hanselmann
  def fn(name):
567 2dbc6857 Michael Hanselmann
    """File name filter.
568 2dbc6857 Michael Hanselmann

569 2dbc6857 Michael Hanselmann
    Ignores files starting with a dot (".") as by Unix convention they're
570 2dbc6857 Michael Hanselmann
    considered hidden. The "lost+found" directory found at the root of some
571 2dbc6857 Michael Hanselmann
    filesystems is also hidden.
572 2dbc6857 Michael Hanselmann

573 2dbc6857 Michael Hanselmann
    """
574 2dbc6857 Michael Hanselmann
    return not (name.startswith(".") or
575 2dbc6857 Michael Hanselmann
                (mountpoint and name == _LOST_AND_FOUND and
576 2dbc6857 Michael Hanselmann
                 os.path.isdir(os.path.join(path, name))))
577 2dbc6857 Michael Hanselmann
578 2dbc6857 Michael Hanselmann
  return filter(fn, os.listdir(path))
579 3865ca48 Michael Hanselmann
580 3865ca48 Michael Hanselmann
581 3865ca48 Michael Hanselmann
def EnsureDirs(dirs):
582 3865ca48 Michael Hanselmann
  """Make required directories, if they don't exist.
583 3865ca48 Michael Hanselmann

584 3865ca48 Michael Hanselmann
  @param dirs: list of tuples (dir_name, dir_mode)
585 3865ca48 Michael Hanselmann
  @type dirs: list of (string, integer)
586 3865ca48 Michael Hanselmann

587 3865ca48 Michael Hanselmann
  """
588 3865ca48 Michael Hanselmann
  for dir_name, dir_mode in dirs:
589 3865ca48 Michael Hanselmann
    try:
590 3865ca48 Michael Hanselmann
      os.mkdir(dir_name, dir_mode)
591 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
592 3865ca48 Michael Hanselmann
      if err.errno != errno.EEXIST:
593 3865ca48 Michael Hanselmann
        raise errors.GenericError("Cannot create needed directory"
594 3865ca48 Michael Hanselmann
                                  " '%s': %s" % (dir_name, err))
595 3865ca48 Michael Hanselmann
    try:
596 3865ca48 Michael Hanselmann
      os.chmod(dir_name, dir_mode)
597 3865ca48 Michael Hanselmann
    except EnvironmentError, err:
598 3865ca48 Michael Hanselmann
      raise errors.GenericError("Cannot change directory permissions on"
599 3865ca48 Michael Hanselmann
                                " '%s': %s" % (dir_name, err))
600 3865ca48 Michael Hanselmann
    if not os.path.isdir(dir_name):
601 3865ca48 Michael Hanselmann
      raise errors.GenericError("%s is not a directory" % dir_name)
602 3865ca48 Michael Hanselmann
603 3865ca48 Michael Hanselmann
604 3865ca48 Michael Hanselmann
def FindFile(name, search_path, test=os.path.exists):
605 3865ca48 Michael Hanselmann
  """Look for a filesystem object in a given path.
606 3865ca48 Michael Hanselmann

607 3865ca48 Michael Hanselmann
  This is an abstract method to search for filesystem object (files,
608 3865ca48 Michael Hanselmann
  dirs) under a given search path.
609 3865ca48 Michael Hanselmann

610 3865ca48 Michael Hanselmann
  @type name: str
611 3865ca48 Michael Hanselmann
  @param name: the name to look for
612 3865ca48 Michael Hanselmann
  @type search_path: str
613 3865ca48 Michael Hanselmann
  @param search_path: location to start at
614 3865ca48 Michael Hanselmann
  @type test: callable
615 3865ca48 Michael Hanselmann
  @param test: a function taking one argument that should return True
616 3865ca48 Michael Hanselmann
      if the a given object is valid; the default value is
617 3865ca48 Michael Hanselmann
      os.path.exists, causing only existing files to be returned
618 3865ca48 Michael Hanselmann
  @rtype: str or None
619 3865ca48 Michael Hanselmann
  @return: full path to the object if found, None otherwise
620 3865ca48 Michael Hanselmann

621 3865ca48 Michael Hanselmann
  """
622 3865ca48 Michael Hanselmann
  # validate the filename mask
623 3865ca48 Michael Hanselmann
  if constants.EXT_PLUGIN_MASK.match(name) is None:
624 3865ca48 Michael Hanselmann
    logging.critical("Invalid value passed for external script name: '%s'",
625 3865ca48 Michael Hanselmann
                     name)
626 3865ca48 Michael Hanselmann
    return None
627 3865ca48 Michael Hanselmann
628 3865ca48 Michael Hanselmann
  for dir_name in search_path:
629 3865ca48 Michael Hanselmann
    # FIXME: investigate switch to PathJoin
630 3865ca48 Michael Hanselmann
    item_name = os.path.sep.join([dir_name, name])
631 3865ca48 Michael Hanselmann
    # check the user test and that we're indeed resolving to the given
632 3865ca48 Michael Hanselmann
    # basename
633 3865ca48 Michael Hanselmann
    if test(item_name) and os.path.basename(item_name) == name:
634 3865ca48 Michael Hanselmann
      return item_name
635 3865ca48 Michael Hanselmann
  return None
636 3865ca48 Michael Hanselmann
637 3865ca48 Michael Hanselmann
638 3865ca48 Michael Hanselmann
def IsNormAbsPath(path):
639 3865ca48 Michael Hanselmann
  """Check whether a path is absolute and also normalized
640 3865ca48 Michael Hanselmann

641 3865ca48 Michael Hanselmann
  This avoids things like /dir/../../other/path to be valid.
642 3865ca48 Michael Hanselmann

643 3865ca48 Michael Hanselmann
  """
644 3865ca48 Michael Hanselmann
  return os.path.normpath(path) == path and os.path.isabs(path)
645 3865ca48 Michael Hanselmann
646 3865ca48 Michael Hanselmann
647 fb17bebd René Nussbaumer
def IsBelowDir(root, other_path):
648 fb17bebd René Nussbaumer
  """Check whether a path is below a root dir.
649 fb17bebd René Nussbaumer

650 fb17bebd René Nussbaumer
  This works around the nasty byte-byte comparisation of commonprefix.
651 fb17bebd René Nussbaumer

652 fb17bebd René Nussbaumer
  """
653 fb17bebd René Nussbaumer
  if not (os.path.isabs(root) and os.path.isabs(other_path)):
654 fb17bebd René Nussbaumer
    raise ValueError("Provided paths '%s' and '%s' are not absolute" %
655 fb17bebd René Nussbaumer
                     (root, other_path))
656 fb17bebd René Nussbaumer
  prepared_root = "%s%s" % (os.path.normpath(root), os.sep)
657 fb17bebd René Nussbaumer
  return os.path.commonprefix([prepared_root,
658 fb17bebd René Nussbaumer
                               os.path.normpath(other_path)]) == prepared_root
659 fb17bebd René Nussbaumer
660 fb17bebd René Nussbaumer
661 3865ca48 Michael Hanselmann
def PathJoin(*args):
662 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
663 3865ca48 Michael Hanselmann

664 3865ca48 Michael Hanselmann
  Requirements:
665 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
666 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
667 3865ca48 Michael Hanselmann
        since we check for normalization at the end
668 3865ca48 Michael Hanselmann

669 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
670 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
671 3865ca48 Michael Hanselmann

672 3865ca48 Michael Hanselmann
  """
673 3865ca48 Michael Hanselmann
  # ensure we're having at least one path passed in
674 3865ca48 Michael Hanselmann
  assert args
675 3865ca48 Michael Hanselmann
  # ensure the first component is an absolute and normalized path name
676 3865ca48 Michael Hanselmann
  root = args[0]
677 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(root):
678 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
679 3865ca48 Michael Hanselmann
  result = os.path.join(*args)
680 3865ca48 Michael Hanselmann
  # ensure that the whole path is normalized
681 3865ca48 Michael Hanselmann
  if not IsNormAbsPath(result):
682 3865ca48 Michael Hanselmann
    raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
683 3865ca48 Michael Hanselmann
  # check that we're still under the original prefix
684 cf00dba0 René Nussbaumer
  if not IsBelowDir(root, result):
685 3865ca48 Michael Hanselmann
    raise ValueError("Error: path joining resulted in different prefix"
686 cf00dba0 René Nussbaumer
                     " (%s != %s)" % (result, root))
687 3865ca48 Michael Hanselmann
  return result
688 3865ca48 Michael Hanselmann
689 3865ca48 Michael Hanselmann
690 3865ca48 Michael Hanselmann
def TailFile(fname, lines=20):
691 3865ca48 Michael Hanselmann
  """Return the last lines from a file.
692 3865ca48 Michael Hanselmann

693 3865ca48 Michael Hanselmann
  @note: this function will only read and parse the last 4KB of
694 3865ca48 Michael Hanselmann
      the file; if the lines are very long, it could be that less
695 3865ca48 Michael Hanselmann
      than the requested number of lines are returned
696 3865ca48 Michael Hanselmann

697 3865ca48 Michael Hanselmann
  @param fname: the file name
698 3865ca48 Michael Hanselmann
  @type lines: int
699 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
700 3865ca48 Michael Hanselmann

701 3865ca48 Michael Hanselmann
  """
702 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
703 3865ca48 Michael Hanselmann
  try:
704 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
705 3865ca48 Michael Hanselmann
    pos = fd.tell()
706 e687ec01 Michael Hanselmann
    pos = max(0, pos - 4096)
707 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
708 3865ca48 Michael Hanselmann
    raw_data = fd.read()
709 3865ca48 Michael Hanselmann
  finally:
710 3865ca48 Michael Hanselmann
    fd.close()
711 3865ca48 Michael Hanselmann
712 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
713 3865ca48 Michael Hanselmann
  return rows[-lines:]
714 3865ca48 Michael Hanselmann
715 3865ca48 Michael Hanselmann
716 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
717 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
718 3865ca48 Michael Hanselmann

719 3865ca48 Michael Hanselmann
  @type value: int
720 3865ca48 Michael Hanselmann
  @param value: Value in bytes
721 3865ca48 Michael Hanselmann
  @rtype: int
722 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
723 3865ca48 Michael Hanselmann

724 3865ca48 Michael Hanselmann
  """
725 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
726 3865ca48 Michael Hanselmann
727 3865ca48 Michael Hanselmann
728 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
729 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
730 3865ca48 Michael Hanselmann

731 3865ca48 Michael Hanselmann
  @type path: string
732 3865ca48 Michael Hanselmann
  @param path: Path to directory
733 3865ca48 Michael Hanselmann
  @rtype: int
734 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
735 3865ca48 Michael Hanselmann

736 3865ca48 Michael Hanselmann
  """
737 3865ca48 Michael Hanselmann
  size = 0
738 3865ca48 Michael Hanselmann
739 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
740 3865ca48 Michael Hanselmann
    for filename in files:
741 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
742 3865ca48 Michael Hanselmann
      size += st.st_size
743 3865ca48 Michael Hanselmann
744 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
745 3865ca48 Michael Hanselmann
746 3865ca48 Michael Hanselmann
747 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
748 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
749 3865ca48 Michael Hanselmann

750 3865ca48 Michael Hanselmann
  @type path: string
751 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
752 3865ca48 Michael Hanselmann
  @rtype: int
753 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
754 3865ca48 Michael Hanselmann

755 3865ca48 Michael Hanselmann
  """
756 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
757 3865ca48 Michael Hanselmann
758 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
759 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
760 3865ca48 Michael Hanselmann
  return (tsize, fsize)
761 3865ca48 Michael Hanselmann
762 3865ca48 Michael Hanselmann
763 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
764 3865ca48 Michael Hanselmann
  """Read a pid from a file.
765 3865ca48 Michael Hanselmann

766 3865ca48 Michael Hanselmann
  @type  pidfile: string
767 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
768 3865ca48 Michael Hanselmann
  @rtype: int
769 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
770 3865ca48 Michael Hanselmann
           otherwise 0
771 3865ca48 Michael Hanselmann

772 3865ca48 Michael Hanselmann
  """
773 3865ca48 Michael Hanselmann
  try:
774 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
775 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
776 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
777 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
778 3865ca48 Michael Hanselmann
    return 0
779 3865ca48 Michael Hanselmann
780 b6522276 Michael Hanselmann
  return _ParsePidFileContents(raw_data)
781 b6522276 Michael Hanselmann
782 b6522276 Michael Hanselmann
783 b6522276 Michael Hanselmann
def _ParsePidFileContents(data):
784 b6522276 Michael Hanselmann
  """Tries to extract a process ID from a PID file's content.
785 b6522276 Michael Hanselmann

786 b6522276 Michael Hanselmann
  @type data: string
787 b6522276 Michael Hanselmann
  @rtype: int
788 b6522276 Michael Hanselmann
  @return: Zero if nothing could be read, PID otherwise
789 b6522276 Michael Hanselmann

790 b6522276 Michael Hanselmann
  """
791 3865ca48 Michael Hanselmann
  try:
792 b6522276 Michael Hanselmann
    pid = int(data)
793 b6522276 Michael Hanselmann
  except (TypeError, ValueError):
794 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
795 3865ca48 Michael Hanselmann
    return 0
796 b6522276 Michael Hanselmann
  else:
797 b6522276 Michael Hanselmann
    return pid
798 3865ca48 Michael Hanselmann
799 3865ca48 Michael Hanselmann
800 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
801 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
802 3865ca48 Michael Hanselmann

803 a4ccecf6 Michael Hanselmann
  This can be used together with L{utils.process.StartDaemon}.
804 3865ca48 Michael Hanselmann

805 3865ca48 Michael Hanselmann
  @type path: string
806 3865ca48 Michael Hanselmann
  @param path: Path to PID file
807 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
808 3865ca48 Michael Hanselmann

809 3865ca48 Michael Hanselmann
  """
810 3865ca48 Michael Hanselmann
  try:
811 3865ca48 Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
812 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
813 3865ca48 Michael Hanselmann
    if err.errno == errno.ENOENT:
814 3865ca48 Michael Hanselmann
      # PID file doesn't exist
815 3865ca48 Michael Hanselmann
      return None
816 3865ca48 Michael Hanselmann
    raise
817 3865ca48 Michael Hanselmann
818 3865ca48 Michael Hanselmann
  try:
819 3865ca48 Michael Hanselmann
    try:
820 3865ca48 Michael Hanselmann
      # Try to acquire lock
821 3865ca48 Michael Hanselmann
      filelock.LockFile(fd)
822 3865ca48 Michael Hanselmann
    except errors.LockError:
823 3865ca48 Michael Hanselmann
      # Couldn't lock, daemon is running
824 3865ca48 Michael Hanselmann
      return int(os.read(fd, 100))
825 3865ca48 Michael Hanselmann
  finally:
826 3865ca48 Michael Hanselmann
    os.close(fd)
827 3865ca48 Michael Hanselmann
828 3865ca48 Michael Hanselmann
  return None
829 3865ca48 Michael Hanselmann
830 3865ca48 Michael Hanselmann
831 0232b768 Michael Hanselmann
_SSH_KEYS_WITH_TWO_PARTS = frozenset(["ssh-dss", "ssh-rsa"])
832 0232b768 Michael Hanselmann
833 0232b768 Michael Hanselmann
834 0232b768 Michael Hanselmann
def _SplitSshKey(key):
835 0232b768 Michael Hanselmann
  """Splits a line for SSH's C{authorized_keys} file.
836 0232b768 Michael Hanselmann

837 0232b768 Michael Hanselmann
  If the line has no options (e.g. no C{command="..."}), only the significant
838 0232b768 Michael Hanselmann
  parts, the key type and its hash, are used. Otherwise the whole line is used
839 0232b768 Michael Hanselmann
  (split at whitespace).
840 0232b768 Michael Hanselmann

841 0232b768 Michael Hanselmann
  @type key: string
842 0232b768 Michael Hanselmann
  @param key: Key line
843 0232b768 Michael Hanselmann
  @rtype: tuple
844 0232b768 Michael Hanselmann

845 0232b768 Michael Hanselmann
  """
846 0232b768 Michael Hanselmann
  parts = key.split()
847 0232b768 Michael Hanselmann
848 0232b768 Michael Hanselmann
  if parts and parts[0] in _SSH_KEYS_WITH_TWO_PARTS:
849 0232b768 Michael Hanselmann
    # If the key has no options in front of it, we only want the significant
850 0232b768 Michael Hanselmann
    # fields
851 0232b768 Michael Hanselmann
    return (False, parts[:2])
852 0232b768 Michael Hanselmann
  else:
853 0232b768 Michael Hanselmann
    # Can't properly split the line, so use everything
854 0232b768 Michael Hanselmann
    return (True, parts)
855 0232b768 Michael Hanselmann
856 0232b768 Michael Hanselmann
857 3865ca48 Michael Hanselmann
def AddAuthorizedKey(file_obj, key):
858 3865ca48 Michael Hanselmann
  """Adds an SSH public key to an authorized_keys file.
859 3865ca48 Michael Hanselmann

860 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
861 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
862 3865ca48 Michael Hanselmann
  @type key: str
863 3865ca48 Michael Hanselmann
  @param key: string containing key
864 3865ca48 Michael Hanselmann

865 3865ca48 Michael Hanselmann
  """
866 0232b768 Michael Hanselmann
  key_fields = _SplitSshKey(key)
867 3865ca48 Michael Hanselmann
868 3865ca48 Michael Hanselmann
  if isinstance(file_obj, basestring):
869 d0c8c01d Iustin Pop
    f = open(file_obj, "a+")
870 3865ca48 Michael Hanselmann
  else:
871 3865ca48 Michael Hanselmann
    f = file_obj
872 3865ca48 Michael Hanselmann
873 3865ca48 Michael Hanselmann
  try:
874 3865ca48 Michael Hanselmann
    nl = True
875 3865ca48 Michael Hanselmann
    for line in f:
876 3865ca48 Michael Hanselmann
      # Ignore whitespace changes
877 0232b768 Michael Hanselmann
      if _SplitSshKey(line) == key_fields:
878 3865ca48 Michael Hanselmann
        break
879 d0c8c01d Iustin Pop
      nl = line.endswith("\n")
880 3865ca48 Michael Hanselmann
    else:
881 3865ca48 Michael Hanselmann
      if not nl:
882 3865ca48 Michael Hanselmann
        f.write("\n")
883 d0c8c01d Iustin Pop
      f.write(key.rstrip("\r\n"))
884 3865ca48 Michael Hanselmann
      f.write("\n")
885 3865ca48 Michael Hanselmann
      f.flush()
886 3865ca48 Michael Hanselmann
  finally:
887 3865ca48 Michael Hanselmann
    f.close()
888 3865ca48 Michael Hanselmann
889 3865ca48 Michael Hanselmann
890 3865ca48 Michael Hanselmann
def RemoveAuthorizedKey(file_name, key):
891 3865ca48 Michael Hanselmann
  """Removes an SSH public key from an authorized_keys file.
892 3865ca48 Michael Hanselmann

893 3865ca48 Michael Hanselmann
  @type file_name: str
894 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
895 3865ca48 Michael Hanselmann
  @type key: str
896 3865ca48 Michael Hanselmann
  @param key: string containing key
897 3865ca48 Michael Hanselmann

898 3865ca48 Michael Hanselmann
  """
899 0232b768 Michael Hanselmann
  key_fields = _SplitSshKey(key)
900 3865ca48 Michael Hanselmann
901 3865ca48 Michael Hanselmann
  fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
902 3865ca48 Michael Hanselmann
  try:
903 d0c8c01d Iustin Pop
    out = os.fdopen(fd, "w")
904 3865ca48 Michael Hanselmann
    try:
905 d0c8c01d Iustin Pop
      f = open(file_name, "r")
906 3865ca48 Michael Hanselmann
      try:
907 3865ca48 Michael Hanselmann
        for line in f:
908 3865ca48 Michael Hanselmann
          # Ignore whitespace changes while comparing lines
909 0232b768 Michael Hanselmann
          if _SplitSshKey(line) != key_fields:
910 3865ca48 Michael Hanselmann
            out.write(line)
911 3865ca48 Michael Hanselmann
912 3865ca48 Michael Hanselmann
        out.flush()
913 3865ca48 Michael Hanselmann
        os.rename(tmpname, file_name)
914 3865ca48 Michael Hanselmann
      finally:
915 3865ca48 Michael Hanselmann
        f.close()
916 3865ca48 Michael Hanselmann
    finally:
917 3865ca48 Michael Hanselmann
      out.close()
918 3865ca48 Michael Hanselmann
  except:
919 3865ca48 Michael Hanselmann
    RemoveFile(tmpname)
920 3865ca48 Michael Hanselmann
    raise
921 3865ca48 Michael Hanselmann
922 3865ca48 Michael Hanselmann
923 3865ca48 Michael Hanselmann
def DaemonPidFileName(name):
924 3865ca48 Michael Hanselmann
  """Compute a ganeti pid file absolute path
925 3865ca48 Michael Hanselmann

926 3865ca48 Michael Hanselmann
  @type name: str
927 3865ca48 Michael Hanselmann
  @param name: the daemon name
928 3865ca48 Michael Hanselmann
  @rtype: str
929 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
930 3865ca48 Michael Hanselmann
      daemon name
931 3865ca48 Michael Hanselmann

932 3865ca48 Michael Hanselmann
  """
933 111a7d04 Michael Hanselmann
  return PathJoin(pathutils.RUN_DIR, "%s.pid" % name)
934 3865ca48 Michael Hanselmann
935 3865ca48 Michael Hanselmann
936 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
937 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
938 3865ca48 Michael Hanselmann

939 3865ca48 Michael Hanselmann
  @type pidfile: string
940 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
941 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
942 3865ca48 Michael Hanselmann
      points to a live process
943 3865ca48 Michael Hanselmann
  @rtype: int
944 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
945 3865ca48 Michael Hanselmann
      you want to unlock the pid file
946 3865ca48 Michael Hanselmann

947 3865ca48 Michael Hanselmann
  """
948 3865ca48 Michael Hanselmann
  # We don't rename nor truncate the file to not drop locks under
949 3865ca48 Michael Hanselmann
  # existing processes
950 b6522276 Michael Hanselmann
  fd_pidfile = os.open(pidfile, os.O_RDWR | os.O_CREAT, 0600)
951 3865ca48 Michael Hanselmann
952 3865ca48 Michael Hanselmann
  # Lock the PID file (and fail if not possible to do so). Any code
953 3865ca48 Michael Hanselmann
  # wanting to send a signal to the daemon should try to lock the PID
954 3865ca48 Michael Hanselmann
  # file before reading it. If acquiring the lock succeeds, the daemon is
955 3865ca48 Michael Hanselmann
  # no longer running and the signal should not be sent.
956 b6522276 Michael Hanselmann
  try:
957 b6522276 Michael Hanselmann
    filelock.LockFile(fd_pidfile)
958 b6522276 Michael Hanselmann
  except errors.LockError:
959 b6522276 Michael Hanselmann
    msg = ["PID file '%s' is already locked by another process" % pidfile]
960 b6522276 Michael Hanselmann
    # Try to read PID file
961 b6522276 Michael Hanselmann
    pid = _ParsePidFileContents(os.read(fd_pidfile, 100))
962 b6522276 Michael Hanselmann
    if pid > 0:
963 b6522276 Michael Hanselmann
      msg.append(", PID read from file is %s" % pid)
964 b6522276 Michael Hanselmann
    raise errors.PidFileLockError("".join(msg))
965 3865ca48 Michael Hanselmann
966 3865ca48 Michael Hanselmann
  os.write(fd_pidfile, "%d\n" % os.getpid())
967 3865ca48 Michael Hanselmann
968 3865ca48 Michael Hanselmann
  return fd_pidfile
969 3865ca48 Michael Hanselmann
970 3865ca48 Michael Hanselmann
971 3865ca48 Michael Hanselmann
def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
972 3865ca48 Michael Hanselmann
  """Reads the watcher pause file.
973 3865ca48 Michael Hanselmann

974 3865ca48 Michael Hanselmann
  @type filename: string
975 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
976 3865ca48 Michael Hanselmann
  @type now: None, float or int
977 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
978 3865ca48 Michael Hanselmann
  @type remove_after: int
979 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
980 3865ca48 Michael Hanselmann
    seconds past the pause end time
981 3865ca48 Michael Hanselmann

982 3865ca48 Michael Hanselmann
  """
983 3865ca48 Michael Hanselmann
  if now is None:
984 3865ca48 Michael Hanselmann
    now = time.time()
985 3865ca48 Michael Hanselmann
986 3865ca48 Michael Hanselmann
  try:
987 3865ca48 Michael Hanselmann
    value = ReadFile(filename)
988 3865ca48 Michael Hanselmann
  except IOError, err:
989 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
990 3865ca48 Michael Hanselmann
      raise
991 3865ca48 Michael Hanselmann
    value = None
992 3865ca48 Michael Hanselmann
993 3865ca48 Michael Hanselmann
  if value is not None:
994 3865ca48 Michael Hanselmann
    try:
995 3865ca48 Michael Hanselmann
      value = int(value)
996 3865ca48 Michael Hanselmann
    except ValueError:
997 3865ca48 Michael Hanselmann
      logging.warning(("Watcher pause file (%s) contains invalid value,"
998 3865ca48 Michael Hanselmann
                       " removing it"), filename)
999 3865ca48 Michael Hanselmann
      RemoveFile(filename)
1000 3865ca48 Michael Hanselmann
      value = None
1001 3865ca48 Michael Hanselmann
1002 3865ca48 Michael Hanselmann
    if value is not None:
1003 3865ca48 Michael Hanselmann
      # Remove file if it's outdated
1004 3865ca48 Michael Hanselmann
      if now > (value + remove_after):
1005 3865ca48 Michael Hanselmann
        RemoveFile(filename)
1006 3865ca48 Michael Hanselmann
        value = None
1007 3865ca48 Michael Hanselmann
1008 3865ca48 Michael Hanselmann
      elif now > value:
1009 3865ca48 Michael Hanselmann
        value = None
1010 3865ca48 Michael Hanselmann
1011 3865ca48 Michael Hanselmann
  return value
1012 90e234a6 Michael Hanselmann
1013 90e234a6 Michael Hanselmann
1014 90e234a6 Michael Hanselmann
def NewUUID():
1015 90e234a6 Michael Hanselmann
  """Returns a random UUID.
1016 90e234a6 Michael Hanselmann

1017 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
1018 90e234a6 Michael Hanselmann
      filesystem.
1019 90e234a6 Michael Hanselmann
  @rtype: str
1020 90e234a6 Michael Hanselmann

1021 90e234a6 Michael Hanselmann
  """
1022 90e234a6 Michael Hanselmann
  return ReadFile(_RANDOM_UUID_FILE, size=128).rstrip("\n")
1023 0c1a5b1e Agata Murawska
1024 0c1a5b1e Agata Murawska
1025 0c1a5b1e Agata Murawska
class TemporaryFileManager(object):
1026 0c1a5b1e Agata Murawska
  """Stores the list of files to be deleted and removes them on demand.
1027 0c1a5b1e Agata Murawska

1028 0c1a5b1e Agata Murawska
  """
1029 0c1a5b1e Agata Murawska
1030 0c1a5b1e Agata Murawska
  def __init__(self):
1031 0c1a5b1e Agata Murawska
    self._files = []
1032 0c1a5b1e Agata Murawska
1033 0c1a5b1e Agata Murawska
  def __del__(self):
1034 0c1a5b1e Agata Murawska
    self.Cleanup()
1035 0c1a5b1e Agata Murawska
1036 0c1a5b1e Agata Murawska
  def Add(self, filename):
1037 0c1a5b1e Agata Murawska
    """Add file to list of files to be deleted.
1038 0c1a5b1e Agata Murawska

1039 0c1a5b1e Agata Murawska
    @type filename: string
1040 0c1a5b1e Agata Murawska
    @param filename: path to filename to be added
1041 0c1a5b1e Agata Murawska

1042 0c1a5b1e Agata Murawska
    """
1043 0c1a5b1e Agata Murawska
    self._files.append(filename)
1044 0c1a5b1e Agata Murawska
1045 0c1a5b1e Agata Murawska
  def Remove(self, filename):
1046 0c1a5b1e Agata Murawska
    """Remove file from list of files to be deleted.
1047 0c1a5b1e Agata Murawska

1048 0c1a5b1e Agata Murawska
    @type filename: string
1049 0c1a5b1e Agata Murawska
    @param filename: path to filename to be deleted
1050 0c1a5b1e Agata Murawska

1051 0c1a5b1e Agata Murawska
    """
1052 0c1a5b1e Agata Murawska
    self._files.remove(filename)
1053 0c1a5b1e Agata Murawska
1054 0c1a5b1e Agata Murawska
  def Cleanup(self):
1055 0c1a5b1e Agata Murawska
    """Delete all files marked for deletion
1056 0c1a5b1e Agata Murawska

1057 0c1a5b1e Agata Murawska
    """
1058 0c1a5b1e Agata Murawska
    while self._files:
1059 0c1a5b1e Agata Murawska
      RemoveFile(self._files.pop())