~yajo/duplicity/duplicity

335 by loafman
patch #6675: Add modelines
1
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
2
#
404 by loafman
Add/update copyright statements in all distribution source files
3
# Copyright 2002 Ben Escoto <ben@emerose.org>
4
# Copyright 2007 Kenneth Loafman <kenneth@loafman.com>
1 by bescoto
Initial checkin
5
#
6
# This file is part of duplicity.
7
#
2 by bescoto
Added full GPL statement in source files at request of Jaime Villate
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
476 by loafman
After email voting among known duplicity contributors,
10
# Free Software Foundation; either version 2 of the License, or (at your
2 by bescoto
Added full GPL statement in source files at request of Jaime Villate
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
1 by bescoto
Initial checkin
21
22
"""Manage temporary files"""
23
1071 by Kenneth Loafman
* Misc fixes for the following PEP8 issues:
24
import os
25
import sys
1279 by Kenneth Loafman
* Fixed bug #1684312 with suggestion from Wade Rossman
26
import shutil
1 by bescoto
Initial checkin
27
477 by loafman
Normalized include statements and tried to insure that all
28
from duplicity import log
773 by Kenneth Loafman
- Ignore ENOENT (file missing) errors where it is safe.
29
from duplicity import util
477 by loafman
Normalized include statements and tried to insure that all
30
from duplicity import path
31
from duplicity import file_naming
32
from duplicity import tempdir
521 by loafman
After merge of Checkpoint/Restart.
33
from duplicity import globals
34
from duplicity import gpg
1 by bescoto
Initial checkin
35
312 by loafman
Change to one statement per line.
36
1 by bescoto
Initial checkin
37
def new_temppath():
521 by loafman
After merge of Checkpoint/Restart.
38
    """
39
    Return a new TempPath
40
    """
304 by loafman
Untabify all files. To compare against previous
41
    filename = tempdir.default().mktemp()
42
    return TempPath(filename)
1 by bescoto
Initial checkin
43
312 by loafman
Change to one statement per line.
44
1 by bescoto
Initial checkin
45
class TempPath(path.Path):
521 by loafman
After merge of Checkpoint/Restart.
46
    """
47
    Path object used as a temporary file
48
    """
304 by loafman
Untabify all files. To compare against previous
49
    def delete(self):
521 by loafman
After merge of Checkpoint/Restart.
50
        """
51
        Forget and delete
52
        """
304 by loafman
Untabify all files. To compare against previous
53
        path.Path.delete(self)
54
        tempdir.default().forget(self.name)
1 by bescoto
Initial checkin
55
304 by loafman
Untabify all files. To compare against previous
56
    def open_with_delete(self, mode):
521 by loafman
After merge of Checkpoint/Restart.
57
        """
58
        Returns a fileobj.  When that is closed, delete file
59
        """
304 by loafman
Untabify all files. To compare against previous
60
        fh = FileobjHooked(path.Path.open(self, mode))
61
        fh.addhook(self.delete)
62
        return fh
1 by bescoto
Initial checkin
63
312 by loafman
Change to one statement per line.
64
864.5.1 by Michael Terry
gracefully handle multiple duplicate base dir entries in the sigtar; avoid writing such entries out
65
def get_fileobj_duppath(dirpath, partname, permname, remname, overwrite=False):
521 by loafman
After merge of Checkpoint/Restart.
66
    """
67
    Return a file object open for writing, will write to filename
304 by loafman
Untabify all files. To compare against previous
68
69
    Data will be processed and written to a temporary file.  When the
70
    return fileobject is closed, rename to final position.  filename
71
    must be a recognizable duplicity data file.
72
    """
521 by loafman
After merge of Checkpoint/Restart.
73
    if not globals.restart:
74
        td = tempdir.TemporaryDirectory(dirpath.name)
75
        tdpname = td.mktemp()
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
76
        tdp = TempDupPath(tdpname, parseresults=file_naming.parse(partname))
77
        fh = FileobjHooked(tdp.filtered_open("wb"), tdp=tdp, dirpath=dirpath,
78
                           partname=partname, permname=permname, remname=remname)
521 by loafman
After merge of Checkpoint/Restart.
79
    else:
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
80
        dp = path.DupPath(dirpath.name, index=(partname,))
864.5.1 by Michael Terry
gracefully handle multiple duplicate base dir entries in the sigtar; avoid writing such entries out
81
        mode = "ab"
82
        if overwrite:
83
            mode = "wb"
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
84
        fh = FileobjHooked(dp.filtered_open(mode), tdp=None, dirpath=dirpath,
85
                           partname=partname, permname=permname, remname=remname)
477 by loafman
Normalized include statements and tried to insure that all
86
304 by loafman
Untabify all files. To compare against previous
87
    def rename_and_forget():
521 by loafman
After merge of Checkpoint/Restart.
88
        tdp.rename(dirpath.append(partname))
