nfs

package module
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2024 License: Apache-2.0 Imports: 25 Imported by: 0

README

Golang Network File Server

NFSv3 protocol implementation in pure Golang.

Current Status:

  • Minimally tested
  • Mounts, read-only and read-write support

Usage

The most interesting demo is currently in example/osview.

Start the server go run ./example/osview ..

The local folder at . will be the initial view in the mount. mutations to metadata or contents will be stored purely in memory and not written back to the OS. When run, this demo will print the port it is listening on.

The mount can be accessed using a command similar to mount -o port=<n>,mountport=<n> -t nfs localhost:/mount <mountpoint> (For Mac users)

or

mount -o port=<n>,mountport=<n>,nfsvers=3,noacl,tcp -t nfs localhost:/mount <mountpoint> (For Linux users)

API

The NFS server runs on a net.Listener to export a file system to NFS clients. Usage is structured similarly to many other golang network servers.

package main

import (
	"fmt"
	"log"
	"net"

	"github.com/go-git/go-billy/v5/memfs"
	nfs "github.com/willscott/go-nfs"
	nfshelper "github.com/willscott/go-nfs/helpers"
)

func main() {
	listener, err := net.Listen("tcp", ":0")
	panicOnErr(err, "starting TCP listener")
	fmt.Printf("Server running at %s\n", listener.Addr())
	mem := memfs.New()
	f, err := mem.Create("hello.txt")
	panicOnErr(err, "creating file")
	_, err = f.Write([]byte("hello world"))
	panicOnErr(err, "writing data")
	f.Close()
	handler := nfshelper.NewNullAuthHandler(mem)
	cacheHelper := nfshelper.NewCachingHandler(handler, 1)
	panicOnErr(nfs.Serve(listener, cacheHelper), "serving nfs")
}

func panicOnErr(err error, desc ...interface{}) {
	if err == nil {
		return
	}
	log.Println(desc...)
	log.Panicln(err)
}

Notes

  • Ports are typically determined through portmap. The need for running portmap (which is the only part that needs a privileged listening port) can be avoided through specific mount options. e.g. mount -o port=n,mountport=n -t nfs host:/mount /localmount

  • This server currently uses billy to provide a file system abstraction layer. There are some edges of the NFS protocol which do not translate to this abstraction.

    • NFS expects access to an inode or equivalent unique identifier to reference files in a file system. These are considered opaque identifiers here, which means they will not work as expected in cases of hard linking.
    • The billy abstraction layer does not extend to exposing uid and gid ownership of files. If ownership is important to your file system, you will need to ensure that the os.FileInfo meets additional constraints. In particular, the Sys() escape hatch is queried by this library, and if your file system populates a syscall.Stat_t concrete struct, the ownership specified in that object will be used.
  • Relevant RFCS: 5531 - RPC protocol, 1813 - NFSv3, 1094 - NFS

Documentation

Index

Constants

View Source
const (
	// FSInfoPropertyLink does the FS support hard links?
	FSInfoPropertyLink = 0x0001
	// FSInfoPropertySymlink does the FS support soft links?
	FSInfoPropertySymlink = 0x0002
	// FSInfoPropertyHomogeneous does the FS need PATHCONF calls for each file
	FSInfoPropertyHomogeneous = 0x0008
	// FSInfoPropertyCanSetTime can the FS support setting access/mod times?
	FSInfoPropertyCanSetTime = 0x0010
)
View Source
const (
	FTYPE_NF3REG  nfs_ftype = 1
	FTYPE_NF3DIR  nfs_ftype = 2
	FTYPE_NF3BLK  nfs_ftype = 3
	FTYPE_NF3CHR  nfs_ftype = 4
	FTYPE_NF3LNK  nfs_ftype = 5
	FTYPE_NF3SOCK nfs_ftype = 6
	FTYPE_NF3FIFO nfs_ftype = 7
)
View Source
const CheckRead = 1 << 15

CheckRead is a size where - if a request to read is larger than this, the server will stat the file to learn it's actual size before allocating a buffer to read into.

View Source
const FHSize = 64

FHSize is the maximum size of a FileHandle

View Source
const MNTNameLen = 255

MNTNameLen is the maximum size of a mount name

View Source
const MaxRead = 1 << 24

MaxRead is the advertised largest buffer the server is willing to read

View Source
const MntPathLen = 1024

MntPathLen is the maximum size of a mount path

View Source
const PathNameMax = 255

PathNameMax is the maximum length for a file name

Variables

