2023-10-04 19:28:41 +02:00
|
|
|
//go:build unix
|
|
|
|
|
|
|
|
package nfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-01-25 17:19:21 +01:00
|
|
|
"fmt"
|
2023-10-04 19:28:41 +02:00
|
|
|
"net"
|
2024-02-06 08:53:12 +01:00
|
|
|
"strings"
|
2023-10-04 19:28:41 +02:00
|
|
|
|
|
|
|
"github.com/go-git/go-billy/v5"
|
2024-01-25 17:19:21 +01:00
|
|
|
"github.com/rclone/rclone/fs"
|
2024-06-25 12:18:39 +02:00
|
|
|
"github.com/rclone/rclone/fs/log"
|
2023-10-04 19:28:41 +02:00
|
|
|
"github.com/rclone/rclone/vfs"
|
|
|
|
"github.com/willscott/go-nfs"
|
|
|
|
)
|
|
|
|
|
2024-06-25 12:18:39 +02:00
|
|
|
// Handler returns a NFS backing that exposes a given file system in response to all mount requests.
|
|
|
|
type Handler struct {
|
|
|
|
vfs *vfs.VFS
|
2024-06-25 17:15:21 +02:00
|
|
|
opt Options
|
2024-06-25 12:18:39 +02:00
|
|
|
billyFS *FS
|
|
|
|
Cache
|
|
|
|
}
|
|
|
|
|
2024-06-24 17:32:09 +02:00
|
|
|
// NewHandler creates a handler for the provided filesystem
|
2024-08-26 11:35:28 +02:00
|
|
|
func NewHandler(ctx context.Context, vfs *vfs.VFS, opt *Options) (handler nfs.Handler, err error) {
|
|
|
|
ci := fs.GetConfig(ctx)
|
2024-06-25 17:15:21 +02:00
|
|
|
h := &Handler{
|
2024-06-25 11:19:25 +02:00
|
|
|
vfs: vfs,
|
2024-06-25 17:15:21 +02:00
|
|
|
opt: *opt,
|
2024-06-25 11:19:25 +02:00
|
|
|
billyFS: &FS{vfs: vfs},
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
2024-06-25 17:15:21 +02:00
|
|
|
h.opt.HandleLimit = h.opt.Limit()
|
|
|
|
h.Cache, err = h.getCache()
|
2024-06-25 12:18:39 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to make cache: %w", err)
|
|
|
|
}
|
2024-08-26 11:35:28 +02:00
|
|
|
var level nfs.LogLevel
|
|
|
|
switch {
|
|
|
|
case ci.LogLevel >= fs.LogLevelDebug: // Debug level, needs -vv
|
|
|
|
level = nfs.TraceLevel
|
|
|
|
case ci.LogLevel >= fs.LogLevelInfo: // Transfers, needs -v
|
|
|
|
level = nfs.InfoLevel
|
|
|
|
case ci.LogLevel >= fs.LogLevelNotice: // Normal logging, -q suppresses
|
|
|
|
level = nfs.WarnLevel
|
|
|
|
case ci.LogLevel >= fs.LogLevelError: // Error - can't be suppressed
|
|
|
|
level = nfs.ErrorLevel
|
|
|
|
default:
|
|
|
|
level = nfs.WarnLevel
|
|
|
|
}
|
|
|
|
nfs.SetLogger(&logger{level: level})
|
2024-06-25 17:15:21 +02:00
|
|
|
return h, nil
|
2023-10-04 19:28:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mount backs Mount RPC Requests, allowing for access control policies.
|
2024-06-24 17:32:09 +02:00
|
|
|
func (h *Handler) Mount(ctx context.Context, conn net.Conn, req nfs.MountRequest) (status nfs.MountStatus, hndl billy.Filesystem, auths []nfs.AuthFlavor) {
|
2023-10-04 19:28:41 +02:00
|
|
|
auths = []nfs.AuthFlavor{nfs.AuthFlavorNull}
|
2024-06-25 11:19:25 +02:00
|
|
|
return nfs.MountStatusOk, h.billyFS, auths
|
2023-10-04 19:28:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Change provides an interface for updating file attributes.
|
2024-06-24 17:32:09 +02:00
|
|
|
func (h *Handler) Change(fs billy.Filesystem) billy.Change {
|
2023-10-04 19:28:41 +02:00
|
|
|
if c, ok := fs.(billy.Change); ok {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FSStat provides information about a filesystem.
|
2024-06-24 17:32:09 +02:00
|
|
|
func (h *Handler) FSStat(ctx context.Context, f billy.Filesystem, s *nfs.FSStat) error {
|
2023-10-04 19:28:41 +02:00
|
|
|
total, _, free := h.vfs.Statfs()
|
|
|
|
s.TotalSize = uint64(total)
|
|
|
|
s.FreeSize = uint64(free)
|
|
|
|
s.AvailableSize = uint64(free)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-25 12:18:39 +02:00
|
|
|
// ToHandle takes a file and represents it with an opaque handle to reference it.
|
|
|
|
// In stateless nfs (when it's serving a unix fs) this can be the device + inode
|
|
|
|
// but we can generalize with a stateful local cache of handed out IDs.
|
|
|
|
func (h *Handler) ToHandle(f billy.Filesystem, s []string) (b []byte) {
|
|
|
|
defer log.Trace("nfs", "path=%q", s)("handle=%X", &b)
|
|
|
|
return h.Cache.ToHandle(f, s)
|
2023-10-04 19:28:41 +02:00
|
|
|
}
|
|
|
|
|
2024-06-25 12:18:39 +02:00
|
|
|
// FromHandle converts from an opaque handle to the file it represents
|
|
|
|
func (h *Handler) FromHandle(b []byte) (f billy.Filesystem, s []string, err error) {
|
|
|
|
defer log.Trace("nfs", "handle=%X", b)("path=%q, err=%v", &s, &err)
|
|
|
|
return h.Cache.FromHandle(b)
|
2023-10-04 19:28:41 +02:00
|
|
|
}
|
|
|
|
|
2024-06-25 12:18:39 +02:00
|
|
|
// HandleLimit exports how many file handles can be safely stored by this cache.
|
2024-06-24 17:32:09 +02:00
|
|
|
func (h *Handler) HandleLimit() int {
|
2024-06-25 12:18:39 +02:00
|
|
|
return h.Cache.HandleLimit()
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-06-25 17:15:21 +02:00
|
|
|
// InvalidateHandle invalidates the handle passed - used on rename and delete
|
2024-06-25 12:18:39 +02:00
|
|
|
func (h *Handler) InvalidateHandle(f billy.Filesystem, b []byte) (err error) {
|
|
|
|
defer log.Trace("nfs", "handle=%X", b)("err=%v", &err)
|
|
|
|
return h.Cache.InvalidateHandle(f, b)
|
2023-10-04 19:28:41 +02:00
|
|
|
}
|
2024-01-25 17:19:21 +01: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 08:53:12 +01: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-08-26 11:35:28 +02:00
|
|
|
// logger handles go-nfs logs and reroutes them to rclone's logging system
|
|
|
|
type logger struct {
|
|
|
|
level nfs.LogLevel
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// logPrint intercepts go-nfs logs and calls rclone's log system instead
|
|
|
|
func (l *logger) logPrint(level fs.LogLevel, args ...interface{}) {
|
|
|
|
fs.LogPrintf(level, "nfs", "%s", fmt.Sprint(args...))
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// logPrintf intercepts go-nfs logs and calls rclone's log system instead
|
|
|
|
func (l *logger) logPrintf(level fs.LogLevel, format string, args ...interface{}) {
|
|
|
|
fs.LogPrintf(level, "nfs", format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Debug reroutes go-nfs Debug messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Debug(args ...interface{}) {
|
|
|
|
if l.level < nfs.DebugLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelDebug, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Debugf reroutes go-nfs Debugf messages to logPrintf
|
|
|
|
func (l *logger) Debugf(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.DebugLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelDebug, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Error reroutes go-nfs Error messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Error(args ...interface{}) {
|
|
|
|
if l.level < nfs.ErrorLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelError, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Errorf reroutes go-nfs Errorf messages to logPrintf
|
|
|
|
func (l *logger) Errorf(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.ErrorLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelError, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fatal reroutes go-nfs Fatal messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Fatal(args ...interface{}) {
|
|
|
|
if l.level < nfs.FatalLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelError, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Fatalf reroutes go-nfs Fatalf messages to logPrintf
|
|
|
|
func (l *logger) Fatalf(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.FatalLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelError, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetLevel returns the nfs.LogLevel
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) GetLevel() nfs.LogLevel {
|
|
|
|
return l.level
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Info reroutes go-nfs Info messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Info(args ...interface{}) {
|
|
|
|
if l.level < nfs.InfoLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelInfo, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Infof reroutes go-nfs Infof messages to logPrintf
|
|
|
|
func (l *logger) Infof(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.InfoLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelInfo, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Panic reroutes go-nfs Panic messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Panic(args ...interface{}) {
|
|
|
|
if l.level < nfs.PanicLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelError, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Panicf reroutes go-nfs Panicf messages to logPrintf
|
|
|
|
func (l *logger) Panicf(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.PanicLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelError, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParseLevel parses the nfs.LogLevel
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) ParseLevel(level string) (nfs.LogLevel, error) {
|
2024-01-25 17:19:21 +01:00
|
|
|
return nfs.Log.ParseLevel(level)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print reroutes go-nfs Print messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Print(args ...interface{}) {
|
|
|
|
if l.level < nfs.InfoLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelInfo, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Printf reroutes go-nfs Printf messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Printf(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.InfoLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelInfo, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetLevel sets the nfs.LogLevel
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) SetLevel(level nfs.LogLevel) {
|
|
|
|
l.level = level
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Trace reroutes go-nfs Trace messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Trace(args ...interface{}) {
|
|
|
|
if l.level < nfs.DebugLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelDebug, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Tracef reroutes go-nfs Tracef messages to logPrintf
|
|
|
|
func (l *logger) Tracef(format string, args ...interface{}) {
|
|
|
|
// FIXME BODGE ... the real fix is probably https://github.com/willscott/go-nfs/pull/28
|
|
|
|
// This comes from `Log.Tracef("request: %v", w.req)` in conn.go
|
|
|
|
// DEBUG : nfs: request: RPC #3285799202 (mount.Umnt)
|
|
|
|
argsS := fmt.Sprint(args...)
|
|
|
|
if strings.Contains(argsS, "mount.Umnt") {
|
|
|
|
onUnmount()
|
|
|
|
}
|
|
|
|
if l.level < nfs.DebugLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelDebug, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Warn reroutes go-nfs Warn messages to Intercept
|
2024-08-26 11:35:28 +02:00
|
|
|
func (l *logger) Warn(args ...interface{}) {
|
|
|
|
if l.level < nfs.WarnLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrint(fs.LogLevelNotice, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|
|
|
|
|
2024-08-26 11:35:28 +02:00
|
|
|
// Warnf reroutes go-nfs Warnf messages to logPrintf
|
|
|
|
func (l *logger) Warnf(format string, args ...interface{}) {
|
|
|
|
if l.level < nfs.WarnLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l.logPrintf(fs.LogLevelNotice, format, args...)
|
2024-01-25 17:19:21 +01:00
|
|
|
}
|