1
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
3
# Copyright 2002 Ben Escoto <ben@emerose.org>
4
# Copyright 2007 Kenneth Loafman <kenneth@loafman.com>
6
# This file is part of duplicity.
8
# Duplicity is free software; you can redistribute it and/or modify it
9
# under the terms of the GNU General Public License as published by the
10
# Free Software Foundation; either version 2 of the License, or (at your
11
# option) any later version.
13
# Duplicity is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
# General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with duplicity; if not, write to the Free Software Foundation,
20
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
"""Manage temporary files"""
26
from duplicity import log
27
from duplicity import util
28
from duplicity import path
29
from duplicity import file_naming
30
from duplicity import tempdir
31
from duplicity import globals
32
from duplicity import gpg
39
filename = tempdir.default().mktemp()
40
return TempPath(filename)
43
class TempPath(path.Path):
45
Path object used as a temporary file
51
path.Path.delete(self)
52
tempdir.default().forget(self.name)
54
def open_with_delete(self, mode):
56
Returns a fileobj. When that is closed, delete file
58
fh = FileobjHooked(path.Path.open(self, mode))
59
fh.addhook(self.delete)
63
def get_fileobj_duppath(dirpath, partname, permname, remname):
65
Return a file object open for writing, will write to filename
67
Data will be processed and written to a temporary file. When the
68
return fileobject is closed, rename to final position. filename
69
must be a recognizable duplicity data file.
71
if not globals.restart:
72
td = tempdir.TemporaryDirectory(dirpath.name)
74
tdp = TempDupPath(tdpname, parseresults = file_naming.parse(partname))
75
fh = FileobjHooked(tdp.filtered_open("wb"), tdp = tdp, dirpath = dirpath,
76
partname = partname, permname = permname, remname = remname)
78
dp = path.DupPath(dirpath.name, index = (partname,))
79
fh = FileobjHooked(dp.filtered_open("ab"), tdp = None, dirpath = dirpath,
80
partname = partname, permname = permname, remname = remname)
82
def rename_and_forget():
83
tdp.rename(dirpath.append(partname))
86
if not globals.restart:
87
fh.addhook(rename_and_forget)
92
def new_tempduppath(parseresults):
94
Return a new TempDupPath, using settings from parseresults
96
filename = tempdir.default().mktemp()
97
return TempDupPath(filename, parseresults = parseresults)
100
class TempDupPath(path.DupPath):
102
Like TempPath, but build around DupPath
108
path.DupPath.delete(self)
109
tempdir.default().forget(self.name)
111
def filtered_open_with_delete(self, mode):
113
Returns a filtered fileobj. When that is closed, delete file
115
fh = FileobjHooked(path.DupPath.filtered_open(self, mode))
116
fh.addhook(self.delete)
119
def open_with_delete(self, mode = "rb"):
121
Returns a fileobj. When that is closed, delete file
123
assert mode == "rb" # Why write a file and then close it immediately?
124
fh = FileobjHooked(path.DupPath.open(self, mode))
125
fh.addhook(self.delete)
131
Simulate a file, but add hook on close
133
def __init__(self, fileobj, tdp = None, dirpath = None,
134
partname = None, permname = None, remname = None):
136
Initializer. fileobj is the file object to simulate
138
self.fileobj = fileobj # the actual file object
139
self.closed = False # True if closed
140
self.hooklist = [] # filled later with thunks to run on close
141
self.tdp = tdp # TempDupPath object
142
self.dirpath = dirpath # path to directory
143
self.partname = partname # partial filename
144
self.permname = permname # permanent filename
145
self.remname = remname # remote filename
147
def write(self, buf):
149
Write fileobj, return result of write()
151
return self.fileobj.write(buf)
155
Flush fileobj and force sync.
158
os.fsync(self.fileobj.fileno())
160
def to_partial(self):
162
We have achieved the first checkpoint, make file visible and permanent.
164
assert not globals.restart
165
self.tdp.rename(self.dirpath.append(self.partname))
171
We have written the last checkpoint, now encrypt or compress
172
and send a copy of it to the remote for final storage.
174
pr = file_naming.parse(self.remname)
175
src = self.dirpath.append(self.partname)
176
tgt = self.dirpath.append(self.remname)
177
src_iter = SrcIter(src)
179
gpg.GzipWriteFile(src_iter, tgt.name, size = sys.maxint)
181
gpg.GPGWriteFile(src_iter, tgt.name, globals.gpg_profile, size = sys.maxint)
183
os.system("cp -p \"%s\" \"%s\"" % (src.name, tgt.name))
184
globals.backend.put(tgt) #@UndefinedVariable
186
util.ignore_missing(os.unlink, tgt.name)
188
log.Warn(_("Unable to delete %s: %s" % (tgt.name, str(e))))
192
We are finished, rename to final, gzip if needed.
194
src = self.dirpath.append(self.partname)
195
tgt = self.dirpath.append(self.permname)
196
src_iter = SrcIter(src)
197
pr = file_naming.parse(self.permname)
199
gpg.GzipWriteFile(src_iter, tgt.name, size = sys.maxint)
202
os.rename(src.name, tgt.name)
204
def read(self, length = -1):
206
Read fileobj, return result of read()
208
return self.fileobj.read(length)
212
Close fileobj, running hooks right afterwards
214
assert not self.fileobj.close()
215
for hook in self.hooklist:
218
def addhook(self, hook):
220
Add hook (function taking no arguments) to run upon closing
222
self.hooklist.append(hook)
227
Return the name of the file
229
return self.fileobj.name
231
name = property(get_name)
236
Data block to return from SrcIter
238
def __init__(self, data):
243
Iterate over source and return Block of data.
245
def __init__(self, src):
247
self.fp = src.open("rb")
248
def next(self, size):
250
res = Block(self.fp.read(size))
252
log.FatalError(_("Failed to read %s: %s") %
253
(self.src.name, sys.exc_info()),
254
log.ErrorCode.generic)
259
def get_footer(self):