pseudo dumpfsを再実装 - MasaHeroの日記の続き
とりあえず適当に書いたのをここに置いておく。
#!/usr/bin/python # -*- coding: utf8 -*- import sys import os import time import shutil import math import filecmp import re class Matcher: def exclude(self, path): pass class NullMatcher(Matcher): def exclude(self, path): return false class SizeMatcher(Matcher): def __init__(self, size_): self.size=self.calc_size(size_) def calc_size(self, size_): tmp=int(re.compile("^[0-9]+").search(size_).group(0)) if re.compile("^[0-9]+[kK]").search(size_): tmp=tmp*1024 elif re.compile("^[0-9]+[mM]").search(size_): tmp=tmp*1024*1024 elif re.compile("^[0-9]+[gG]").search(size_): tmp=tmp*1024*1024*1024 return tmp def exclude(self, path): stats=os.stat(path) if stats.st_size>self.size: return true else: return false class NameMatcher(Matcher): def __init__(self, pattern_): self.pattern=re.compile(pattern_) def exclude(self, path): if self.pattern.search(path): return true else: return false class pdumpfs: """Pseudo DumpFS this make backup like plan9's dumpfs """ src="" dest="" rev_path="" latest="" matcher=[] def __init__(self, src_, dest_): self.src=os.path.abspath(src_) self.dest=os.path.abspath(dest_) basename_=os.path.split(self.src)[1] if basename_=="": basename_=os.path.split(os.path.split(self.src)[0]) self.latest=os.path.join(self.dest, "latest", basename_) self.matcher[0]=NullMatcher() def _make_link(self, path): os.link(path.replace(self.src, self.latest, 1), path.replace(self.src, self.rev_path, 1)) def _copy_symlink(self, path): """Copy symbolic link""" linkto=os.readlink(path).replace(self.src, "") if os.path.exists(os.readlink(path)): while true: linkto=os.path.join("..", linkto) reflinkto=os.path.abspath(os.path.join(os.path.basename(path), linkto)) if os.path.abspath(os.readlink(path))==reflinkto: break os.symlink(linkto, path.replace(self.src, self.rev_path, 1)) def make_copy(self, path): if self._exclude(path): pass elif self._is_internal_link(path): self._copy_symlink(path) elif os.path.isdir(path): self._copy_dir(path) else: dst=path.replace(self.src, self.rev_path, 1) shutil.copy2(path, dst) def make_rev_path(self): self.ntime=int(math.floor(time.time())) self.rev_path=os.path.join(self.dest, ".rev", `self.ntime`) while true: if os.path.exists(self.rev_path): self.ntime+=1 self.rev_path=os.path.join(self.dest, ".rev", `self.ntime`) else: break os.makedirs(self.rev_path) self.tmp_path=os.path.join(self.dest, time.strftime("%Y", time.localtime(self.ntime)), time.strftime("%m", time.localtime(self.ntime)), time.strftime("%d", time.localtime(self.ntime))) if not os.path.exists(self.tmp_path): os.makedirs(self.tmp_path) basename_=os.path.split(self.src)[1] if basename_=="": basename_=os.path.split(os.path.split(self.src)[0]) self.tmp_path2=os.path.join(self.tmp_path, basename_) if os.path.exists(self.tmp_path2): os.remove(self.tmp_path2) os.symlink(self.rev_path, self.tmp_path2) def update_latest_link(self): if not os.path.exists(os.path.join(self.dest, "latest")): os.makedirs(os.path.join(self.dest, "latest")) if os.path.exists(self.latest): os.remove(self.latest) os.symlink(self.rev_path, self.latest) def compare(self): if os.path.exists(os.path.join(self.dest, "latest")): os.makedirs(os.path.join(self.dest, "latest")) basename_=os.path.split(self.dest)[1] if basename_=="": basename_=os.path.split(os.path.split(self.dest)[0]) latest_=os.path.join(self.dest, "latest", basename_) if not os.path.exists(latest_): self._copy_all() else: comparer=filecmp.dircmp(latest_, self.src) for fn in comparer.right_only: self.make_copy(os.path.join(self.src, fn)) for fn in comparer.common_funny: self.make_copy(os.path.join(self.src, fn)) for fn in comparer.diff_files: self.make_copy(os.path.join(self.src, fn)) for fn in comparer.same_files: self.make_link(os.path.join(self.latest, fn)) for subdir_ in comparer.subdirs: self._compare_subdir(comparer.subdirs[subdir_], subdir_) def _copy_all(self): self._copy_dir(self.src) def _copy_dir(self, src_): dst=src.replace(self.src, self.rev_path, 1) names=os.listdir(src_) errors = [] for name in names: srcname = os.path.join(src_, name) dstname = os.path.join(dst, name) try: if os.path.isdir(srcname): os.makedirs(dstname) self._copy_dir(srcname) else: self.make_copy(srcname) # XXX What about devices, sockets etc.? except (IOError, os.error), why: errors.append((srcname, dstname, why)) if errors: raise Error, errors def _is_internal_link(self, path): """Check Symlink is linked to internal of src tree""" if os.path.islink(path): linkto=os.readlink(path) if os.path.commonprefix(self.src, os.path.abspath(linkto))== self.src: return true else: return false else: return false def _compare_subdir(self, subdircmp, subdirpath): for fn in subdircmp.right_only: self.make_copy(os.path.join(self.src, subdirpath, fn)) for fn in subdircmp.common_funny: self.make_copy(os.path.join(self.src, subdirpath, fn)) for fn in subdircmp.diff_files: self.make_copy(os.path.join(self.src, subdirpath, fn)) for fn in subdircmp.same_files: self.make_link(os.path.join(self.latest, subdirpath, fn)) for subdir_ in subdircmp.subdirs: self._compare_subdir(subdircmp.subdirs[subdir_], os.path.join(subdirpath, subdir_)) def _exclude(self,path): ret=false for mt in self.matcher: ret=mt.exclude(path) return ret def add_matcher(self,matcher_): self.matcher.append(matcher_)
ライセンスは宣伝条項抜きのBSDライセンスで。
追記:
賢明な人はすでに気づいていると思うが適当に書いただけあって上記のソースではまともに動かない。デバッグの必要あり。
ライセンスについてはブログ情報 - MasaHeroの日記に全文書いておいた。