summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/fs.go
diff options
context:
space:
mode:
authorZhaozhong Ni <nzz@google.com>2018-05-08 11:36:11 -0700
committerShentubot <shentubot@google.com>2018-05-08 11:36:59 -0700
commit174161013de22be6a42b02ee06611a9de9e20b18 (patch)
treeb910f76ea3ba8b14a29df2d28010fd0af1db18bb /pkg/sentry/fs/fs.go
parent3ac3ea1d6afea0b128112e6a46b8bf47b4b0e02a (diff)
Capture restore file system corruption errors in exit error.
PiperOrigin-RevId: 195850822 Change-Id: I4d7bdd8fe129c5ed461b73e1d7458be2cf5680c2
Diffstat (limited to 'pkg/sentry/fs/fs.go')
-rw-r--r--pkg/sentry/fs/fs.go63
1 files changed, 60 insertions, 3 deletions
diff --git a/pkg/sentry/fs/fs.go b/pkg/sentry/fs/fs.go
index f54f767d3..6ec9ff446 100644
--- a/pkg/sentry/fs/fs.go
+++ b/pkg/sentry/fs/fs.go
@@ -55,11 +55,19 @@ package fs
import (
"sync"
+
+ "gvisor.googlesource.com/gvisor/pkg/log"
)
-// work is a sync.WaitGroup that can be used to queue asynchronous operations
-// via Do. Callers can use Barrier to ensure no operations are outstanding.
-var work sync.WaitGroup
+var (
+ // work is a sync.WaitGroup that can be used to queue asynchronous
+ // operations via Do. Callers can use Barrier to ensure no operations
+ // are outstanding.
+ work sync.WaitGroup
+
+ // asyncError is used to store up to one asynchronous execution error.
+ asyncError = make(chan error, 1)
+)
// AsyncBarrier waits for all outstanding asynchronous work to complete.
func AsyncBarrier() {
@@ -75,6 +83,43 @@ func Async(f func()) {
}()
}
+// AsyncErrorBarrier waits for all outstanding asynchronous work to complete, or
+// the first async error to arrive. Other unfinished async executions will
+// continue in the background. Other past and future async errors are ignored.
+func AsyncErrorBarrier() error {
+ wait := make(chan struct{}, 1)
+ go func() { // S/R-SAFE: Does not touch persistent state.
+ work.Wait()
+ wait <- struct{}{}
+ }()
+ select {
+ case <-wait:
+ select {
+ case err := <-asyncError:
+ return err
+ default:
+ return nil
+ }
+ case err := <-asyncError:
+ return err
+ }
+}
+
+// CatchError tries to capture the potential async error returned by the
+// function. At most one async error will be captured globally so excessive
+// errors will be dropped.
+func CatchError(f func() error) func() {
+ return func() {
+ if err := f(); err != nil {
+ select {
+ case asyncError <- err:
+ default:
+ log.Warningf("excessive async error dropped: %v", err)
+ }
+ }
+ }
+}
+
// ErrSaveRejection indicates a failed save due to unsupported file system state
// such as dangling open fd, etc.
type ErrSaveRejection struct {
@@ -86,3 +131,15 @@ type ErrSaveRejection struct {
func (e ErrSaveRejection) Error() string {
return "save rejected due to unsupported file system state: " + e.Err.Error()
}
+
+// 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 save rejection error.
+func (e ErrCorruption) Error() string {
+ return "restore failed due to external file system state in corruption: " + e.Err.Error()
+}