~ubuntu-branches/ubuntu/trusty/duplicity/trusty

« back to all changes in this revision

Viewing changes to src/dup_temp.py

  • Committer: Package Import Robot
  • Author(s): Michael Terry
  • Date: 2011-12-06 14:15:01 UTC
  • mfrom: (1.9.4)
  • Revision ID: package-import@ubuntu.com-20111206141501-nvfaaauqivpwyb7f
Tags: 0.6.17-0ubuntu1
* New upstream release
* debian/patches/06_use_passphrase.dpatch,
  debian/patches/07_large_rackspace_list.dpatch,
  debian/patches/08_check_volumes.dpatch:
  - Dropped, applied upstream
* debian/rules:
  - Run new upstream test suite during build
* debian/control:
  - Add rdiff as a build-dep to run above test suite
* debian/patches/06testfixes.dpatch:
  - Fix a few tests to not fail erroneously
* debian/patches/07fixincresume.dpatch:
  - Fix a bug with resuming an incremental backup that would result in
    a bogus error.  Also patches in a test for it.
* debian/tests/full-cycle-local:
  - New DEP-8 test script that backs up locally, restores, and checks files
* debian/tests/full-cycle-u1:
  - New DEP-8 test script that does the same as above, but to Ubuntu One
* debian/tests/control:
  - Start of DEP-8 test suite.  Only enable above full-cycle-local test
    for automatic execution.  The other is for manual testing right now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
2
 
#
3
 
# Copyright 2002 Ben Escoto <ben@emerose.org>
4
 
# Copyright 2007 Kenneth Loafman <kenneth@loafman.com>
5
 
#
6
 
# This file is part of duplicity.
7
 
#
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.
12
 
#
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.
17
 
#
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
21
 
 
22
 
"""Manage temporary files"""
23
 
 
24
 
import os, sys
25
 
 
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
33
 
 
34
 
 
35
 
def new_temppath():
36
 
    """
37
 
    Return a new TempPath
38
 
    """
39
 
    filename = tempdir.default().mktemp()
40
 
    return TempPath(filename)
41
 
 
42
 
 
43
 
class TempPath(path.Path):
44
 
    """
45
 
    Path object used as a temporary file
46
 
    """
47
 
    def delete(self):
48
 
        """
49
 
        Forget and delete
50
 
        """
51
 
        path.Path.delete(self)
52
 
        tempdir.default().forget(self.name)
53
 
 
54
 
    def open_with_delete(self, mode):
55
 
        """
56
 
        Returns a fileobj.  When that is closed, delete file
57
 
        """
58
 
        fh = FileobjHooked(path.Path.open(self, mode))
59
 
        fh.addhook(self.delete)
60
 
        return fh
61
 
 
62
 
 
63
 
def get_fileobj_duppath(dirpath, partname, permname, remname):
64
 
    """
65
 
    Return a file object open for writing, will write to filename
66
 
 
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.
70
 
    """
71
 
    if not globals.restart:
72
 
        td = tempdir.TemporaryDirectory(dirpath.name)
73
 
        tdpname = td.mktemp()
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)
77
 
    else:
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)
81
 
 
82
 
    def rename_and_forget():
83
 
        tdp.rename(dirpath.append(partname))
84
 
        td.forget(tdpname)
85
 
 
86
 
    if not globals.restart:
87
 
        fh.addhook(rename_and_forget)
88
 
 
89
 
    return fh
90
 
 
91
 
 
92
 
def new_tempduppath(parseresults):
93
 
    """
94
 
    Return a new TempDupPath, using settings from parseresults
95
 
    """
96
 
    filename = tempdir.default().mktemp()
97
 
    return TempDupPath(filename, parseresults = parseresults)
98
 
 
99
 
 
100
 
class TempDupPath(path.DupPath):
101
 
    """
102
 
    Like TempPath, but build around DupPath
103
 
    """
104
 
    def delete(self):
105
 
        """
106
 
        Forget and delete
107
 
        """
108
 
        path.DupPath.delete(self)
109
 
        tempdir.default().forget(self.name)
110
 
 
111
 
    def filtered_open_with_delete(self, mode):
112
 
        """
113
 
        Returns a filtered fileobj.  When that is closed, delete file
114
 
        """
115
 
        fh = FileobjHooked(path.DupPath.filtered_open(self, mode))
116
 
        fh.addhook(self.delete)
117
 
        return fh