View Source
var (
	// ErrInputInvalid is returned when input cannot be parsed
	ErrInputInvalid = errors.New("invalid input")
	// ErrAlreadySent is returned when writing a header/status multiple times
	ErrAlreadySent = errors.New("response already started")
)

Functions

func RegisterMessageHandler

func RegisterMessageHandler(protocol uint32, proc uint32, handler HandleFunc) error

RegisterMessageHandler registers a handler for a specific XDR procedure.

func Serve

func Serve(l net.Listener, handler Handler) error

Serve is a singleton listener paralleling http.Serve

func SetLogger

func SetLogger(logger Logger)

func WritePostOpAttrs

func WritePostOpAttrs(writer io.Writer, post *FileAttribute) error

WritePostOpAttrs writes the `post_op_attr` representation of a files attributes

func WriteWcc

func WriteWcc(writer io.Writer, pre *FileCacheAttribute, post *FileAttribute) error

WriteWcc writes the `wcc_data` representation of an object.

Types

type AuthError

type AuthError struct {
	AuthStat
}

AuthError is an RPCError

func (*AuthError) Code

func (a *AuthError) Code() ResponseCode

Code for AuthErrors is ResponseCodeAuthError

func (*AuthError) Error

func (a *AuthError) Error() string

Error is a textual representaiton of the auth error. From the RFC

func (*AuthError) MarshalBinary

func (a *AuthError) MarshalBinary() (data []byte, err error)

MarshalBinary sends the specific auth status

type AuthFlavor

type AuthFlavor uint32

AuthFlavor is a form of authentication, per rfc1057 section 7.2

const (
	AuthFlavorNull  AuthFlavor = 0
	AuthFlavorUnix  AuthFlavor = 1
	AuthFlavorShort AuthFlavor = 2
	AuthFlavorDES   AuthFlavor = 3
)

AuthFlavor Codes

type AuthStat

type AuthStat uint32

AuthStat is an enumeration of why authentication ahs failed

const (
	AuthStatOK AuthStat = iota
	AuthStatBadCred
	AuthStatRejectedCred
	AuthStatBadVerifier
	AuthStatRejectedVerfier
	AuthStatTooWeak
	AuthStatInvalidResponse
	AuthStatFailed
	AuthStatKerbGeneric
	AuthStatTimeExpire
	AuthStatTktFile
	AuthStatDecode
	AuthStatNetAddr
	AuthStatRPCGSSCredProblem
	AuthStatRPCGSSCTXProblem
)

AuthStat Codes

type CachingHandler

type CachingHandler interface {
	VerifierFor(path string, contents []fs.FileInfo) uint64

	// fs.FileInfo needs to be sorted by Name(), nil in case of a cache-miss
	DataForVerifier(path string, verifier uint64) []fs.FileInfo
}

CachingHandler represents the optional caching work that a user may wish to over-ride with their own implementations, but which can be otherwise provided through defaults.

type DefaultLogger

type DefaultLogger struct {
	Level LogLevel
}

func (*DefaultLogger) Debug

func (l *DefaultLogger) Debug(args ...interface{})

func (*DefaultLogger) Debugf

func (l *DefaultLogger) Debugf(format string, args ...interface{})

func (*DefaultLogger) Error

func (l *DefaultLogger) Error(args ...interface{})

func (*DefaultLogger) Errorf

func (l *DefaultLogger) Errorf(format string, args ...interface{})

func (*DefaultLogger) Fatal

func (l *DefaultLogger) Fatal(args ...interface{})

func (*DefaultLogger) Fatalf

func (l *DefaultLogger) Fatalf(format string, args ...interface{})

func (*DefaultLogger) GetLevel

func (l *DefaultLogger) GetLevel() LogLevel

func (*DefaultLogger) Info

func (l *DefaultLogger) Info(args ...interface{})

func (*DefaultLogger) Infof

func (l *DefaultLogger) Infof(format string, args ...interface{})

func (*DefaultLogger) Panic

func (l *DefaultLogger) Panic(args ...interface{})

func (*DefaultLogger) Panicf

func (l *DefaultLogger) Panicf(format string, args ...interface{})

func (*DefaultLogger) ParseLevel

func (l *DefaultLogger) ParseLevel(level string) (LogLevel, error)

func (*DefaultLogger) Print

func (l *DefaultLogger) Print(args ...interface{})

func (*DefaultLogger) Printf

func (l *DefaultLogger) Printf(format string, args ...interface{})

func (*DefaultLogger) SetLevel

func (l *DefaultLogger) SetLevel(level LogLevel)

func (*DefaultLogger) Trace

func (l *DefaultLogger) Trace(args ...interface{})

func (*DefaultLogger) Tracef

