author |
<albertogli@telpin.com.ar> 2005-03-02 04:44:00 UTC |
committer |
<albertogli@telpin.com.ar> 2005-03-02 04:44:00 UTC |
abk | +257 | -0 |
diff --git a/abk b/abk new file mode 100644 index 0000000..29b164f --- /dev/null +++ b/abk @@ -0,0 +1,257 @@ +#!/usr/bin/python + +import sys +import os +import bz2 +import time +import sha +import mmap +import cPickle +from stat import * + + +# +# constants +# + +PSIZE = 4 * 1024 + + +# +# classes +# + +class file_info: + "Represents a file" + def __init__(self, name): + self.name = name + self.mtime = 0 + self.mode = 0 + self.uid = 0 + self.gid = 0 + self.mtime = 0 + self.type = '' + self.linkto = None + self.rdev = None + self.hash = None + self.stat = None + + def __repr__(self): + return self.name + + def load(self): + "Loads data from the file." + s = os.lstat(self.name) + self.stat = s + if S_ISREG(s.st_mode): + self.type = 'r' + elif S_ISLNK(s.st_mode): + self.type = 'l' + elif S_ISCHR(s.st_mode): + self.type = 'c' + self.rdev = s.st_rdev + elif S_ISBLK(s.st_mode): + self.type = 'b' + self.rdev = s.st_rdev + elif S_ISFIFO(s.st_mode): + self.type = 'f' + self.linkto = os.readlink(self.name) + elif S_ISDIR(s.st_mode): + self.type = 'd' + else: + self.type = 'u' + + self.mtime = s.st_mtime + self.mode = s.st_mode + self.uid = s.st_uid + self.gid = s.st_gid + + # FIXME: hacer opcional para Damian + if self.type == 'r': + self.hash = self.hash_file() + + def __eq__(self, other): + "Compares to other file_info object." + if self.name != other.name: return 0 + if self.mtime != other.mtime: return 0 + if self.type != other.type: return 0 + if self.size != other.size: return 0 + if self.mode != other.mode: return 0 + if self.uid != other.uid: return 0 + if self.gid != other.gid: return 0 + if self.hash != other.hash: return 0 + + if self.mode == 'b' or self.mode == 'c': + if self.rdev != other.rdev: + return 0 + if self.mode == 'l': + if self.linkto != other.linkto: + return 0 + + return 1 + + def __ne__(self, other): + return not (self == other) + + def copy_file_reg(self, dst): + "Copy a regular file." + sfile = open(self.name, 'r') + dfile = open(dst, 'w') + + # the data + data = sfile.read(PSIZE) + while data: + dfile.write(data) + data = sfile.read(PSIZE) + + sfile.close() + dfile.close() + + os.chmod(dst, self.mode & 07777) # FIXME: is this linux-only? + os.chown(dst, self.uid, self.gid) + + + def copy_file_link(self, dst): + "Copy a symbolic link." + linkto = os.readlink(self.name) + os.symlink(linkto, dst) + os.chown(dst, self.uid, self.gid) + # note that we don't chmod, because it will change the + # destination owner instead of the link's. + + + def copy_file_dev(self, dst): + "Copy a device file." + major = os.major(self.rdev) + minor = os.minor(self.rdev) + dev = os.makedev(major, minor) + os.mknod(dst, self.mode, dev) + os.chmod(dst, self.mode & 07777) + os.chown(dst, self.uid, self.gid) + + + def copy_file(self, dst): + "Copies a file, along with its permissions and ownership." + if self.type == 'r': + self.copy_file_reg(dst) + elif self.type == 'l': + self.copy_file_link(dst) + elif self.type == 'b' or self.type == 'c': + self.copy_file_dev(dst) + elif self.type == 'f': + # we just create fifos + os.mkfifo(dst, self.type & 07777) + elif self.type == 'd': + # we just create directories + #os.mkdir(dst, self.type & 07777) + os.makedirs(dst, self.type & 07777) + else: + raise 'Unk type: 0x%x %d' % (self.mode, self.name) + + + def hash_file(self): + "Returns the sha1sum of a file." + hash = sha.new() + f = open(self.name) + data = f.read(PSIZE) + while data: + hash.update(data) + data = f.read(PSIZE) + f.close() + return hash.hexdigest() + + + +class index_file: + "Represents the index file." + def __init__(self, name): + self.name = name + self.db = {} + + def load(self): + "Loads data from the file." + try: + f = open(self.name) + except IOError: + # probably file doesn't exist, ignore + return + self.db = cPickle.load(f) + f.close() + + def save(self): + "Saves the index to the disk." + f = open(self.name, 'w') + cPickle.dump(self.db, f, cPickle.HIGHEST_PROTOCOL) + f.close() + + def put_file(self, filename): + "Incorporates a file into the index." + self.db[filename] = file_info(filename) + self.db[filename].load() + + def get_file(self, filename): + "Get the file_info object for the given filename." + return self.db[filename] + + def del_file(self, filename): + "Remove a file from the index." + del(self.db[filename]) + + def populate(self, root): + "Populate the index from a root path." + self.put_file(root) + tree = os.walk(root) + for path, childs, files in tree: + for f in files: + name = os.path.join(path, f) + self.put_file(name) + for c in childs: + name = os.path.join(path, c) + self.put_file(name) + + +def bz2_file(src, dst = None): + "Compress a file using bz2." + if not dst: + dst = src + '.bz2' + + sfile = open(src) + dfile = open(dst, 'w') + + bcomp = bz2.BZ2Compressor() + data = sfile.read(PSIZE) + while data: + dfile.write(bcomp.compress(data)) + data = sfile.read(PSIZE) + dfile.write(bcomp.flush()) + sfile.close() + dfile.close() + + + + +# +# main +# +src_path = sys.argv[1] +srcidx_path = sys.argv[2] +dst_path = sys.argv[3] +dstidx_path = sys.argv[4] + +# load destination index +dstidx = index_file(dstidx_path) +dstidx.load() + +# create source index +srcidx = index_file(srcidx_path) +srcidx.populate(src_path) +srcidx.save() + +# compare them +dkeys = dstidx.db.keys() +for f in srcidx.db.keys(): + if f not in dkeys: + dst = os.path.join(dst_path, f) + print 'copy', f, dst + srcidx.db[f].copy_file(dst) +