/usr/share/pyshared/duplicity/misc.py is in duplicity 0.6.18-0ubuntu3.
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 | # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2002 Ben Escoto <ben@emerose.org>
# Copyright 2007 Kenneth Loafman <kenneth@loafman.com>
#
# This file is part of duplicity.
#
# Duplicity 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.
#
# Duplicity 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 duplicity; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Miscellaneous classes and methods"""
import os
from duplicity import log
class MiscError(Exception):
"""Signifies a miscellaneous error..."""
pass
class FileVolumeWriter:
"""Split up an incoming fileobj into multiple volumes on disk
This class can also be used as an iterator. It returns the
filenames of the files it writes.
"""
volume_size = 50 * 1024 * 1024
blocksize = 64 * 1024
def __init__(self, infp, file_prefix):
"""FileVolumeWriter initializer
infp is a file object opened for reading. It will be closed
at end. file_prefix is the full path of the volumes that will
be written. If more than one is required, it will be appended
with .1, .2, etc.
"""
self.infp = infp
self.prefix = file_prefix
self.current_index = 1
self.finished = None # set to true when completely done
self.buffer = "" # holds data that belongs in next volume
def get_initial_buf(self):
"""Get first value of buffer, from self.buffer or infp"""
if self.buffer:
buf = self.buffer
self.buffer = ""
return buf
else:
return self.infp.read(self.blocksize)
def write_volume(self, outfp):
"""Write self.volume_size bytes from self.infp to outfp
Return None if we have reached end of infp without reaching
volume size, and false otherwise.
"""
bytes_written, buf = 0, self.get_initial_buf()
while len(buf) + bytes_written <= self.volume_size:
if not buf:
# reached end of input
outfp.close()
return None
if len(buf) + bytes_written > self.volume_size:
break
outfp.write(buf)
bytes_written += len(buf)
buf = self.infp.read(self.blocksize)
remainder = self.volume_size - bytes_written
assert remainder < len(buf)
outfp.write(buf[:remainder])
outfp.close()
self.buffer = buf[remainder:]
return 1
def next(self):
"""Write next file, return filename"""
if self.finished:
raise StopIteration
filename = "%s.%d" % (self.prefix, self.current_index)
log.Info(_("Starting to write %s") % filename)
outfp = open(filename, "wb")
if not self.write_volume(outfp):
# end of input
self.finished = 1
if self.current_index == 1:
# special case first index
log.Notice(_("One only volume required.\n"
"Renaming %s to %s") % (filename, self.prefix))
os.rename(filename, self.prefix)
return self.prefix
else:
self.current_index += 1
return filename
def __iter__(self):
return self
class BufferedFile:
"""Buffer file open for reading, so reads will happen in fixed sizes
This is currently used to buffer a GzipFile, because that class
apparently doesn't respond well to arbitrary read sizes.
"""
def __init__(self, fileobj, blocksize = 32 * 1024):
self.fileobj = fileobj
self.buffer = ""
self.blocksize = blocksize
def read(self, length = -1):
"""Return length bytes, or all if length < 0"""
if length < 0:
while 1:
buf = self.fileobj.read(self.blocksize)
if not buf:
break
self.buffer += buf
real_length = len(self.buffer)
else:
while len(self.buffer) < length:
buf = self.fileobj.read(self.blocksize)
if not buf:
break
self.buffer += buf
real_length = min(length, len(self.buffer))
result = self.buffer[:real_length]
self.buffer = self.buffer[real_length:]
return result
def close(self):
self.fileobj.close()
def copyfileobj(infp, outfp, byte_count = -1):
"""Copy byte_count bytes from infp to outfp, or all if byte_count < 0
Returns the number of bytes actually written (may be less than
byte_count if find eof. Does not close either fileobj.
"""
blocksize = 64 * 1024
bytes_written = 0
if byte_count < 0:
while 1:
buf = infp.read(blocksize)
if not buf:
break
bytes_written += len(buf)
outfp.write(buf)
else:
while bytes_written + blocksize <= byte_count:
buf = infp.read(blocksize)
if not buf:
break
bytes_written += len(buf)
outfp.write(buf)
buf = infp.read(byte_count - bytes_written)
bytes_written += len(buf)
outfp.write(buf)
return bytes_written
def copyfileobj_close(infp, outfp):
"""Copy infp to outfp, closing afterwards"""
copyfileobj(infp, outfp)
if infp.close():
raise MiscError("Error closing input file")
if outfp.close():
raise MiscError("Error closing output file")
|