func (l *DefaultLogger) Tracef(format string, args ...interface{})

func (*DefaultLogger) Warn

func (l *DefaultLogger) Warn(args ...interface{})

func (*DefaultLogger) Warnf

func (l *DefaultLogger) Warnf(format string, args ...interface{})

type DirOpArg

type DirOpArg struct {
	Handle   []byte
	Filename []byte
}

DirOpArg is a common serialization used for referencing an object in a directory

type FSStat

type FSStat struct {
	TotalSize      uint64
	FreeSize       uint64
	AvailableSize  uint64
	TotalFiles     uint64
	FreeFiles      uint64
	AvailableFiles uint64
	// CacheHint is called "invarsec" in the nfs standard
	CacheHint time.Duration
}

FSStat returns metadata about a file system

type FileAttribute

type FileAttribute struct {
	Type                FileType
	FileMode            uint32
	Nlink               uint32
	UID                 uint32
	GID                 uint32
	Filesize            uint64
	Used                uint64
	SpecData            [2]uint32
	FSID                uint64
	Fileid              uint64
	Atime, Mtime, Ctime FileTime
}

FileAttribute holds metadata about a filesystem object

func ToFileAttribute

func ToFileAttribute(info os.FileInfo, filePath string) *FileAttribute

ToFileAttribute creates an NFS fattr3 struct from an OS.FileInfo

func (FileAttribute) AsCache

func (f FileAttribute) AsCache() *FileCacheAttribute

AsCache provides the wcc view of the file attributes

func (*FileAttribute) Mode

func (f *FileAttribute) Mode() os.FileMode

Mode provides the OS interpreted mode of the file attributes

type FileCacheAttribute

type FileCacheAttribute struct {
	Filesize     uint64
	Mtime, Ctime FileTime
}

FileCacheAttribute is the subset of FileAttribute used by wcc_attr

type FileHandle

type FileHandle []byte

FileHandle maps to a fhandle3

type FileTime

type FileTime struct {
	Seconds  uint32
	Nseconds uint32
}

FileTime is the NFS wire time format This is equivalent to go-nfs-client/nfs.NFS3Time

func ToNFSTime

func ToNFSTime(t time.Time) FileTime

ToNFSTime generates the nfs 64bit time format from a golang time.

func (FileTime) EqualTimespec

func (t FileTime) EqualTimespec(sec int64, nsec int64) bool

EqualTimespec returns if this time is equal to a local time spec

func (FileTime) Native

func (t FileTime) Native() *time.Time

Native generates a golang time from an nfs time spec

type FileType

type FileType uint32

FileType represents a NFS File Type

const (
	FileTypeRegular FileType = iota + 1
	FileTypeDirectory
	FileTypeBlock
	FileTypeCharacter
	FileTypeLink
	FileTypeSocket
	FileTypeFIFO
)

Enumeration of NFS FileTypes

func (FileType) String

func (f FileType) String() string

type HandleFunc

type HandleFunc func(ctx context.Context, w *response, userHandler Handler) error

HandleFunc represents a handler for a specific protocol message.

type Handler

type Handler interface {
	Mount(context.Context, net.Conn, MountRequest) (MountStatus, billy.Filesystem, []AuthFlavor)

	// Change can return 'nil' if filesystem is read-only
	// If the returned value can be cast to `UnixChange`, mknod and link RPCs will be available.
	Change(billy.Filesystem) billy.Change

	// Fill in information about a file system's free space.
	FSStat(context.Context, billy.Filesystem, *FSStat) error

	// represent file objects as opaque references
	// Can be safely implemented via helpers/cachinghandler.
	ToHandle(fs billy.Filesystem, path []string) []byte
	FromHandle(fh []byte) (billy.Filesystem, []string, error)
	InvalidateHandle(billy.Filesystem, []byte) error

	// How many handles can be safely maintained by the handler.
	HandleLimit() int
}

Handler represents the interface of the file system / vfs being exposed over NFS

type LogLevel

type LogLevel int
const (
	PanicLevel LogLevel = iota
	FatalLevel
	ErrorLevel
	WarnLevel
	InfoLevel
	DebugLevel
	TraceLevel
)

type Logger

type Logger interface {
	SetLevel(level LogLevel)
	GetLevel() LogLevel
	ParseLevel(level string) (LogLevel, error)

	Panic(args ...interface{})
	Fatal(args ...interface{})
	Error(args ...interface{})
	Warn(args ...interface{})
	Info(args ...interface{})
	Debug(args ...interface{})
	Trace(args ...interface{})
	Print(args ...interface{})

	Panicf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
	Warnf(format string, args ...interface{})
	Infof(format string, args ...interface{})
	Debugf(format string, args ...interface{})
	Tracef(format string, args ...interface{})
	Printf(format string, args ...interface{})
}
var (
	Log Logger = &DefaultLogger{}
)