304 by loafman
Untabify all files. To compare against previous
89
        td.forget(tdpname)
90
521 by loafman
After merge of Checkpoint/Restart.
91
    if not globals.restart:
92
        fh.addhook(rename_and_forget)
304 by loafman
Untabify all files. To compare against previous
93
94
    return fh
1 by bescoto
Initial checkin
95
312 by loafman
Change to one statement per line.
96
1 by bescoto
Initial checkin
97
def new_tempduppath(parseresults):
521 by loafman
After merge of Checkpoint/Restart.
98
    """
99
    Return a new TempDupPath, using settings from parseresults
100
    """
304 by loafman
Untabify all files. To compare against previous
101
    filename = tempdir.default().mktemp()
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
102
    return TempDupPath(filename, parseresults=parseresults)
1 by bescoto
Initial checkin
103
312 by loafman
Change to one statement per line.
104
1 by bescoto
Initial checkin
105
class TempDupPath(path.DupPath):
521 by loafman
After merge of Checkpoint/Restart.
106
    """
107
    Like TempPath, but build around DupPath
108
    """
304 by loafman
Untabify all files. To compare against previous
109
    def delete(self):
521 by loafman
After merge of Checkpoint/Restart.
110
        """
111
        Forget and delete
112
        """
304 by loafman
Untabify all files. To compare against previous
113
        path.DupPath.delete(self)
114
        tempdir.default().forget(self.name)
115
116
    def filtered_open_with_delete(self, mode):
521 by loafman
After merge of Checkpoint/Restart.
117
        """
118
        Returns a filtered fileobj.  When that is closed, delete file
119
        """
304 by loafman
Untabify all files. To compare against previous
120
        fh = FileobjHooked(path.DupPath.filtered_open(self, mode))
121
        fh.addhook(self.delete)
122
        return fh
123
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
124
    def open_with_delete(self, mode="rb"):
521 by loafman
After merge of Checkpoint/Restart.
125
        """
126
        Returns a fileobj.  When that is closed, delete file
127
        """
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
128
        assert mode == "rb"  # Why write a file and then close it immediately?
304 by loafman
Untabify all files. To compare against previous
129
        fh = FileobjHooked(path.DupPath.open(self, mode))
130
        fh.addhook(self.delete)
131
        return fh
1 by bescoto
Initial checkin
132
312 by loafman
Change to one statement per line.
133
1 by bescoto
Initial checkin
134
class FileobjHooked:
521 by loafman
After merge of Checkpoint/Restart.
135
    """
136
    Simulate a file, but add hook on close
137
    """
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
138
    def __init__(self, fileobj, tdp=None, dirpath=None,
139
                 partname=None, permname=None, remname=None):
521 by loafman
After merge of Checkpoint/Restart.
140
        """
141
        Initializer.  fileobj is the file object to simulate
142
        """
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
143
        self.fileobj = fileobj  # the actual file object
144
        self.closed = False  # True if closed
145
        self.hooklist = []  # filled later with thunks to run on close
146
        self.tdp = tdp  # TempDupPath object
147
        self.dirpath = dirpath  # path to directory
148
        self.partname = partname  # partial filename
149
        self.permname = permname  # permanent filename
150
        self.remname = remname  # remote filename
304 by loafman
Untabify all files. To compare against previous
151
152
    def write(self, buf):
521 by loafman
After merge of Checkpoint/Restart.
153
        """
1210 by ken
* Fixed bug #1570293
154
        Write fileobj, return result of write()
521 by loafman
After merge of Checkpoint/Restart.
155
        """
1210 by ken
* Fixed bug #1570293
156
        return self.fileobj.write(buf)
477 by loafman
Normalized include statements and tried to insure that all
157
521 by loafman
After merge of Checkpoint/Restart.
158
    def flush(self):
159
        """
160
        Flush fileobj and force sync.
161
        """
162
        self.fileobj.flush()
163
        os.fsync(self.fileobj.fileno())
164
165
    def to_partial(self):
166
        """
167
        We have achieved the first checkpoint, make file visible and permanent.
168
        """
169
        assert not globals.restart
170
        self.tdp.rename(self.dirpath.append(self.partname))
171
        self.fileobj.flush()
172
        del self.hooklist[0]
173
174
    def to_remote(self):
175
        """
176
        We have written the last checkpoint, now encrypt or compress
177
        and send a copy of it to the remote for final storage.
178
        """
179
        pr = file_naming.parse(self.remname)
180
        src = self.dirpath.append(self.partname)
181
        tgt = self.dirpath.append(self.remname)
182
        src_iter = SrcIter(src)
183
        if pr.compressed:
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
184
            gpg.GzipWriteFile(src_iter, tgt.name, size=sys.maxsize)
521 by loafman
After merge of Checkpoint/Restart.
185
        elif pr.encrypted:
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
186
            gpg.GPGWriteFile(src_iter, tgt.name, globals.gpg_profile, size=sys.maxsize)
