Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
210 lines (158 sloc) 5.57 KB
#
# Copyright (c) 2010-2013 Liraz Siri <liraz@turnkeylinux.org>
#
# This file is part of TKLBAM (TurnKey GNU/Linux BAckup and Migration).
#
# TKLBAM is open source software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of
# the License, or (at your option) any later version.
#
import re
import os
import stat
from os.path import *
from pathmap import PathMap
class Error(Exception):
pass
class DirIndex(dict):
class Record:
def __init__(self, path, mod, uid, gid, size, mtime,
symlink=None):
self.path = path
self.mod = mod
self.uid = uid
self.gid = gid
self.size = size
self.mtime = mtime
self.symlink = symlink
@classmethod
def frompath(cls, path):
st = os.lstat(path)
symlink = os.readlink(path) \
if stat.S_ISLNK(st.st_mode) else None
rec = cls(path,
st.st_mode,
st.st_uid, st.st_gid,
st.st_size, st.st_mtime,
symlink)
return rec
@classmethod
def fromline(cls, line):
vals = line.strip().split('\t')
if len(vals) not in (6, 7):
raise Error("bad index record: " + line)
path = vals[0]
del vals[0]
vals = [ int(val, 16) for val in vals[:5] ] + vals[5:]
return cls(path, *vals)
def fmt(self):
vals = [ self.path ]
for val in ( self.mod, self.uid, self.gid, self.size, self.mtime ):
vals.append("%x" % val)
if self.symlink:
vals.append(self.symlink)
return "\t".join(vals)
def __repr__(self):
return "DirIndex.Record(%s, mod=%s, uid=%d, gid=%d, size=%d, mtime=%d)" % \
(`self.path`, oct(self.mod), self.uid, self.gid, self.size, self.mtime)
@classmethod
def create(cls, path_index, paths):
"""create index from paths"""
di = cls()
di.walk(*paths)
di.save(path_index)
return di
def __init__(self, fromfile=None):
if fromfile:
for line in file(fromfile).readlines():
if not line.strip():
continue
rec = DirIndex.Record.fromline(line)
self[rec.path] = rec
def add_path(self, path):
"""add a single path to the DirIndex"""
self[path] = DirIndex.Record.frompath(path)
def walk(self, *paths):
"""walk paths and add files to index"""
pathmap = PathMap(paths)
def _walk(dir):
dentries = []
for dentry in os.listdir(dir):
path = join(dir, dentry)
if path in pathmap.excludes:
continue
dentries.append(dentry)
if not islink(path) and isdir(path):
for val in _walk(path):
yield val
yield dir, dentries
for path in pathmap.includes:
if not lexists(path):
continue
self.add_path(path)
if islink(path) or not isdir(path):
continue
for dpath, dentries in _walk(path):
for dentry in dentries:
path = join(dpath, dentry)
self.add_path(path)
def prune(self, *paths):
"""prune index down to paths that are included AND not excluded"""
pathmap = PathMap(paths)
for path in self.keys():
if not path in pathmap:
del self[path]
def save(self, tofile):
fh = file(tofile, "w")
paths = self.keys()
paths.sort()
for path in paths:
print >> fh, self[path].fmt()
def diff(self, other):
a = set(self)
b = set(other)
files_new = []
paths_stat = []
for path in (b - a):
mod = other[path].mod
# ignore Unix sockets
if stat.S_ISSOCK(mod):
continue
if stat.S_ISDIR(mod):
paths_stat.append(path)
else:
files_new.append(path)
paths_in_both = b & a
files_edited = []
def attrs_equal(attrs, a, b):
for attr in attrs:
if getattr(a, attr) != getattr(b, attr):
return False
return True
def symlink_equal(a, b):
if a.symlink and (a.symlink == b.symlink):
return True
return False
for path in paths_in_both:
if not attrs_equal(('size', 'mtime'), self[path], other[path]):
mod = other[path].mod
if not (stat.S_ISDIR(mod) or stat.S_ISSOCK(mod)) \
and not symlink_equal(self[path], other[path]):
files_edited.append(path)
continue
if not attrs_equal(('mod', 'uid', 'gid'), self[path], other[path]):
paths_stat.append(path)
return files_new, files_edited, paths_stat
create = DirIndex.create
def read_paths(fh):
paths = []
for line in fh.readlines():
path = re.sub(r'#.*', '', line).strip()
if not path:
continue
# only accept absolute paths
if not re.match(r'^-?/', path):
raise Error(`path` + " is not an absolute path")
paths.append(path)
return paths