Repository: borgbackup/borg

Issue:
EROFS due to O_NOATIME with volume shadow copy in WSL1
## 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).

borg 1.1.17

#### Operating system (distribution) and version.

Windows 10 21h1

#### Hardware / network configuration, and filesystems used.

WSL1 drvfs

## Describe the problem you're observing.

#### Can you reproduce the problem? If so, describe how. If not, describe troubleshooting steps you took before opening the issue.

A volume shadow copy is made available in WSL1 with these steps:

1. Create a symlink in Windows with the shadow copy's UNC path.
    `mklink /d c \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\`
2. Map symlink to a drive letter.
    `subst z: c`
3. Mount the drive in WSL1.
    `mount -t drvfs -o ro z: c`

When borg encounters a file in this mounted volume owned by the invoking user, the file is opened with `O_NOATIME` among other flags, which produces this error `open: [Errno 30] Read-only file system`. When the file is not owned by the invoking user, `O_NOATIME` is not used and it works fine.

Can be confirmed with this patch

```diff
diff --git a/src/borg/archive.py b/src/borg/archive.py
index 93385c9d..4c08f9a7 100644
--- a/src/borg/archive.py
+++ b/src/borg/archive.py
@@ -47,7 +47,7 @@
 has_link = hasattr(os, 'link')

 flags_normal = os.O_RDONLY | getattr(os, 'O_BINARY', 0)
-flags_noatime = flags_normal | getattr(os, 'O_NOATIME', 0)
+flags_noatime = flags_normal


 class Statistics:
```




Discussion:
That rather sounds like a bug in windows considering how it treats O_NOATIME.

You mounted the filesystem read-only, so of course it is read-only. Not sure why it throws an error then saying it is readonly although borg did not try to write to it, but just tried to open a file without touching the atime timestamp.

BTW, can you add the full traceback leading to this error message?
There is no traceback because borg doesn't crash, the exception gets caught and the files get skipped with something like `audio.log: open: [Errno 30] Read-only file system: 'audio.log'`, from `os.open`.

I agree it's a bug with WSL1 but the chances of getting this fixed in WSL1 is slim. If you are willing consider a workaround in borg I can propose a patch to fix this. Otherwise I don't have any questions and this can be closed.
I added a env var for such stuff called `BORG_WORKAROUNDS`(at least in master branch, check 1.1-maint yourself).
Some code in borg to work around this issue could be triggered by a new entry value for that list.

Just open a PR if you'ld like to work on this.


Resolving patch:
diff --git a/src/borg/helpers/fs.py b/src/borg/helpers/fs.py
--- a/src/borg/helpers/fs.py
+++ b/src/borg/helpers/fs.py
@@ -286,6 +286,13 @@ def os_open(*, flags, path=None, parent_fd=None, name=None, noatime=False):
                 raise
             # Was this EPERM due to the O_NOATIME flag? Try again without it:
             fd = os.open(fname, _flags_normal, dir_fd=parent_fd)
+        except OSError as exc:
+            # O_NOATIME causes EROFS when accessing a volume shadow copy in WSL1
+            from . import workarounds
+            if 'retry_erofs' in workarounds and exc.errno == errno.EROFS and _flags_noatime != _flags_normal:
+                fd = os.open(fname, _flags_normal, dir_fd=parent_fd)
+            else:
+                raise
     else:
         fd = os.open(fname, _flags_normal, dir_fd=parent_fd)
     return fd


Test patch: