New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Snapshotting root of drive fails when Volume Shadow Copy enabled #3842
Comments
Same case on windows server 2019, doesn't seem to happen on Windows 10 on my other pc. |
After running with debugging, I may know the issue.
I believe I have ran into an issue like this before, it is caused by the shadow volume path not ending in
Notice the trailing |
I tried to recompile with just a quick fix in kopia/fs/localfs/local_fs_os.go Lines 114 to 116 in 4cf9582
|
I've traced the source of the error to the kopia/fs/localfs/local_fs_os.go Lines 66 to 69 in 2b92388
The first time this is called on a VSS snapshot, the kopia/fs/localfs/local_fs_os.go Lines 25 to 33 in 2b92388
The underlying error is returned by I managed to write a patch that fixes this issue for me, but I'm not entirely convinced this is the right part of the code to fix it in. I'd love if someone more familiar with this part of the code could look into this more... diff --git a/fs/localfs/local_fs_os.go b/fs/localfs/local_fs_os.go
index e790b5b3..2a3ffbb2 100644
--- a/fs/localfs/local_fs_os.go
+++ b/fs/localfs/local_fs_os.go
@@ -66,7 +66,21 @@ func (it *filesystemDirectoryIterator) Close() {
func (fsd *filesystemDirectory) Iterate(ctx context.Context) (fs.DirectoryIterator, error) {
fullPath := fsd.fullPath()
- f, direrr := os.Open(fullPath) //nolint:gosec
+ // Trying to list the shadow volume root directory will fail unless it contains a trailing \
+ // We detect that here and fix it
+ var openPath = fullPath
+ if runtime.GOOS == "windows" {
+ const shadowVolumePrefix = `\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy`
+ if strings.HasPrefix(fullPath, shadowVolumePrefix) {
+ remainder := fullPath[len(shadowVolumePrefix):]
+ // Check if we're at the root of the shadow volume, any subdirectories should contain a path separator
+ if !strings.ContainsRune(remainder, os.PathSeparator) {
+ openPath = fullPath + string(os.PathSeparator)
+ }
+ }
+ }
+
+ f, direrr := os.Open(openPath) //nolint:gosec
if direrr != nil {
return nil, errors.Wrap(direrr, "unable to read directory")
} |
I also encountered this bug when attempting to create a backup of an entire volume using a Volume Shadow Copy, and then I found your bug report. Thank you for the detailed analysis. At first, I wondered why you didn't choose a similar construct as in the kopia/fs/localfs/local_fs_os.go Lines 113 to 123 in 89c8eb4
However, when I tried to apply the same solution, I realized why: As you correctly analyzed, the problem occurs when calling Nonetheless, I slightly modified your proposed solution, as I don't see the necessity for introducing the remainder variable. --- a/fs/localfs/local_fs_os.go
+++ b/fs/localfs/local_fs_os.go
@@ -66,12 +66,24 @@ func (it *filesystemDirectoryIterator) Close() {
func (fsd *filesystemDirectory) Iterate(ctx context.Context) (fs.DirectoryIterator, error) {
fullPath := fsd.fullPath()
+ if runtime.GOOS == "windows" {
+ const shadowVolumePrefix = `\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy`
+
+ if strings.HasPrefix(fullPath, shadowVolumePrefix) &&
+ !strings.HasSuffix(fullPath, string(filepath.Separator)) {
+ fullPath = fullPath + string(filepath.Separator)
+ }
+ }
+
f, direrr := os.Open(fullPath) //nolint:gosec
if direrr != nil {
return nil, errors.Wrap(direrr, "unable to read directory")
}
- childPrefix := fullPath + string(filepath.Separator)
+ childPrefix := fullPath
+ if !strings.HasSuffix(childPrefix, string(filepath.Separator)) {
+ childPrefix += string(filepath.Separator)
+ }
return &filesystemDirectoryIterator{dirHandle: f, childPrefix: childPrefix}, nil
}
However, in principle, I wonder if this is really the ideal place: Wouldn't it be better to patch Lines 51 to 53 in 89c8eb4
and ensure that the return value ends with a trailing backslash if |
Thanks for the additional eyes,
The remainder part ensures we only apply the trailing path separator to the top level shadow volume root directory, no directory below it requires it. The remainder code is checking if we're at the root or in a subdirectory. I separated openPath and fullPath because only the Open call needs the trailing slash, all the rest of Kopia's code doesn't expect a trailing slash there. I don't know if it would actually break anything, but I didn't see a reason to change the behavior. This is the same reason I didn't modify the fullPath function itself. |
After checking your patch again, I think your childPrefix check actually handles the second part of my comment. For some reason I was under the impression that fullPath got used or returned somewhere by itself, but since it's just being used for childPrefix, your check should be sufficient. The first part of my comment is still valid I believe. |
Snapshotting fails with
ERROR upload error: The parameter is incorrect.
when snapshotting the root of the drive with--enable-volume-shadow-copy
set toalways
. Snapshotting a sub directory in the root works correctly. All commands were run with administrator privileges.The text was updated successfully, but these errors were encountered: