/usr/lib/python3/dist-packages/dulwich/file.py is in python3-dulwich 0.18.5-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | # file.py -- Safe access to git files
# Copyright (C) 2010 Google, Inc.
#
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
# General Public License as public by the Free Software Foundation; version 2.0
# or (at your option) any later version. You can redistribute it and/or
# modify it under the terms of either of these two licenses.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# You should have received a copy of the licenses; if not, see
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
# License, Version 2.0.
#
"""Safe access to git files."""
import errno
import io
import os
import sys
import tempfile
def ensure_dir_exists(dirname):
"""Ensure a directory exists, creating if necessary."""
try:
os.makedirs(dirname)
except OSError as e:
if e.errno != errno.EEXIST:
raise
def _fancy_rename(oldname, newname):
"""Rename file with temporary backup file to rollback if rename fails"""
if not os.path.exists(newname):
try:
os.rename(oldname, newname)
except OSError:
raise
return
# destination file exists
try:
(fd, tmpfile) = tempfile.mkstemp(".tmp", prefix=oldname+".", dir=".")
os.close(fd)
os.remove(tmpfile)
except OSError:
# either file could not be created (e.g. permission problem)
# or could not be deleted (e.g. rude virus scanner)
raise
try:
os.rename(newname, tmpfile)
except OSError:
raise # no rename occurred
try:
os.rename(oldname, newname)
except OSError:
os.rename(tmpfile, newname)
raise
os.remove(tmpfile)
def GitFile(filename, mode='rb', bufsize=-1):
"""Create a file object that obeys the git file locking protocol.
:return: a builtin file object or a _GitFile object
:note: See _GitFile for a description of the file locking protocol.
Only read-only and write-only (binary) modes are supported; r+, w+, and a
are not. To read and write from the same file, you can take advantage of
the fact that opening a file for write does not actually open the file you
request.
"""
if 'a' in mode:
raise IOError('append mode not supported for Git files')
if '+' in mode:
raise IOError('read/write mode not supported for Git files')
if 'b' not in mode:
raise IOError('text mode not supported for Git files')
if 'w' in mode:
return _GitFile(filename, mode, bufsize)
else:
return io.open(filename, mode, bufsize)
class FileLocked(Exception):
"""File is already locked."""
def __init__(self, filename, lockfilename):
self.filename = filename
self.lockfilename = lockfilename
super(FileLocked, self).__init__(filename, lockfilename)
class _GitFile(object):
"""File that follows the git locking protocol for writes.
All writes to a file foo will be written into foo.lock in the same
directory, and the lockfile will be renamed to overwrite the original file
on close.
:note: You *must* call close() or abort() on a _GitFile for the lock to be
released. Typically this will happen in a finally block.
"""
PROXY_PROPERTIES = set(['closed', 'encoding', 'errors', 'mode', 'name',
'newlines', 'softspace'])
PROXY_METHODS = ('__iter__', 'flush', 'fileno', 'isatty', 'read',
'readline', 'readlines', 'seek', 'tell',
'truncate', 'write', 'writelines')
def __init__(self, filename, mode, bufsize):
self._filename = filename
self._lockfilename = '%s.lock' % self._filename
try:
fd = os.open(
self._lockfilename,
os.O_RDWR | os.O_CREAT | os.O_EXCL |
getattr(os, "O_BINARY", 0))
except OSError as e:
if e.errno == errno.EEXIST:
raise FileLocked(filename, self._lockfilename)
raise
self._file = os.fdopen(fd, mode, bufsize)
self._closed = False
for method in self.PROXY_METHODS:
setattr(self, method, getattr(self._file, method))
def abort(self):
"""Close and discard the lockfile without overwriting the target.
If the file is already closed, this is a no-op.
"""
if self._closed:
return
self._file.close()
try:
os.remove(self._lockfilename)
self._closed = True
except OSError as e:
# The file may have been removed already, which is ok.
if e.errno != errno.ENOENT:
raise
self._closed = True
def close(self):
"""Close this file, saving the lockfile over the original.
:note: If this method fails, it will attempt to delete the lockfile.
However, it is not guaranteed to do so (e.g. if a filesystem
becomes suddenly read-only), which will prevent future writes to
this file until the lockfile is removed manually.
:raises OSError: if the original file could not be overwritten. The
lock file is still closed, so further attempts to write to the same
file object will raise ValueError.
"""
if self._closed:
return
os.fsync(self._file.fileno())
self._file.close()
try:
try:
os.rename(self._lockfilename, self._filename)
except OSError as e:
if sys.platform == 'win32' and e.errno == errno.EEXIST:
# Windows versions prior to Vista don't support atomic
# renames
_fancy_rename(self._lockfilename, self._filename)
else:
raise
finally:
self.abort()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def __getattr__(self, name):
"""Proxy property calls to the underlying file."""
if name in self.PROXY_PROPERTIES:
return getattr(self._file, name)
raise AttributeError(name)
|