type MountProcedure

type MountProcedure uint32

MountProcedure is the valid RPC calls for the mount service.

const (
	MountProcNull MountProcedure = iota
	MountProcMount
	MountProcDump
	MountProcUmnt
	MountProcUmntAll
	MountProcExport
)

MountProcedure Codes

func (MountProcedure) String

func (m MountProcedure) String() string

type MountRequest

type MountRequest struct {
	rpc.Header
	Dirpath []byte
}

MountRequest contains the format of a client request to open a mount.

type MountResponse

type MountResponse struct {
	rpc.Header
	FileHandle
	AuthFlavors []int
}

MountResponse is the server's response with status `MountStatusOk`

type MountStatus

type MountStatus uint32

MountStatus defines the response to the Mount Procedure

const (
	MountStatusOk             MountStatus = 0
	MountStatusErrPerm        MountStatus = 1
	MountStatusErrNoEnt       MountStatus = 2
	MountStatusErrIO          MountStatus = 5
	MountStatusErrAcces       MountStatus = 13
	MountStatusErrNotDir      MountStatus = 20
	MountStatusErrInval       MountStatus = 22
	MountStatusErrNameTooLong MountStatus = 63
	MountStatusErrNotSupp     MountStatus = 10004
	MountStatusErrServerFault MountStatus = 10006
)

MountStatus Codes

type NFSProcedure

type NFSProcedure uint32

NFSProcedure is the valid RPC calls for the nfs service.

const (
	NFSProcedureNull NFSProcedure = iota
	NFSProcedureGetAttr
	NFSProcedureSetAttr
	NFSProcedureLookup
	NFSProcedureAccess
	NFSProcedureReadlink
	NFSProcedureRead
	NFSProcedureWrite
	NFSProcedureCreate
	NFSProcedureMkDir
	NFSProcedureSymlink
	NFSProcedureMkNod
	NFSProcedureRemove
	NFSProcedureRmDir
	NFSProcedureRename
	NFSProcedureLink
	NFSProcedureReadDir
	NFSProcedureReadDirPlus
	NFSProcedureFSStat
	NFSProcedureFSInfo
	NFSProcedurePathConf
	NFSProcedureCommit
)

NfsProcedure Codes

func (NFSProcedure) String

func (n NFSProcedure) String() string

type NFSStatus

type NFSStatus uint32

NFSStatus (nfsstat3) is a result code for nfs rpc calls

const (
	NFSStatusOk          NFSStatus = 0
	NFSStatusPerm        NFSStatus = 1
	NFSStatusNoEnt       NFSStatus = 2
	NFSStatusIO          NFSStatus = 5
	NFSStatusNXIO        NFSStatus = 6
	NFSStatusAccess      NFSStatus = 13
	NFSStatusExist       NFSStatus = 17
	NFSStatusXDev        NFSStatus = 18
	NFSStatusNoDev       NFSStatus = 19
	NFSStatusNotDir      NFSStatus = 20
	NFSStatusIsDir       NFSStatus = 21
	NFSStatusInval       NFSStatus = 22
	NFSStatusFBig        NFSStatus = 27
	NFSStatusNoSPC       NFSStatus = 28
	NFSStatusROFS        NFSStatus = 30
	NFSStatusMlink       NFSStatus = 31
	NFSStatusNameTooLong NFSStatus = 63
	NFSStatusNotEmpty    NFSStatus = 66
	NFSStatusDQuot       NFSStatus = 69
	NFSStatusStale       NFSStatus = 70
	NFSStatusRemote      NFSStatus = 71
	NFSStatusBadHandle   NFSStatus = 10001
	NFSStatusNotSync     NFSStatus = 10002
	NFSStatusBadCookie   NFSStatus = 10003
	NFSStatusNotSupp     NFSStatus = 10004
	NFSStatusTooSmall    NFSStatus = 10005
	NFSStatusServerFault NFSStatus = 10006
	NFSStatusBadType     NFSStatus = 10007
	NFSStatusJukebox     NFSStatus = 10008
)

NFSStatus codes

func (NFSStatus) String

func (s NFSStatus) String() string

type NFSStatusError

type NFSStatusError struct {
	NFSStatus
	WrappedErr error
}

NFSStatusError represents an error at the NFS level.

func (*NFSStatusError) Code

func (s *NFSStatusError) Code() ResponseCode

