base.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import errno
  2. import os
  3. import socket
  4. import uuid
  5. from borg.helpers import truncate_and_unlink
  6. """
  7. platform base module
  8. ====================
  9. Contains platform API implementations based on what Python itself provides. More specific
  10. APIs are stubs in this module.
  11. When functions in this module use platform APIs themselves they access the public
  12. platform API: that way platform APIs provided by the platform-specific support module
  13. are correctly composed into the base functionality.
  14. """
  15. API_VERSION = '1.2_02'
  16. fdatasync = getattr(os, 'fdatasync', os.fsync)
  17. from .xattr import ENOATTR
  18. def listxattr(path, *, follow_symlinks=False):
  19. """
  20. Return xattr names of a file (list of bytes objects).
  21. *path* can either be a path (bytes) or an open file descriptor (int).
  22. *follow_symlinks* indicates whether symlinks should be followed
  23. and only applies when *path* is not an open file descriptor.
  24. """
  25. return []
  26. def getxattr(path, name, *, follow_symlinks=False):
  27. """
  28. Read xattr and return its value (as bytes).
  29. *path* can either be a path (bytes) or an open file descriptor (int).
  30. *name* is the name of the xattr to read (bytes).
  31. *follow_symlinks* indicates whether symlinks should be followed
  32. and only applies when *path* is not an open file descriptor.
  33. """
  34. # as this base dummy implementation returns [] from listxattr,
  35. # it must raise here for any given name:
  36. raise OSError(ENOATTR, os.strerror(ENOATTR), path)
  37. def setxattr(path, name, value, *, follow_symlinks=False):
  38. """
  39. Write xattr on *path*.
  40. *path* can either be a path (bytes) or an open file descriptor (int).
  41. *name* is the name of the xattr to read (bytes).
  42. *value* is the value to write (bytes).
  43. *follow_symlinks* indicates whether symlinks should be followed
  44. and only applies when *path* is not an open file descriptor.
  45. """
  46. def acl_get(path, item, st, numeric_owner=False):
  47. """
  48. Saves ACL Entries
  49. If `numeric_owner` is True the user/group field is not preserved only uid/gid
  50. """
  51. def acl_set(path, item, numeric_owner=False):
  52. """
  53. Restore ACL Entries
  54. If `numeric_owner` is True the stored uid/gid is used instead
  55. of the user/group names
  56. """
  57. try:
  58. from os import lchflags
  59. def set_flags(path, bsd_flags, fd=None):
  60. lchflags(path, bsd_flags)
  61. except ImportError:
  62. def set_flags(path, bsd_flags, fd=None):
  63. pass
  64. def get_flags(path, st, fd=None):
  65. """Return BSD-style file flags for path or stat without following symlinks."""
  66. return getattr(st, 'st_flags', 0)
  67. def sync_dir(path):
  68. fd = os.open(path, os.O_RDONLY)
  69. try:
  70. os.fsync(fd)
  71. except OSError as os_error:
  72. # Some network filesystems don't support this and fail with EINVAL.
  73. # Other error codes (e.g. EIO) shouldn't be silenced.
  74. if os_error.errno != errno.EINVAL:
  75. raise
  76. finally:
  77. os.close(fd)
  78. def safe_fadvise(fd, offset, len, advice):
  79. if hasattr(os, 'posix_fadvise'):
  80. advice = getattr(os, 'POSIX_FADV_' + advice)
  81. try:
  82. os.posix_fadvise(fd, offset, len, advice)
  83. except OSError:
  84. # usually, posix_fadvise can't fail for us, but there seem to
  85. # be failures when running borg under docker on ARM, likely due
  86. # to a bug outside of borg.
  87. # also, there is a python wrapper bug, always giving errno = 0.
  88. # https://github.com/borgbackup/borg/issues/2095
  89. # as this call is not critical for correct function (just to
  90. # optimize cache usage), we ignore these errors.
  91. pass
  92. class SyncFile:
  93. """
  94. A file class that is supposed to enable write ordering (one way or another) and data durability after close().
  95. The degree to which either is possible varies with operating system, file system and hardware.
  96. This fallback implements a naive and slow way of doing this. On some operating systems it can't actually
  97. guarantee any of the above, since fsync() doesn't guarantee it. Furthermore it may not be possible at all
  98. to satisfy the above guarantees on some hardware or operating systems. In these cases we hope that the thorough
  99. checksumming implemented catches any corrupted data due to misordered, delayed or partial writes.
  100. Note that POSIX doesn't specify *anything* about power failures (or similar failures). A system that
  101. routinely loses files or corrupts file on power loss is POSIX compliant.
  102. TODO: Use F_FULLSYNC on OSX.
  103. TODO: A Windows implementation should use CreateFile with FILE_FLAG_WRITE_THROUGH.
  104. """
  105. def __init__(self, path, binary=False):
  106. mode = 'xb' if binary else 'x'
  107. self.fd = open(path, mode)
  108. self.fileno = self.fd.fileno()
  109. def __enter__(self):
  110. return self
  111. def __exit__(self, exc_type, exc_val, exc_tb):
  112. self.close()
  113. def write(self, data):
  114. self.fd.write(data)
  115. def sync(self):
  116. """
  117. Synchronize file contents. Everything written prior to sync() must become durable before anything written
  118. after sync().
  119. """
  120. from .. import platform
  121. self.fd.flush()
  122. platform.fdatasync(self.fileno)
  123. # tell the OS that it does not need to cache what we just wrote,
  124. # avoids spoiling the cache for the OS and other processes.
  125. safe_fadvise(self.fileno, 0, 0, 'DONTNEED')
  126. def close(self):
  127. """sync() and close."""
  128. from .. import platform
  129. dirname = None
  130. try:
  131. dirname = os.path.dirname(self.fd.name)
  132. self.sync()
  133. finally:
  134. self.fd.close()
  135. if dirname:
  136. platform.sync_dir(dirname)
  137. class SaveFile:
  138. """
  139. Update file contents atomically.
  140. Must be used as a context manager (defining the scope of the transaction).
  141. On a journaling file system the file contents are always updated
  142. atomically and won't become corrupted, even on power failures or
  143. crashes (for caveats see SyncFile).
  144. """
  145. SUFFIX = '.tmp'
  146. def __init__(self, path, binary=False):
  147. self.binary = binary
  148. self.path = path
  149. self.tmppath = self.path + self.SUFFIX
  150. def __enter__(self):
  151. from .. import platform
  152. try:
  153. truncate_and_unlink(self.tmppath)
  154. except FileNotFoundError:
  155. pass
  156. self.fd = platform.SyncFile(self.tmppath, self.binary)
  157. return self.fd
  158. def __exit__(self, exc_type, exc_val, exc_tb):
  159. from .. import platform
  160. self.fd.close()
  161. if exc_type is not None:
  162. truncate_and_unlink(self.tmppath)
  163. return
  164. os.replace(self.tmppath, self.path)
  165. platform.sync_dir(os.path.dirname(self.path))
  166. def swidth(s):
  167. """terminal output width of string <s>
  168. For western scripts, this is just len(s), but for cjk glyphs, 2 cells are used.
  169. """
  170. return len(s)
  171. # patched socket.getfqdn() - see https://bugs.python.org/issue5004
  172. def getfqdn(name=''):
  173. """Get fully qualified domain name from name.
  174. An empty argument is interpreted as meaning the local host.
  175. """
  176. name = name.strip()
  177. if not name or name == '0.0.0.0':
  178. name = socket.gethostname()
  179. try:
  180. addrs = socket.getaddrinfo(name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME)
  181. except socket.error:
  182. pass
  183. else:
  184. for addr in addrs:
  185. if addr[3]:
  186. name = addr[3]
  187. break
  188. return name
  189. # for performance reasons, only determine hostname / fqdn / hostid once.
  190. # XXX this sometimes requires live internet access for issuing a DNS query in the background.
  191. hostname = socket.gethostname()
  192. fqdn = getfqdn(hostname)
  193. # some people put the fqdn into /etc/hostname (which is wrong, should be the short hostname)
  194. # fix this (do the same as "hostname --short" cli command does internally):
  195. hostname = hostname.split('.')[0]
  196. # uuid.getnode() is problematic in some environments (e.g. OpenVZ, see #3968) where the virtual MAC address
  197. # is all-zero. uuid.getnode falls back to returning a random value in that case, which is not what we want.
  198. # thus, we offer BORG_HOST_ID where a user can set an own, unique id for each of his hosts.
  199. hostid = os.environ.get('BORG_HOST_ID')
  200. if not hostid:
  201. hostid = '%s@%s' % (fqdn, uuid.getnode())
  202. def get_process_id():
  203. """
  204. Return identification tuple (hostname, pid, thread_id) for 'us'.
  205. This always returns the current pid, which might be different from before, e.g. if daemonize() was used.
  206. Note: Currently thread_id is *always* zero.
  207. """
  208. thread_id = 0
  209. pid = os.getpid()
  210. return hostid, pid, thread_id
  211. def process_alive(host, pid, thread):
  212. """
  213. Check if the (host, pid, thread_id) combination corresponds to a potentially alive process.
  214. """
  215. raise NotImplementedError
  216. def local_pid_alive(pid):
  217. """Return whether *pid* is alive."""
  218. raise NotImplementedError