2023-10-04 17:28:41 +00:00
|
|
|
//go:build unix
|
|
|
|
// +build unix
|
|
|
|
|
|
|
|
package nfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-01-25 16:19:21 +00:00
|
|
|
"fmt"
|
2023-10-04 17:28:41 +00:00
|
|
|
"net"
|
2024-02-06 07:53:12 +00:00
|
|
|
"strings"
|
2023-10-04 17:28:41 +00:00
|
|
|
|
|
|
|
"github.com/go-git/go-billy/v5"
|
2024-01-25 16:19:21 +00:00
|
|
|
"github.com/rclone/rclone/fs"
|
2023-10-04 17:28:41 +00:00
|
|
|
"github.com/rclone/rclone/vfs"
|
|
|
|
"github.com/willscott/go-nfs"
|
|
|
|
nfshelper "github.com/willscott/go-nfs/helpers"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewBackendAuthHandler creates a handler for the provided filesystem
|
2024-01-25 16:19:21 +00:00
|
|
|
func NewBackendAuthHandler(vfs *vfs.VFS, opt *Options) nfs.Handler {
|
|
|
|
handler := &BackendAuthHandler{
|
|
|
|
vfs: vfs,
|
|
|
|
opt: opt,
|
|
|
|
}
|
|
|
|
handler.opt.HandleLimit = handler.opt.Limit()
|
|
|
|
handler.cache = cacheHelper(handler, handler.HandleLimit())
|
|
|
|
return handler
|
2023-10-04 17:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BackendAuthHandler returns a NFS backing that exposes a given file system in response to all mount requests.
|
|
|
|
type BackendAuthHandler struct {
|
2024-01-25 16:19:21 +00:00
|
|
|
vfs *vfs.VFS
|
|
|
|
opt *Options
|
|
|
|
cache nfs.Handler
|
2023-10-04 17:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mount backs Mount RPC Requests, allowing for access control policies.
|
|
|
|
func (h *BackendAuthHandler) Mount(ctx context.Context, conn net.Conn, req nfs.MountRequest) (status nfs.MountStatus, hndl billy.Filesystem, auths []nfs.AuthFlavor) {
|
|
|
|
status = nfs.MountStatusOk
|
|
|
|
hndl = &FS{vfs: h.vfs}
|
|
|
|
auths = []nfs.AuthFlavor{nfs.AuthFlavorNull}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change provides an interface for updating file attributes.
|
|
|
|
func (h *BackendAuthHandler) Change(fs billy.Filesystem) billy.Change {
|
|
|
|
if c, ok := fs.(billy.Change); ok {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FSStat provides information about a filesystem.
|
|
|
|
func (h *BackendAuthHandler) FSStat(ctx context.Context, f billy.Filesystem, s *nfs.FSStat) error {
|
|
|
|
total, _, free := h.vfs.Statfs()
|
|
|
|
s.TotalSize = uint64(total)
|
|
|
|
s.FreeSize = uint64(free)
|
|
|
|
s.AvailableSize = uint64(free)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToHandle handled by CachingHandler
|
|
|
|
func (h *BackendAuthHandler) ToHandle(f billy.Filesystem, s []string) []byte {
|
2024-01-25 16:19:21 +00:00
|
|
|
return h.cache.ToHandle(f, s)
|
2023-10-04 17:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FromHandle handled by CachingHandler
|
2024-01-25 16:19:21 +00:00
|
|
|
func (h *BackendAuthHandler) FromHandle(b []byte) (billy.Filesystem, []string, error) {
|
|
|
|
return h.cache.FromHandle(b)
|
2023-10-04 17:28:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// HandleLimit handled by cachingHandler
|
|
|
|
func (h *BackendAuthHandler) HandleLimit() int {
|
2024-01-25 16:19:21 +00:00
|
|
|
return h.opt.HandleLimit
|
2023-10-04 17:28:41 +00:00
|
|
|
}
|
|
|
|
|
2024-01-13 16:32:42 +00:00
|
|
|
// InvalidateHandle is called on removes or renames
|
|
|
|
func (h *BackendAuthHandler) InvalidateHandle(billy.Filesystem, []byte) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-25 16:19:21 +00:00
|
|
|
func newHandler(vfs *vfs.VFS, opt *Options) nfs.Handler {
|
|
|
|
handler := NewBackendAuthHandler(vfs, opt)
|
|
|
|
nfs.SetLogger(&LogIntercepter{Level: nfs.DebugLevel})
|
|
|
|
return handler
|
|
|
|
}
|
|
|
|
|
|
|
|
func cacheHelper(handler nfs.Handler, limit int) nfs.Handler {
|
|
|
|
cacheHelper := nfshelper.NewCachingHandler(handler, limit)
|
2023-10-04 17:28:41 +00:00
|
|
|
return cacheHelper
|
|
|
|
}
|
2024-01-25 16:19:21 +00:00
|
|
|
|
|
|
|
// Limit overrides the --nfs-cache-handle-limit value if out-of-range
|
|
|
|
func (o *Options) Limit() int {
|
|
|
|
if o.HandleLimit < 0 {
|
|
|
|
return 1000000
|
|
|
|
}
|
|
|
|
if o.HandleLimit <= 5 {
|
|
|
|
return 5
|
|
|
|
}
|
|
|
|
return o.HandleLimit
|
|
|
|
}
|
|
|
|
|
2024-02-06 07:53:12 +00:00
|
|
|
// OnUnmountFunc registers a function to call when externally unmounted
|
|
|
|
var OnUnmountFunc func()
|
|
|
|
|
|
|
|
func onUnmount() {
|
|
|
|
fs.Infof(nil, "unmount detected")
|
|
|
|
if OnUnmountFunc != nil {
|
|
|
|
OnUnmountFunc()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-25 16:19:21 +00:00
|
|
|
// LogIntercepter intercepts noisy go-nfs logs and reroutes them to DEBUG
|
|
|
|
type LogIntercepter struct {
|
|
|
|
Level nfs.LogLevel
|
|
|
|
}
|
|
|
|
|
|
|
|
// Intercept intercepts go-nfs logs and calls fs.Debugf instead
|
|
|
|
func (l *LogIntercepter) Intercept(args ...interface{}) {
|
|
|
|
args = append([]interface{}{"[NFS DEBUG] "}, args...)
|
|
|
|
argsS := fmt.Sprint(args...)
|
|
|
|
fs.Debugf(nil, "%v", argsS)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interceptf intercepts go-nfs logs and calls fs.Debugf instead
|
|
|
|
func (l *LogIntercepter) Interceptf(format string, args ...interface{}) {
|
2024-02-06 07:53:12 +00:00
|
|
|
argsS := fmt.Sprint(args...)
|
|
|
|
// bit of a workaround... the real fix is probably https://github.com/willscott/go-nfs/pull/28
|
|
|
|
if strings.Contains(argsS, "mount.Umnt") {
|
|
|
|
onUnmount()
|
|
|
|
}
|
|
|
|
|
2024-01-25 16:19:21 +00:00
|
|
|
fs.Debugf(nil, "[NFS DEBUG] "+format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debug reroutes go-nfs Debug messages to Intercept
|
|
|
|
func (l *LogIntercepter) Debug(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debugf reroutes go-nfs Debugf messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Debugf(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error reroutes go-nfs Error messages to Intercept
|
|
|
|
func (l *LogIntercepter) Error(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Errorf reroutes go-nfs Errorf messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Errorf(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fatal reroutes go-nfs Fatal messages to Intercept
|
|
|
|
func (l *LogIntercepter) Fatal(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fatalf reroutes go-nfs Fatalf messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Fatalf(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLevel returns the nfs.LogLevel
|
|
|
|
func (l *LogIntercepter) GetLevel() nfs.LogLevel {
|
|
|
|
return l.Level
|
|
|
|
}
|
|
|
|
|
|
|
|
// Info reroutes go-nfs Info messages to Intercept
|
|
|
|
func (l *LogIntercepter) Info(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Infof reroutes go-nfs Infof messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Infof(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Panic reroutes go-nfs Panic messages to Intercept
|
|
|
|
func (l *LogIntercepter) Panic(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Panicf reroutes go-nfs Panicf messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Panicf(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseLevel parses the nfs.LogLevel
|
|
|
|
func (l *LogIntercepter) ParseLevel(level string) (nfs.LogLevel, error) {
|
|
|
|
return nfs.Log.ParseLevel(level)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print reroutes go-nfs Print messages to Intercept
|
|
|
|
func (l *LogIntercepter) Print(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Printf reroutes go-nfs Printf messages to Intercept
|
|
|
|
func (l *LogIntercepter) Printf(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLevel sets the nfs.LogLevel
|
|
|
|
func (l *LogIntercepter) SetLevel(level nfs.LogLevel) {
|
|
|
|
l.Level = level
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trace reroutes go-nfs Trace messages to Intercept
|
|
|
|
func (l *LogIntercepter) Trace(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tracef reroutes go-nfs Tracef messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Tracef(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warn reroutes go-nfs Warn messages to Intercept
|
|
|
|
func (l *LogIntercepter) Warn(args ...interface{}) {
|
|
|
|
l.Intercept(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warnf reroutes go-nfs Warnf messages to Interceptf
|
|
|
|
func (l *LogIntercepter) Warnf(format string, args ...interface{}) {
|
|
|
|
l.Interceptf(format, args...)
|
|
|
|
}
|