diff options
author | Lars Wirzenius <liw@liw.fi> | 2016-10-09 15:45:07 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2016-10-09 15:45:07 +0300 |
commit | dae83a778f9b35f50f317bca60b3d078a3e5cfa3 (patch) | |
tree | 73b56b0236e9b71d48d91a9b2d7bbb2593855dc4 /summainlib.py | |
parent | 4a1f69bd222f8136542a9505bb31db3905f64a2f (diff) | |
download | summain-dae83a778f9b35f50f317bca60b3d078a3e5cfa3.tar.gz |
Make it possible to use bumper for releases
Diffstat (limited to 'summainlib.py')
-rw-r--r-- | summainlib.py | 294 |
1 files changed, 0 insertions, 294 deletions
diff --git a/summainlib.py b/summainlib.py deleted file mode 100644 index d5ea048..0000000 --- a/summainlib.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright (C) 2010, 2011 Lars Wirzenius -# -# This program is free 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. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - - -import base64 -import grp -import hashlib -import hmac -import os -import pwd -import stat -import time -import urllib -import errno - -import _summain - - -__version__ = '0.20' - - -class NumberNormalizer(object): - - '''Normalize inode and device numbers. - - When we make two manifests of the same directory tree, but the - tree may have been moved to another disk, the inode and device - numbers may be different. This should not be a cause for concern, - however. What is important is that if two names were hardlinked - to the same file before, they still are, and if they weren't, - they still aren't. - - To achieve this, we normalize the inode and device numbers. - The input files are fed to the normalizer in a deterministic - sequence, and the sequence defines the numbers we use. Thus, - if the input files have inode numbers [42, 13, 105], we produce - [1, 2, 3]. If one of the input numbers is repeated, that number - is re-used. - - This is not a perfect solution. If the second manifest has a - new file, it will throw off the entire remaining sequence, causing - a big diff. But we'll live with that. - - ''' - - def __init__(self): - self.reset() - - def get(self, input_number, numbers, next_number): - if input_number in numbers: - return numbers[input_number], next_number - else: - numbers[input_number] = next_number - return numbers[input_number], next_number + 1 - - def get_ino(self, ino): - output, self.next_ino = self.get(ino, self.ino_numbers, self.next_ino) - return output - - def get_dev(self, dev): - output, self.next_dev = self.get(dev, self.dev_numbers, self.next_dev) - return output - - def reset(self): - '''This is used by unit tests.''' - self.ino_numbers = dict() - self.next_ino = 1 - - self.dev_numbers = dict() - self.next_dev = 1 - - -class PathNormalizer(object): - - '''Normalize a filesystem path. - - Paths are normalized by using SHA-1 on a secret plus the real path. - The checksum is the normalized path. - - ''' - - def __init__(self, secret): - self._secret = secret - - def normalize(self, path): - return hmac.new(self._secret, path).hexdigest() - - -class SamePath(object): # pragma: no cover - - def normalize(self, path): - return path - - -RESULT_RET = 0 -RESULT_DEV = 1 -RESULT_INO = 2 -RESULT_MODE = 3 -RESULT_NLINK = 4 -RESULT_UID = 5 -RESULT_GID = 6 -RESULT_RDEV = 7 -RESULT_SIZE = 8 -RESULT_BLKSIZE = 9 -RESULT_BLOCKS = 10 -RESULT_ATIME_SEC = 11 -RESULT_ATIME_NSEC = 12 -RESULT_MTIME_SEC = 13 -RESULT_MTIME_NSEC = 14 -RESULT_CTIME_SEC = 15 -RESULT_CTIME_NSEC = 16 - - -class FilesystemObject(object): - - '''An object in the file system. - - Responsible for gathering information and formatting it for - reporting. - - The optional arguments are intended for unit tests. - - ''' - - def __init__(self, filename, nn, pn, exclude, - stat_result=None, sha1=None, sha224=None, - sha256=None, sha384=None, sha512=None, - md5=None, open_file=None, readlink=None, - xattrs=None): - self.filename = filename - self.relative = None - self._exclude = set(self._normalize_key(k) for k in exclude) - self._pn = pn - self._nn = nn - self._md5 = md5 or hashlib.md5() - self._sha1 = sha1 or hashlib.sha1() - self._sha224 = sha224 or hashlib.sha224() - self._sha256 = sha256 or hashlib.sha256() - self._sha384 = sha384 or hashlib.sha384() - self._sha512 = sha512 or hashlib.sha512() - self._stat_result = stat_result or _summain.lstat(filename) - self._xattrs = (xattrs if xattrs is not None - else self.get_xattrs(filename)) - self.open_file = open_file or file - self.readlink = readlink or os.readlink - self.values = dict() - - def _compute_name(self): - if self.relative is None: - name = self.filename - else: - name = self.relative # pragma: no cover - return urllib.quote(self._pn.normalize(name)) - - def _compute_mtime(self): - return self.format_time(self._stat_result[RESULT_MTIME_SEC], - self._stat_result[RESULT_MTIME_NSEC]) - - def _compute_mode(self): - return '%o' % self._stat_result[RESULT_MODE] - - def _compute_ino(self): - return '%d' % self._nn.get_ino(self._stat_result[RESULT_INO]) - - def _compute_dev(self): - return '%d' % self._nn.get_dev(self._stat_result[RESULT_DEV]) - - def _compute_nlink(self): - return '%d' % self._stat_result[RESULT_NLINK] - - def _compute_size(self): - if not stat.S_ISDIR(self._stat_result[RESULT_MODE]): - return '%d' % self._stat_result[RESULT_SIZE] - - def _compute_uid(self): - return '%d' % self._stat_result[RESULT_UID] - - def _compute_username(self): - return self.lookup_username(self._stat_result[RESULT_UID]) - - def _compute_gid(self): - return '%d' % self._stat_result[RESULT_GID] - - def _compute_group(self): - return self.lookup_group(self._stat_result[RESULT_GID]) - - def _compute_md5(self): - return self.compute_checksum(self.filename, self._md5) - - def _compute_sha1(self): - return self.compute_checksum(self.filename, self._sha1) - - def _compute_sha224(self): - return self.compute_checksum(self.filename, self._sha224) - - def _compute_sha256(self): - return self.compute_checksum(self.filename, self._sha256) - - def _compute_sha384(self): - return self.compute_checksum(self.filename, self._sha384) - - def _compute_sha512(self): - return self.compute_checksum(self.filename, self._sha512) - - def _compute_target(self): - if stat.S_ISLNK(self._stat_result[RESULT_MODE]): - return self.readlink(self.filename) - - def _compute_xattrs(self): # pragma: no cover - if len(self._xattrs) == 0: - return '' - - def quote(s): - if s.isalnum(): - return '"%s"' % s - else: - return '0s' + base64.urlsafe_b64encode(s) - - parts = [' %s=%s' % (k, quote(self._xattrs[k])) for k in self._xattrs] - return '\n' + '\n'.join(parts) - - def format_time(self, secs, nsecs): - s = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(secs)) - s += '.%09d' % nsecs - s += ' +0000' - return s - - def lookup_username(self, uid): - return pwd.getpwuid(uid).pw_name - - def lookup_group(self, gid): - return grp.getgrgid(gid).gr_name - - def compute_checksum(self, filename, checksummer): - if stat.S_ISREG(self._stat_result[RESULT_MODE]): - with self.open_file(filename) as f: - while True: - data = f.read(64 * 1024) # 64 KiB seems reasonable. - if not data: - break - checksummer.update(data) - return checksummer.hexdigest() - else: - return '' - - def _normalize_key(self, key): - key = key.lower() - key = '_'.join(key.split('-')) - return key - - def __getitem__(self, key): - normalized = self._normalize_key(key) - if normalized in self._exclude: - return '' - if key not in self.values: - method = '_compute_%s' % self._normalize_key(key) - if hasattr(self, method): - value = getattr(self, method)() - if value is not None: - self.values[key] = value - else: - raise KeyError(key) - return self.values.get(key, '') - - def isdir(self): # pragma: no cover - '''Is this a directory?''' - return stat.S_ISDIR(int(self['Mode'], 8)) - - def get_xattrs(self, filename): # pragma: no cover - ret = _summain.llistxattr(filename) - if type(ret) is int: - # Some file types don't support xattr, e.g. named pipes on FreeBSD: - if ret == errno.EOPNOTSUPP: - return {} - raise OSError((ret, os.strerror(ret), filename)) - - names = [s for s in ret.split('\0') if s] - - xattrs = {} - for name in names: - xattrs[name] = _summain.lgetxattr(filename, name) - return xattrs |