Statistics
| Branch: | Tag: | Revision:

root / lib / utils / io.py @ 1cc324f0

History | View | Annotate | Download (29.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 fc84ef94 Michele Tartara
import grp
33 fc84ef94 Michele Tartara
import pwd
34 3865ca48 Michael Hanselmann
35 3865ca48 Michael Hanselmann
from ganeti import errors
36 3865ca48 Michael Hanselmann
from ganeti import constants
37 111a7d04 Michael Hanselmann
from ganeti import pathutils
38 3865ca48 Michael Hanselmann
from ganeti.utils import filelock
39 3865ca48 Michael Hanselmann
40 2dbc6857 Michael Hanselmann
#: Directory used by fsck(8) to store recovered data, usually at a file
41 2dbc6857 Michael Hanselmann
#: system's root directory
42 2dbc6857 Michael Hanselmann
_LOST_AND_FOUND = "lost+found"
43 2dbc6857 Michael Hanselmann
44 c47eddb8 Bernardo Dal Seno
# Possible values for keep_perms in WriteFile()
45 c47eddb8 Bernardo Dal Seno
KP_NEVER = 0
46 c47eddb8 Bernardo Dal Seno
KP_ALWAYS = 1
47 c47eddb8 Bernardo Dal Seno
KP_IF_EXISTS = 2
48 c47eddb8 Bernardo Dal Seno
49 c47eddb8 Bernardo Dal Seno
KEEP_PERMS_VALUES = [
50 c47eddb8 Bernardo Dal Seno
  KP_NEVER,
51 c47eddb8 Bernardo Dal Seno
  KP_ALWAYS,
52 c47eddb8 Bernardo Dal Seno
  KP_IF_EXISTS,
53 c47eddb8 Bernardo Dal Seno
  ]
54 c47eddb8 Bernardo Dal Seno
55 90e234a6 Michael Hanselmann
56 eb93b673 Guido Trotter
def ErrnoOrStr(err):
57 eb93b673 Guido Trotter
  """Format an EnvironmentError exception.
58 eb93b673 Guido Trotter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

262 3865ca48 Michael Hanselmann
  Either the path to the file or the fd must be given.
263 3865ca48 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

648 2826897c Michael Hanselmann
  This works around the nasty byte-byte comparison of commonprefix.
649 fb17bebd René Nussbaumer

650 fb17bebd René Nussbaumer
  """
651 fb17bebd René Nussbaumer
  if not (os.path.isabs(root) and os.path.isabs(other_path)):
652 fb17bebd René Nussbaumer
    raise ValueError("Provided paths '%s' and '%s' are not absolute" %
653 fb17bebd René Nussbaumer
                     (root, other_path))
654 2826897c Michael Hanselmann
655 2826897c Michael Hanselmann
  norm_other = os.path.normpath(other_path)
656 2826897c Michael Hanselmann
657 2826897c Michael Hanselmann
  if norm_other == os.sep:
658 2826897c Michael Hanselmann
    # The root directory can never be below another path
659 2826897c Michael Hanselmann
    return False
660 2826897c Michael Hanselmann
661 2826897c Michael Hanselmann
  norm_root = os.path.normpath(root)
662 2826897c Michael Hanselmann
663 2826897c Michael Hanselmann
  if norm_root == os.sep:
664 2826897c Michael Hanselmann
    # This is the root directory, no need to add another slash
665 2826897c Michael Hanselmann
    prepared_root = norm_root
666 2826897c Michael Hanselmann
  else:
667 2826897c Michael Hanselmann
    prepared_root = "%s%s" % (norm_root, os.sep)
668 2826897c Michael Hanselmann
669 2826897c Michael Hanselmann
  return os.path.commonprefix([prepared_root, norm_other]) == prepared_root
670 fb17bebd René Nussbaumer
671 fb17bebd René Nussbaumer
672 3865ca48 Michael Hanselmann
def PathJoin(*args):
673 3865ca48 Michael Hanselmann
  """Safe-join a list of path components.
674 3865ca48 Michael Hanselmann

675 3865ca48 Michael Hanselmann
  Requirements:
676 3865ca48 Michael Hanselmann
      - the first argument must be an absolute path
677 3865ca48 Michael Hanselmann
      - no component in the path must have backtracking (e.g. /../),
678 3865ca48 Michael Hanselmann
        since we check for normalization at the end
679 3865ca48 Michael Hanselmann

680 3865ca48 Michael Hanselmann
  @param args: the path components to be joined
681 3865ca48 Michael Hanselmann
  @raise ValueError: for invalid paths
682 3865ca48 Michael Hanselmann

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

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

708 3865ca48 Michael Hanselmann
  @param fname: the file name
709 3865ca48 Michael Hanselmann
  @type lines: int
710 3865ca48 Michael Hanselmann
  @param lines: the (maximum) number of lines to return
711 3865ca48 Michael Hanselmann

712 3865ca48 Michael Hanselmann
  """
713 3865ca48 Michael Hanselmann
  fd = open(fname, "r")
714 3865ca48 Michael Hanselmann
  try:
715 3865ca48 Michael Hanselmann
    fd.seek(0, 2)
716 3865ca48 Michael Hanselmann
    pos = fd.tell()
717 e687ec01 Michael Hanselmann
    pos = max(0, pos - 4096)
718 3865ca48 Michael Hanselmann
    fd.seek(pos, 0)
719 3865ca48 Michael Hanselmann
    raw_data = fd.read()
720 3865ca48 Michael Hanselmann
  finally:
721 3865ca48 Michael Hanselmann
    fd.close()
722 3865ca48 Michael Hanselmann
723 3865ca48 Michael Hanselmann
  rows = raw_data.splitlines()
724 3865ca48 Michael Hanselmann
  return rows[-lines:]
725 3865ca48 Michael Hanselmann
726 3865ca48 Michael Hanselmann
727 3865ca48 Michael Hanselmann
def BytesToMebibyte(value):
728 3865ca48 Michael Hanselmann
  """Converts bytes to mebibytes.
729 3865ca48 Michael Hanselmann

730 3865ca48 Michael Hanselmann
  @type value: int
731 3865ca48 Michael Hanselmann
  @param value: Value in bytes
732 3865ca48 Michael Hanselmann
  @rtype: int
733 3865ca48 Michael Hanselmann
  @return: Value in mebibytes
734 3865ca48 Michael Hanselmann

735 3865ca48 Michael Hanselmann
  """
736 3865ca48 Michael Hanselmann
  return int(round(value / (1024.0 * 1024.0), 0))
737 3865ca48 Michael Hanselmann
738 3865ca48 Michael Hanselmann
739 3865ca48 Michael Hanselmann
def CalculateDirectorySize(path):
740 3865ca48 Michael Hanselmann
  """Calculates the size of a directory recursively.
741 3865ca48 Michael Hanselmann

742 3865ca48 Michael Hanselmann
  @type path: string
743 3865ca48 Michael Hanselmann
  @param path: Path to directory
744 3865ca48 Michael Hanselmann
  @rtype: int
745 3865ca48 Michael Hanselmann
  @return: Size in mebibytes
746 3865ca48 Michael Hanselmann

747 3865ca48 Michael Hanselmann
  """
748 3865ca48 Michael Hanselmann
  size = 0
749 3865ca48 Michael Hanselmann
750 3865ca48 Michael Hanselmann
  for (curpath, _, files) in os.walk(path):
751 3865ca48 Michael Hanselmann
    for filename in files:
752 3865ca48 Michael Hanselmann
      st = os.lstat(PathJoin(curpath, filename))
753 3865ca48 Michael Hanselmann
      size += st.st_size
754 3865ca48 Michael Hanselmann
755 3865ca48 Michael Hanselmann
  return BytesToMebibyte(size)
756 3865ca48 Michael Hanselmann
757 3865ca48 Michael Hanselmann
758 3865ca48 Michael Hanselmann
def GetFilesystemStats(path):
759 3865ca48 Michael Hanselmann
  """Returns the total and free space on a filesystem.
760 3865ca48 Michael Hanselmann

761 3865ca48 Michael Hanselmann
  @type path: string
762 3865ca48 Michael Hanselmann
  @param path: Path on filesystem to be examined
763 3865ca48 Michael Hanselmann
  @rtype: int
764 3865ca48 Michael Hanselmann
  @return: tuple of (Total space, Free space) in mebibytes
765 3865ca48 Michael Hanselmann

766 3865ca48 Michael Hanselmann
  """
767 3865ca48 Michael Hanselmann
  st = os.statvfs(path)
768 3865ca48 Michael Hanselmann
769 3865ca48 Michael Hanselmann
  fsize = BytesToMebibyte(st.f_bavail * st.f_frsize)
770 3865ca48 Michael Hanselmann
  tsize = BytesToMebibyte(st.f_blocks * st.f_frsize)
771 3865ca48 Michael Hanselmann
  return (tsize, fsize)
772 3865ca48 Michael Hanselmann
773 3865ca48 Michael Hanselmann
774 3865ca48 Michael Hanselmann
def ReadPidFile(pidfile):
775 3865ca48 Michael Hanselmann
  """Read a pid from a file.
776 3865ca48 Michael Hanselmann

777 3865ca48 Michael Hanselmann
  @type  pidfile: string
778 3865ca48 Michael Hanselmann
  @param pidfile: path to the file containing the pid
779 3865ca48 Michael Hanselmann
  @rtype: int
780 3865ca48 Michael Hanselmann
  @return: The process id, if the file exists and contains a valid PID,
781 3865ca48 Michael Hanselmann
           otherwise 0
782 3865ca48 Michael Hanselmann

783 3865ca48 Michael Hanselmann
  """
784 3865ca48 Michael Hanselmann
  try:
785 3865ca48 Michael Hanselmann
    raw_data = ReadOneLineFile(pidfile)
786 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
787 3865ca48 Michael Hanselmann
    if err.errno != errno.ENOENT:
788 3865ca48 Michael Hanselmann
      logging.exception("Can't read pid file")
789 3865ca48 Michael Hanselmann
    return 0
790 3865ca48 Michael Hanselmann
791 b6522276 Michael Hanselmann
  return _ParsePidFileContents(raw_data)
792 b6522276 Michael Hanselmann
793 b6522276 Michael Hanselmann
794 b6522276 Michael Hanselmann
def _ParsePidFileContents(data):
795 b6522276 Michael Hanselmann
  """Tries to extract a process ID from a PID file's content.
796 b6522276 Michael Hanselmann

797 b6522276 Michael Hanselmann
  @type data: string
798 b6522276 Michael Hanselmann
  @rtype: int
799 b6522276 Michael Hanselmann
  @return: Zero if nothing could be read, PID otherwise
800 b6522276 Michael Hanselmann

801 b6522276 Michael Hanselmann
  """
802 3865ca48 Michael Hanselmann
  try:
803 b6522276 Michael Hanselmann
    pid = int(data)
804 b6522276 Michael Hanselmann
  except (TypeError, ValueError):
805 3865ca48 Michael Hanselmann
    logging.info("Can't parse pid file contents", exc_info=True)
806 3865ca48 Michael Hanselmann
    return 0
807 b6522276 Michael Hanselmann
  else:
808 b6522276 Michael Hanselmann
    return pid
809 3865ca48 Michael Hanselmann
810 3865ca48 Michael Hanselmann
811 3865ca48 Michael Hanselmann
def ReadLockedPidFile(path):
812 3865ca48 Michael Hanselmann
  """Reads a locked PID file.
813 3865ca48 Michael Hanselmann

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

816 3865ca48 Michael Hanselmann
  @type path: string
817 3865ca48 Michael Hanselmann
  @param path: Path to PID file
818 3865ca48 Michael Hanselmann
  @return: PID as integer or, if file was unlocked or couldn't be opened, None
819 3865ca48 Michael Hanselmann

820 3865ca48 Michael Hanselmann
  """
821 3865ca48 Michael Hanselmann
  try:
822 3865ca48 Michael Hanselmann
    fd = os.open(path, os.O_RDONLY)
823 3865ca48 Michael Hanselmann
  except EnvironmentError, err:
824 3865ca48 Michael Hanselmann
    if err.errno == errno.ENOENT:
825 3865ca48 Michael Hanselmann
      # PID file doesn't exist
826 3865ca48 Michael Hanselmann
      return None
827 3865ca48 Michael Hanselmann
    raise
828 3865ca48 Michael Hanselmann
829 3865ca48 Michael Hanselmann
  try:
830 3865ca48 Michael Hanselmann
    try:
831 3865ca48 Michael Hanselmann
      # Try to acquire lock
832 3865ca48 Michael Hanselmann
      filelock.LockFile(fd)
833 3865ca48 Michael Hanselmann
    except errors.LockError:
834 3865ca48 Michael Hanselmann
      # Couldn't lock, daemon is running
835 3865ca48 Michael Hanselmann
      return int(os.read(fd, 100))
836 3865ca48 Michael Hanselmann
  finally:
837 3865ca48 Michael Hanselmann
    os.close(fd)
838 3865ca48 Michael Hanselmann
839 3865ca48 Michael Hanselmann
  return None
840 3865ca48 Michael Hanselmann
841 3865ca48 Michael Hanselmann
842 0232b768 Michael Hanselmann
def _SplitSshKey(key):
843 0232b768 Michael Hanselmann
  """Splits a line for SSH's C{authorized_keys} file.
844 0232b768 Michael Hanselmann

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

849 0232b768 Michael Hanselmann
  @type key: string
850 0232b768 Michael Hanselmann
  @param key: Key line
851 0232b768 Michael Hanselmann
  @rtype: tuple
852 0232b768 Michael Hanselmann

853 0232b768 Michael Hanselmann
  """
854 0232b768 Michael Hanselmann
  parts = key.split()
855 0232b768 Michael Hanselmann
856 d12b9f66 Michael Hanselmann
  if parts and parts[0] in constants.SSHAK_ALL:
857 0232b768 Michael Hanselmann
    # If the key has no options in front of it, we only want the significant
858 0232b768 Michael Hanselmann
    # fields
859 0232b768 Michael Hanselmann
    return (False, parts[:2])
860 0232b768 Michael Hanselmann
  else:
861 0232b768 Michael Hanselmann
    # Can't properly split the line, so use everything
862 0232b768 Michael Hanselmann
    return (True, parts)
863 0232b768 Michael Hanselmann
864 0232b768 Michael Hanselmann
865 3865ca48 Michael Hanselmann
def AddAuthorizedKey(file_obj, key):
866 3865ca48 Michael Hanselmann
  """Adds an SSH public key to an authorized_keys file.
867 3865ca48 Michael Hanselmann

868 3865ca48 Michael Hanselmann
  @type file_obj: str or file handle
869 3865ca48 Michael Hanselmann
  @param file_obj: path to authorized_keys file
870 3865ca48 Michael Hanselmann
  @type key: str
871 3865ca48 Michael Hanselmann
  @param key: string containing key
872 3865ca48 Michael Hanselmann

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

901 3865ca48 Michael Hanselmann
  @type file_name: str
902 3865ca48 Michael Hanselmann
  @param file_name: path to authorized_keys file
903 3865ca48 Michael Hanselmann
  @type key: str
904 3865ca48 Michael Hanselmann
  @param key: string containing key
905 3865ca48 Michael Hanselmann

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

934 3865ca48 Michael Hanselmann
  @type name: str
935 3865ca48 Michael Hanselmann
  @param name: the daemon name
936 3865ca48 Michael Hanselmann
  @rtype: str
937 3865ca48 Michael Hanselmann
  @return: the full path to the pidfile corresponding to the given
938 3865ca48 Michael Hanselmann
      daemon name
939 3865ca48 Michael Hanselmann

940 3865ca48 Michael Hanselmann
  """
941 111a7d04 Michael Hanselmann
  return PathJoin(pathutils.RUN_DIR, "%s.pid" % name)
942 3865ca48 Michael Hanselmann
943 3865ca48 Michael Hanselmann
944 3865ca48 Michael Hanselmann
def WritePidFile(pidfile):
945 3865ca48 Michael Hanselmann
  """Write the current process pidfile.
946 3865ca48 Michael Hanselmann

947 3865ca48 Michael Hanselmann
  @type pidfile: string
948 3865ca48 Michael Hanselmann
  @param pidfile: the path to the file to be written
949 3865ca48 Michael Hanselmann
  @raise errors.LockError: if the pid file already exists and
950 3865ca48 Michael Hanselmann
      points to a live process
951 3865ca48 Michael Hanselmann
  @rtype: int
952 3865ca48 Michael Hanselmann
  @return: the file descriptor of the lock file; do not close this unless
953 3865ca48 Michael Hanselmann
      you want to unlock the pid file
954 3865ca48 Michael Hanselmann

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

982 3865ca48 Michael Hanselmann
  @type filename: string
983 3865ca48 Michael Hanselmann
  @param filename: Path to watcher pause file
984 3865ca48 Michael Hanselmann
  @type now: None, float or int
985 3865ca48 Michael Hanselmann
  @param now: Current time as Unix timestamp
986 3865ca48 Michael Hanselmann
  @type remove_after: int
987 3865ca48 Michael Hanselmann
  @param remove_after: Remove watcher pause file after specified amount of
988 3865ca48 Michael Hanselmann
    seconds past the pause end time
989 3865ca48 Michael Hanselmann

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

1025 90e234a6 Michael Hanselmann
  @note: This is a Linux-specific method as it uses the /proc
1026 90e234a6 Michael Hanselmann
      filesystem.
1027 90e234a6 Michael Hanselmann
  @rtype: str
1028 90e234a6 Michael Hanselmann

1029 90e234a6 Michael Hanselmann
  """
1030 80a0546b Michele Tartara
  return ReadFile(constants.RANDOM_UUID_FILE, size=128).rstrip("\n")
1031 0c1a5b1e Agata Murawska
1032 0c1a5b1e Agata Murawska
1033 0c1a5b1e Agata Murawska
class TemporaryFileManager(object):
1034 0c1a5b1e Agata Murawska
  """Stores the list of files to be deleted and removes them on demand.
1035 0c1a5b1e Agata Murawska

1036 0c1a5b1e Agata Murawska
  """
1037 0c1a5b1e Agata Murawska
1038 0c1a5b1e Agata Murawska
  def __init__(self):
1039 0c1a5b1e Agata Murawska
    self._files = []
1040 0c1a5b1e Agata Murawska
1041 0c1a5b1e Agata Murawska
  def __del__(self):
1042 0c1a5b1e Agata Murawska
    self.Cleanup()
1043 0c1a5b1e Agata Murawska
1044 0c1a5b1e Agata Murawska
  def Add(self, filename):
1045 0c1a5b1e Agata Murawska
    """Add file to list of files to be deleted.
1046 0c1a5b1e Agata Murawska

1047 0c1a5b1e Agata Murawska
    @type filename: string
1048 0c1a5b1e Agata Murawska
    @param filename: path to filename to be added
1049 0c1a5b1e Agata Murawska

1050 0c1a5b1e Agata Murawska
    """
1051 0c1a5b1e Agata Murawska
    self._files.append(filename)
1052 0c1a5b1e Agata Murawska
1053 0c1a5b1e Agata Murawska
  def Remove(self, filename):
1054 0c1a5b1e Agata Murawska
    """Remove file from list of files to be deleted.
1055 0c1a5b1e Agata Murawska

1056 0c1a5b1e Agata Murawska
    @type filename: string
1057 0c1a5b1e Agata Murawska
    @param filename: path to filename to be deleted
1058 0c1a5b1e Agata Murawska

1059 0c1a5b1e Agata Murawska
    """
1060 0c1a5b1e Agata Murawska
    self._files.remove(filename)
1061 0c1a5b1e Agata Murawska
1062 0c1a5b1e Agata Murawska
  def Cleanup(self):
1063 0c1a5b1e Agata Murawska
    """Delete all files marked for deletion
1064 0c1a5b1e Agata Murawska

1065 0c1a5b1e Agata Murawska
    """
1066 0c1a5b1e Agata Murawska
    while self._files:
1067 0c1a5b1e Agata Murawska
      RemoveFile(self._files.pop())
1068 fc84ef94 Michele Tartara
1069 fc84ef94 Michele Tartara
1070 fc84ef94 Michele Tartara
def IsUserInGroup(uid, gid):
1071 fc84ef94 Michele Tartara
  """Returns True if the user belongs to the group.
1072 fc84ef94 Michele Tartara

1073 fc84ef94 Michele Tartara
  @type uid: int
1074 fc84ef94 Michele Tartara
  @param uid: the user id
1075 fc84ef94 Michele Tartara
  @type gid: int
1076 fc84ef94 Michele Tartara
  @param gid: the group id
1077 fc84ef94 Michele Tartara
  @rtype: bool
1078 fc84ef94 Michele Tartara

1079 fc84ef94 Michele Tartara
  """
1080 fc84ef94 Michele Tartara
  user = pwd.getpwuid(uid)
1081 fc84ef94 Michele Tartara
  group = grp.getgrgid(gid)
1082 fc84ef94 Michele Tartara
  return user.pw_gid == gid or user.pw_name in group.gr_mem
1083 fc84ef94 Michele Tartara
1084 fc84ef94 Michele Tartara
1085 fc84ef94 Michele Tartara
def CanRead(username, filename):
1086 fc84ef94 Michele Tartara
  """Returns True if the user can access (read) the file.
1087 fc84ef94 Michele Tartara

1088 fc84ef94 Michele Tartara
  @type username: string
1089 fc84ef94 Michele Tartara
  @param username: the name of the user
1090 fc84ef94 Michele Tartara
  @type filename: string
1091 fc84ef94 Michele Tartara
  @param filename: the name of the file
1092 fc84ef94 Michele Tartara
  @rtype: bool
1093 fc84ef94 Michele Tartara

1094 fc84ef94 Michele Tartara
  """
1095 fc84ef94 Michele Tartara
  filestats = os.stat(filename)
1096 fc84ef94 Michele Tartara
  user = pwd.getpwnam(username)
1097 fc84ef94 Michele Tartara
  uid = user.pw_uid
1098 fc84ef94 Michele Tartara
  user_readable = filestats.st_mode & stat.S_IRUSR != 0
1099 fc84ef94 Michele Tartara
  group_readable = filestats.st_mode & stat.S_IRGRP != 0
1100 fc84ef94 Michele Tartara
  return ((filestats.st_uid == uid and user_readable)
1101 fc84ef94 Michele Tartara
          or (filestats.st_uid != uid and
1102 fc84ef94 Michele Tartara
              IsUserInGroup(uid, filestats.st_gid) and group_readable))