#
# This code is covered under the terms of the GNU Library General
# Public License as described in the bup LICENSE file.
+
+from __future__ import absolute_import
+from copy import deepcopy
+from errno import EACCES, EINVAL, ENOTTY, ENOSYS, EOPNOTSUPP
+from io import BytesIO
+from time import gmtime, strftime
import errno, os, sys, stat, time, pwd, grp, socket, struct
-from cStringIO import StringIO
+
from bup import vint, xstat
from bup.drecurse import recursive_dirlist
from bup.helpers import add_error, mkdirp, log, is_superuser, format_filesize
_rec_tag_hardlink_target = 8 # hard link target path
_rec_tag_common_v2 = 9 # times, user, group, type, perms, etc. (current)
+_warned_about_attr_einval = None
+
class ApplyError(Exception):
# Thrown when unable to apply any given bit of metadata to a path.
st = None
try:
st = xstat.lstat(path)
- except OSError, e:
+ except OSError as e:
if e.errno != errno.ENOENT:
raise
if st:
if stat.S_ISDIR(st.st_mode):
try:
os.rmdir(path)
- except OSError, e:
+ except OSError as e:
if e.errno in (errno.ENOTEMPTY, errno.EEXIST):
msg = 'refusing to overwrite non-empty dir ' + path
raise Exception(msg)
elif stat.S_ISSOCK(self.mode):
try:
os.mknod(path, 0o600 | stat.S_IFSOCK)
- except OSError, e:
+ except OSError as e:
if e.errno in (errno.EINVAL, errno.EPERM):
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.bind(path)
if lutime and stat.S_ISLNK(self.mode):
try:
lutime(path, (self.atime, self.mtime))
- except OSError, e:
+ except OSError as e:
if e.errno == errno.EACCES:
raise ApplyError('lutime: %s' % e)
else:
else:
try:
utime(path, (self.atime, self.mtime))
- except OSError, e:
+ except OSError as e:
if e.errno == errno.EACCES:
raise ApplyError('utime: %s' % e)
else:
if uid != -1 or gid != -1:
try:
os.lchown(path, uid, gid)
- except OSError, e:
+ except OSError as e:
if e.errno == errno.EPERM:
add_error('lchown: %s' % e)
elif sys.platform.startswith('cygwin') \
try:
if stat.S_ISLNK(st.st_mode):
self.symlink_target = os.readlink(path)
- except OSError, e:
+ except OSError as e:
add_error('readlink: %s' % e)
def _encode_symlink_target(self):
return self.symlink_target
def _load_symlink_target_rec(self, port):
- self.symlink_target = vint.read_bvec(port)
+ target = vint.read_bvec(port)
+ self.symlink_target = target
+ self.size = len(target)
## Hardlink targets
if stat.S_ISDIR(st.st_mode):
def_acl = posix1e.ACL(filedef=path)
def_acls = [def_acl, def_acl]
- except EnvironmentError, e:
+ except EnvironmentError as e:
if e.errno not in (errno.EOPNOTSUPP, errno.ENOSYS):
raise
if acls:
def apply_acl(acl_rep, kind):
try:
acl = posix1e.ACL(text = acl_rep)
- except IOError, e:
+ except IOError as e:
if e.errno == 0:
# pylibacl appears to return an IOError with errno
# set to 0 if a group referred to by the ACL rep
raise
try:
acl.applyto(path, kind)
- except IOError, e:
+ except IOError as e:
if e.errno == errno.EPERM or e.errno == errno.EOPNOTSUPP:
raise ApplyError('POSIX1e ACL applyto: %s' % e)
else:
attr = get_linux_file_attr(path)
if attr != 0:
self.linux_attr = attr
- except OSError, e:
+ except OSError as e:
if e.errno == errno.EACCES:
add_error('read Linux attr: %s' % e)
- elif e.errno in (errno.ENOTTY, errno.ENOSYS, errno.EOPNOTSUPP):
+ elif e.errno in (ENOTTY, ENOSYS, EOPNOTSUPP):
# Assume filesystem doesn't support attrs.
return
+ elif e.errno == EINVAL:
+ global _warned_about_attr_einval
+ if not _warned_about_attr_einval:
+ log("Ignoring attr EINVAL;"
+ + " if you're not using ntfs-3g, please report: "
+ + repr(path) + '\n')
+ _warned_about_attr_einval = True
+ return
else:
raise
return
try:
set_linux_file_attr(path, self.linux_attr)
- except OSError, e:
- if e.errno in (errno.ENOTTY, errno.EOPNOTSUPP, errno.ENOSYS,
- errno.EACCES):
+ except OSError as e:
+ if e.errno in (EACCES, ENOTTY, EOPNOTSUPP, ENOSYS):
raise ApplyError('Linux chattr: %s (0x%s)'
% (e, hex(self.linux_attr)))
+ elif e.errno == EINVAL:
+ msg = "if you're not using ntfs-3g, please report"
+ raise ApplyError('Linux chattr: %s (0x%s) (%s)'
+ % (e, hex(self.linux_attr), msg))
else:
raise
if not xattr: return
try:
self.linux_xattr = xattr.get_all(path, nofollow=True)
- except EnvironmentError, e:
+ except EnvironmentError as e:
if e.errno != errno.EOPNOTSUPP:
raise
def _load_linux_xattr_rec(self, file):
data = vint.read_bvec(file)
- memfile = StringIO(data)
+ memfile = BytesIO(data)
result = []
for i in range(vint.read_vuint(memfile)):
key = vint.read_bvec(memfile)
return
try:
existing_xattrs = set(xattr.list(path, nofollow=True))
- except IOError, e:
+ except IOError as e:
if e.errno == errno.EACCES:
raise ApplyError('xattr.set %r: %s' % (path, e))
else:
or v != xattr.get(path, k, nofollow=True):
try:
xattr.set(path, k, v, nofollow=True)
- except IOError, e:
+ except IOError as e:
if e.errno == errno.EPERM \
or e.errno == errno.EOPNOTSUPP:
raise ApplyError('xattr.set %r: %s' % (path, e))
for k in existing_xattrs:
try:
xattr.remove(path, k, nofollow=True)
- except IOError, e:
- if e.errno == errno.EPERM:
+ except IOError as e:
+ if e.errno in (errno.EPERM, errno.EACCES):
raise ApplyError('xattr.remove %r: %s' % (path, e))
else:
raise
self.linux_xattr = None
self.posix1e_acl = None
+ def __eq__(self, other):
+ if not isinstance(other, Metadata): return False
+ if self.mode != other.mode: return False
+ if self.mtime != other.mtime: return False
+ if self.ctime != other.ctime: return False
+ if self.atime != other.atime: return False
+ if self.path != other.path: return False
+ if self.uid != other.uid: return False
+ if self.gid != other.gid: return False
+ if self.size != other.size: return False
+ if self.user != other.user: return False
+ if self.group != other.group: return False
+ if self.symlink_target != other.symlink_target: return False
+ if self.hardlink_target != other.hardlink_target: return False
+ if self.linux_attr != other.linux_attr: return False
+ if self.posix1e_acl != other.posix1e_acl: return False
+ return True
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash((self.mode,
+ self.mtime,
+ self.ctime,
+ self.atime,
+ self.path,
+ self.uid,
+ self.gid,
+ self.size,
+ self.user,
+ self.group,
+ self.symlink_target,
+ self.hardlink_target,
+ self.linux_attr,
+ self.posix1e_acl))
+
def __repr__(self):
result = ['<%s instance at %s' % (self.__class__, hex(id(self)))]
- if self.path:
+ if self.path is not None:
result += ' path:' + repr(self.path)
- if self.mode:
+ if self.mode is not None:
result += ' mode:' + repr(xstat.mode_str(self.mode)
- + '(%s)' % hex(self.mode))
- if self.uid:
+ + '(%s)' % oct(self.mode))
+ if self.uid is not None:
result += ' uid:' + str(self.uid)
- if self.gid:
+ if self.gid is not None:
result += ' gid:' + str(self.gid)
- if self.user:
+ if self.user is not None:
result += ' user:' + repr(self.user)
- if self.group:
+ if self.group is not None:
result += ' group:' + repr(self.group)
- if self.size:
+ if self.size is not None:
result += ' size:' + repr(self.size)
for name, val in (('atime', self.atime),
('mtime', self.mtime),
('ctime', self.ctime)):
- result += ' %s:%r' \
- % (name,
- time.strftime('%Y-%m-%d %H:%M %z',
- time.gmtime(xstat.fstime_floor_secs(val))))
+ if val is not None:
+ result += ' %s:%r (%d)' \
+ % (name,
+ strftime('%Y-%m-%d %H:%M %z',
+ gmtime(xstat.fstime_floor_secs(val))),
+ val)
result += '>'
return ''.join(result)
vint.write_vuint(port, _rec_tag_end)
def encode(self, include_path=True):
- port = StringIO()
+ port = BytesIO()
self.write(port, include_path)
return port.getvalue()
+ def copy(self):
+ return deepcopy(self)
+
@staticmethod
def read(port):
# This method should either return a valid Metadata object,
self._apply_linux_xattr_rec):
try:
apply_metadata(path, restore_numeric_ids=num_ids)
- except ApplyError, e:
+ except ApplyError as e:
add_error(e)
def same_file(self, other):
mode_str = xstat.mode_str(meta.mode)
symlink_target = meta.symlink_target
mtime_secs = xstat.fstime_floor_secs(meta.mtime)
- mtime_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(mtime_secs))
+ mtime_str = strftime('%Y-%m-%d %H:%M', time.localtime(mtime_secs))
if meta.user and not numeric_ids:
user_str = meta.user
elif meta.uid != None: