Kaynağa Gözat

Merge pull request #8767 from ThomasWaldmann/backports-1.2

Backports to 1.2-maint
TW 1 ay önce
ebeveyn
işleme
9aaa74c7d3
3 değiştirilmiş dosya ile 37 ekleme ve 10 silme
  1. 11 0
      docs/faq.rst
  2. 18 9
      src/borg/archiver.py
  3. 8 1
      src/borg/helpers/yes.py

+ 11 - 0
docs/faq.rst

@@ -478,6 +478,17 @@ will only be applied to new chunks, not existing chunks. So it's safe
 to change them.
 
 
+Why is backing up an unmodified FAT filesystem slow on Linux?
+-------------------------------------------------------------
+
+By default, the files cache used by BorgBackup considers the inode of files.
+When an inode number changes compared to the last backup, it hashes the file
+again. The ``vfat`` kernel driver does not produce stable inode numbers by
+default.  One way to achieve stable inode numbering is mounting the filesystem
+using ``nfs=nostale_ro``. Doing so implies mounting the filesystem read-only.
+Another option is to not consider inode numbers in the files cache by passing
+``--files-cache=ctime,size``.
+
 Security
 ########
 

+ 18 - 9
src/borg/archiver.py

@@ -208,7 +208,7 @@ def with_archive(method):
 
 def parse_storage_quota(storage_quota):
     parsed = parse_file_size(storage_quota)
-    if parsed < parse_file_size('10M'):
+    if parsed != 0 and parsed < parse_file_size('10M'):
         raise argparse.ArgumentTypeError('quota is too small (%s). At least 10M are required.' % storage_quota)
     return parsed
 
@@ -1913,11 +1913,12 @@ class Archiver:
     @with_repository(manifest=False, exclusive=True)
     def do_compact(self, args, repository):
         """compact segment files in the repository"""
-        # see the comment in do_with_lock about why we do it like this:
-        data = repository.get(Manifest.MANIFEST_ID)
-        repository.put(Manifest.MANIFEST_ID, data)
-        threshold = args.threshold / 100
-        repository.commit(compact=True, threshold=threshold, cleanup_commits=args.cleanup_commits)
+        if not args.dry_run:  # support --dry-run to simplify scripting
+            # see the comment in do_with_lock about why we do it like this:
+            data = repository.get(Manifest.MANIFEST_ID)
+            repository.put(Manifest.MANIFEST_ID, data)
+            threshold = args.threshold / 100
+            repository.commit(compact=True, threshold=threshold, cleanup_commits=args.cleanup_commits)
         return EXIT_SUCCESS
 
     @with_repository(exclusive=True, manifest=False)
@@ -1940,8 +1941,15 @@ class Archiver:
                     except ValueError:
                         raise ValueError('Invalid value') from None
                     if name == 'storage_quota':
-                        if parse_file_size(value) < parse_file_size('10M'):
+                        wanted = parse_file_size(value)
+                        minimum = parse_file_size('10M')
+                        if wanted != 0 and wanted < minimum:
                             raise ValueError('Invalid value: storage_quota < 10M')
+                    elif name == 'additional_free_space':
+                        wanted = parse_file_size(value)
+                        minimum = parse_file_size('10M')
+                        if wanted != 0 and wanted < minimum:
+                            raise ValueError('Invalid value: additional_free_space < 10M')
                     elif name == 'max_segment_size':
                         if parse_file_size(value) >= MAX_SEGMENT_SIZE_LIMIT:
                             raise ValueError('Invalid value: max_segment_size >= %d' % MAX_SEGMENT_SIZE_LIMIT)
@@ -3188,8 +3196,8 @@ class Archiver:
         Unmounting in these cases could cause an active rsync or similar process
         to unintentionally delete data.
 
-        When running in the foreground ^C/SIGINT unmounts cleanly, but other
-        signals or crashes do not.
+        When running in the foreground, ^C/SIGINT cleanly unmounts the filesystem,
+        but other signals or crashes do not.
         """)
 
         if parser.prog == 'borgfs':
@@ -3456,6 +3464,7 @@ class Archiver:
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False),
                                help='repository to compact')
+        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true', help='do nothing')
         subparser.add_argument('--cleanup-commits', dest='cleanup_commits', action='store_true',
                                help='cleanup commit-only 17-byte segment files')
         subparser.add_argument('--threshold', metavar='PERCENT', dest='threshold',

+ 8 - 1
src/borg/helpers/yes.py

@@ -8,6 +8,9 @@ FALSISH = ('No', 'NO', 'no', 'N', 'n', '0', )
 TRUISH = ('Yes', 'YES', 'yes', 'Y', 'y', '1', )
 DEFAULTISH = ('Default', 'DEFAULT', 'default', 'D', 'd', '', )
 
+ERROR = "error"
+assert ERROR not in TRUISH + FALSISH + DEFAULTISH
+
 
 def yes(msg=None, false_msg=None, true_msg=None, default_msg=None,
         retry_msg=None, invalid_msg=None, env_msg='{} (from {})',
@@ -77,10 +80,14 @@ def yes(msg=None, false_msg=None, true_msg=None, default_msg=None,
             if not prompt:
                 return default
             try:
-                answer = input()
+                answer = input()  # this may raise UnicodeDecodeError, #6984
+                if answer == ERROR:  # for testing purposes
+                    raise UnicodeDecodeError("?", b"?", 0, 1, "?")  # args don't matter
             except EOFError:
                 # avoid defaultish[0], defaultish could be empty
                 answer = truish[0] if default else falsish[0]
+            except UnicodeDecodeError:
+                answer = ERROR
         if answer in defaultish:
             if default_msg:
                 output(default_msg, 'accepted_default')