56
48
from duplicity import dup_collections
57
49
from duplicity import dup_temp
58
50
from duplicity import dup_time
59
from duplicity import errors
60
51
from duplicity import file_naming
61
from duplicity import globals
52
from duplicity import config
62
53
from duplicity import gpg
63
54
from duplicity import log
64
55
from duplicity import manifest
65
56
from duplicity import patchdir
66
57
from duplicity import path
67
58
from duplicity import progress
68
from duplicity import robust
69
59
from duplicity import tempdir
70
60
from duplicity import util
115
105
# check if we can reuse an already set (signing_)passphrase
116
106
# if signing key is also an encryption key assume that the passphrase is identical
117
107
if (for_signing and
118
(globals.gpg_profile.sign_key in globals.gpg_profile.recipients or
119
globals.gpg_profile.sign_key in globals.gpg_profile.hidden_recipients) and
108
(config.gpg_profile.sign_key in config.gpg_profile.recipients or
109
config.gpg_profile.sign_key in config.gpg_profile.hidden_recipients) and
120
110
u'PASSPHRASE' in os.environ): # noqa
121
111
log.Notice(_(u"Reuse configured PASSPHRASE as SIGN_PASSPHRASE"))
122
112
return os.environ[u'PASSPHRASE']
123
113
# if one encryption key is also the signing key assume that the passphrase is identical
124
114
if (not for_signing and
125
(globals.gpg_profile.sign_key in globals.gpg_profile.recipients or
126
globals.gpg_profile.sign_key in globals.gpg_profile.hidden_recipients) and
115
(config.gpg_profile.sign_key in config.gpg_profile.recipients or
116
config.gpg_profile.sign_key in config.gpg_profile.hidden_recipients) and
127
117
u'SIGN_PASSPHRASE' in os.environ): # noqa
128
118
log.Notice(_(u"Reuse configured SIGN_PASSPHRASE as PASSPHRASE"))
129
119
return os.environ[u'SIGN_PASSPHRASE']
151
141
# for a full backup, we don't need a password if
152
142
# there is no sign_key and there are recipients
153
143
elif (action == u"full" and
154
(globals.gpg_profile.recipients or globals.gpg_profile.hidden_recipients) and not
155
globals.gpg_profile.sign_key):
144
(config.gpg_profile.recipients or config.gpg_profile.hidden_recipients) and not
145
config.gpg_profile.sign_key):
158
148
# for an inc backup, we don't need a password if
159
149
# there is no sign_key and there are recipients
160
150
elif (action == u"inc" and
161
(globals.gpg_profile.recipients or globals.gpg_profile.hidden_recipients) and not
162
globals.gpg_profile.sign_key):
151
(config.gpg_profile.recipients or config.gpg_profile.hidden_recipients) and not
152
config.gpg_profile.sign_key):
165
155
# Finally, ask the user for the passphrase
171
161
# if the user made a typo in the first passphrase
172
162
if use_cache and n == 2:
174
pass1 = globals.gpg_profile.signing_passphrase
164
pass1 = config.gpg_profile.signing_passphrase
176
pass1 = globals.gpg_profile.passphrase
166
pass1 = config.gpg_profile.passphrase
179
if use_cache and globals.gpg_profile.signing_passphrase:
180
pass1 = globals.gpg_profile.signing_passphrase
169
if use_cache and config.gpg_profile.signing_passphrase:
170
pass1 = config.gpg_profile.signing_passphrase
182
172
pass1 = getpass_safe(_(u"GnuPG passphrase for signing key:") + u" ")
184
if use_cache and globals.gpg_profile.passphrase:
185
pass1 = globals.gpg_profile.passphrase
174
if use_cache and config.gpg_profile.passphrase:
175
pass1 = config.gpg_profile.passphrase
187
177
pass1 = getpass_safe(_(u"GnuPG passphrase:") + u" ")
199
189
use_cache = False
202
if not pass1 and not (globals.gpg_profile.recipients or
203
globals.gpg_profile.hidden_recipients) and not for_signing:
192
if not pass1 and not (config.gpg_profile.recipients or
193
config.gpg_profile.hidden_recipients) and not for_signing:
204
194
log.Log(_(u"Cannot use empty passphrase with symmetric encryption! Please try again."),
205
195
log.WARNING, force_print=True)
206
196
use_cache = False
345
335
from encrypted to non in the middle of a backup chain), so we check
346
336
that the vol1 filename on the server matches the settings of this run.
348
if ((globals.gpg_profile.recipients or globals.gpg_profile.hidden_recipients) and
349
not globals.gpg_profile.sign_key):
338
if ((config.gpg_profile.recipients or config.gpg_profile.hidden_recipients) and
339
not config.gpg_profile.sign_key):
350
340
# When using gpg encryption without a signing key, we skip this validation
351
341
# step to ensure that we can still backup without needing the secret key
352
342
# on the machine.
355
345
vol1_filename = file_naming.get(backup_type, 1,
356
encrypted=globals.encryption,
357
gzipped=globals.compression)
346
encrypted=config.encryption,
347
gzipped=config.compression)
358
348
if vol1_filename != backup_set.volume_name_dict[1]:
359
349
log.FatalError(_(u"Restarting backup, but current encryption "
360
350
u"settings do not match original settings"),
361
351
log.ErrorCode.enryption_mismatch)
363
353
# Settings are same, let's check passphrase itself if we are encrypted
364
if globals.encryption:
365
fileobj = restore_get_enc_fileobj(globals.backend, vol1_filename,
354
if config.encryption:
355
fileobj = restore_get_enc_fileobj(config.backend, vol1_filename,
366
356
manifest.volume_info_dict[1])
369
if not globals.restart:
359
if not config.restart:
370
360
# normal backup start
372
362
mf = manifest.Manifest(fh=man_outfp)
375
365
# restart from last known position
376
mf = globals.restart.last_backup.get_local_manifest()
377
globals.restart.checkManifest(mf)
378
globals.restart.setLastSaved(mf)
379
validate_encryption_settings(globals.restart.last_backup, mf)
366
mf = config.restart.last_backup.get_local_manifest()
367
config.restart.checkManifest(mf)
368
config.restart.setLastSaved(mf)
369
validate_encryption_settings(config.restart.last_backup, mf)
380
370
mf.fh = man_outfp
381
last_block = globals.restart.last_block
371
last_block = config.restart.last_block
382
372
log.Notice(_(u"Restarting after volume %s, file %s, block %s") %
383
(globals.restart.start_vol,
384
util.uindex(globals.restart.last_index),
385
globals.restart.last_block))
386
vol_num = globals.restart.start_vol
373
(config.restart.start_vol,
374
util.uindex(config.restart.last_index),
375
config.restart.last_block))
376
vol_num = config.restart.start_vol
387
377
restart_position_iterator(tarblock_iter)
403
393
# is an assert put in place to avoid someone accidentally
404
394
# enabling concurrency above 1, before adequate work has been
405
395
# done on the backends to make them support concurrency.
406
assert globals.async_concurrency <= 1
396
assert config.async_concurrency <= 1
408
io_scheduler = asyncscheduler.AsyncScheduler(globals.async_concurrency)
398
io_scheduler = asyncscheduler.AsyncScheduler(config.async_concurrency)
409
399
async_waiters = []
411
401
while not at_end:
417
407
dest_filename = file_naming.get(backup_type, vol_num,
418
encrypted=globals.encryption,
419
gzipped=globals.compression)
408
encrypted=config.encryption,
409
gzipped=config.compression)
420
410
tdp = dup_temp.new_tempduppath(file_naming.parse(dest_filename))
423
if globals.encryption:
424
at_end = gpg.GPGWriteFile(tarblock_iter, tdp.name, globals.gpg_profile,
426
elif globals.compression:
427
at_end = gpg.GzipWriteFile(tarblock_iter, tdp.name, globals.volsize)
413
if config.encryption:
414
at_end = gpg.GPGWriteFile(tarblock_iter, tdp.name, config.gpg_profile,
416
elif config.compression:
417
at_end = gpg.GzipWriteFile(tarblock_iter, tdp.name, config.volsize)
429
at_end = gpg.PlainWriteFile(tarblock_iter, tdp.name, globals.volsize)
419
at_end = gpg.PlainWriteFile(tarblock_iter, tdp.name, config.volsize)
432
422
# Add volume information to manifest
452
442
log.Progress(_(u'Processed volume %d') % vol_num, diffdir.stats.SourceFileSize)
453
443
# Snapshot (serialize) progress now as a Volume has been completed.
454
444
# This is always the last restore point when it comes to restart a failed backup
456
446
progress.tracker.snapshot_progress(vol_num)
458
448
# for testing purposes only - assert on inc or full
459
assert globals.fail_on_volume != vol_num, u"Forced assertion for testing at volume %d" % vol_num
449
assert config.fail_on_volume != vol_num, u"Forced assertion for testing at volume %d" % vol_num
461
451
# Collect byte count from all asynchronous jobs; also implicitly waits
462
452
# for them all to complete.
522
512
perm_sig_filename = file_naming.get(sig_type,
524
remote_sig_filename = file_naming.get(sig_type, encrypted=globals.encryption,
525
gzipped=globals.compression)
514
remote_sig_filename = file_naming.get(sig_type, encrypted=config.encryption,
515
gzipped=config.compression)
527
fh = dup_temp.get_fileobj_duppath(globals.archive_dir_path,
517
fh = dup_temp.get_fileobj_duppath(config.archive_dir_path,
528
518
part_sig_filename,
529
519
perm_sig_filename,
530
520
remote_sig_filename,
546
536
progress.tracker = progress.ProgressTracker()
547
537
# Fake a backup to compute total of moving bytes
548
tarblock_iter = diffdir.DirFull(globals.select)
538
tarblock_iter = diffdir.DirFull(config.select)
549
539
dummy_backup(tarblock_iter)
550
540
# Store computed stats to compute progress later
551
541
progress.tracker.set_evidence(diffdir.stats, True)
552
# Reinit the globals.select iterator, so
542
# Reinit the config.select iterator, so
553
543
# the core of duplicity can rescan the paths
554
544
commandline.set_selection()
555
545
progress.progress_thread = progress.LogProgressThread()
558
tarblock_iter = diffdir.DirFull(globals.select)
548
tarblock_iter = diffdir.DirFull(config.select)
559
549
bytes_written = dummy_backup(tarblock_iter)
560
550
col_stats.set_values(sig_chain_warning=None)
562
552
sig_outfp = get_sig_fileobj(u"full-sig")
563
553
man_outfp = get_man_fileobj(u"full")
564
tarblock_iter = diffdir.DirFull_WriteSig(globals.select,
554
tarblock_iter = diffdir.DirFull_WriteSig(config.select,
566
556
bytes_written = write_multivol(u"full", tarblock_iter,
567
557
man_outfp, sig_outfp,
570
560
# close sig file, send to remote, and rename to final
571
561
sig_outfp.close()
608
598
return col_stats.matched_chain_pair[0]
611
def print_statistics(stats, bytes_written):
601
def print_statistics(stats, bytes_written): # pylint: disable=unused-argument
613
If globals.print_statistics, print stats after adding bytes_written
603
If config.print_statistics, print stats after adding bytes_written
618
if globals.print_statistics:
608
if config.print_statistics:
619
609
diffdir.stats.TotalDestinationSizeChange = bytes_written
620
610
logstring = diffdir.stats.get_stats_logstring(_(u"Backup Statistics"))
621
611
log.Log(logstring, log.NOTICE, force_print=True)
636
626
assert dup_time.curtime != dup_time.prevtime, \
637
627
u"time not moving forward at appropriate pace - system clock issues?"
640
630
progress.tracker = progress.ProgressTracker()
641
631
# Fake a backup to compute total of moving bytes
642
tarblock_iter = diffdir.DirDelta(globals.select,
632
tarblock_iter = diffdir.DirDelta(config.select,
643
633
sig_chain.get_fileobjs())
644
634
dummy_backup(tarblock_iter)
645
635
# Store computed stats to compute progress later
646
636
progress.tracker.set_evidence(diffdir.stats, False)
647
# Reinit the globals.select iterator, so
637
# Reinit the config.select iterator, so
648
638
# the core of duplicity can rescan the paths
649
639
commandline.set_selection()
650
640
progress.progress_thread = progress.LogProgressThread()
653
tarblock_iter = diffdir.DirDelta(globals.select,
643
tarblock_iter = diffdir.DirDelta(config.select,
654
644
sig_chain.get_fileobjs())
655
645
bytes_written = dummy_backup(tarblock_iter)
657
647
new_sig_outfp = get_sig_fileobj(u"new-sig")
658
648
new_man_outfp = get_man_fileobj(u"inc")
659
tarblock_iter = diffdir.DirDelta_WriteSig(globals.select,
649
tarblock_iter = diffdir.DirDelta_WriteSig(config.select,
660
650
sig_chain.get_fileobjs(),
662
652
bytes_written = write_multivol(u"inc", tarblock_iter,
663
653
new_man_outfp, new_sig_outfp,
666
656
# close sig file and rename to final
667
657
new_sig_outfp.close()
723
if not patchdir.Write_ROPaths(globals.local_path,
713
if not patchdir.Write_ROPaths(config.local_path,
724
714
restore_get_patched_rop_iter(col_stats)):
725
if globals.restore_dir:
715
if config.restore_dir:
726
716
log.FatalError(_(u"%s not found in archive - no files restored.")
727
% (util.fsdecode(globals.restore_dir)),
717
% (util.fsdecode(config.restore_dir)),
728
718
log.ErrorCode.restore_dir_not_found)
730
720
log.FatalError(_(u"No files found in archive - nothing restored."),
738
728
@type col_stats: CollectionStatus object
739
729
@param col_stats: collection status
741
if globals.restore_dir:
742
index = tuple(globals.restore_dir.split(b"/"))
731
if config.restore_dir:
732
index = tuple(config.restore_dir.split(b"/"))
745
time = globals.restore_time or dup_time.curtime
735
time = config.restore_time or dup_time.curtime
746
736
backup_chain = col_stats.get_backup_chain_at_time(time)
747
737
assert backup_chain, col_stats.all_backup_chains
748
738
backup_setlist = backup_chain.get_sets_at_time(time)
763
753
log.Progress(_(u'Processed volume %d of %d') % (cur_vol[0], num_vols),
764
754
cur_vol[0], num_vols)
766
if hasattr(globals.backend, u'pre_process_download'):
756
if hasattr(config.backend, u'pre_process_download'):
768
758
for backup_set in backup_setlist:
769
759
manifest = backup_set.get_manifest()
770
760
volumes = manifest.get_containing_volumes(index)
771
761
for vol_num in volumes:
772
762
file_names.append(backup_set.volume_name_dict[vol_num])
773
globals.backend.pre_process_download(file_names)
763
config.backend.pre_process_download(file_names)
775
765
fileobj_iters = list(map(get_fileobj_iter, backup_setlist))
776
766
tarfiles = list(map(patchdir.TarFile_FromFileobjs, fileobj_iters))
782
772
Return plaintext fileobj from encrypted filename on backend
784
774
If volume_info is set, the hash of the file will be checked,
785
assuming some hash is available. Also, if globals.sign_key is
775
assuming some hash is available. Also, if config.sign_key is
786
776
set, a fatal error will be raised if file not signed by sign_key.
837
827
u"""Thunk run when closing volume file"""
838
828
actual_sig = fileobj.fileobj.get_signature()
839
829
actual_sig = u"None" if actual_sig is None else actual_sig
840
sign_key = globals.gpg_profile.sign_key
830
sign_key = config.gpg_profile.sign_key
841
831
sign_key = u"None" if sign_key is None else sign_key
842
832
ofs = -min(len(actual_sig), len(sign_key))
843
833
if actual_sig[ofs:] != sign_key[ofs:]:
901
891
filestr = u"\n".join(map(util.fsdecode, extraneous))
903
893
log.Notice(ngettext(u"Deleting this file from backend:",
904
894
u"Deleting these files from backend:",
905
895
len(extraneous)) + u"\n" + filestr)
906
if not globals.dry_run:
896
if not config.dry_run:
907
897
col_stats.backend.delete(ext_remote)
908
898
for fn in ext_local:
910
globals.archive_dir_path.append(fn).delete()
900
config.archive_dir_path.append(fn).delete()
911
901
except Exception:
930
assert globals.keep_chains is not None
920
assert config.keep_chains is not None
932
globals.remove_time = col_stats.get_nth_last_full_backup_time(globals.keep_chains)
922
config.remove_time = col_stats.get_nth_last_full_backup_time(config.keep_chains)
934
924
remove_old(col_stats)
937
927
def remove_old(col_stats):
939
Remove backup files older than globals.remove_time from backend
929
Remove backup files older than config.remove_time from backend
941
931
@type col_stats: CollectionStatus object
942
932
@param col_stats: collection status
962
952
_(u"Which can't be deleted because newer sets depend on them.")))
964
954
if (col_stats.matched_chain_pair and
965
col_stats.matched_chain_pair[1].end_time < globals.remove_time):
955
col_stats.matched_chain_pair[1].end_time < config.remove_time):
966
956
log.Warn(_(u"Current active backup chain is older than specified time. "
967
957
u"However, it will not be deleted. To remove all your backups, "
968
958
u"manually purge the repository."))
970
chainlist = col_stats.get_chains_older_than(globals.remove_time)
960
chainlist = col_stats.get_chains_older_than(config.remove_time)
972
if globals.remove_all_inc_of_but_n_full_mode:
962
if config.remove_all_inc_of_but_n_full_mode:
973
963
# ignore chains without incremental backups:
974
964
chainlist = list(x for x in chainlist if
975
965
(isinstance(x, dup_collections.SignatureChain) and x.inclist) or
978
968
if not chainlist:
979
969
log.Notice(_(u"No old backup sets found, nothing deleted."))
982
972
log.Notice(ngettext(u"Deleting backup chain at time:",
983
973
u"Deleting backup chains at times:",
984
974
len(chainlist)) +
985
975
u"\n" + chain_times_str(chainlist))
986
976
# Add signature files too, since they won't be needed anymore
987
chainlist += col_stats.get_signature_chains_older_than(globals.remove_time)
977
chainlist += col_stats.get_signature_chains_older_than(config.remove_time)
988
978
chainlist.reverse() # save oldest for last
989
979
for chain in chainlist:
990
980
# if remove_all_inc_of_but_n_full_mode mode, remove only
991
981
# incrementals one and not full
992
if globals.remove_all_inc_of_but_n_full_mode:
982
if config.remove_all_inc_of_but_n_full_mode:
993
983
if isinstance(chain, dup_collections.SignatureChain):
994
984
chain_desc = _(u"Deleting any incremental signature chain rooted at %s")
1021
1011
action = u"replicate"
1022
time = globals.restore_time or dup_time.curtime
1023
src_stats = dup_collections.CollectionsStatus(globals.src_backend, None, action).set_values(sig_chain_warning=None)
1024
tgt_stats = dup_collections.CollectionsStatus(globals.backend, None, action).set_values(sig_chain_warning=None)
1012
time = config.restore_time or dup_time.curtime
1013
src_stats = dup_collections.CollectionsStatus(config.src_backend, None, action).set_values(sig_chain_warning=None)
1014
tgt_stats = dup_collections.CollectionsStatus(config.backend, None, action).set_values(sig_chain_warning=None)
1026
src_list = globals.src_backend.list()
1027
tgt_list = globals.backend.list()
1016
src_list = config.src_backend.list()
1017
tgt_list = config.backend.list()
1029
1019
src_chainlist = src_stats.get_signature_chains(local=False, filelist=src_list)[0]
1030
1020
tgt_chainlist = tgt_stats.get_signature_chains(local=False, filelist=tgt_list)[0]
1054
1044
dup_time.setprevtime(src_sig.start_time)
1055
1045
dup_time.setcurtime(src_sig.time or src_sig.end_time)
1056
1046
log.Notice(_(u"Replicating %s.") % (src_sig_filename,))
1057
fileobj = globals.src_backend.get_fileobj_read(src_sig_filename)
1058
filename = file_naming.get(src_sig.type, encrypted=globals.encryption, gzipped=globals.compression)
1047
fileobj = config.src_backend.get_fileobj_read(src_sig_filename)
1048
filename = file_naming.get(src_sig.type, encrypted=config.encryption, gzipped=config.compression)
1059
1049
tdp = dup_temp.new_tempduppath(file_naming.parse(filename))
1060
1050
tmpobj = tdp.filtered_open(mode=u'wb')
1061
1051
util.copyfileobj(fileobj, tmpobj) # decrypt, compress, (re)-encrypt
1062
1052
fileobj.close()
1064
globals.backend.put(tdp, filename)
1054
config.backend.put(tdp, filename)
1067
1057
src_chainlist = src_stats.get_backup_chains(filename_list=src_list)[0]
1093
1083
mf = manifest.Manifest(fh=mf_tdp.filtered_open(mode=u'wb'))
1094
1084
for i, filename in list(src_set.volume_name_dict.items()):
1095
1085
log.Notice(_(u"Replicating %s.") % (filename,))
1096
fileobj = restore_get_enc_fileobj(globals.src_backend, filename, rmf.volume_info_dict[i])
1097
filename = file_naming.get(src_set.type, i, encrypted=globals.encryption, gzipped=globals.compression)
1086
fileobj = restore_get_enc_fileobj(config.src_backend, filename, rmf.volume_info_dict[i])
1087
filename = file_naming.get(src_set.type, i, encrypted=config.encryption, gzipped=config.compression)
1098
1088
tdp = dup_temp.new_tempduppath(file_naming.parse(filename))
1099
1089
tmpobj = tdp.filtered_open(mode=u'wb')
1100
1090
util.copyfileobj(fileobj, tmpobj) # decrypt, compress, (re)-encrypt
1101
1091
fileobj.close()
1103
globals.backend.put(tdp, filename)
1093
config.backend.put(tdp, filename)
1105
1095
vi = copy.copy(rmf.volume_info_dict[i])
1106
1096
vi.set_hash(u"SHA1", gpg.get_hash(u"SHA1", tdp))
1113
1103
mf_fileobj = mf_tdp.filtered_open_with_delete(mode=u'rb')
1114
1104
mf_final_filename = file_naming.get(src_set.type,
1116
encrypted=globals.encryption,
1117
gzipped=globals.compression)
1106
encrypted=config.encryption,
1107
gzipped=config.compression)
1118
1108
mf_final_tdp = dup_temp.new_tempduppath(file_naming.parse(mf_final_filename))
1119
1109
mf_final_fileobj = mf_final_tdp.filtered_open(mode=u'wb')
1120
1110
util.copyfileobj(mf_fileobj, mf_final_fileobj) # compress, encrypt
1121
1111
mf_fileobj.close()
1122
1112
mf_final_fileobj.close()
1123
globals.backend.put(mf_final_tdp, mf_final_filename)
1113
config.backend.put(mf_final_tdp, mf_final_filename)
1124
1114
mf_final_tdp.delete()
1126
globals.src_backend.close()
1127
globals.backend.close()
1116
config.src_backend.close()
1117
config.backend.close()
1130
1120
def sync_archive(col_stats):
1147
1137
Otherwise, only the metadata for the target chain needs sync.
1149
if globals.metadata_sync_mode == u"full":
1139
if config.metadata_sync_mode == u"full":
1151
assert globals.metadata_sync_mode == u"partial"
1141
assert config.metadata_sync_mode == u"partial"
1152
1142
parsed = file_naming.parse(filename)
1154
1144
target_chain = col_stats.get_backup_chain_at_time(
1155
globals.restore_time or dup_time.curtime)
1145
config.restore_time or dup_time.curtime)
1156
1146
except dup_collections.CollectionsError:
1157
1147
# With zero or multiple chains at this time, do a full sync
1294
1284
gpg.GzipWriteFile(src_iter, tdp.name, size=sys.maxsize)
1296
tdp.move(globals.archive_dir_path.append(loc_name))
1286
tdp.move(config.archive_dir_path.append(loc_name))
1298
1288
# get remote metafile list
1299
remlist = globals.backend.list()
1289
remlist = config.backend.list()
1300
1290
remote_metafiles, ignored, rem_needpass = get_metafiles(remlist)
1302
1292
# get local metafile list
1303
loclist = globals.archive_dir_path.listdir()
1293
loclist = config.archive_dir_path.listdir()
1304
1294
local_metafiles, local_partials, loc_needpass = get_metafiles(loclist)
1306
1296
# we have the list of metafiles on both sides. remote is always
1334
1324
local_missing.sort()
1335
1325
local_spurious.sort()
1336
if not globals.dry_run:
1326
if not config.dry_run:
1337
1327
log.Notice(_(u"Synchronizing remote metadata to local cache..."))
1338
1328
if local_missing and (rem_needpass or loc_needpass):
1339
1329
# password for the --encrypt-key
1340
globals.gpg_profile.passphrase = get_passphrase(1, u"sync")
1330
config.gpg_profile.passphrase = get_passphrase(1, u"sync")
1341
1331
for fn in local_spurious:
1342
1332
remove_local(fn)
1343
if hasattr(globals.backend, u'pre_process_download'):
1344
globals.backend.pre_process_download(local_missing)
1333
if hasattr(config.backend, u'pre_process_download'):
1334
config.backend.pre_process_download(local_missing)
1345
1335
for fn in local_missing:
1346
1336
copy_to_local(fn)
1347
1337
col_stats.set_values()
1399
1389
# Calculate space we need for at least 2 volumes of full or inc
1400
1390
# plus about 30% of one volume for the signature files.
1401
1391
freespace = stats.f_frsize * stats.f_bavail
1402
needspace = (((globals.async_concurrency + 1) * globals.volsize) +
1403
int(0.30 * globals.volsize))
1392
needspace = (((config.async_concurrency + 1) * config.volsize) +
1393
int(0.30 * config.volsize))
1404
1394
if freespace < needspace:
1405
1395
log.FatalError(_(u"Temp space has %d available, backup needs approx %d.") %
1406
1396
(freespace, needspace), log.ErrorCode.not_enough_freespace)
1524
1514
# determine what action we're performing and process command line
1525
1515
action = commandline.ProcessCommandLine(sys.argv[1:])
1527
globals.lockpath = os.path.join(globals.archive_dir_path.name, b"lockfile")
1528
globals.lockfile = fasteners.process_lock.InterProcessLock(globals.lockpath)
1529
log.Debug(_(u"Acquiring lockfile %s") % globals.lockpath)
1530
if not globals.lockfile.acquire(blocking=False):
1517
config.lockpath = os.path.join(config.archive_dir_path.name, b"lockfile")
1518
config.lockfile = fasteners.process_lock.InterProcessLock(config.lockpath)
1519
log.Debug(_(u"Acquiring lockfile %s") % config.lockpath)
1520
if not config.lockfile.acquire(blocking=False):
1531
1521
log.FatalError(
1532
1522
u"Another duplicity instance is already running with this archive directory\n",
1533
1523
log.ErrorCode.user_error)
1574
1564
if last_backup.partial:
1575
1565
if action in [u"full", u"inc"]:
1576
1566
# set restart parms from last_backup info
1577
globals.restart = Restart(last_backup)
1567
config.restart = Restart(last_backup)
1578
1568
# (possibly) reset action
1579
action = globals.restart.type
1569
action = config.restart.type
1580
1570
# reset the time strings
1581
1571
if action == u"full":
1582
dup_time.setcurtime(globals.restart.time)
1572
dup_time.setcurtime(config.restart.time)
1584
dup_time.setcurtime(globals.restart.end_time)
1585
dup_time.setprevtime(globals.restart.start_time)
1574
dup_time.setcurtime(config.restart.end_time)
1575
dup_time.setprevtime(config.restart.start_time)
1586
1576
# log it -- main restart heavy lifting is done in write_multivol
1587
1577
log.Notice(_(u"Last %s backup left a partial set, restarting." % action))
1603
1593
log.Notice(_(u"Last full backup date:") + u" " + dup_time.timetopretty(last_full_time))
1605
1595
log.Notice(_(u"Last full backup date: none"))
1606
if not globals.restart and action == u"inc" and globals.full_force_time is not None and \
1607
last_full_time < globals.full_force_time:
1596
if not config.restart and action == u"inc" and config.full_force_time is not None and \
1597
last_full_time < config.full_force_time:
1608
1598
log.Notice(_(u"Last full backup is too old, forcing full backup"))
1609
1599
action = u"full"
1610
1600
log.PrintCollectionStatus(col_stats)
1621
1611
elif action == u"list-current":
1622
1612
list_current(col_stats)
1623
1613
elif action == u"collection-status":
1624
if not globals.file_changed:
1614
if not config.file_changed:
1625
1615
log.PrintCollectionStatus(col_stats, True)
1627
log.PrintCollectionFileChangedStatus(col_stats, globals.file_changed, True)
1617
log.PrintCollectionFileChangedStatus(col_stats, config.file_changed, True)
1628
1618
elif action == u"cleanup":
1629
1619
cleanup(col_stats)
1630
1620
elif action == u"remove-old":
1640
1630
# the passphrase for full and inc is used by --sign-key
1641
1631
# the sign key can have a different passphrase than the encrypt
1642
1632
# key, therefore request a passphrase
1643
if globals.gpg_profile.sign_key:
1644
globals.gpg_profile.signing_passphrase = get_passphrase(1, action, True)
1633
if config.gpg_profile.sign_key:
1634
config.gpg_profile.signing_passphrase = get_passphrase(1, action, True)
1646
1636
# if there are no recipients (no --encrypt-key), it must be a
1647
1637
# symmetric key. Therefore, confirm the passphrase
1648
if not (globals.gpg_profile.recipients or globals.gpg_profile.hidden_recipients):
1649
globals.gpg_profile.passphrase = get_passphrase(2, action)
1638
if not (config.gpg_profile.recipients or config.gpg_profile.hidden_recipients):
1639
config.gpg_profile.passphrase = get_passphrase(2, action)
1650
1640
# a limitation in the GPG implementation does not allow for
1651
1641
# inputting different passphrases, this affects symmetric+sign.
1652
1642
# Allow an empty passphrase for the key though to allow a non-empty
1653
1643
# symmetric key
1654
if (globals.gpg_profile.signing_passphrase and
1655
globals.gpg_profile.passphrase != globals.gpg_profile.signing_passphrase):
1644
if (config.gpg_profile.signing_passphrase and
1645
config.gpg_profile.passphrase != config.gpg_profile.signing_passphrase):
1656
1646
log.FatalError(_(
1657
1647
u"When using symmetric encryption, the signing passphrase "
1658
1648
u"must equal the encryption passphrase."),