Revision 087b34fe lib/utils.py

b/lib/utils.py
855 855
    return f.read(128).rstrip("\n")
856 856
  finally:
857 857
    f.close()
858

  
859

  
860
def WriteFile(file_name, fn=None, data=None,
861
              mode=None, uid=-1, gid=-1,
862
              atime=None, mtime=None):
863
  """(Over)write a file atomically.
864

  
865
  The file_name and either fn (a function taking one argument, the
866
  file descriptor, and which should write the data to it) or data (the
867
  contents of the file) must be passed. The other arguments are
868
  optional and allow setting the file mode, owner and group, and the
869
  mtime/atime of the file.
870

  
871
  If the function doesn't raise an exception, it has succeeded and the
872
  target file has the new contents. If the file has raised an
873
  exception, an existing target file should be unmodified and the
874
  temporary file should be removed.
875

  
876
  """
877
  if not os.path.isabs(file_name):
878
    raise errors.ProgrammerError("Path passed to WriteFile is not"
879
                                 " absolute: '%s'" % file_name)
880

  
881
  if [fn, data].count(None) != 1:
882
    raise errors.ProgrammerError("fn or data required")
883

  
884
  if [atime, mtime].count(None) == 1:
885
    raise errors.ProgrammerError("Both atime and mtime must be either"
886
                                 " set or None")
887

  
888

  
889
  dir_name, base_name = os.path.split(file_name)
890
  fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
891
  # here we need to make sure we remove the temp file, if any error
892
  # leaves it in place
893
  try:
894
    if uid != -1 or gid != -1:
895
      os.chown(new_name, uid, gid)
896
    if mode:
897
      os.chmod(new_name, mode)
898
    if data is not None:
899
      os.write(fd, data)
900
    else:
901
      fn(fd)
902
    os.fsync(fd)
903
    if atime is not None and mtime is not None:
904
      os.utime(new_name, (atime, mtime))
905
    os.rename(new_name, file_name)
906
  finally:
907
    os.close(fd)
908
    RemoveFile(new_name)

Also available in: Unified diff