Report correct error when restore fails

When file corruption is detected, report vfs.ErrCorruption to
distinguish corruption error from other restore errors.

Updates #1035

PiperOrigin-RevId: 404588445
This commit is contained in:
Fabricio Voznika
2021-10-20 10:45:45 -07:00
committed by gVisor bot
parent bdf4e41c86
commit c23d67f3c0
2 changed files with 24 additions and 21 deletions
+8 -8
View File
@@ -277,18 +277,18 @@ func (d *dentry) restoreFile(ctx context.Context, file p9file, qid p9.QID, attrM
if d.isRegularFile() {
if opts.ValidateFileSizes {
if !attrMask.Size {
return fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(d))
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(d))}
}
if d.size != attr.Size {
return fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d), d.size, attr.Size)
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d), d.size, attr.Size)}
}
}
if opts.ValidateFileModificationTimestamps {
if !attrMask.MTime {
return fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(d))
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(d))}
}
if want := dentryTimestampFromP9(attr.MTimeSeconds, attr.MTimeNanoSeconds); d.mtime != want {
return fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d), linux.NsecToStatxTimestamp(d.mtime), linux.NsecToStatxTimestamp(want))
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d), linux.NsecToStatxTimestamp(d.mtime), linux.NsecToStatxTimestamp(want))}
}
}
}
@@ -326,18 +326,18 @@ func (d *dentry) restoreFileLisa(ctx context.Context, inode *lisafs.Inode, opts
if d.isRegularFile() {
if opts.ValidateFileSizes {
if inode.Stat.Mask&linux.STATX_SIZE != 0 {
return fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(d))
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(d))}
}
if d.size != inode.Stat.Size {
return fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d), d.size, inode.Stat.Size)
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d), d.size, inode.Stat.Size)}
}
}
if opts.ValidateFileModificationTimestamps {
if inode.Stat.Mask&linux.STATX_MTIME != 0 {
return fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(d))
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(d))}
}
if want := dentryTimestampFromLisa(inode.Stat.Mtime); d.mtime != want {
return fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d), linux.NsecToStatxTimestamp(d.mtime), linux.NsecToStatxTimestamp(want))
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d), linux.NsecToStatxTimestamp(d.mtime), linux.NsecToStatxTimestamp(want))}
}
}
}
+16 -13
View File
@@ -15,7 +15,6 @@
package vfs
import (
"fmt"
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -24,6 +23,18 @@ import (
"gvisor.dev/gvisor/pkg/waiter"
)
// ErrCorruption indicates a failed restore due to external file system state in
// corruption.
type ErrCorruption struct {
// Err is the wrapped error.
Err error
}
// Error returns a sensible description of the restore error.
func (e ErrCorruption) Error() string {
return "restore failed due to external file system state in corruption: " + e.Err.Error()
}
// FilesystemImplSaveRestoreExtension is an optional extension to
// FilesystemImpl.
type FilesystemImplSaveRestoreExtension interface {
@@ -37,38 +48,30 @@ type FilesystemImplSaveRestoreExtension interface {
// PrepareSave prepares all filesystems for serialization.
func (vfs *VirtualFilesystem) PrepareSave(ctx context.Context) error {
failures := 0
for fs := range vfs.getFilesystems() {
if ext, ok := fs.impl.(FilesystemImplSaveRestoreExtension); ok {
if err := ext.PrepareSave(ctx); err != nil {
ctx.Warningf("%T.PrepareSave failed: %v", fs.impl, err)
failures++
fs.DecRef(ctx)
return err
}
}
fs.DecRef(ctx)
}
if failures != 0 {
return fmt.Errorf("%d filesystems failed to prepare for serialization", failures)
}
return nil
}
// CompleteRestore completes restoration from checkpoint for all filesystems
// after deserialization.
func (vfs *VirtualFilesystem) CompleteRestore(ctx context.Context, opts *CompleteRestoreOptions) error {
failures := 0
for fs := range vfs.getFilesystems() {
if ext, ok := fs.impl.(FilesystemImplSaveRestoreExtension); ok {
if err := ext.CompleteRestore(ctx, *opts); err != nil {
ctx.Warningf("%T.CompleteRestore failed: %v", fs.impl, err)
failures++
fs.DecRef(ctx)
return err
}
}
fs.DecRef(ctx)
}
if failures != 0 {
return fmt.Errorf("%d filesystems failed to complete restore after deserialization", failures)
}
return nil
}