Code for NFS issues are successful RPC responses

func (*NFSStatusError) Error

func (s *NFSStatusError) Error() string

Error is The wrapped error

func (*NFSStatusError) MarshalBinary

func (s *NFSStatusError) MarshalBinary() (data []byte, err error)

MarshalBinary - The binary form of the code.

func (*NFSStatusError) Unwrap

func (s *NFSStatusError) Unwrap() error

Unwrap unpacks wrapped errors

type RPCError

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
}

RPCError provides the error interface for errors thrown by procedures to be transmitted over the XDR RPC channel

type RPCMismatchError

type RPCMismatchError struct {
	Low  uint32
	High uint32
}

RPCMismatchError is an RPCError

func (*RPCMismatchError) Code

func (r *RPCMismatchError) Code() ResponseCode

Code for RPCMismatchError is ResponseCodeRPCMismatch

func (*RPCMismatchError) Error

func (r *RPCMismatchError) Error() string

func (*RPCMismatchError) MarshalBinary

func (r *RPCMismatchError) MarshalBinary() (data []byte, err error)

MarshalBinary sends the specific rpc mismatch range

type ResponseCode

type ResponseCode uint32

ResponseCode is a combination of accept_stat and reject_stat.

const (
	ResponseCodeSuccess ResponseCode = iota
	ResponseCodeProgUnavailable
	ResponseCodeProcUnavailable
	ResponseCodeGarbageArgs
	ResponseCodeSystemErr
	ResponseCodeRPCMismatch
	ResponseCodeAuthError
)

ResponseCode Codes

type ResponseCodeProcUnavailableError

type ResponseCodeProcUnavailableError struct {
}

ResponseCodeProcUnavailableError is an RPCError

func (*ResponseCodeProcUnavailableError) Code

Code for ResponseCodeProcUnavailableError

func (*ResponseCodeProcUnavailableError) Error

func (*ResponseCodeProcUnavailableError) MarshalBinary

func (r *ResponseCodeProcUnavailableError) MarshalBinary() (data []byte, err error)

MarshalBinary - this error has no associated body

type ResponseCodeSystemError

type ResponseCodeSystemError struct {
}

ResponseCodeSystemError is an RPCError

func (*ResponseCodeSystemError) Code

Code for ResponseCodeSystemError

func (*ResponseCodeSystemError) Error

func (r *ResponseCodeSystemError) Error() string

func (*ResponseCodeSystemError) MarshalBinary

func (r *ResponseCodeSystemError) MarshalBinary() (data []byte, err error)

MarshalBinary - this error has no associated body

type Server

type Server struct {
	Handler
	ID [8]byte
	context.Context
}

Server is a handle to the listening NFS server.

func (*Server) Serve

func (s *Server) Serve(l net.Listener) error

Serve listens on the provided listener port for incoming client requests.

type SetFileAttributes

type SetFileAttributes struct {
	SetMode  *uint32
	SetUID   *uint32
	SetGID   *uint32
	SetSize  *uint64
	SetAtime *time.Time
	SetMtime *time.Time
}

SetFileAttributes represents a command to update some metadata about a file.

func ReadSetFileAttributes

func ReadSetFileAttributes(r io.Reader) (*SetFileAttributes, error)

ReadSetFileAttributes reads an sattr3 xdr stream into a go struct.

func (*SetFileAttributes) Apply

func (s *SetFileAttributes) Apply(changer billy.Change, fs billy.Filesystem, file string) error

Apply uses a `Change` implementation to set defined attributes on a provided file.

func (*SetFileAttributes) Mode

func (s *SetFileAttributes) Mode(def os.FileMode) os.FileMode

Mode returns a mode if specified or the provided default mode.

type StatusErrorWithBody

type StatusErrorWithBody struct {
	NFSStatusError
	Body []byte
}

StatusErrorWithBody is an NFS error with a payload.

func (*StatusErrorWithBody) MarshalBinary

func (s *StatusErrorWithBody) MarshalBinary() (data []byte, err error)

MarshalBinary provides the wire format of the error response

type UnixChange

type UnixChange interface {
	billy.Change
	Mknod(path string, mode uint32, major uint32, minor uint32) error
	Mkfifo(path string, mode uint32) error
	Socket(path string) error
	Link(path string, link string) error
}

UnixChange extends the billy `Change` interface with support for special files.

Directories

Path Synopsis
example
memfs
Package memfs is a variant of "github.com/go-git/go-billy/v5/memfs" with stable mtimes for items.
Package memfs is a variant of "github.com/go-git/go-billy/v5/memfs" with stable mtimes for items.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL