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 }