231 lines
6 KiB
Go
231 lines
6 KiB
Go
|
package nfs
|
||
|
|
||
|
import (
|
||
|
"encoding"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
// RPCError provides the error interface for errors thrown by
|
||
|
// procedures to be transmitted over the XDR RPC channel
|
||
|
type RPCError interface {
|
||
|
// An RPCError is an `error` with this method
|
||
|
Error() string
|
||
|
// Code is the RPC Response code to send
|
||
|
Code() ResponseCode
|
||
|
// BinaryMarshaler is the on-wire representation of this error
|
||
|
encoding.BinaryMarshaler
|
||
|
}
|
||
|
|
||
|
// AuthStat is an enumeration of why authentication ahs failed
|
||
|
type AuthStat uint32
|
||
|
|
||
|
// AuthStat Codes
|
||
|
const (
|
||
|
AuthStatOK AuthStat = iota
|
||
|
AuthStatBadCred
|
||
|
AuthStatRejectedCred
|
||
|
AuthStatBadVerifier
|
||
|
AuthStatRejectedVerfier
|
||
|
AuthStatTooWeak
|
||
|
AuthStatInvalidResponse
|
||
|
AuthStatFailed
|
||
|
AuthStatKerbGeneric
|
||
|
AuthStatTimeExpire
|
||
|
AuthStatTktFile
|
||
|
AuthStatDecode
|
||
|
AuthStatNetAddr
|
||
|
AuthStatRPCGSSCredProblem
|
||
|
AuthStatRPCGSSCTXProblem
|
||
|
)
|
||
|
|
||
|
// AuthError is an RPCError
|
||
|
type AuthError struct {
|
||
|
AuthStat
|
||
|
}
|
||
|
|
||
|
// Code for AuthErrors is ResponseCodeAuthError
|
||
|
func (a *AuthError) Code() ResponseCode {
|
||
|
return ResponseCodeAuthError
|
||
|
}
|
||
|
|
||
|
// Error is a textual representaiton of the auth error. From the RFC
|
||
|
func (a *AuthError) Error() string {
|
||
|
switch a.AuthStat {
|
||
|
case AuthStatOK:
|
||
|
return "Auth Status: OK"
|
||
|
case AuthStatBadCred:
|
||
|
return "Auth Status: bad credential"
|
||
|
case AuthStatRejectedCred:
|
||
|
return "Auth Status: client must begin new session"
|
||
|
case AuthStatBadVerifier:
|
||
|
return "Auth Status: bad verifier"
|
||
|
case AuthStatRejectedVerfier:
|
||
|
return "Auth Status: verifier expired or replayed"
|
||
|
case AuthStatTooWeak:
|
||
|
return "Auth Status: rejected for security reasons"
|
||
|
case AuthStatInvalidResponse:
|
||
|
return "Auth Status: bogus response verifier"
|
||
|
case AuthStatFailed:
|
||
|
return "Auth Status: reason unknown"
|
||
|
case AuthStatKerbGeneric:
|
||
|
return "Auth Status: kerberos generic error"
|
||
|
case AuthStatTimeExpire:
|
||
|
return "Auth Status: time of credential expired"
|
||
|
case AuthStatTktFile:
|
||
|
return "Auth Status: problem with ticket file"
|
||
|
case AuthStatDecode:
|
||
|
return "Auth Status: can't decode authenticator"
|
||
|
case AuthStatNetAddr:
|
||
|
return "Auth Status: wrong net address in ticket"
|
||
|
case AuthStatRPCGSSCredProblem:
|
||
|
return "Auth Status: no credentials for user"
|
||
|
case AuthStatRPCGSSCTXProblem:
|
||
|
return "Auth Status: problem with context"
|
||
|
}
|
||
|
return "Auth Status: Unknown"
|
||
|
}
|
||
|
|
||
|
// MarshalBinary sends the specific auth status
|
||
|
func (a *AuthError) MarshalBinary() (data []byte, err error) {
|
||
|
var resp [4]byte
|
||
|
binary.LittleEndian.PutUint32(resp[:], uint32(a.AuthStat))
|
||
|
return resp[:], nil
|
||
|
}
|
||
|
|
||
|
// RPCMismatchError is an RPCError
|
||
|
type RPCMismatchError struct {
|
||
|
Low uint32
|
||
|
High uint32
|
||
|
}
|
||
|
|
||
|
// Code for RPCMismatchError is ResponseCodeRPCMismatch
|
||
|
func (r *RPCMismatchError) Code() ResponseCode {
|
||
|
return ResponseCodeRPCMismatch
|
||
|
}
|
||
|
|
||
|
func (r *RPCMismatchError) Error() string {
|
||
|
return fmt.Sprintf("RPC Mismatch: Expected version between %d and %d.", r.Low, r.High)
|
||
|
}
|
||
|
|
||
|
// MarshalBinary sends the specific rpc mismatch range
|
||
|
func (r *RPCMismatchError) MarshalBinary() (data []byte, err error) {
|
||
|
var resp [8]byte
|
||
|
binary.LittleEndian.PutUint32(resp[0:4], uint32(r.Low))
|
||
|
binary.LittleEndian.PutUint32(resp[4:8], uint32(r.High))
|
||
|
return resp[:], nil
|
||
|
}
|
||
|
|
||
|
// ResponseCodeProcUnavailableError is an RPCError
|
||
|
type ResponseCodeProcUnavailableError struct {
|
||
|
}
|
||
|
|
||
|
// Code for ResponseCodeProcUnavailableError
|
||
|
func (r *ResponseCodeProcUnavailableError) Code() ResponseCode {
|
||
|
return ResponseCodeProcUnavailable
|
||
|
}
|
||
|
|
||
|
func (r *ResponseCodeProcUnavailableError) Error() string {
|
||
|
return "The requested procedure is unexported"
|
||
|
}
|
||
|
|
||
|
// MarshalBinary - this error has no associated body
|
||
|
func (r *ResponseCodeProcUnavailableError) MarshalBinary() (data []byte, err error) {
|
||
|
return []byte{}, nil
|
||
|
}
|
||
|
|
||
|
// ResponseCodeSystemError is an RPCError
|
||
|
type ResponseCodeSystemError struct {
|
||
|
}
|
||
|
|
||
|
// Code for ResponseCodeSystemError
|
||
|
func (r *ResponseCodeSystemError) Code() ResponseCode {
|
||
|
return ResponseCodeSystemErr
|
||
|
}
|
||
|
|
||
|
func (r *ResponseCodeSystemError) Error() string {
|
||
|
return "memory allocation failure"
|
||
|
}
|
||
|
|
||
|
// MarshalBinary - this error has no associated body
|
||
|
func (r *ResponseCodeSystemError) MarshalBinary() (data []byte, err error) {
|
||
|
return []byte{}, nil
|
||
|
}
|
||
|
|
||
|
// basicErrorFormatter is the default error handler for response errors.
|
||
|
// if the error is already formatted, it is directly written. Otherwise,
|
||
|
// ResponseCodeSystemError is sent to the client.
|
||
|
func basicErrorFormatter(err error) RPCError {
|
||
|
var rpcErr RPCError
|
||
|
if errors.As(err, &rpcErr) {
|
||
|
return rpcErr
|
||
|
}
|
||
|
return &ResponseCodeSystemError{}
|
||
|
}
|
||
|
|
||
|
// NFSStatusError represents an error at the NFS level.
|
||
|
type NFSStatusError struct {
|
||
|
NFSStatus
|
||
|
WrappedErr error
|
||
|
}
|
||
|
|
||
|
// Error is The wrapped error
|
||
|
func (s *NFSStatusError) Error() string {
|
||
|
message := s.NFSStatus.String()
|
||
|
if s.WrappedErr != nil {
|
||
|
message = fmt.Sprintf("%s: %v", message, s.WrappedErr)
|
||
|
}
|
||
|
return message
|
||
|
}
|
||
|
|
||
|
// Code for NFS issues are successful RPC responses
|
||
|
func (s *NFSStatusError) Code() ResponseCode {
|
||
|
return ResponseCodeSuccess
|
||
|
}
|
||
|
|
||
|
// MarshalBinary - The binary form of the code.
|
||
|
func (s *NFSStatusError) MarshalBinary() (data []byte, err error) {
|
||
|
var resp [4]byte
|
||
|
binary.BigEndian.PutUint32(resp[0:4], uint32(s.NFSStatus))
|
||
|
return resp[:], nil
|
||
|
}
|
||
|
|
||
|
// Unwrap unpacks wrapped errors
|
||
|
func (s *NFSStatusError) Unwrap() error {
|
||
|
return s.WrappedErr
|
||
|
}
|
||
|
|
||
|
// StatusErrorWithBody is an NFS error with a payload.
|
||
|
type StatusErrorWithBody struct {
|
||
|
NFSStatusError
|
||
|
Body []byte
|
||
|
}
|
||
|
|
||
|
// MarshalBinary provides the wire format of the error response
|
||
|
func (s *StatusErrorWithBody) MarshalBinary() (data []byte, err error) {
|
||
|
head, err := s.NFSStatusError.MarshalBinary()
|
||
|
return append(head, s.Body...), err
|
||
|
}
|
||
|
|
||
|
// errFormatterWithBody appends a provided body to errors
|
||
|
func errFormatterWithBody(body []byte) func(err error) RPCError {
|
||
|
return func(err error) RPCError {
|
||
|
if nerr, ok := err.(*NFSStatusError); ok {
|
||
|
return &StatusErrorWithBody{*nerr, body[:]}
|
||
|
}
|
||
|
var rErr RPCError
|
||
|
if errors.As(err, &rErr) {
|
||
|
return rErr
|
||
|
}
|
||
|
return &ResponseCodeSystemError{}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
opAttrErrorBody = [4]byte{}
|
||
|
opAttrErrorFormatter = errFormatterWithBody(opAttrErrorBody[:])
|
||
|
wccDataErrorBody = [8]byte{}
|
||
|
wccDataErrorFormatter = errFormatterWithBody(wccDataErrorBody[:])
|
||
|
)
|