diff --git a/attic/archive.py b/attic/archive.py index 171da3fe0..33770650d 100644 --- a/attic/archive.py +++ b/attic/archive.py @@ -82,7 +82,7 @@ class ChunkBuffer: chunks = list(bytes(s) for s in self.chunker.chunkify(self.buffer)) self.buffer.seek(0) self.buffer.truncate(0) - # Leave the last parital chunk in the buffer unless flush is True + # Leave the last partial chunk in the buffer unless flush is True end = None if flush or len(chunks) == 1 else -1 for chunk in chunks[:end]: self.chunks.append(self.write_chunk(chunk)) @@ -399,7 +399,7 @@ class Archive: chunks = [cache.chunk_incref(id_, self.stats) for id_ in ids] # Only chunkify the file if needed if chunks is None: - with open(path, 'rb') as fd: + with Archive._open_rb(path, st) as fd: chunks = [] for chunk in self.chunker.chunkify(fd): chunks.append(cache.add_chunk(self.key.id_hash(chunk), chunk, self.stats)) @@ -414,6 +414,45 @@ class Archive: for name, info in manifest.archives.items(): yield Archive(repository, key, manifest, name, cache=cache) + @staticmethod + def _open_rb(path, st): + flags_noatime = None + euid = None + + def open_simple(p, s): + return open(p, 'rb') + + def open_noatime_if_owner(p, s): + if s.st_uid == euid: + return os.fdopen(os.open(p, flags_noatime), 'rb') + else: + return open(p, 'rb') + + def open_noatime(p, s): + try: + fd = os.open(p, flags_noatime) + except PermissionError: + # Was this EPERM due to the O_NOATIME flag? + fo = open(p, 'rb') + # Yes, it was -- otherwise the above line would have thrown + # another exception. + euid = os.geteuid() + # So in future, let's check whether the file is owned by us + # before attempting to use O_NOATIME. + Archive._open_rb = open_noatime_if_owner + return fo + return os.fdopen(fd, 'rb') + + o_noatime = getattr(os, 'O_NOATIME', None) + if o_noatime is not None: + flags_noatime = os.O_RDONLY | getattr(os, 'O_BINARY', 0) | o_noatime + # Always use O_NOATIME version. + Archive._open_rb = open_noatime + else: + # Always use non-O_NOATIME version. + Archive._open_rb = open_simple + return Archive._open_rb(path, st) + class RobustUnpacker(): """A restartable/robust version of the streaming msgpack unpacker