Repository: borgbackup/borg

Issue:
borg mount fails silently on trailing spaces
#### Your borg version (borg -V).
borg 1.1.8
#### Operating system (distribution) and version.
Debian buster

#### Full borg commandline that lead to the problem (leave away excludes and passwords)
borg mount  --prefix clientname  /path/to/repo  emptydir  ""

## Describe the problem you're observing.
borg mount silently fails when a command contains trailing spaces, as the one above. Of course I do not type such commands, but they occur as the result of bash scripts expansion of empty parameters, for example.

borg mount does mount the repo and the archives appear in 'emptydir', but they are all empty.



Discussion:
IIRC, any trailing arguments will be interpreted to match archive contents. So, if you give the empty string there, it won't match anything.
Reproduced with borg 1.2.4+.

This is done in `_process_archive`, `paths` are all arguments after the mountpoint.
```
        matcher = Archiver.build_matcher(self._args.patterns, self._args.paths)
```

Resolving patch:
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -48,7 +48,7 @@
     from .helpers import EXIT_SUCCESS, EXIT_WARNING, EXIT_ERROR, EXIT_SIGNAL_BASE
     from .helpers import Error, NoManifestError, set_ec
     from .helpers import positive_int_validator, location_validator, archivename_validator, ChunkerParams, Location
-    from .helpers import PrefixSpec, GlobSpec, CommentSpec, SortBySpec, FilesCacheMode
+    from .helpers import PrefixSpec, GlobSpec, CommentSpec, PathSpec, SortBySpec, FilesCacheMode
     from .helpers import BaseFormatter, ItemFormatter, ArchiveFormatter
     from .helpers import format_timedelta, format_file_size, parse_file_size, format_archive
     from .helpers import safe_encode, remove_surrogates, bin_to_hex, prepare_dump_dict, eval_escapes
@@ -3061,7 +3061,7 @@ def define_borg_mount(parser):
             parser.add_argument('--numeric-ids', dest='numeric_ids', action='store_true',
                                   help='use numeric user and group identifiers from archive(s)')
             define_archive_filters_group(parser)
-            parser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+            parser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec,
                                    help='paths to extract; patterns are supported')
             define_exclusion_group(parser, strip_components=True)
 
@@ -3732,7 +3732,7 @@ def define_borg_mount(parser):
         subparser.add_argument('location', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                help='name of archive to create (must be also a valid directory name)')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str, action='extend',
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec, action='extend',
                                help='paths to archive')
 
         # borg debug
@@ -4052,7 +4052,7 @@ def define_borg_mount(parser):
         subparser.add_argument('archive2', metavar='ARCHIVE2',
                                type=archivename_validator(),
                                help='ARCHIVE2 name (no repository location allowed)')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec,
                                help='paths of items inside the archives to compare; patterns are supported')
         define_exclusion_group(subparser)
 
@@ -4109,7 +4109,7 @@ def define_borg_mount(parser):
                                help='archive to export')
         subparser.add_argument('tarfile', metavar='FILE',
                                help='output tar file. "-" to write to stdout instead.')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec,
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
@@ -4167,7 +4167,7 @@ def define_borg_mount(parser):
         subparser.add_argument('location', metavar='ARCHIVE',
                                type=location_validator(archive=True),
                                help='archive to extract')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec,
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
@@ -4421,7 +4421,7 @@ def define_borg_mount(parser):
         subparser.set_defaults(func=self.do_key_export)
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False))
-        subparser.add_argument('path', metavar='PATH', nargs='?', type=str,
+        subparser.add_argument('path', metavar='PATH', nargs='?', type=PathSpec,
                                help='where to store the backup')
         subparser.add_argument('--paper', dest='paper', action='store_true',
                                help='Create an export suitable for printing and later type-in')
@@ -4452,7 +4452,7 @@ def define_borg_mount(parser):
         subparser.set_defaults(func=self.do_key_import)
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False))
-        subparser.add_argument('path', metavar='PATH', nargs='?', type=str,
+        subparser.add_argument('path', metavar='PATH', nargs='?', type=PathSpec,
                                help='path to the backup (\'-\' to read from stdin)')
         subparser.add_argument('--paper', dest='paper', action='store_true',
                                help='interactively import from a backup done with ``--paper``')
@@ -4580,7 +4580,7 @@ def define_borg_mount(parser):
         subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
                                type=location_validator(),
                                help='repository or archive to list contents of')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec,
                                help='paths to list; patterns are supported')
         define_archive_filters_group(subparser)
         define_exclusion_group(subparser)
@@ -4798,7 +4798,7 @@ def define_borg_mount(parser):
         subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
                                type=location_validator(),
                                help='repository or archive to recreate')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=PathSpec,
                                help='paths to recreate; patterns are supported')
 
         # borg rename
diff --git a/src/borg/helpers/parseformat.py b/src/borg/helpers/parseformat.py
--- a/src/borg/helpers/parseformat.py
+++ b/src/borg/helpers/parseformat.py
@@ -223,6 +223,12 @@ def replace_placeholders(text, overrides={}):
 CommentSpec = replace_placeholders
 
 
+def PathSpec(text):
+    if not text:
+        raise argparse.ArgumentTypeError('Empty strings are not accepted as paths.')
+    return text
+
+
 def SortBySpec(text):
     from .manifest import AI_HUMAN_SORT_KEYS
     for token in text.split(','):


Test patch: