<commit_before>#
#

# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

"""Utility functions for I/O.

"""

import os
import logging
import shutil
import tempfile
import errno
import time
import stat

from ganeti import errors
from ganeti import constants
from ganeti.utils import filelock


#: Path generating random UUID
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"


def ErrnoOrStr(err):
  """Format an EnvironmentError exception.

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

  @type err: L{EnvironmentError}
  @param err: the exception to format

  """
  if hasattr(err, "errno"):
    detail = errno.errorcode[err.errno]
  else:
    detail = str(err)
  return detail


def ReadFile(file_name, size=-1, preread=None):
  """Reads a file.

  @type size: int
  @param size: Read at most size bytes (if negative, entire file)
  @type preread: callable receiving file handle as single parameter
  @param preread: Function called before file is read
  @rtype: str
  @return: the (possibly partial) content of the file

  """
  f = open(file_name, "r")
  try:
    if preread:
      preread(f)

    return f.read(size)
  finally:
    f.close()


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):
  """(Over)write a file atomically.

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

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

  @type file_name: str
  @param file_name: the target filename
  @type fn: callable
  @param fn: content writing function, called with
      file descriptor as parameter
  @type data: str
  @param data: contents of the file
  @type mode: int
  @param mode: file mode
  @type uid: int
  @param uid: the owner of the file
  @type gid: int
  @param gid: the group of the file
  @type atime: int
  @param atime: a custom access time to be set on the file
  @type mtime: int
  @param mtime: a custom modification time to be set on the file
  @type close: boolean
  @param close: whether to close file after writing it
  @type prewrite: callable
  @param prewrite: function to be called before writing content
  @type postwrite: callable
  @param postwrite: function to be called after writing content

  @rtype: None or int
  @return: None if the 'close' parameter evaluates to True,
      otherwise the file descriptor

  @raise errors.ProgrammerError: if any of the arguments are not valid

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

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

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

  if backup and not dry_run and os.path.isfile(file_name):
    CreateBackup(file_name)

  # Whether temporary file needs to be removed (e.g. if any error occurs)
  do_remove = True

  # Function result
  result = None

  (dir_name, base_name) = os.path.split(file_name)
  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
                                    dir=dir_name)
  try:
    try:
      if uid != -1 or gid != -1:
        os.chown(new_name, uid, gid)
      if mode:
        os.chmod(new_name, mode)
      if callable(prewrite):
        prewrite(fd)
      if data is not None:
        if isinstance(data, unicode):
          data = data.encode()<commit_msg>utils.WriteFile: Add new parameter to preserve file permissions<commit_after>#
#

# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

"""Utility functions for I/O.

"""

import os
import logging
import shutil
import tempfile
import errno
import time
import stat

from ganeti import errors
from ganeti import constants
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.

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

  @type err: L{EnvironmentError}
  @param err: the exception to format

  """
  if hasattr(err, "errno"):
    detail = errno.errorcode[err.errno]
  else:
    detail = str(err)
  return detail


def ReadFile(file_name, size=-1, preread=None):
  """Reads a file.

  @type size: int
  @param size: Read at most size bytes (if negative, entire file)
  @type preread: callable receiving file handle as single parameter
  @param preread: Function called before file is read
  @rtype: str
  @return: the (possibly partial) content of the file

  """
  f = open(file_name, "r")
  try:
    if preread:
      preread(f)

    return f.read(size)
  finally:
    f.close()


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, keep_perms=KP_NEVER):
  """(Over)write a file atomically.

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

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

  @type file_name: str
  @param file_name: the target filename
  @type fn: callable
  @param fn: content writing function, called with
      file descriptor as parameter
  @type data: str
  @param data: contents of the file
  @type mode: int
  @param mode: file mode
  @type uid: int
  @param uid: the owner of the file
  @type gid: int
  @param gid: the group of the file
  @type atime: int
  @param atime: a custom access time to be set on the file
  @type mtime: int
  @param mtime: a custom modification time to be set on the file
  @type close: boolean
  @param close: whether to close file after writing it
  @type prewrite: callable
  @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,
      otherwise the file descriptor

  @raise errors.ProgrammerError: if any of the arguments are not valid

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

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

  if [atime, mtime].count(None) == 1:
    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

  # Function result
  result = None

  (dir_name, base_name) = os.path.split(file_name)
  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
                                    dir=dir_name)
  try:
    try:
      if uid != -1 or gid != -1:
        os.chown(new_name, uid, gid)
      if mode:
        os.chmod(new_name, mode)
      if callable(prewrite):
        prewrite(fd)
      if data is not None:
        if isinstance(data, unicode):
          data = data.encode()
{'fraction_non_alphanumeric': 0.07922535211267606, 'fraction_numerical': 0.006602112676056338, 'mean_word_length': 3.120783460282916, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1596771', 'n_tokens_mistral': 3526, 'n_tokens_neox': 3361, 'n_words': 1440}