118
 
 
119
 
    def open_with_delete(self, mode = "rb"):
120
 
        """
121
 
        Returns a fileobj.  When that is closed, delete file
122
 
        """
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)
126
 
        return fh
127
 
 
128
 
 
129
 
class FileobjHooked:
130
 
    """
131
 
    Simulate a file, but add hook on close
132
 
    """
133
 
    def __init__(self, fileobj, tdp = None, dirpath = None,
134
 
                 partname = None, permname = None, remname = None):
135
 
        """
136
 
        Initializer.  fileobj is the file object to simulate
137
 
        """
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
146
 
 
147
 
    def write(self, buf):
148
 
        """
149
 
        Write fileobj, return result of write()
150
 
        """
151
 
        return self.fileobj.write(buf)
152
 
 
153
 
    def flush(self):
154
 
        """
155
 
        Flush fileobj and force sync.
156
 
        """
157
 
        self.fileobj.flush()
158
 
        os.fsync(self.fileobj.fileno())
159
 
 
160
 
    def to_partial(self):
161
 
        """
162
 
        We have achieved the first checkpoint, make file visible and permanent.
163
 
        """
164
 
        assert not globals.restart
165
 
        self.tdp.rename(self.dirpath.append(self.partname))
166
 
        self.fileobj.flush()
167
 
        del self.hooklist[0]
168
 
 
169
 
    def to_remote(self):
170
 
        """
171
 
        We have written the last checkpoint, now encrypt or compress
172
 
        and send a copy of it to the remote for final storage.
173
 
        """
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)
178
 
        if pr.compressed:
179
 
            gpg.GzipWriteFile(src_iter, tgt.name, size = sys.maxint)
180
 
        elif pr.encrypted:
181
 
            gpg.GPGWriteFile(src_iter, tgt.name, globals.gpg_profile, size = sys.maxint)
182
 
        else:
183
 
            os.system("cp -p \"%s\" \"%s\"" % (src.name, tgt.name))
184
 
        globals.backend.put(tgt) #@UndefinedVariable
185
 
        try:
186
 
            util.ignore_missing(os.unlink, tgt.name)
187
 
        except Exception, e:
188
 
            log.Warn(_("Unable to delete %s: %s" % (tgt.name, str(e))))
189
 
 
190
 
    def to_final(self):
191
 
        """
192
 
        We are finished, rename to final, gzip if needed.
193
 
        """
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)
198
 
        if pr.compressed:
199
 
            gpg.GzipWriteFile(src_iter, tgt.name, size = sys.maxint)
200
 
            os.unlink(src.name)
201
 
        else:
202
 
            os.rename(src.name, tgt.name)
203
 
 
204
 
    def read(self, length = -1):
205
 
        """
206
 
        Read fileobj, return result of read()
207
 
        """
208
 
        return self.fileobj.read(length)
209
 
 
210
 
    def close(self):
211
 
        """
212
 
        Close fileobj, running hooks right afterwards
213
 
        """
214
 
        assert not self.fileobj.close()
215
 
        for hook in self.hooklist:
216
 
            hook()
217
 
 
218
 
    def addhook(self, hook):
219
 
        """
220
 
        Add hook (function taking no arguments) to run upon closing
221
 
        """
222
 
        self.hooklist.append(hook)
223
 
 
224
 
 
225
 
    def get_name(self):
226
 
        """
227
 
        Return the name of the file
228
 
        """
229
 
        return self.fileobj.name
230
 
 
231
 
    name = property(get_name)
232
 
 
233
 
 
234
 
class Block:
235
 
    """
236
 
    Data block to return from SrcIter
237
 
    """
238
 
    def __init__(self, data):
239
 
        self.data = data
240
 
 
241
 
class SrcIter:
242
 
    """
243
 
    Iterate over source and return Block of data.
244
 
    """
245
 
    def __init__(self, src):
246
 
        self.src = src
247
 
        self.fp = src.open("rb")
248
 
    def next(self, size):
249
 
        try:
250
 
            res = Block(self.fp.read(size))
251
 
        except Exception:
252
 
            log.FatalError(_("Failed to read %s: %s") %
253
 
                           (self.src.name, sys.exc_info()),
254
 
                           log.ErrorCode.generic)
255
 
        if not res.data:
256
 
            self.fp.close()
257
 
            raise StopIteration
258
 
        return res
259
 
    def get_footer(self):
260
 
        return ""