github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/union/upstream/upstream.go (about) 1 package upstream 2 3 import ( 4 "context" 5 "io" 6 "math" 7 "path" 8 "path/filepath" 9 "strings" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/pkg/errors" 15 "github.com/rclone/rclone/fs" 16 "github.com/rclone/rclone/fs/cache" 17 ) 18 19 var ( 20 // ErrUsageFieldNotSupported stats the usage field is not supported by the backend 21 ErrUsageFieldNotSupported = errors.New("this usage field is not supported") 22 ) 23 24 // Fs is a wrap of any fs and its configs 25 type Fs struct { 26 fs.Fs 27 RootFs fs.Fs 28 RootPath string 29 writable bool 30 creatable bool 31 usage *fs.Usage // Cache the usage 32 cacheTime time.Duration // cache duration 33 cacheExpiry int64 // usage cache expiry time 34 cacheMutex sync.RWMutex 35 cacheOnce sync.Once 36 cacheUpdate bool // if the cache is updating 37 } 38 39 // Directory describes a wrapped Directory 40 // 41 // This is a wrapped Directory which contains the upstream Fs 42 type Directory struct { 43 fs.Directory 44 f *Fs 45 } 46 47 // Object describes a wrapped Object 48 // 49 // This is a wrapped Object which contains the upstream Fs 50 type Object struct { 51 fs.Object 52 f *Fs 53 } 54 55 // Entry describe a warpped fs.DirEntry interface with the 56 // information of upstream Fs 57 type Entry interface { 58 fs.DirEntry 59 UpstreamFs() *Fs 60 } 61 62 // New creates a new Fs based on the 63 // string formatted `type:root_path(:ro/:nc)` 64 func New(remote, root string, cacheTime time.Duration) (*Fs, error) { 65 _, configName, fsPath, err := fs.ParseRemote(remote) 66 if err != nil { 67 return nil, err 68 } 69 f := &Fs{ 70 RootPath: root, 71 writable: true, 72 creatable: true, 73 cacheExpiry: time.Now().Unix(), 74 cacheTime: cacheTime, 75 usage: &fs.Usage{}, 76 } 77 if strings.HasSuffix(fsPath, ":ro") { 78 f.writable = false 79 f.creatable = false 80 fsPath = fsPath[0 : len(fsPath)-3] 81 } else if strings.HasSuffix(fsPath, ":nc") { 82 f.writable = true 83 f.creatable = false 84 fsPath = fsPath[0 : len(fsPath)-3] 85 } 86 if configName != "local" { 87 fsPath = configName + ":" + fsPath 88 } 89 rFs, err := cache.Get(fsPath) 90 if err != nil && err != fs.ErrorIsFile { 91 return nil, err 92 } 93 f.RootFs = rFs 94 rootString := path.Join(fsPath, filepath.ToSlash(root)) 95 myFs, err := cache.Get(rootString) 96 if err != nil && err != fs.ErrorIsFile { 97 return nil, err 98 } 99 f.Fs = myFs 100 return f, err 101 } 102 103 // WrapDirectory wraps an fs.Directory to include the info 104 // of the upstream Fs 105 func (f *Fs) WrapDirectory(e fs.Directory) *Directory { 106 if e == nil { 107 return nil 108 } 109 return &Directory{ 110 Directory: e, 111 f: f, 112 } 113 } 114 115 // WrapObject wraps an fs.Object to include the info 116 // of the upstream Fs 117 func (f *Fs) WrapObject(o fs.Object) *Object { 118 if o == nil { 119 return nil 120 } 121 return &Object{ 122 Object: o, 123 f: f, 124 } 125 } 126 127 // WrapEntry wraps an fs.DirEntry to include the info 128 // of the upstream Fs 129 func (f *Fs) WrapEntry(e fs.DirEntry) (Entry, error) { 130 switch e.(type) { 131 case fs.Object: 132 return f.WrapObject(e.(fs.Object)), nil 133 case fs.Directory: 134 return f.WrapDirectory(e.(fs.Directory)), nil 135 default: 136 return nil, errors.Errorf("unknown object type %T", e) 137 } 138 } 139 140 // UpstreamFs get the upstream Fs the entry is stored in 141 func (e *Directory) UpstreamFs() *Fs { 142 return e.f 143 } 144 145 // UpstreamFs get the upstream Fs the entry is stored in 146 func (o *Object) UpstreamFs() *Fs { 147 return o.f 148 } 149 150 // UnWrap returns the Object that this Object is wrapping or 151 // nil if it isn't wrapping anything 152 func (o *Object) UnWrap() fs.Object { 153 return o.Object 154 } 155 156 // IsCreatable return if the fs is allowed to create new objects 157 func (f *Fs) IsCreatable() bool { 158 return f.creatable 159 } 160 161 // IsWritable return if the fs is allowed to write 162 func (f *Fs) IsWritable() bool { 163 return f.writable 164 } 165 166 // Put in to the remote path with the modTime given of the given size 167 // 168 // May create the object even if it returns an error - if so 169 // will return the object and the error, otherwise will return 170 // nil and the error 171 func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 172 o, err := f.Fs.Put(ctx, in, src, options...) 173 if err != nil { 174 return o, err 175 } 176 f.cacheMutex.Lock() 177 defer f.cacheMutex.Unlock() 178 size := src.Size() 179 if f.usage.Used != nil { 180 *f.usage.Used += size 181 } 182 if f.usage.Free != nil { 183 *f.usage.Free -= size 184 } 185 if f.usage.Objects != nil { 186 *f.usage.Objects++ 187 } 188 return o, nil 189 } 190 191 // PutStream uploads to the remote path with the modTime given of indeterminate size 192 // 193 // May create the object even if it returns an error - if so 194 // will return the object and the error, otherwise will return 195 // nil and the error 196 func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 197 do := f.Features().PutStream 198 if do == nil { 199 return nil, fs.ErrorNotImplemented 200 } 201 o, err := do(ctx, in, src, options...) 202 if err != nil { 203 return o, err 204 } 205 f.cacheMutex.Lock() 206 defer f.cacheMutex.Unlock() 207 size := o.Size() 208 if f.usage.Used != nil { 209 *f.usage.Used += size 210 } 211 if f.usage.Free != nil { 212 *f.usage.Free -= size 213 } 214 if f.usage.Objects != nil { 215 *f.usage.Objects++ 216 } 217 return o, nil 218 } 219 220 // Update in to the object with the modTime given of the given size 221 // 222 // When called from outside an Fs by rclone, src.Size() will always be >= 0. 223 // But for unknown-sized objects (indicated by src.Size() == -1), Upload should either 224 // return an error or update the object properly (rather than e.g. calling panic). 225 func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error { 226 size := o.Size() 227 err := o.Object.Update(ctx, in, src, options...) 228 if err != nil { 229 return err 230 } 231 o.f.cacheMutex.Lock() 232 defer o.f.cacheMutex.Unlock() 233 delta := o.Size() - size 234 if delta <= 0 { 235 return nil 236 } 237 if o.f.usage.Used != nil { 238 *o.f.usage.Used += size 239 } 240 if o.f.usage.Free != nil { 241 *o.f.usage.Free -= size 242 } 243 return nil 244 } 245 246 // About gets quota information from the Fs 247 func (f *Fs) About(ctx context.Context) (*fs.Usage, error) { 248 if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() { 249 err := f.updateUsage() 250 if err != nil { 251 return nil, ErrUsageFieldNotSupported 252 } 253 } 254 f.cacheMutex.RLock() 255 defer f.cacheMutex.RUnlock() 256 return f.usage, nil 257 } 258 259 // GetFreeSpace get the free space of the fs 260 func (f *Fs) GetFreeSpace() (int64, error) { 261 if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() { 262 err := f.updateUsage() 263 if err != nil { 264 return math.MaxInt64, ErrUsageFieldNotSupported 265 } 266 } 267 f.cacheMutex.RLock() 268 defer f.cacheMutex.RUnlock() 269 if f.usage.Free == nil { 270 return math.MaxInt64, ErrUsageFieldNotSupported 271 } 272 return *f.usage.Free, nil 273 } 274 275 // GetUsedSpace get the used space of the fs 276 func (f *Fs) GetUsedSpace() (int64, error) { 277 if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() { 278 err := f.updateUsage() 279 if err != nil { 280 return 0, ErrUsageFieldNotSupported 281 } 282 } 283 f.cacheMutex.RLock() 284 defer f.cacheMutex.RUnlock() 285 if f.usage.Used == nil { 286 return 0, ErrUsageFieldNotSupported 287 } 288 return *f.usage.Used, nil 289 } 290 291 // GetNumObjects get the number of objects of the fs 292 func (f *Fs) GetNumObjects() (int64, error) { 293 if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() { 294 err := f.updateUsage() 295 if err != nil { 296 return 0, ErrUsageFieldNotSupported 297 } 298 } 299 f.cacheMutex.RLock() 300 defer f.cacheMutex.RUnlock() 301 if f.usage.Objects == nil { 302 return 0, ErrUsageFieldNotSupported 303 } 304 return *f.usage.Objects, nil 305 } 306 307 func (f *Fs) updateUsage() (err error) { 308 if do := f.RootFs.Features().About; do == nil { 309 return ErrUsageFieldNotSupported 310 } 311 done := false 312 f.cacheOnce.Do(func() { 313 f.cacheMutex.Lock() 314 err = f.updateUsageCore(false) 315 f.cacheMutex.Unlock() 316 done = true 317 }) 318 if done { 319 return err 320 } 321 if !f.cacheUpdate { 322 f.cacheUpdate = true 323 go func() { 324 _ = f.updateUsageCore(true) 325 f.cacheUpdate = false 326 }() 327 } 328 return nil 329 } 330 331 func (f *Fs) updateUsageCore(lock bool) error { 332 // Run in background, should not be cancelled by user 333 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 334 defer cancel() 335 usage, err := f.RootFs.Features().About(ctx) 336 if err != nil { 337 f.cacheUpdate = false 338 return err 339 } 340 if lock { 341 f.cacheMutex.Lock() 342 defer f.cacheMutex.Unlock() 343 } 344 // Store usage 345 atomic.StoreInt64(&f.cacheExpiry, time.Now().Add(f.cacheTime).Unix()) 346 f.usage = usage 347 return nil 348 }