summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rdiff-backup/CHANGELOG6
-rw-r--r--rdiff-backup/rdiff_backup/Security.py4
-rw-r--r--rdiff-backup/rdiff_backup/rpath.py115
3 files changed, 67 insertions, 58 deletions
diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG
index 01a2626..70cc99f 100644
--- a/rdiff-backup/CHANGELOG
+++ b/rdiff-backup/CHANGELOG
@@ -1,6 +1,12 @@
New in v1.1.17 (????/??/??)
---------------------------
+Move make_file_dict_python so that it is run on the remote end instead of
+the local end. This improves performance for Windows hosts since it eliminates
+the lag due to checking os.name. It also makes the Windows design parallel
+to the Posix design, since the Windows method now returns a dictionary across
+the wire. (Andrew Ferguson)
+
Catch EPERM error when trying to write extended attributes. (Andrew Ferguson)
Allow rdiff-backup to be built into a single executable on Windows using
diff --git a/rdiff-backup/rdiff_backup/Security.py b/rdiff-backup/rdiff_backup/Security.py
index 2e9cd9d..8dc26bc 100644
--- a/rdiff-backup/rdiff_backup/Security.py
+++ b/rdiff-backup/rdiff_backup/Security.py
@@ -41,7 +41,7 @@ disallowed_server_globals = ["server", "security_level", "restrict_path"]
#
# The keys are files request, the value is the index of the argument
# taking a file.
-file_requests = {'os.listdir':0, 'C.make_file_dict':0, 'os.chmod':0,
+file_requests = {'os.listdir':0, 'rpath.make_file_dict':0, 'os.chmod':0,
'os.chown':0, 'os.remove':0, 'os.removedirs':0,
'os.rename':0, 'os.renames':0, 'os.rmdir':0, 'os.unlink':0,
'os.utime':0, 'os.lchown':0, 'os.link':1, 'os.symlink':1,
@@ -136,7 +136,7 @@ def set_allowed_requests(sec_level):
"sys.stdout.write", "robust.install_signal_handlers"]
if (sec_level == "read-only" or sec_level == "update-only" or
sec_level == "all"):
- l.extend(["C.make_file_dict", "os.listdir", "rpath.ea_get",
+ l.extend(["rpath.make_file_dict", "os.listdir", "rpath.ea_get",
"rpath.acl_get", "rpath.setdata_local",
"log.Log.log_to_file", "os.getuid", "Time.setcurtime_local",
"rpath.gzip_open_local_read", "rpath.open_local_read",
diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py
index 2ee7043..ec276d4 100644
--- a/rdiff-backup/rdiff_backup/rpath.py
+++ b/rdiff-backup/rdiff_backup/rpath.py
@@ -36,7 +36,7 @@ are dealing with are local or remote.
"""
import os, stat, re, sys, shutil, gzip, socket, time, errno
-import Globals, Time, static, log, user_group
+import Globals, Time, static, log, user_group, C
class SkipFileException(Exception):
@@ -264,16 +264,65 @@ def rename(rp_source, rp_dest):
rp_dest.data = rp_source.data
rp_source.data = {'type': None}
-def tupled_lstat(filename):
- """Like os.lstat, but return only a tuple, or None if os.error
+def make_file_dict(filename):
+ """Generate the data dictionary for the given RPath
- Later versions of os.lstat return a special lstat object,
- which can confuse the pickler and cause errors in remote
- operations. This has been fixed in Python 2.2.1.
+ This is a global function so that os.name can be called locally,
+ thus avoiding network lag and so that we only need to send the
+ filename over the network, thus avoiding the need to pickle an
+ (incomplete) rpath object.
+ """
+ if os.name != 'nt':
+ return C.make_file_dict(filename)
+ else:
+ return make_file_dict_python(filename)
+def make_file_dict_python(filename):
+ """Create the data dictionary using a Python call to os.lstat
+
+ We do this on Windows since Python's implementation is much better
+ than the one in cmodule.c Eventually, we will move to using
+ this on all platforms since CPUs have gotten much faster than
+ they were when it was necessary to write cmodule.c
"""
- try: return tuple(os.lstat(filename))
- except os.error: return None
+ try:
+ statblock = os.lstat(filename)
+ except os.error:
+ return {'type':None}
+ data = {}
+ mode = statblock[stat.ST_MODE]
+
+ if stat.S_ISREG(mode): type = 'reg'
+ elif stat.S_ISDIR(mode): type = 'dir'
+ elif stat.S_ISCHR(mode):
+ type = 'dev'
+ s = statblock.st_rdev
+ data['devnums'] = ('c',) + (s >> 8, s & 0xff)
+ elif stat.S_ISBLK(mode):
+ type = 'dev'
+ s = statblock.st_rdev
+ data['devnums'] = ('b',) + (s >> 8, s & 0xff)
+ elif stat.S_ISFIFO(mode): type = 'fifo'
+ elif stat.S_ISLNK(mode):
+ type = 'sym'
+ data['linkname'] = os.readlink(filename)
+ elif stat.S_ISSOCK(mode): type = 'sock'
+ else: raise C.UnknownFileError(filename)
+ data['type'] = type
+ data['size'] = statblock[stat.ST_SIZE]
+ data['perms'] = stat.S_IMODE(mode)
+ data['uid'] = statblock[stat.ST_UID]
+ data['gid'] = statblock[stat.ST_GID]
+ data['inode'] = statblock[stat.ST_INO]
+ data['devloc'] = statblock[stat.ST_DEV]
+ data['nlink'] = statblock[stat.ST_NLINK]
+
+ if not (type == 'sym' or type == 'dev'):
+ # mtimes on symlinks and dev files don't work consistently
+ data['mtime'] = long(statblock[stat.ST_MTIME])
+ data['atime'] = long(statblock[stat.ST_ATIME])
+ data['ctime'] = long(statblock[stat.ST_CTIME])
+ return data
def make_socket_local(rpath):
"""Make a local socket at the given path
@@ -826,51 +875,10 @@ class RPath(RORPath):
self.path = "/".join((self.base,) + self.index)
def setdata(self):
- """Set data dictionary using C extension"""
- try:
- self.data = self.conn.C.make_file_dict(self.path)
- except AttributeError:
- self.data = self.make_file_dict_python()
+ """Set data dictionary using the wrapper"""
+ self.data = self.conn.rpath.make_file_dict(self.path)
if self.lstat(): self.conn.rpath.setdata_local(self)
- def make_file_dict_python(self):
- """Create the data dictionary"""
- statblock = self.conn.rpath.tupled_lstat(self.path)
- if statblock is None:
- return {'type':None}
- data = {}
- mode = statblock[stat.ST_MODE]
-
- if stat.S_ISREG(mode): type = 'reg'
- elif stat.S_ISDIR(mode): type = 'dir'
- elif stat.S_ISCHR(mode):
- type = 'dev'
- data['devnums'] = ('c',) + self._getdevnums()
- elif stat.S_ISBLK(mode):
- type = 'dev'
- data['devnums'] = ('b',) + self._getdevnums()
- elif stat.S_ISFIFO(mode): type = 'fifo'
- elif stat.S_ISLNK(mode):
- type = 'sym'
- data['linkname'] = self.conn.os.readlink(self.path)
- elif stat.S_ISSOCK(mode): type = 'sock'
- else: raise C.UnknownFileError(self.path)
- data['type'] = type
- data['size'] = statblock[stat.ST_SIZE]
- data['perms'] = stat.S_IMODE(mode)
- data['uid'] = statblock[stat.ST_UID]
- data['gid'] = statblock[stat.ST_GID]
- data['inode'] = statblock[stat.ST_INO]
- data['devloc'] = statblock[stat.ST_DEV]
- data['nlink'] = statblock[stat.ST_NLINK]
-
- if not (type == 'sym' or type == 'dev'):
- # mtimes on symlinks and dev files don't work consistently
- data['mtime'] = long(statblock[stat.ST_MTIME])
- data['atime'] = long(statblock[stat.ST_ATIME])
- data['ctime'] = long(statblock[stat.ST_CTIME])
- return data
-
def check_consistency(self):
"""Raise an error if consistency of rp broken
@@ -884,11 +892,6 @@ class RPath(RORPath):
"\nName: %s\nOld: %s --> New: %s\n" % \
(self.path, temptype, self.data['type'])
- def _getdevnums(self):
- """Return tuple for special file (major, minor)"""
- s = self.conn.reval("lambda path: os.lstat(path).st_rdev", self.path)
- return (s >> 8, s & 0xff)
-
def chmod(self, permissions):
"""Wrapper around os.chmod"""
try: