Make sure all the par2 files aren't empty. Previously, we only
checked whether the top-level PACK.idx index file was empty, ignoring
the vol files. That meant that we might skip generation, even if the
vol files were broken.
Use stem instead of base in some cases to refer to the
/some/where/pack-HASH packfile "stems" since base is potentially
misleading, suggesting "basename", which we also need, e.g. this also
allows us to change "last" (which is the basename) to base.
lib/bup/cmd/fsck.py | 68 ++++++++++++++++++++++++++++-----------------
lib/bup/helpers.py | 1 +
note/
0.33.x.md | 7 ++++-
3 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/lib/bup/cmd/fsck.py b/lib/bup/cmd/fsck.py
index accf75f7f..cb1cbb45d 100644
--- a/lib/bup/cmd/fsck.py
+++ b/lib/bup/cmd/fsck.py
@@ -9,9 +9,9 @@ import glob, os, subprocess, sys
from bup import options, git
from bup.compat import argv_bytes
from bup.helpers \
- import (EXIT_FALSE, EXIT_TRUE,
+ import (EXIT_ERROR, EXIT_FALSE, EXIT_TRUE,
Sha1, chunkyreader, istty2, log, progress, temp_dir)
-from
bup.io import byte_stream
+from
bup.io import byte_stream, path_msg
par2_ok = 0
@@ -144,39 +144,63 @@ def git_verify(base):
return run([b'git', b'verify-pack', b'--', base])
-def do_pack(base, last, par2_exists, out):
+def do_pack(stem, out):
+ pack_dir, base = os.path.split(stem)
+
+ # Check par2 status. We look for empty *.par2 files because C-c
+ # during "par2 create" may produce them. For now, we decide the
+ # existing data is OK if the pack-HASH.par2 index file exists, and
+ # no pack-HASH*.par2 files are empty.
+ par2_idx = base + b'.par2'
+ par2_idx_ent, par2_empty = None, False
+ empty_severity = 'warning:' if opt.generate else 'error:'
+ for ent in [ent for ent in os.scandir(pack_dir)
+ if ent.name.startswith(base) and ent.name.endswith(b'.par2')]:
+ if
ent.name == par2_idx:
+ par2_idx_ent = ent
+ if ent.stat().st_size == 0:
+ log(f'{empty_severity} empty file - {path_msg(
ent.name)}\n')
+ par2_empty = True
+ if par2_empty:
+ if not opt.generate:
+ # FIXME: could print multiple times for parallel jobs
+ log(f'{empty_severity} please remove empty (broken) *.par2 files\n')
+ return EXIT_ERROR
+
+ par2_exists = par2_idx_ent and not par2_empty
+
code = 0
if par2_ok and par2_exists and (opt.repair or not opt.generate):
- vresult = par2_verify(base)
+ vresult = par2_verify(stem)
if vresult != 0:
if opt.repair:
- rresult = par2_repair(base)
+ rresult = par2_repair(stem)
if rresult != 0:
action_result = b'failed'
- log('%s par2 repair: failed (%d)\n' % (last, rresult))
+ log('%s par2 repair: failed (%d)\n' % (base, rresult))
code = rresult
else:
action_result = b'repaired'
- log('%s par2 repair: succeeded (0)\n' % last)
+ log('%s par2 repair: succeeded (0)\n' % base)
code = 100
else:
action_result = b'failed'
- log('%s par2 verify: failed (%d)\n' % (last, vresult))
+ log('%s par2 verify: failed (%d)\n' % (base, vresult))
code = vresult
else:
action_result = b'ok'
elif not opt.generate or (par2_ok and not par2_exists):
- gresult = git_verify(base)
+ gresult = git_verify(stem)
if gresult != 0:
action_result = b'failed'
- log('%s git verify: failed (%d)\n' % (last, gresult))
+ log('%s git verify: failed (%d)\n' % (base, gresult))
code = gresult
else:
if par2_ok and opt.generate:
- presult = par2_generate(base)
+ presult = par2_generate(stem)
if presult != 0:
action_result = b'failed'
- log('%s par2 create: failed (%d)\n' % (last, presult))
+ log('%s par2 create: failed (%d)\n' % (base, presult))
code = presult
else:
action_result = b'generated'
@@ -186,7 +210,7 @@ def do_pack(base, last, par2_exists, out):
assert(opt.generate and (not par2_ok or par2_exists))
action_result = b'exists' if par2_exists else b'skipped'
if opt.verbose:
- out.write(last + b' ' + action_result + b'\n')
+ out.write(base + b' ' + action_result + b'\n')
return code
@@ -232,27 +256,21 @@ def main(argv):
outstanding = {}
for name in extra:
if name.endswith(b'.pack'):
- base = name[:-5]
+ stem = name[:-5]
elif name.endswith(b'.idx'):
- base = name[:-4]
+ stem = name[:-4]
elif name.endswith(b'.par2'):
- base = name[:-5]
+ stem = name[:-5]
elif os.path.exists(name + b'.pack'):
- base = name
+ stem = name
else:
raise Exception('%r is not a pack file!' % name)
- (dir,last) = os.path.split(base)
- par2_exists = os.path.exists(base + b'.par2')
- if par2_exists and os.stat(base + b'.par2').st_size == 0:
- par2_exists = 0
sys.stdout.flush() # Not sure we still need this, but it'll flush out too
- debug('fsck: checking %r (%s)\n'
- % (last, par2_ok and par2_exists and 'par2' or 'git'))
if not opt.verbose:
progress('fsck (%d/%d)\r' % (count, len(extra)))
if not
opt.jobs:
- nc = do_pack(base, last, par2_exists, out)
+ nc = do_pack(stem, out)
code = code or nc
count += 1
else:
@@ -268,7 +286,7 @@ def main(argv):
outstanding[pid] = 1
else: # child
try:
- sys.exit(do_pack(base, last, par2_exists, out))
+ sys.exit(do_pack(stem, out))
except Exception as e:
log('exception: %r\n' % e)
sys.exit(99)
diff --git a/lib/bup/helpers.py b/lib/bup/helpers.py
index 365c246be..c609d6ed5 100644
--- a/lib/bup/helpers.py
+++ b/lib/bup/helpers.py
@@ -22,6 +22,7 @@ from bup.options import _tty_width as tty_width
EXIT_TRUE = 0
EXIT_FALSE = 1
+EXIT_ERROR = 2
buglvl = int(os.environ.get('BUP_DEBUG', 0))
diff --git a/note/
0.33.x.md b/note/
0.33.x.md
index 1fbd153fa..e41a1f92f 100644
--- a/note/
0.33.x.md
+++ b/note/
0.33.x.md
@@ -7,7 +7,12 @@ May require attention
* The `par2` command (invoked by `bup fsck -g`) may generate empty
recovery files if interrupted (say via C-c). To mitigate this, bup
now runs `par2` in a temporary directory, and only moves the
- recovery files into place if the generation succeeds. See also
+ recovery files into place if the generation succeeds. It will also
+ look for any existing, empty par2 files associated with packfiles it
+ has been asked to examine. If found, they will provoke an error
+ unless bup has been asked to `--generate` new ones, in which case
+ bup will just issue a warning, since the pending `par2 generate ...`
+ should correct the problem. See also
https://github.com/Parchive/par2cmdline/issues/84
* Previously, any `bup on REMOTE ...` commands that attempted to read
--
2.43.0