diff --git a/lib/utils/io.py b/lib/utils/io.py
index 7e6fab8eb6c140860ea79aed34075040cf0d95eb..ebae9dff6be791caaf1ac68df75fba879270bafc 100644
--- a/lib/utils/io.py
+++ b/lib/utils/io.py
@@ -38,6 +38,17 @@ from ganeti.utils import filelock
#: Path generating random UUID
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
+# Possible values for keep_perms in WriteFile()
+KP_NEVER = 0
+KP_ALWAYS = 1
+KP_IF_EXISTS = 2
+
+KEEP_PERMS_VALUES = [
+ KP_NEVER,
+ KP_ALWAYS,
+ KP_IF_EXISTS,
+ ]
+
def ErrnoOrStr(err):
"""Format an EnvironmentError exception.
@@ -82,7 +93,7 @@ def WriteFile(file_name, fn=None, data=None,
mode=None, uid=-1, gid=-1,
atime=None, mtime=None, close=True,
dry_run=False, backup=False,
- prewrite=None, postwrite=None):
+ prewrite=None, postwrite=None, keep_perms=KP_NEVER):
"""(Over)write a file atomically.
The file_name and either fn (a function taking one argument, the
@@ -119,6 +130,14 @@ def WriteFile(file_name, fn=None, data=None,
@param prewrite: function to be called before writing content
@type postwrite: callable
@param postwrite: function to be called after writing content
+ @type keep_perms: members of L{KEEP_PERMS_VALUES}
+ @param keep_perms: if L{KP_NEVER} (default), owner, group, and mode are
+ taken from the other parameters; if L{KP_ALWAYS}, owner, group, and
+ mode are copied from the existing file; if L{KP_IF_EXISTS}, owner,
+ group, and mode are taken from the file, and if the file doesn't
+ exist, they are taken from the other parameters. It is an error to
+ pass L{KP_ALWAYS} when the file doesn't exist or when C{uid}, C{gid},
+ or C{mode} are set to non-default values.
@rtype: None or int
@return: None if the 'close' parameter evaluates to True,
@@ -138,9 +157,28 @@ def WriteFile(file_name, fn=None, data=None,
raise errors.ProgrammerError("Both atime and mtime must be either"
" set or None")
+ if not keep_perms in KEEP_PERMS_VALUES:
+ raise errors.ProgrammerError("Invalid value for keep_perms: %s" %
+ keep_perms)
+ if keep_perms == KP_ALWAYS and (uid != -1 or gid != -1 or mode is not None):
+ raise errors.ProgrammerError("When keep_perms==KP_ALWAYS, 'uid', 'gid',"
+ " and 'mode' cannot be set")
+
if backup and not dry_run and os.path.isfile(file_name):
CreateBackup(file_name)
+ if keep_perms == KP_ALWAYS or keep_perms == KP_IF_EXISTS:
+ # os.stat() raises an exception if the file doesn't exist
+ try:
+ file_stat = os.stat(file_name)
+ mode = stat.S_IMODE(file_stat.st_mode)
+ uid = file_stat.st_uid
+ gid = file_stat.st_gid
+ except OSError:
+ if keep_perms == KP_ALWAYS:
+ raise
+ # else: if keeep_perms == KP_IF_EXISTS it's ok if the file doesn't exist
+
# Whether temporary file needs to be removed (e.g. if any error occurs)
do_remove = True