diff --git a/lib/utils.py b/lib/utils.py
index fda1f6b3c6ab108615829d0ee97f9e1b1840b161..28c2be828a4f8d7c71c39b7f30691790934f3a94 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -959,8 +959,9 @@ def NewUUID():
 
 def WriteFile(file_name, fn=None, data=None,
               mode=None, uid=-1, gid=-1,
-              atime=None, mtime=None,
-              check_abspath=True, dry_run=False, backup=False):
+              atime=None, mtime=None, close=True,
+              check_abspath=True, dry_run=False, backup=False,
+              prewrite=None, postwrite=None):
   """(Over)write a file atomically.
 
   The file_name and either fn (a function taking one argument, the
@@ -974,6 +975,22 @@ def WriteFile(file_name, fn=None, data=None,
   exception, an existing target file should be unmodified and the
   temporary file should be removed.
 
+  Args:
+    file_name: New filename
+    fn: Content writing function, called with file descriptor as parameter
+    data: Content as string
+    mode: File mode
+    uid: Owner
+    gid: Group
+    atime: Access time
+    mtime: Modification time
+    close: Whether to close file after writing it
+    prewrite: Function object called before writing content
+    postwrite: Function object called after writing content
+
+  Returns:
+    None if "close" parameter evaluates to True, otherwise file descriptor.
+
   """
   if check_abspath and not os.path.isabs(file_name):
     raise errors.ProgrammerError("Path passed to WriteFile is not"
@@ -998,19 +1015,29 @@ def WriteFile(file_name, fn=None, data=None,
       os.chown(new_name, uid, gid)
     if mode:
       os.chmod(new_name, mode)
+    if callable(prewrite):
+      prewrite(fd)
     if data is not None:
       os.write(fd, data)
     else:
       fn(fd)
+    if callable(postwrite):
+      postwrite(fd)
     os.fsync(fd)
     if atime is not None and mtime is not None:
       os.utime(new_name, (atime, mtime))
     if not dry_run:
       os.rename(new_name, file_name)
   finally:
-    os.close(fd)
+    if close:
+      os.close(fd)
+      result = None
+    else:
+      result = fd
     RemoveFile(new_name)
 
+  return result
+
 
 def all(seq, pred=bool):
   "Returns True if pred(x) is True for every element in the iterable"