521 by loafman
After merge of Checkpoint/Restart.
187
        else:
1279 by Kenneth Loafman
* Fixed bug #1684312 with suggestion from Wade Rossman
188
            shutil.copyfile(src.name, tgt.name)
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
189
        globals.backend.move(tgt)  # @UndefinedVariable
521 by loafman
After merge of Checkpoint/Restart.
190
191
    def to_final(self):
192
        """
193
        We are finished, rename to final, gzip if needed.
194
        """
195
        src = self.dirpath.append(self.partname)
196
        tgt = self.dirpath.append(self.permname)
197
        src_iter = SrcIter(src)
198
        pr = file_naming.parse(self.permname)
199
        if pr.compressed:
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
200
            gpg.GzipWriteFile(src_iter, tgt.name, size=sys.maxsize)
521 by loafman
After merge of Checkpoint/Restart.
201
            os.unlink(src.name)
202
        else:
203
            os.rename(src.name, tgt.name)
204
1033 by Kenneth Loafman
* Source formatted, using PyDev, all source files to fix some easily fixed
205
    def read(self, length=-1):
521 by loafman
After merge of Checkpoint/Restart.
206
        """
207
        Read fileobj, return result of read()
208
        """
312 by loafman
Change to one statement per line.
209
        return self.fileobj.read(length)
304 by loafman
Untabify all files. To compare against previous
210
777.1.1 by Michael Terry
first pass at dropping tarfile
211
    def tell(self):
212
        """
213
        Returns current location of fileobj
214
        """
215
        return self.fileobj.tell()
216
217
    def seek(self, offset):
218
        """
219
        Seeks to a location of fileobj
220
        """
221
        return self.fileobj.seek(offset)
222
304 by loafman
Untabify all files. To compare against previous
223
    def close(self):
521 by loafman
After merge of Checkpoint/Restart.
224
        """
225
        Close fileobj, running hooks right afterwards
226
        """
304 by loafman
Untabify all files. To compare against previous
227
        assert not self.fileobj.close()
312 by loafman
Change to one statement per line.
228
        for hook in self.hooklist:
229
            hook()
304 by loafman
Untabify all files. To compare against previous
230
231
    def addhook(self, hook):
521 by loafman
After merge of Checkpoint/Restart.
232
        """
233
        Add hook (function taking no arguments) to run upon closing
234
        """
304 by loafman
Untabify all files. To compare against previous
235
        self.hooklist.append(hook)
236
721 by Kenneth Loafman
433591 AttributeError: FileobjHooked instance has no attribute 'name'
237
    def get_name(self):
238
        """
239
        Return the name of the file
240
        """
241
        return self.fileobj.name
242
243
    name = property(get_name)
244
245
521 by loafman
After merge of Checkpoint/Restart.
246
class Block:
247
    """
248
    Data block to return from SrcIter
249
    """
250
    def __init__(self, data):
251
        self.data = data
252
1070 by Kenneth Loafman
* Misc fixes for the following PEP8 issues:
253
521 by loafman
After merge of Checkpoint/Restart.
254
class SrcIter:
255
    """
256
    Iterate over source and return Block of data.
257
    """
258
    def __init__(self, src):
259
        self.src = src
260
        self.fp = src.open("rb")
1070 by Kenneth Loafman
* Misc fixes for the following PEP8 issues:
261
902.1.6 by Michael Terry
Fix block-loss when restarting inside a multi-block file due to block sizes being variable
262
    def next(self):
521 by loafman
After merge of Checkpoint/Restart.
263
        try:
902.1.6 by Michael Terry
Fix block-loss when restarting inside a multi-block file due to block sizes being variable
264
            res = Block(self.fp.read(self.get_read_size()))
747.1.1 by Michael Terry
always catch Exceptions, not BaseExceptions
265
        except Exception:
1065 by Kenneth Loafman
* Merged in lp:~angusgr/duplicity/exclude-older-than
266
            log.FatalError(_("Failed to read %s: %s") %
939.3.1 by Michael Terry
Promote filenames to unicode when printing to console and ensure that filenames from backend are bytes
267
                           (util.ufn(self.src.name), sys.exc_info()),
521 by loafman
After merge of Checkpoint/Restart.
268
                           log.ErrorCode.generic)
269
        if not res.data:
270
            self.fp.close()
271
            raise StopIteration
272
        return res
1070 by Kenneth Loafman
* Misc fixes for the following PEP8 issues:
273
902.1.6 by Michael Terry
Fix block-loss when restarting inside a multi-block file due to block sizes being variable
274
    def get_read_size(self):
275
        return 128 * 1024
1070 by Kenneth Loafman
* Misc fixes for the following PEP8 issues:
276
521 by loafman
After merge of Checkpoint/Restart.
277
    def get_footer(self):
278
        return ""