mirror of
https://github.com/rclone/rclone.git
synced 2024-12-30 00:43:41 +08:00
1a8b7662e7
Some checks failed
Docker beta build / Build image job (push) Has been cancelled
Before this we ignored the logging levels and logged everything as debug. This will obey the rclone logging flags and log at the correct level.
289 lines
7.7 KiB
Go
289 lines
7.7 KiB
Go
//go:build unix
|
|
|
|
package nfs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/go-git/go-billy/v5"
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/log"
|
|
"github.com/rclone/rclone/vfs"
|
|
"github.com/willscott/go-nfs"
|
|
)
|
|
|
|
// Handler returns a NFS backing that exposes a given file system in response to all mount requests.
|
|
type Handler struct {
|
|
vfs *vfs.VFS
|
|
opt Options
|
|
billyFS *FS
|
|
Cache
|
|
}
|
|
|
|
// NewHandler creates a handler for the provided filesystem
|
|
func NewHandler(ctx context.Context, vfs *vfs.VFS, opt *Options) (handler nfs.Handler, err error) {
|
|
ci := fs.GetConfig(ctx)
|
|
h := &Handler{
|
|
vfs: vfs,
|
|
opt: *opt,
|
|
billyFS: &FS{vfs: vfs},
|
|
}
|
|
h.opt.HandleLimit = h.opt.Limit()
|
|
h.Cache, err = h.getCache()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to make cache: %w", err)
|
|
}
|
|
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})
|
|
return h, nil
|
|
}
|
|
|
|
// Mount backs Mount RPC Requests, allowing for access control policies.
|
|
func (h *Handler) Mount(ctx context.Context, conn net.Conn, req nfs.MountRequest) (status nfs.MountStatus, hndl billy.Filesystem, auths []nfs.AuthFlavor) {
|
|
auths = []nfs.AuthFlavor{nfs.AuthFlavorNull}
|
|
return nfs.MountStatusOk, h.billyFS, auths
|
|
}
|
|
|
|
// Change provides an interface for updating file attributes.
|
|
func (h *Handler) 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 *Handler) 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 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)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// HandleLimit exports how many file handles can be safely stored by this cache.
|
|
func (h *Handler) HandleLimit() int {
|
|
return h.Cache.HandleLimit()
|
|
}
|
|
|
|
// InvalidateHandle invalidates the handle passed - used on rename and delete
|
|
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)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// OnUnmountFunc registers a function to call when externally unmounted
|
|
var OnUnmountFunc func()
|
|
|
|
func onUnmount() {
|
|
fs.Infof(nil, "unmount detected")
|
|
if OnUnmountFunc != nil {
|
|
OnUnmountFunc()
|
|
}
|
|
}
|
|
|
|
// logger handles go-nfs logs and reroutes them to rclone's logging system
|
|
type logger struct {
|
|
level nfs.LogLevel
|
|
}
|
|
|
|
// 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...))
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// Debug reroutes go-nfs Debug messages to Intercept
|
|
func (l *logger) Debug(args ...interface{}) {
|
|
if l.level < nfs.DebugLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelDebug, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// Error reroutes go-nfs Error messages to Intercept
|
|
func (l *logger) Error(args ...interface{}) {
|
|
if l.level < nfs.ErrorLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelError, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// Fatal reroutes go-nfs Fatal messages to Intercept
|
|
func (l *logger) Fatal(args ...interface{}) {
|
|
if l.level < nfs.FatalLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelError, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// GetLevel returns the nfs.LogLevel
|
|
func (l *logger) GetLevel() nfs.LogLevel {
|
|
return l.level
|
|
}
|
|
|
|
// Info reroutes go-nfs Info messages to Intercept
|
|
func (l *logger) Info(args ...interface{}) {
|
|
if l.level < nfs.InfoLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelInfo, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// Panic reroutes go-nfs Panic messages to Intercept
|
|
func (l *logger) Panic(args ...interface{}) {
|
|
if l.level < nfs.PanicLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelError, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// ParseLevel parses the nfs.LogLevel
|
|
func (l *logger) ParseLevel(level string) (nfs.LogLevel, error) {
|
|
return nfs.Log.ParseLevel(level)
|
|
}
|
|
|
|
// Print reroutes go-nfs Print messages to Intercept
|
|
func (l *logger) Print(args ...interface{}) {
|
|
if l.level < nfs.InfoLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelInfo, args...)
|
|
}
|
|
|
|
// Printf reroutes go-nfs Printf messages to Intercept
|
|
func (l *logger) Printf(format string, args ...interface{}) {
|
|
if l.level < nfs.InfoLevel {
|
|
return
|
|
}
|
|
l.logPrintf(fs.LogLevelInfo, format, args...)
|
|
}
|
|
|
|
// SetLevel sets the nfs.LogLevel
|
|
func (l *logger) SetLevel(level nfs.LogLevel) {
|
|
l.level = level
|
|
}
|
|
|
|
// Trace reroutes go-nfs Trace messages to Intercept
|
|
func (l *logger) Trace(args ...interface{}) {
|
|
if l.level < nfs.DebugLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelDebug, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|
|
|
|
// Warn reroutes go-nfs Warn messages to Intercept
|
|
func (l *logger) Warn(args ...interface{}) {
|
|
if l.level < nfs.WarnLevel {
|
|
return
|
|
}
|
|
l.logPrint(fs.LogLevelNotice, args...)
|
|
}
|
|
|
|
// 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...)
|
|
}
|