author | Alberto Bertogli
<albertogli@telpin.com.ar> 2005-04-12 22:59:50 UTC |
committer | Alberto Bertogli
<albertogli@telpin.com.ar> 2005-04-12 22:59:50 UTC |
parent | c2d81ffc6d766ba158b9805b8b39b95adae5be9f |
abk | +48 | -39 |
diff --git a/abk b/abk index 2dec45e..13c9bfa 100644 --- a/abk +++ b/abk @@ -29,7 +29,7 @@ VERSION = "0.03" def finfo_load(finfo): "Loads data from the file." - s = os.lstat(finfo.name) + s = os.lstat(finfo.fullname) finfo.stat = s if S_ISREG(s.st_mode): finfo.type = 'r' @@ -43,7 +43,7 @@ def finfo_load(finfo): finfo.rdev = s.st_rdev elif S_ISFIFO(s.st_mode): finfo.type = 'f' - finfo.linkto = os.readlink(finfo.name) + finfo.linkto = os.readlink(finfo.fullname) elif S_ISDIR(s.st_mode): finfo.type = 'd' else: @@ -82,7 +82,7 @@ def finfo_cmp_data(finfo, other): def finfo_copy_file_reg_raw(finfo, dst): "Copy a regular file." - sfile = open(finfo.name, 'r') + sfile = open(finfo.fullname, 'r') dfile = open(dst, 'w') # the data @@ -97,7 +97,7 @@ def finfo_copy_file_reg_raw(finfo, dst): def finfo_copy_file_reg_bzip2(finfo, dst): "Copy a regular file, destination is bz2 compressed." import bz2 - sfile = open(finfo.name) + sfile = open(finfo.fullname) dfile = open(dst, 'w') bcomp = bz2.BZ2Compressor() @@ -112,7 +112,7 @@ def finfo_copy_file_reg_bzip2(finfo, dst): def finfo_copy_file_reg_gzip(finfo, dst): "Copy a regular file, destination is gzip compressed." import gzip - sfile = open(finfo.name) + sfile = open(finfo.fullname) dfile = gzip.open(dst, 'w') data = sfile.read(PSIZE) @@ -126,7 +126,7 @@ def finfo_copy_file_reg_gzip(finfo, dst): def finfo_copy_file_link(finfo, dst): "Copy a symbolic link." - linkto = os.readlink(finfo.name) + linkto = os.readlink(finfo.fullname) os.symlink(linkto, dst) @@ -177,7 +177,7 @@ def finfo_hash_file_sha(finfo): "Returns the sha1sum of a file." import sha hash = sha.new() - f = open(finfo.name) + f = open(finfo.fullname) data = f.read(PSIZE) while data: hash.update(data) @@ -189,7 +189,7 @@ def finfo_hash_file_md5(finfo): "Returns the md5sum of a file." import md5 hash = md5.new() - f = open(finfo.name) + f = open(finfo.fullname) data = f.read(PSIZE) while data: hash.update(data) @@ -204,8 +204,9 @@ def finfo_hash_file_none(finfo): class file_info: "Represents a file" - def __init__(self, name): + def __init__(self, name, fullname): self.name = name + self.fullname = fullname self.mode = 0 self.uid = 0 self.gid = 0 @@ -250,6 +251,7 @@ class index_file: self.name = name self.db = {} self.names = [] + self.pathdb = {} def load(self): "Loads data from the file." @@ -263,19 +265,22 @@ class index_file: def save(self): "Saves the index to the disk." + for f in self.db.keys(): + self.db[f].fullname = '' f = open(self.name, 'w') cPickle.dump((self.db, self.names), f, cPickle.HIGHEST_PROTOCOL) f.close() - def put_file(self, filename): + def put_file(self, filename, fullpath): "Incorporates a file into the index." - self.db[filename] = file_info(filename) + self.db[filename] = file_info(filename, fullpath) self.db[filename].load() if self.db[filename].type == 'u': # ignore files of unknown types, like unix sockets - del(self.db[filename].type) + del(self.db[filename]) return self.names.append(filename) + self.pathdb[filename] = fullpath def get_file(self, filename): "Get the file_info object for the given filename." @@ -283,17 +288,19 @@ class index_file: def populate(self, root): "Populate the index from a root path." - self.put_file(root) + root = os.path.abspath(root) + base, reduced = os.path.split(root) + self.put_file(reduced, root) tree = os.walk(root, topdown = True) for path, childs, files in tree: for f in files: - #name = os.path.join(path, f) - name = path + '/' + f - self.put_file(name) + full = path + '/' + f + name = relative_path(base, full) + self.put_file(name, full) for c in childs: - #name = os.path.join(path, c) - name = path + '/' + c - self.put_file(name) + full = path + '/' + c + name = relative_path(base, full) + self.put_file(name, full) def quiet_unlink(path): @@ -326,30 +333,26 @@ def make_path(f): # it can fail if already exist pass +def relative_path(base, path): + """If base = '/x/x/b' and path = '/x/x/b/c/d', returns 'b/c/d'. Both + must be absolute for simplicity.""" + res = path[len(base):] + while res[0] == '/': + res = res[1:] + return res + # # main operations # -def make_sync(src_path, srcidx_path, dst_path, dstidx_path): +def make_sync(sources, srcidx_path, dst_path, dstidx_path): "Sync two directories." - # reduce multiple '/' at the end to none (or only one if it's root) - while src_path[-1] == '/' and src_path != '/': - src_path = src_path[:-1] - # destination and indexes are always a complete path srcidx_path = os.path.join(os.getcwd(), srcidx_path) dst_path = os.path.join(os.getcwd(), dst_path) dstidx_path = os.path.join(os.getcwd(), dstidx_path) - # move to the source's parent, and use the new source so we - # create only one level directories. - if src_path != '/': - parent, src = os.path.split(src_path) - if parent: - os.chdir(parent) - src_path = src - # load destination index printv("* loading destination index") dstidx = index_file(dstidx_path) @@ -358,8 +361,9 @@ def make_sync(src_path, srcidx_path, dst_path, dstidx_path): # create source index printv("* building source index") srcidx = index_file(srcidx_path) - srcidx.populate(src_path) - srcidx.save() + for src_path in sources: + printv("\t* " + src_path) + srcidx.populate(src_path) printv("* sync") @@ -406,6 +410,11 @@ def make_sync(src_path, srcidx_path, dst_path, dstidx_path): printv('unlink\t', f, dst) force_unlink(dst, dstidx.db[f].type) + # we save the index at last because it voids file_info.fullpath so we + # don't save unnecesary information + printv('* saving index') + srcidx.save() + def show_idx(idx_path): printv("* loading index") @@ -472,8 +481,8 @@ commands: shows the given index file contents mkidx idx_file dir builds an index file for the given directory - sync idx src dst - synchronizes src with dst, using the given idx index file""" + sync idx src1 [src2 ... srcN] dst + synchronizes all sources with dst, using the given idx index file""" parser = AbkOptionParser(usage=usage, description="A backup script - " "Alberto Bertogli (albertogli@telpin.com.ar)", version="%prog " + VERSION, prog='abk') @@ -546,13 +555,13 @@ elif cmd == 'sync': try: old_idx_path = args[1] new_idx_path = old_idx_path - src_path = args[2] - dst_path = args[3] + sources = args[2:-1] + dst_path = args[-1] except: parser.error("Missing parameter(s) for command sync.") if opts.new_idx: new_idx_path = opts.new_idx - make_sync(src_path, new_idx_path, dst_path, old_idx_path) + make_sync(sources, new_idx_path, dst_path, old_idx_path) else: parser.error("Unknown command (%s)." % cmd)