github.com/grailbio/base@v0.0.11/file/fsnodefuse/err.go (about) 1 package fsnodefuse 2 3 import ( 4 "fmt" 5 "runtime/debug" 6 "sync/atomic" 7 "syscall" 8 9 "github.com/grailbio/base/errors" 10 "github.com/grailbio/base/log" 11 "github.com/hanwen/go-fuse/v2/fs" 12 ) 13 14 // numHandledPanics is the total number of panics handled by handlePanicErrno. 15 // It can be used in testing to verify whether we triggered panics when 16 // handling operations. It must be accessed atomically, i.e. using atomic.* 17 // functions. 18 var numHandledPanics uint32 19 20 // handlePanicErrno is a last resort to prevent panics from reaching go-fuse and breaking the FUSE mount. 21 // All go-fuse-facing APIs that return Errno should defer it. 22 func handlePanicErrno(errno *syscall.Errno) { 23 r := recover() 24 if r == nil { 25 return 26 } 27 atomic.AddUint32(&numHandledPanics, 1) 28 *errno = errToErrno(makePanicErr(r)) 29 } 30 31 // handlePanicErr is like handlePanicErrno but for APIs that don't return Errno. 32 func handlePanicErr(dst *error) { 33 r := recover() 34 if r == nil { 35 return 36 } 37 *dst = makePanicErr(r) 38 } 39 40 func makePanicErr(recovered interface{}) error { 41 if err, ok := recovered.(error); ok { 42 return errors.E(err, fmt.Sprintf("recovered panic, stack:\n%v", string(debug.Stack()))) 43 } 44 return errors.E(fmt.Sprintf("recovered panic: %v, stack:\n%v", recovered, string(debug.Stack()))) 45 } 46 47 func errToErrno(err error) syscall.Errno { 48 if err == nil { 49 return fs.OK 50 } 51 e := errors.Recover(err) 52 kind := e.Kind 53 errno, ok := kind.Errno() 54 if ok { 55 log.Error.Printf("returning errno: %v for error: %v", errno, e) 56 return errno 57 } 58 log.Error.Printf("error with no good errno match: kind: %v, err: %v", kind, err) 59 return syscall.EIO 60 }