github.com/artpar/rclone@v1.67.3/cmd/serve/nfs/handler.go (about)

     1  //go:build unix
     2  
     3  package nfs
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"net"
     9  	"strings"
    10  
    11  	"github.com/artpar/rclone/fs"
    12  	"github.com/artpar/rclone/vfs"
    13  	"github.com/go-git/go-billy/v5"
    14  	"github.com/willscott/go-nfs"
    15  	nfshelper "github.com/willscott/go-nfs/helpers"
    16  )
    17  
    18  // NewBackendAuthHandler creates a handler for the provided filesystem
    19  func NewBackendAuthHandler(vfs *vfs.VFS, opt *Options) nfs.Handler {
    20  	handler := &BackendAuthHandler{
    21  		vfs: vfs,
    22  		opt: opt,
    23  	}
    24  	handler.opt.HandleLimit = handler.opt.Limit()
    25  	handler.cache = cacheHelper(handler, handler.HandleLimit())
    26  	return handler
    27  }
    28  
    29  // BackendAuthHandler returns a NFS backing that exposes a given file system in response to all mount requests.
    30  type BackendAuthHandler struct {
    31  	vfs   *vfs.VFS
    32  	opt   *Options
    33  	cache nfs.Handler
    34  }
    35  
    36  // Mount backs Mount RPC Requests, allowing for access control policies.
    37  func (h *BackendAuthHandler) Mount(ctx context.Context, conn net.Conn, req nfs.MountRequest) (status nfs.MountStatus, hndl billy.Filesystem, auths []nfs.AuthFlavor) {
    38  	status = nfs.MountStatusOk
    39  	hndl = &FS{vfs: h.vfs}
    40  	auths = []nfs.AuthFlavor{nfs.AuthFlavorNull}
    41  	return
    42  }
    43  
    44  // Change provides an interface for updating file attributes.
    45  func (h *BackendAuthHandler) Change(fs billy.Filesystem) billy.Change {
    46  	if c, ok := fs.(billy.Change); ok {
    47  		return c
    48  	}
    49  	return nil
    50  }
    51  
    52  // FSStat provides information about a filesystem.
    53  func (h *BackendAuthHandler) FSStat(ctx context.Context, f billy.Filesystem, s *nfs.FSStat) error {
    54  	total, _, free := h.vfs.Statfs()
    55  	s.TotalSize = uint64(total)
    56  	s.FreeSize = uint64(free)
    57  	s.AvailableSize = uint64(free)
    58  	return nil
    59  }
    60  
    61  // ToHandle handled by CachingHandler
    62  func (h *BackendAuthHandler) ToHandle(f billy.Filesystem, s []string) []byte {
    63  	return h.cache.ToHandle(f, s)
    64  }
    65  
    66  // FromHandle handled by CachingHandler
    67  func (h *BackendAuthHandler) FromHandle(b []byte) (billy.Filesystem, []string, error) {
    68  	return h.cache.FromHandle(b)
    69  }
    70  
    71  // HandleLimit handled by cachingHandler
    72  func (h *BackendAuthHandler) HandleLimit() int {
    73  	return h.opt.HandleLimit
    74  }
    75  
    76  // InvalidateHandle is called on removes or renames
    77  func (h *BackendAuthHandler) InvalidateHandle(billy.Filesystem, []byte) error {
    78  	return nil
    79  }
    80  
    81  func newHandler(vfs *vfs.VFS, opt *Options) nfs.Handler {
    82  	handler := NewBackendAuthHandler(vfs, opt)
    83  	nfs.SetLogger(&LogIntercepter{Level: nfs.DebugLevel})
    84  	return handler
    85  }
    86  
    87  func cacheHelper(handler nfs.Handler, limit int) nfs.Handler {
    88  	cacheHelper := nfshelper.NewCachingHandler(handler, limit)
    89  	return cacheHelper
    90  }
    91  
    92  // Limit overrides the --nfs-cache-handle-limit value if out-of-range
    93  func (o *Options) Limit() int {
    94  	if o.HandleLimit < 0 {
    95  		return 1000000
    96  	}
    97  	if o.HandleLimit <= 5 {
    98  		return 5
    99  	}
   100  	return o.HandleLimit
   101  }
   102  
   103  // OnUnmountFunc registers a function to call when externally unmounted
   104  var OnUnmountFunc func()
   105  
   106  func onUnmount() {
   107  	fs.Infof(nil, "unmount detected")
   108  	if OnUnmountFunc != nil {
   109  		OnUnmountFunc()
   110  	}
   111  }
   112  
   113  // LogIntercepter intercepts noisy go-nfs logs and reroutes them to DEBUG
   114  type LogIntercepter struct {
   115  	Level nfs.LogLevel
   116  }
   117  
   118  // Intercept intercepts go-nfs logs and calls fs.Debugf instead
   119  func (l *LogIntercepter) Intercept(args ...interface{}) {
   120  	args = append([]interface{}{"[NFS DEBUG] "}, args...)
   121  	argsS := fmt.Sprint(args...)
   122  	fs.Debugf(nil, "%v", argsS)
   123  }
   124  
   125  // Interceptf intercepts go-nfs logs and calls fs.Debugf instead
   126  func (l *LogIntercepter) Interceptf(format string, args ...interface{}) {
   127  	argsS := fmt.Sprint(args...)
   128  	// bit of a workaround... the real fix is probably https://github.com/willscott/go-nfs/pull/28
   129  	if strings.Contains(argsS, "mount.Umnt") {
   130  		onUnmount()
   131  	}
   132  
   133  	fs.Debugf(nil, "[NFS DEBUG] "+format, args...)
   134  }
   135  
   136  // Debug reroutes go-nfs Debug messages to Intercept
   137  func (l *LogIntercepter) Debug(args ...interface{}) {
   138  	l.Intercept(args...)
   139  }
   140  
   141  // Debugf reroutes go-nfs Debugf messages to Interceptf
   142  func (l *LogIntercepter) Debugf(format string, args ...interface{}) {
   143  	l.Interceptf(format, args...)
   144  }
   145  
   146  // Error reroutes go-nfs Error messages to Intercept
   147  func (l *LogIntercepter) Error(args ...interface{}) {
   148  	l.Intercept(args...)
   149  }
   150  
   151  // Errorf reroutes go-nfs Errorf messages to Interceptf
   152  func (l *LogIntercepter) Errorf(format string, args ...interface{}) {
   153  	l.Interceptf(format, args...)
   154  }
   155  
   156  // Fatal reroutes go-nfs Fatal messages to Intercept
   157  func (l *LogIntercepter) Fatal(args ...interface{}) {
   158  	l.Intercept(args...)
   159  }
   160  
   161  // Fatalf reroutes go-nfs Fatalf messages to Interceptf
   162  func (l *LogIntercepter) Fatalf(format string, args ...interface{}) {
   163  	l.Interceptf(format, args...)
   164  }
   165  
   166  // GetLevel returns the nfs.LogLevel
   167  func (l *LogIntercepter) GetLevel() nfs.LogLevel {
   168  	return l.Level
   169  }
   170  
   171  // Info reroutes go-nfs Info messages to Intercept
   172  func (l *LogIntercepter) Info(args ...interface{}) {
   173  	l.Intercept(args...)
   174  }
   175  
   176  // Infof reroutes go-nfs Infof messages to Interceptf
   177  func (l *LogIntercepter) Infof(format string, args ...interface{}) {
   178  	l.Interceptf(format, args...)
   179  }
   180  
   181  // Panic reroutes go-nfs Panic messages to Intercept
   182  func (l *LogIntercepter) Panic(args ...interface{}) {
   183  	l.Intercept(args...)
   184  }
   185  
   186  // Panicf reroutes go-nfs Panicf messages to Interceptf
   187  func (l *LogIntercepter) Panicf(format string, args ...interface{}) {
   188  	l.Interceptf(format, args...)
   189  }
   190  
   191  // ParseLevel parses the nfs.LogLevel
   192  func (l *LogIntercepter) ParseLevel(level string) (nfs.LogLevel, error) {
   193  	return nfs.Log.ParseLevel(level)
   194  }
   195  
   196  // Print reroutes go-nfs Print messages to Intercept
   197  func (l *LogIntercepter) Print(args ...interface{}) {
   198  	l.Intercept(args...)
   199  }
   200  
   201  // Printf reroutes go-nfs Printf messages to Intercept
   202  func (l *LogIntercepter) Printf(format string, args ...interface{}) {
   203  	l.Interceptf(format, args...)
   204  }
   205  
   206  // SetLevel sets the nfs.LogLevel
   207  func (l *LogIntercepter) SetLevel(level nfs.LogLevel) {
   208  	l.Level = level
   209  }
   210  
   211  // Trace reroutes go-nfs Trace messages to Intercept
   212  func (l *LogIntercepter) Trace(args ...interface{}) {
   213  	l.Intercept(args...)
   214  }
   215  
   216  // Tracef reroutes go-nfs Tracef messages to Interceptf
   217  func (l *LogIntercepter) Tracef(format string, args ...interface{}) {
   218  	l.Interceptf(format, args...)
   219  }
   220  
   221  // Warn reroutes go-nfs Warn messages to Intercept
   222  func (l *LogIntercepter) Warn(args ...interface{}) {
   223  	l.Intercept(args...)
   224  }
   225  
   226  // Warnf reroutes go-nfs Warnf messages to Interceptf
   227  func (l *LogIntercepter) Warnf(format string, args ...interface{}) {
   228  	l.Interceptf(format, args...)
   229  }