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  }