Commit 94f13694 authored by owsla's avatar owsla

Add ability to requote a repository


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@863 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent d0ed4f68
New in v1.1.15 (????/??/??) New in v1.1.15 (????/??/??)
--------------------------- ---------------------------
New feature: If quoting requirements change, rdiff-backup can requote the
entire repository if user specifies the --force option. (Andrew Ferguson)
Don't print the warning message about unsupported hard links if the user Don't print the warning message about unsupported hard links if the user
has specified the --no-hard-links option. (Suggested by Andreas Olsson) has specified the --no-hard-links option. (Suggested by Andreas Olsson)
......
...@@ -29,7 +29,8 @@ handle that error.) ...@@ -29,7 +29,8 @@ handle that error.)
""" """
import re, types from __future__ import generators
import os, re, types
import Globals, log, rpath import Globals, log, rpath
# If true, enable character quoting, and set characters making # If true, enable character quoting, and set characters making
...@@ -71,7 +72,7 @@ def init_quoting_regexps(): ...@@ -71,7 +72,7 @@ def init_quoting_regexps():
re.compile("[%s]|%s" % (chars_to_quote, quoting_char), re.S) re.compile("[%s]|%s" % (chars_to_quote, quoting_char), re.S)
unquoting_regexp = re.compile("%s[0-9]{3}" % quoting_char, re.S) unquoting_regexp = re.compile("%s[0-9]{3}" % quoting_char, re.S)
except re.error: except re.error:
log.Log.FatalError("Error '%s' when processing char quote list '%s'" % log.Log.FatalError("Error '%s' when processing char quote list %r" %
(re.error, chars_to_quote)) (re.error, chars_to_quote))
def quote(path): def quote(path):
...@@ -171,3 +172,84 @@ def get_quotedrpath(rp, separate_basename = 0): ...@@ -171,3 +172,84 @@ def get_quotedrpath(rp, separate_basename = 0):
def get_quoted_sep_base(filename): def get_quoted_sep_base(filename):
"""Get QuotedRPath from filename assuming last bit is quoted""" """Get QuotedRPath from filename assuming last bit is quoted"""
return get_quotedrpath(rpath.RPath(Globals.local_connection, filename), 1) return get_quotedrpath(rpath.RPath(Globals.local_connection, filename), 1)
def update_quoting(rbdir):
"""Update the quoting of a repository by renaming any
files that should be quoted differently.
"""
def requote(name):
unquoted_name = unquote(name)
quoted_name = quote(unquoted_name)
if name != quoted_name:
return quoted_name
else:
return None
def process(dirpath_rp, name, list):
new_name = requote(name)
if new_name:
if list:
list.remove(name)
list.append(new_name)
name_rp = dirpath_rp.append(name)
new_rp = dirpath_rp.append(new_name)
log.Log("Re-quoting %s to %s" % (name_rp.path, new_rp.path), 5)
rpath.move(name_rp, new_rp)
assert rbdir.conn is Globals.local_connection
mirror_rp = rbdir.get_parent_rp()
mirror = mirror_rp.path
log.Log("Re-quoting repository %s" % mirror_rp.path, 3)
try:
os_walk = os.walk
except AttributeError:
os_walk = walk
for dirpath, dirs, files in os_walk(mirror):
dirpath_rp = mirror_rp.newpath(dirpath)
for name in dirs: process(dirpath_rp, name, dirs)
for name in files: process(dirpath_rp, name, None)
"""
os.walk() copied directly from Python 2.5.1's os.py
Backported here for Python 2.2 support. os.walk() was first added
in Python 2.3.
"""
def walk(top, topdown=True, onerror=None):
from os import error, listdir
from os.path import join, isdir, islink
# We may not have read permission for top, in which case we can't
# get a list of the files the directory contains. os.path.walk
# always suppressed the exception then, rather than blow up for a
# minor reason when (say) a thousand readable directories are still
# left to visit. That logic is copied here.
try:
# Note that listdir and error are globals in this module due
# to earlier import-*.
names = listdir(top)
except error, err:
if onerror is not None:
onerror(err)
return
dirs, nondirs = [], []
for name in names:
if isdir(join(top, name)):
dirs.append(name)
else:
nondirs.append(name)
if topdown:
yield top, dirs, nondirs
for name in dirs:
path = join(top, name)
if not islink(path):
for x in walk(path, topdown, onerror):
yield x
if not topdown:
yield top, dirs, nondirs
...@@ -329,7 +329,7 @@ def Backup(rpin, rpout): ...@@ -329,7 +329,7 @@ def Backup(rpin, rpout):
SetConnections.BackupInitConnections(rpin.conn, rpout.conn) SetConnections.BackupInitConnections(rpin.conn, rpout.conn)
backup_check_dirs(rpin, rpout) backup_check_dirs(rpin, rpout)
backup_set_rbdir(rpin, rpout) backup_set_rbdir(rpin, rpout)
rpout.conn.fs_abilities.backup_set_globals(rpin) rpout.conn.fs_abilities.backup_set_globals(rpin, force)
if Globals.chars_to_quote: rpout = backup_quoted_rpaths(rpout) if Globals.chars_to_quote: rpout = backup_quoted_rpaths(rpout)
init_user_group_mapping(rpout.conn) init_user_group_mapping(rpout.conn)
backup_final_init(rpout) backup_final_init(rpout)
......
...@@ -577,7 +577,7 @@ class BackupSetGlobals(SetGlobals): ...@@ -577,7 +577,7 @@ class BackupSetGlobals(SetGlobals):
log.Log("Backup: must_escape_dos_devices = %d" % \ log.Log("Backup: must_escape_dos_devices = %d" % \
(self.src_fsa.escape_dos_devices or local_edd), 4) (self.src_fsa.escape_dos_devices or local_edd), 4)
def set_chars_to_quote(self, rbdir): def set_chars_to_quote(self, rbdir, force):
"""Set chars_to_quote setting for backup session """Set chars_to_quote setting for backup session
Unlike the other options, the chars_to_quote setting also Unlike the other options, the chars_to_quote setting also
...@@ -585,10 +585,12 @@ class BackupSetGlobals(SetGlobals): ...@@ -585,10 +585,12 @@ class BackupSetGlobals(SetGlobals):
directory, not just the current fs features. directory, not just the current fs features.
""" """
ctq = self.compare_ctq_file(rbdir, self.get_ctq_from_fsas()) (ctq, update) = self.compare_ctq_file(rbdir,
self.get_ctq_from_fsas(), force)
SetConnections.UpdateGlobal('chars_to_quote', ctq) SetConnections.UpdateGlobal('chars_to_quote', ctq)
if Globals.chars_to_quote: FilenameMapping.set_init_quote_vals() if Globals.chars_to_quote: FilenameMapping.set_init_quote_vals()
return update
def get_ctq_from_fsas(self): def get_ctq_from_fsas(self):
"""Determine chars_to_quote just from filesystems, no ctq file""" """Determine chars_to_quote just from filesystems, no ctq file"""
...@@ -608,25 +610,33 @@ class BackupSetGlobals(SetGlobals): ...@@ -608,25 +610,33 @@ class BackupSetGlobals(SetGlobals):
if ctq: ctq.append(';') # Quote quoting char if quoting anything if ctq: ctq.append(';') # Quote quoting char if quoting anything
return "".join(ctq) return "".join(ctq)
def compare_ctq_file(self, rbdir, suggested_ctq): def compare_ctq_file(self, rbdir, suggested_ctq, force):
"""Compare ctq file with suggested result, return actual ctq""" """Compare ctq file with suggested result, return actual ctq"""
ctq_rp = rbdir.append("chars_to_quote") ctq_rp = rbdir.append("chars_to_quote")
if not ctq_rp.lstat(): if not ctq_rp.lstat():
if Globals.chars_to_quote is None: actual_ctq = suggested_ctq if Globals.chars_to_quote is None: actual_ctq = suggested_ctq
else: actual_ctq = Globals.chars_to_quote else: actual_ctq = Globals.chars_to_quote
ctq_rp.write_string(actual_ctq) ctq_rp.write_string(actual_ctq)
return actual_ctq return (actual_ctq, None)
if Globals.chars_to_quote is None: actual_ctq = ctq_rp.get_data() if Globals.chars_to_quote is None: actual_ctq = ctq_rp.get_data()
else: actual_ctq = Globals.chars_to_quote # Globals override else: actual_ctq = Globals.chars_to_quote # Globals override
if actual_ctq == suggested_ctq: return actual_ctq if actual_ctq == suggested_ctq: return (actual_ctq, None)
if suggested_ctq == "": if suggested_ctq == "":
log.Log("Warning: File system no longer needs quoting, " log.Log("Warning: File system no longer needs quoting, "
"but we will retain for backwards compatibility.", 2) "but we will retain for backwards compatibility.", 2)
return actual_ctq return (actual_ctq, None)
if Globals.chars_to_quote is None: if Globals.chars_to_quote is None:
log.Log.FatalError("""New quoting requirements! if force:
log.Log("Warning: migrating rdiff-backup repository from"
"old quoting chars %r to new quoting chars %r" %
(actual_ctq, suggested_ctq), 2)
ctq_rp.delete()
ctq_rp.write_string(suggested_ctq)
return (suggested_ctq, 1)
else:
log.Log.FatalError("""New quoting requirements!
The quoting chars this session needs %r do not match The quoting chars this session needs %r do not match
the repository settings %r listed in the repository settings %r listed in
...@@ -637,7 +647,11 @@ This may be caused when you copy an rdiff-backup repository from a ...@@ -637,7 +647,11 @@ This may be caused when you copy an rdiff-backup repository from a
normal file system onto a windows one that cannot support the same normal file system onto a windows one that cannot support the same
characters, or if you backup a case-sensitive file system onto a characters, or if you backup a case-sensitive file system onto a
case-insensitive one that previously only had case-insensitive ones case-insensitive one that previously only had case-insensitive ones
backed up onto it.""" % (suggested_ctq, actual_ctq, ctq_rp.path)) backed up onto it.
By specificying the --force option, rdiff-backup will migrate the
repository from the old quoting chars to the new ones.""" %
(suggested_ctq, actual_ctq, ctq_rp.path))
class RestoreSetGlobals(SetGlobals): class RestoreSetGlobals(SetGlobals):
...@@ -719,7 +733,7 @@ class SingleSetGlobals(RestoreSetGlobals): ...@@ -719,7 +733,7 @@ class SingleSetGlobals(RestoreSetGlobals):
('carbonfile_active', 'carbonfile_write', 'carbonfile_conn')) ('carbonfile_active', 'carbonfile_write', 'carbonfile_conn'))
def backup_set_globals(rpin): def backup_set_globals(rpin, force):
"""Given rps for source filesystem and repository, set fsa globals """Given rps for source filesystem and repository, set fsa globals
This should be run on the destination connection, because we may This should be run on the destination connection, because we may
...@@ -742,10 +756,14 @@ def backup_set_globals(rpin): ...@@ -742,10 +756,14 @@ def backup_set_globals(rpin):
bsg.set_change_ownership() bsg.set_change_ownership()
bsg.set_high_perms() bsg.set_high_perms()
bsg.set_symlink_perms() bsg.set_symlink_perms()
bsg.set_chars_to_quote(Globals.rbdir) update_quoting = bsg.set_chars_to_quote(Globals.rbdir, force)
bsg.set_escape_dos_devices() bsg.set_escape_dos_devices()
bsg.set_must_escape_dos_devices(Globals.rbdir) bsg.set_must_escape_dos_devices(Globals.rbdir)
if update_quoting and force:
FilenameMapping.update_quoting(Globals.rbdir)
def restore_set_globals(rpout): def restore_set_globals(rpout):
"""Set fsa related globals for restore session, given in/out rps""" """Set fsa related globals for restore session, given in/out rps"""
assert rpout.conn is Globals.local_connection assert rpout.conn is Globals.local_connection
......
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