Repository: borgbackup/borg

Issue:
Borg should raise clear Exception on cache permission mismatch 
## Have you checked borgbackup docs, FAQ, and open Github issues?

Yes

## Is this a BUG / ISSUE report or a QUESTION?

ISSUE

## Describe the problem you're observing.

I use `borg` for backup `/home` with my unprivileged user. 

I also use `borg` to backup systemrelevant files and VM disks using `sudo borg`. I do use `sudo` to catch files my unprivileged user does not have access to.

After every run of privileged `sudo borg` the ownership of `~/.config/borg` and `~/.config/cache` changes to `root:root` for the relevant backup hash path.

This makes it impossible to use any  `borg` related command as unprivileged user against the archive.
The workaround is to set back the ownership.

This is accepted behavior for me, so far.

It would be great if borgs exception handling would become more clear about the error it has.

Currently the exception is printed in raw format:

```
borg list /run/media/gt0015/b86a9ff9-6c1f-4f76-9a60-9849f92289da/backup
Local Exception
Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/borg/archiver.py", line 4434, in main
    exit_code = archiver.run(args)
  File "/usr/lib64/python3.6/site-packages/borg/archiver.py", line 4366, in run
    return set_ec(func(args))
  File "/usr/lib64/python3.6/site-packages/borg/archiver.py", line 143, in wrapper
    assert_secure(repository, kwargs['manifest'], self.lock_wait)
  File "/usr/lib64/python3.6/site-packages/borg/cache.py", line 206, in assert_secure
    sm.assert_secure(manifest, manifest.key, lock_wait=lock_wait)
  File "/usr/lib64/python3.6/site-packages/borg/cache.py", line 171, in assert_secure
    with cache_config:
  File "/usr/lib64/python3.6/site-packages/borg/cache.py", line 238, in __enter__
    self.open()
  File "/usr/lib64/python3.6/site-packages/borg/cache.py", line 262, in open
    self.load()
  File "/usr/lib64/python3.6/site-packages/borg/cache.py", line 267, in load
    self._check_upgrade(self.config_path)
  File "/usr/lib64/python3.6/site-packages/borg/cache.py", line 326, in _check_upgrade
    raise Exception('%s does not look like a Borg cache.' % config_path) from None
Exception: /home/gt0015/.cache/borg/31eeac759ae30fc173c017dccc30b646868a6e46e4c7143d85f99a70647841c2/config does not look like a Borg cache.

Platform: Linux t470p 4.19.10-200.fc28.x86_64 #1 SMP Mon Dec 17 15:46:19 UTC 2018 x86_64
Linux: Fedora 28 Twenty Eight
Borg: 1.1.7  Python: CPython 3.6.7
PID: 32486  CWD: /home/gt0015
sys.argv: ['/usr/bin/borg', 'list', '/run/media/gt0015/b86a9ff9-6c1f-4f76-9a60-9849f92289da/backup']
SSH_ORIGINAL_COMMAND: None
```
It would be great that borg would catch this exception and print a human readable error message, pointing out the ownership mismatch.


Discussion:
Hmm, that almost looks a bit like a bad exception handler. We need to check this, a "PermissionDenied" exception should not be catched and lead to other exception like seen above.
This is from stdlib configparser code:
```
            try:
                with open(filename, encoding=encoding) as fp:
                    self._read(fp, filename)
            except OSError:
                continue
```
So looks like a **diaper pattern**, but not in the borg code, so not much we can do about it.
What you can do about your fundamental permissions issue:

Use `sudo -H`, so the borg running as root user will use root's home directory and not mess up the user's home dir. Of course you will need to put borg keys etc. there also.
we can open the file ourselves and use the configparser.read_file(fd) method.

Resolving patch:
diff --git a/src/borg/cache.py b/src/borg/cache.py
--- a/src/borg/cache.py
+++ b/src/borg/cache.py
@@ -263,7 +263,8 @@ def open(self):
 
     def load(self):
         self._config = configparser.ConfigParser(interpolation=None)
-        self._config.read(self.config_path)
+        with open(self.config_path) as fd:
+            self._config.read_file(fd)
         self._check_upgrade(self.config_path)
         self.id = self._config.get('cache', 'repository')
         self.manifest_id = unhexlify(self._config.get('cache', 'manifest'))
diff --git a/src/borg/repository.py b/src/borg/repository.py
--- a/src/borg/repository.py
+++ b/src/borg/repository.py
@@ -382,7 +382,8 @@ def open(self, path, exclusive, lock_wait=None, lock=True):
         else:
             self.lock = None
         self.config = ConfigParser(interpolation=None)
-        self.config.read(os.path.join(self.path, 'config'))
+        with open(os.path.join(self.path, 'config')) as fd:
+            self.config.read_file(fd)
         if 'repository' not in self.config.sections() or self.config.getint('repository', 'version') != 1:
             self.close()
             raise self.InvalidRepository(path)


Test patch: