Repository: borgbackup/borg

Issue:
borg config storage_quota returns invalid value for a value like 120G
## Have you checked borgbackup docs, FAQ, and open Github issues?

Yes

## Is this a BUG / ISSUE report or a QUESTION?

Bug

## System information. For client/server mode post info for both machines.

#### Your borg version (borg -V).

1.1.14

#### Operating system (distribution) and version.

Debian 8.11

#### Hardware / network configuration, and filesystems used.

ext4 on mdadm RAID set

#### How much data is handled by borg?

24,75 TB original, 12,50 TB compressed, 67,14 GB compressed.

#### Full borg commandline that lead to the problem (leave away excludes and passwords)

borg config /local/repository/path storage_quota 120G

## Describe the problem you're observing.

The command fails with an "invalid value" error. However I would expect the syntax to be valid.

I think the cause is at: https://github.com/borgbackup/borg/blob/699256edbd072c60b995c493c2146aabe27905bb/src/borg/archiver.py#L1744
It seems like it's expecting an integer literal. This is in contrast with, for instance, `additional_free_space` parameter:
https://github.com/borgbackup/borg/blob/699256edbd072c60b995c493c2146aabe27905bb/src/borg/archiver.py#L1753
However the documentation of borg init says that the quota amount can be specified with a syntax like 120G: https://borgbackup.readthedocs.io/en/stable/usage/init.html
Also, I think it's error prone to specify such a configuration parameter value using an integer literal.

#### Include any warning/errors/backtraces from the system logs

```
Local Exception
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/borg/archiver.py", line 4591, in main
    exit_code = archiver.run(args)
  File "/usr/local/lib/python3.4/dist-packages/borg/archiver.py", line 4523, in run
    return set_ec(func(args))
  File "/usr/local/lib/python3.4/dist-packages/borg/archiver.py", line 176, in wrapper
    return method(self, args, repository=repository, **kwargs)
  File "/usr/local/lib/python3.4/dist-packages/borg/archiver.py", line 1884, in do_config
    validate(section, name, args.value)
  File "/usr/local/lib/python3.4/dist-packages/borg/archiver.py", line 1795, in repo_validate
    raise ValueError('Invalid value') from None
ValueError: Invalid value

Platform: Linux openmediavault 3.16.0-11-686-pae #1 SMP Debian 3.16.84-1 (2020-06-09) i686
Linux: debian 8.11 
Borg: 1.1.14  Python: CPython 3.4.2 msgpack: 0.5.6
PID: 24510  CWD: /media/f6280d17-6fef-460e-bb67-dfe3c26d87bf/home/borg
sys.argv: ['/usr/local/bin/borg', 'config', '/media/f6280d17-6fef-460e-bb67-dfe3c26d87bf/backup/borg/hppb/mauro/home', 'storage_quota', '120G']
SSH_ORIGINAL_COMMAND: None
```


Discussion:
Yeah, looks like there is some inconsistency due to differently capable argument parsing.

If somebody cleans this up, please check all the config values.

Resolving patch:
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -1798,21 +1798,24 @@ def do_config(self, args, repository):
         def repo_validate(section, name, value=None, check_value=True):
             if section not in ['repository', ]:
                 raise ValueError('Invalid section')
-            if name in ['segments_per_dir', 'max_segment_size', 'storage_quota', ]:
+            if name in ['segments_per_dir', ]:
                 if check_value:
                     try:
                         int(value)
                     except ValueError:
                         raise ValueError('Invalid value') from None
-                    if name == 'max_segment_size':
-                        if int(value) >= MAX_SEGMENT_SIZE_LIMIT:
-                            raise ValueError('Invalid value: max_segment_size >= %d' % MAX_SEGMENT_SIZE_LIMIT)
-            elif name in ['additional_free_space', ]:
+            elif name in ['max_segment_size', 'additional_free_space', 'storage_quota', ]:
                 if check_value:
                     try:
                         parse_file_size(value)
                     except ValueError:
                         raise ValueError('Invalid value') from None
+                    if name == 'storage_quota':
+                        if parse_file_size(value) < parse_file_size('10M'):
+                            raise ValueError('Invalid value: storage_quota < 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)
             elif name in ['append_only', ]:
                 if check_value and value not in ['0', '1']:
                     raise ValueError('Invalid value')
diff --git a/src/borg/repository.py b/src/borg/repository.py
--- a/src/borg/repository.py
+++ b/src/borg/repository.py
@@ -431,7 +431,7 @@ def open(self, path, exclusive, lock_wait=None, lock=True):
         if 'repository' not in self.config.sections() or self.config.getint('repository', 'version') != 1:
             self.close()
             raise self.InvalidRepository(path)
-        self.max_segment_size = self.config.getint('repository', 'max_segment_size')
+        self.max_segment_size = parse_file_size(self.config.get('repository', 'max_segment_size'))
         if self.max_segment_size >= MAX_SEGMENT_SIZE_LIMIT:
             self.close()
             raise self.InvalidRepositoryConfig(path, 'max_segment_size >= %d' % MAX_SEGMENT_SIZE_LIMIT)  # issue 3592
@@ -442,7 +442,7 @@ def open(self, path, exclusive, lock_wait=None, lock=True):
         self.append_only = self.append_only or self.config.getboolean('repository', 'append_only', fallback=False)
         if self.storage_quota is None:
             # self.storage_quota is None => no explicit storage_quota was specified, use repository setting.
-            self.storage_quota = self.config.getint('repository', 'storage_quota', fallback=0)
+            self.storage_quota = parse_file_size(self.config.get('repository', 'storage_quota', fallback=0))
         self.id = unhexlify(self.config.get('repository', 'id').strip())
         self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
         if self.check_segment_magic:


Test patch: