Commit 14d78769 authored by bescoto's avatar bescoto

Don't gzip 0 length files


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@712 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 874b5402
...@@ -6,6 +6,10 @@ Fix for restoring files in directories with really long names. ...@@ -6,6 +6,10 @@ Fix for restoring files in directories with really long names.
rdiff-backup will now exit by default if it thinks another rdiff-backup will now exit by default if it thinks another
rdiff-backup process is currently working on the same repository. rdiff-backup process is currently working on the same repository.
Empty error_log, mirror_metadata, extended_attribute, and
access_control_lists files will no longer be gzipped (suggestion by
Hans F. Nordhaug).
New in v1.1.4 (2005/12/13) New in v1.1.4 (2005/12/13)
-------------------------- --------------------------
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
"""Manage logging, displaying and recording messages with required verbosity""" """Manage logging, displaying and recording messages with required verbosity"""
import time, sys, traceback, types import time, sys, traceback, types, rpath
import Globals, static, re import Globals, static, re
...@@ -203,24 +203,17 @@ class ErrorLog: ...@@ -203,24 +203,17 @@ class ErrorLog:
""" """
_log_fileobj = None _log_fileobj = None
_log_inc_rp = None
def open(cls, time_string, compress = 1): def open(cls, time_string, compress = 1):
"""Open the error log, prepare for writing""" """Open the error log, prepare for writing"""
if not Globals.isbackup_writer: if not Globals.isbackup_writer:
return Globals.backup_writer.log.ErrorLog.open(time_string, return Globals.backup_writer.log.ErrorLog.open(time_string,
compress) compress)
assert not cls._log_fileobj and not cls._log_inc_rp, "log already open" assert not cls._log_fileobj, "log already open"
assert Globals.isbackup_writer assert Globals.isbackup_writer
if compress: typestr = 'data.gz'
else: typestr = 'data'
cls._log_inc_rp = Globals.rbdir.append("error_log.%s.%s" %
(time_string, typestr))
assert not cls._log_inc_rp.lstat(), ("""Error file %s already exists.
This is probably caused by your attempting to run two backups simultaneously base_rp = Globals.rbdir.append("error_log.%s.data" % (time_string,))
or within one second of each other. Wait a second and try again.""" % if compress: cls._log_fileobj = rpath.MaybeGzip(base_rp)
(cls._log_inc_rp.path,)) else: cls._log_fileobj = cls._log_inc_rp.open("wb", compress = 0)
cls._log_fileobj = cls._log_inc_rp.open("wb", compress = compress)
def isopen(cls): def isopen(cls):
"""True if the error log file is currently open""" """True if the error log file is currently open"""
...@@ -267,7 +260,7 @@ or within one second of each other. Wait a second and try again.""" % ...@@ -267,7 +260,7 @@ or within one second of each other. Wait a second and try again.""" %
if not Globals.isbackup_writer: if not Globals.isbackup_writer:
return Globals.backup_writer.log.ErrorLog.close() return Globals.backup_writer.log.ErrorLog.close()
assert not cls._log_fileobj.close() assert not cls._log_fileobj.close()
cls._log_fileobj = cls._log_inc_rp = None cls._log_fileobj = None
static.MakeClass(ErrorLog) static.MakeClass(ErrorLog)
...@@ -355,19 +355,33 @@ class FlatFile: ...@@ -355,19 +355,33 @@ class FlatFile:
_extractor = FlatExtractor # Override to class that iterates objects _extractor = FlatExtractor # Override to class that iterates objects
_object_to_record = None # Set to function converting object to record _object_to_record = None # Set to function converting object to record
_prefix = None # Set to required prefix _prefix = None # Set to required prefix
def __init__(self, rp, mode, check_path = 1, compress = 1): def __init__(self, rp_base, mode, check_path = 1, compress = 1,
"""Open rp for reading ('r') or writing ('w')""" callback = None):
self.rp = rp """Open rp (or rp+'.gz') for reading ('r') or writing ('w')
If callback is available, it will be called on the rp upon
closing (because the rp may not be known in advance).
"""
self.mode = mode self.mode = mode
self.callback = callback
self._record_buffer = [] self._record_buffer = []
if check_path: if check_path:
assert rp.isincfile() and rp.getincbase_str() == self._prefix, rp assert (rp_base.isincfile() and
compress = rp.isinccompressed() rp_base.getincbase_str() == self._prefix), rp_base
compress = 1
if mode == 'r': if mode == 'r':
self.rp = rp_base
self.fileobj = self.rp.open("rb", compress) self.fileobj = self.rp.open("rb", compress)
else: else:
assert mode == 'w' and not self.rp.lstat(), (mode, rp) assert mode == 'w'
self.fileobj = self.rp.open("wb", compress) if compress and not rp_base.isinccompressed():
def callback(rp): self.rp = rp
self.fileobj = rpath.MaybeGzip(rp_base, callback)
else:
self.rp = rp_base
assert not self.rp.lstat(), self.rp
self.fileobj = self.rp.open("wb", compress = compress)
def write_record(self, record): def write_record(self, record):
"""Write a (text) record into the file""" """Write a (text) record into the file"""
...@@ -398,12 +412,11 @@ class FlatFile: ...@@ -398,12 +412,11 @@ class FlatFile:
if self._buffering_on and self._record_buffer: if self._buffering_on and self._record_buffer:
self.fileobj.write("".join(self._record_buffer)) self.fileobj.write("".join(self._record_buffer))
self._record_buffer = [] self._record_buffer = []
try: fileno = self.fileobj.fileno() # will not work if GzipFile
except AttributeError: fileno = self.fileobj.fileobj.fileno()
os.fsync(fileno)
result = self.fileobj.close() result = self.fileobj.close()
self.fileobj = None self.fileobj = None
self.rp.fsync_with_dir()
self.rp.setdata() self.rp.setdata()
if self.callback: self.callback(self.rp)
return result return result
class MetadataFile(FlatFile): class MetadataFile(FlatFile):
...@@ -449,6 +462,7 @@ class Manager: ...@@ -449,6 +462,7 @@ class Manager:
def add_incrp(self, rp): def add_incrp(self, rp):
"""Add rp to list of inc rps in the rbdir""" """Add rp to list of inc rps in the rbdir"""
assert rp.isincfile(), rp
self.rplist.append(rp) self.rplist.append(rp)
time = rp.getinctime() time = rp.getinctime()
if self.timerpmap.has_key(time): if self.timerpmap.has_key(time):
...@@ -508,12 +522,11 @@ class Manager: ...@@ -508,12 +522,11 @@ class Manager:
"""Used in the get_xx_writer functions, returns a writer class""" """Used in the get_xx_writer functions, returns a writer class"""
if time is None: timestr = Time.curtimestr if time is None: timestr = Time.curtimestr
else: timestr = Time.timetostring(time) else: timestr = Time.timetostring(time)
filename = '%s.%s.%s.gz' % (prefix, timestr, typestr) filename = '%s.%s.%s' % (prefix, timestr, typestr)
rp = Globals.rbdir.append(filename) rp = Globals.rbdir.append(filename)
assert not rp.lstat(), "File %s already exists!" % (rp.path,) assert not rp.lstat(), "File %s already exists!" % (rp.path,)
assert rp.isincfile() assert rp.isincfile()
self.add_incrp(rp) return flatfileclass(rp, 'w', callback = self.add_incrp)
return flatfileclass(rp, 'w')
def get_meta_writer(self, typestr, time): def get_meta_writer(self, typestr, time):
"""Return MetadataFile object opened for writing at given time""" """Return MetadataFile object opened for writing at given time"""
......
...@@ -1168,9 +1168,9 @@ class RPath(RORPath): ...@@ -1168,9 +1168,9 @@ class RPath(RORPath):
self.fsync(fp) self.fsync(fp)
if Globals.fsync_directories: self.get_parent_rp().fsync() if Globals.fsync_directories: self.get_parent_rp().fsync()
def get_data(self): def get_data(self, compressed = None):
"""Open file as a regular file, read data, close, return data""" """Open file as a regular file, read data, close, return data"""
fp = self.open("rb") fp = self.open("rb", compressed)
s = fp.read() s = fp.read()
assert not fp.close() assert not fp.close()
return s return s
...@@ -1263,6 +1263,57 @@ class GzipFile(gzip.GzipFile): ...@@ -1263,6 +1263,57 @@ class GzipFile(gzip.GzipFile):
""" """
def __del__(self): pass def __del__(self): pass
def __getattr__(self, name):
if name == 'fileno': return self.fileobj.fileno
else: raise AttributeError(name)
class MaybeGzip:
"""Represent a file object that may or may not be compressed
We don't want to compress 0 length files. This class lets us
delay the opening of the file until either the first write (so we
know it has data and should be compressed), or close (when there's
no data).
"""
def __init__(self, base_rp, callback = None):
"""Return file-like object with filename based on base_rp"""
assert not base_rp.lstat(), base_rp
self.base_rp = base_rp
# callback will be called with final write rp as only argument
self.callback = callback
self.fileobj = None # Will be None unless data gets written
self.closed = 0
def __getattr__(self, name):
if name == 'fileno': return self.fileobj.fileno
else: raise AttributeError(name)
def get_gzipped_rp(self):
"""Return gzipped rp by adding .gz to base_rp"""
if self.base_rp.index:
newind = self.base_rp.index[:-1] + (self.base_rp.index[-1]+'.gz',)
return self.base_rp.new_index(newind)
else: return self.base_rp.append_path('.gz')
def write(self, buf):
"""Write buf to fileobj"""
if self.fileobj: return self.fileobj.write(buf)
if not buf: return
new_rp = self.get_gzipped_rp()
if self.callback: self.callback(new_rp)
self.fileobj = new_rp.open("w", compress = 1)
return self.fileobj.write(buf)
def close(self):
"""Close related fileobj, pass return value"""
if self.closed: return None
self.closed = 1
if self.fileobj: return self.fileobj.close()
if self.callback: self.callback(self.base_rp)
self.base_rp.touch()
def setdata_local(rpath): def setdata_local(rpath):
......
...@@ -454,5 +454,33 @@ class CheckPath(unittest.TestCase): ...@@ -454,5 +454,33 @@ class CheckPath(unittest.TestCase):
bin2 = RPath(Globals.local_connection, "/bin") bin2 = RPath(Globals.local_connection, "/bin")
assert bin.path == "/bin", bin2.path assert bin.path == "/bin", bin2.path
class Gzip(RPathTest):
"""Test the gzip related functions/classes"""
def test_maybe_gzip(self):
"""Test MaybeGzip"""
dirrp = rpath.RPath(self.lc, "testfiles/output")
re_init_dir(dirrp)
base_rp = dirrp.append('foo')
fileobj = rpath.MaybeGzip(base_rp)
fileobj.close()
base_rp.setdata()
assert base_rp.isreg(), base_rp
assert base_rp.getsize() == 0
base_rp.delete()
base_gz = dirrp.append('foo.gz')
assert not base_gz.lstat()
fileobj = rpath.MaybeGzip(base_rp)
fileobj.write("lala")
fileobj.close()
base_rp.setdata()
base_gz.setdata()
assert not base_rp.lstat()
assert base_gz.isreg(), base_gz
data = base_gz.get_data(compressed = 1)
assert data == "lala", data
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment