#!/usr/bin/python import os import stat import sys import subprocess import shutil import getpass import tempfile import getopt import time def PathFromList(lst): if lst is None or len(lst) < 0: return None retval = '/' for f in lst: retval = os.path.join(retval, f) return retval def PathToList(path): if path is None: return path path = '%s' % path tmp = path.split('/') retval = [] for m in tmp: if m: retval.append(m) return retval class Importer(object): post_commit = """#!/bin/sh REPOS="$1" TXN="$2" $(dirname $0)/../../encrypt.py -p \ -t "${TXN}" \ -b "$(dirname $0)/../.." >> %(log_file)s 2>&1 exit $? """ def __init__(self, base, log_file): self.base = base self.log_file = log_file self.clear_repos = os.path.join(self.base, 'clear_repos') self.clear_checkout = os.path.join(self.base, 'clear_checkout') self.enc_repos = os.path.join(self.base, 'enc_repos') self.enc_checkout = os.path.join(self.base, 'enc_checkout') self.enc_src = self.enc_checkout self.enc_mnt = os.path.join(self.base, 'enc_mnt') def RemoveNonEncFSFiles(self, path): pass def Rsync(self, src, tgt): def __cb(paths, dirname, fnames): (src, tgt,) = paths for f in fnames: path = os.path.join(dirname, f) path = os.path.normpath(os.path.abspath(path)) st = os.stat(path) if stat.S_ISDIR(st): os.makedirs(path.replace(src, tgt)) elif stat.S_ISREG(st) or stat.S_ISLINK(st): shutil.copy(path, path.replace(src, tgt)) tgt = os.path.normpath(os.path.abspath(tgt)) src = os.path.normpath(os.path.abspath(src)) os.path.walk(src, __cb, (src, tgt,)) def EncMount(self, src, mnt): cmd = [ '/usr/bin/encfs', src, mnt, ] print cmd ; sys.stdout.flush() p = subprocess.Popen(cmd, stdin=subprocess.PIPE) setup = 'x\n1\n256\n4096\n2\nn\nn\nn\n' p.stdin.write(setup) p.wait() def Hook(self, tid): print '=================================================================' print 'TID: %s' % tid print '=================================================================' # update the clear checkout cmd = [ '/usr/bin/svn', 'up', self.clear_checkout ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # copy over the data in encrypted form for f in os.listdir(self.clear_checkout): if f in ['.', '..', '.svn']: continue cmd = [ '/usr/bin/rsync', '-avvlr', '--delete', '--exclude', '.svn', os.path.join(self.clear_checkout, f), '%s/'%self.enc_mnt, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # syncronize subversion's view of the world cmd = [ '/usr/bin/svn', 'status', self.enc_checkout, ] print cmd ; sys.stdout.flush() p = subprocess.Popen(cmd, stdout=subprocess.PIPE) raw = p.stdout.read() p.wait() lines = raw.split('\n') for line in lines: if not line: continue path = line[7:] if line.startswith('!'): cmd = [ '/usr/bin/svn', 'rm', '--force', path, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) elif line.startswith('?'): cmd = [ '/usr/bin/svn', 'add', path, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # determine the old log entry cmd = [ "/usr/bin/svn", "pg", "--revprop", "svn:log", "file://%s" % self.clear_repos, "-r", '%s'%tid, ] print cmd ; sys.stdout.flush() p = subprocess.Popen(cmd, stdout=subprocess.PIPE) log = p.stdout.read() p.wait() # commit the changes cmd = [ "/usr/bin/svn", "ci", "-m", log, self.enc_checkout ] print cmd ; sys.stdout.flush() subprocess.call(cmd) def Import(self, dump_file, output): # clear the log file if os.path.exists(self.log_file): os.unlink(self.log_file) # svnadmin create --fs-type=fsfs clear_repos cmd = [ '/usr/bin/svnadmin', 'create', '--fs-type=fsfs', self.clear_repos, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # svnadmin create --fs-type=fsfs enc_repos cmd = [ '/usr/bin/svnadmin', 'create', '--fs-type=fsfs', self.enc_repos, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # create the post-commit hook file commit_file = os.path.join(self.clear_repos, 'hooks/post-commit') fd = open(commit_file, 'w') fd.write(self.post_commit%{'log_file':self.log_file}) fd.close() os.chmod(commit_file, 0755) # copy this file to the base directory shutil.copy(sys.argv[0], self.base) # mkdir the secure mountpoint os.mkdir(self.enc_mnt) # svn checkout from the secure repository cmd = [ '/usr/bin/svn', 'co', 'file://%s' % self.enc_repos, self.enc_checkout, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # encfs mount the secure mountpoint self.EncMount(self.enc_checkout, self.enc_mnt) # create the clear checkout cmd = [ '/usr/bin/svn', 'co', 'file://%s'%self.clear_repos, self.clear_checkout, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # add the .encfs file efile = None for f in os.listdir(self.enc_checkout): if f.startswith('.encfs'): efile = f break cmd = [ '/usr/bin/svn', 'add', '%s/%s' % (self.enc_checkout, efile), ] print cmd ; sys.stdout.flush() subprocess.call(cmd) cmd = [ '/usr/bin/svn', 'ci', '%s/%s' % (self.enc_checkout, efile), '-m', 'Adding the encfs control file' ] print cmd ; sys.stdout.flush() subprocess.call(cmd) # launch the import cmd = [ 'svnadmin', 'load', '--use-post-commit-hook', self.clear_repos, ] print cmd ; sys.stdout.flush() p = subprocess.Popen(cmd, stdin=subprocess.PIPE) fd = open(dump_file) while True: raw = fd.read() if not raw: break p.stdin.write(raw) fd.close() p.stdin.close() p.wait() # create a dump file cmd = [ '/usr/bin/svnadmin', 'dump', self.enc_repos, ] print cmd ; sys.stdout.flush() p = subprocess.Popen(cmd, stdout=subprocess.PIPE) fd = open(output, 'w') while True: raw = p.stdout.read(4096) if not raw: break fd.write(raw) fd.close() p.wait() # clean up the temporary directory cmd = [ '/bin/fusermount', '-u', self.enc_mnt, ] print cmd ; sys.stdout.flush() subprocess.call(cmd) time.sleep(2) shutil.rmtree(self.base) def Usage(argv): print """ %s -b -d -h: help -l -p: post-commit hook flag -t """ % argv[0] def Main(argv): need_help = False commit_hook = False tid = 0 base = None log_file = 'encryption.log' output = 'encrypted.dump' # parse the command-line arguments opts = 'b:d:hl:o:pt:T' optlist, lst = getopt.getopt(argv[1:], opts) for opt in optlist: if False:pass elif opt[0] in ['-b']: base = opt[1] elif opt[0] in ['-d']: dump_file = opt[1] elif opt[0] in ['-h']: need_help = True elif opt[0] in ['-l']: log_file = opt[1] elif opt[0] in ['-o']: output = opt[1] elif opt[0] in ['-p']: commit_hook = True elif opt[0] in ['-t']: tid = int(opt[1]) # check the help flag if need_help: Usage(argv) return 0 # fixme(kungfoo): check the arguments if base is None: base = tempfile.mkdtemp(prefix="/tmp/import_") imp = Importer(os.path.normpath(os.path.abspath(base)), log_file) print commit_hook if commit_hook: return imp.Hook(tid) else: return imp.Import(dump_file, output) if __name__ == '__main__': sys.exit(Main(sys.argv))