3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
8 from __future__ import absolute_import
9 import sys, os, glob, subprocess
11 from bup import options, git
12 from bup.helpers import Sha1, chunkyreader, istty2, log, progress
15 nullf = open('/dev/null')
22 # at least in python 2.5, using "stdout=2" or "stdout=sys.stderr" below
23 # doesn't actually work, because subprocess closes fd #2 right before
24 # execing for some reason. So we work around it by duplicating the fd
26 fd = os.dup(2) # copy stderr
28 p = subprocess.Popen(argv, stdout=fd, close_fds=False)
37 p = subprocess.Popen(['par2', '--help'],
38 stdout=nullf, stderr=nullf, stdin=nullf)
41 log('fsck: warning: par2 not found; disabling recovery features.\n')
46 if opt.verbose >= lvl:
54 def par2_generate(base):
55 return run(['par2', 'create', '-n1', '-c200'] + parv(2)
56 + ['--', base, base+'.pack', base+'.idx'])
58 def par2_verify(base):
59 return run(['par2', 'verify'] + parv(3) + ['--', base])
61 def par2_repair(base):
62 return run(['par2', 'repair'] + parv(2) + ['--', base])
64 def quick_verify(base):
65 f = open(base + '.pack', 'rb')
68 assert(len(wantsum) == 20)
71 for b in chunkyreader(f, os.fstat(f.fileno()).st_size - 20):
73 if sum.digest() != wantsum:
74 raise ValueError('expected %r, got %r' % (wantsum.encode('hex'),
82 except Exception as e:
83 log('error: %s\n' % e)
87 return run(['git', 'verify-pack', '--', base])
90 def do_pack(base, last, par2_exists):
92 if par2_ok and par2_exists and (opt.repair or not opt.generate):
93 vresult = par2_verify(base)
96 rresult = par2_repair(base)
98 action_result = 'failed'
99 log('%s par2 repair: failed (%d)\n' % (last, rresult))
102 action_result = 'repaired'
103 log('%s par2 repair: succeeded (0)\n' % last)
106 action_result = 'failed'
107 log('%s par2 verify: failed (%d)\n' % (last, vresult))
111 elif not opt.generate or (par2_ok and not par2_exists):
112 gresult = git_verify(base)
114 action_result = 'failed'
115 log('%s git verify: failed (%d)\n' % (last, gresult))
118 if par2_ok and opt.generate:
119 presult = par2_generate(base)
121 action_result = 'failed'
122 log('%s par2 create: failed (%d)\n' % (last, presult))
125 action_result = 'generated'
129 assert(opt.generate and (not par2_ok or par2_exists))
130 action_result = 'exists' if par2_exists else 'skipped'
132 print last, action_result
137 bup fsck [options...] [filenames...]
139 r,repair attempt to repair errors using par2 (dangerous!)
140 g,generate generate auto-repair information using par2
141 v,verbose increase verbosity (can be used more than once)
142 quick just check pack sha1sum, don't use git verify-pack
143 j,jobs= run 'n' jobs in parallel
144 par2-ok immediately return 0 if par2 is ok, 1 if not
145 disable-par2 ignore par2 even if it is available
147 o = options.Options(optspec)
148 (opt, flags, extra) = o.parse(sys.argv[1:])
153 sys.exit(0) # 'true' in sh
159 git.check_repo_or_die()
162 debug('fsck: No filenames given: checking all packs.\n')
163 extra = glob.glob(git.repo('objects/pack/*.pack'))
169 if name.endswith('.pack'):
171 elif name.endswith('.idx'):
173 elif name.endswith('.par2'):
175 elif os.path.exists(name + '.pack'):
178 raise Exception('%s is not a pack file!' % name)
179 (dir,last) = os.path.split(base)
180 par2_exists = os.path.exists(base + '.par2')
181 if par2_exists and os.stat(base + '.par2').st_size == 0:
184 debug('fsck: checking %s (%s)\n'
185 % (last, par2_ok and par2_exists and 'par2' or 'git'))
187 progress('fsck (%d/%d)\r' % (count, len(extra)))
190 nc = do_pack(base, last, par2_exists)
194 while len(outstanding) >= opt.jobs:
197 if pid in outstanding:
206 sys.exit(do_pack(base, last, par2_exists))
207 except Exception as e:
208 log('exception: %r\n' % e)
211 while len(outstanding):
214 if pid in outstanding:
219 progress('fsck (%d/%d)\r' % (count, len(extra)))
222 debug('fsck done. \n')