1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Miscellaneous classes and methods"""
23
24 import os
25
26 from duplicity import log
27
28
30 """Signifies a miscellaneous error..."""
31 pass
32
33
35 """Split up an incoming fileobj into multiple volumes on disk
36
37 This class can also be used as an iterator. It returns the
38 filenames of the files it writes.
39
40 """
41 volume_size = 50 * 1024 * 1024
42 blocksize = 64 * 1024
44 """FileVolumeWriter initializer
45
46 infp is a file object opened for reading. It will be closed
47 at end. file_prefix is the full path of the volumes that will
48 be written. If more than one is required, it will be appended
49 with .1, .2, etc.
50
51 """
52 self.infp = infp
53 self.prefix = file_prefix
54 self.current_index = 1
55 self.finished = None
56 self.buffer = ""
57
59 """Get first value of buffer, from self.buffer or infp"""
60 if self.buffer:
61 buf = self.buffer
62 self.buffer = ""
63 return buf
64 else:
65 return self.infp.read(self.blocksize)
66
68 """Write self.volume_size bytes from self.infp to outfp
69
70 Return None if we have reached end of infp without reaching
71 volume size, and false otherwise.
72
73 """
74 bytes_written, buf = 0, self.get_initial_buf()
75 while len(buf) + bytes_written <= self.volume_size:
76 if not buf:
77
78 outfp.close()
79 return None
80 if len(buf) + bytes_written > self.volume_size:
81 break
82 outfp.write(buf)
83 bytes_written += len(buf)
84 buf = self.infp.read(self.blocksize)
85
86 remainder = self.volume_size - bytes_written
87 assert remainder < len(buf)
88 outfp.write(buf[:remainder])
89 outfp.close()
90 self.buffer = buf[remainder:]
91 return 1
92
94 """Write next file, return filename"""
95 if self.finished:
96 raise StopIteration
97
98 filename = "%s.%d" % (self.prefix, self.current_index)
99 log.Info(_("Starting to write %s") % filename)
100 outfp = open(filename, "wb")
101
102 if not self.write_volume(outfp):
103
104 self.finished = 1
105 if self.current_index == 1:
106
107 log.Notice(_("One only volume required.\n"
108 "Renaming %s to %s") % (filename, self.prefix))
109 os.rename(filename, self.prefix)
110 return self.prefix
111 else:
112 self.current_index += 1
113 return filename
114
117
118
120 """Buffer file open for reading, so reads will happen in fixed sizes
121
122 This is currently used to buffer a GzipFile, because that class
123 apparently doesn't respond well to arbitrary read sizes.
124
125 """
126 - def __init__(self, fileobj, blocksize = 32 * 1024):
130
131 - def read(self, length = -1):
132 """Return length bytes, or all if length < 0"""
133 if length < 0:
134 while 1:
135 buf = self.fileobj.read(self.blocksize)
136 if not buf:
137 break
138 self.buffer += buf
139 real_length = len(self.buffer)
140 else:
141 while len(self.buffer) < length:
142 buf = self.fileobj.read(self.blocksize)
143 if not buf:
144 break
145 self.buffer += buf
146 real_length = min(length, len(self.buffer))
147 result = self.buffer[:real_length]
148 self.buffer = self.buffer[real_length:]
149 return result
150
153
154
156 """Copy byte_count bytes from infp to outfp, or all if byte_count < 0
157
158 Returns the number of bytes actually written (may be less than
159 byte_count if find eof. Does not close either fileobj.
160
161 """
162 blocksize = 64 * 1024
163 bytes_written = 0
164 if byte_count < 0:
165 while 1:
166 buf = infp.read(blocksize)
167 if not buf:
168 break
169 bytes_written += len(buf)
170 outfp.write(buf)
171 else:
172 while bytes_written + blocksize <= byte_count:
173 buf = infp.read(blocksize)
174 if not buf:
175 break
176 bytes_written += len(buf)
177 outfp.write(buf)
178 buf = infp.read(byte_count - bytes_written)
179 bytes_written += len(buf)
180 outfp.write(buf)
181 return bytes_written
182
184 """Copy infp to outfp, closing afterwards"""
185 copyfileobj(infp, outfp)
186 if infp.close():
187 raise MiscError("Error closing input file")
188 if outfp.close():
189 raise MiscError("Error closing output file")
190