github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/union/upstream/upstream.go (about) 1 // Package upstream provides utility functionality to union. 2 package upstream 3 4 import ( 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "math" 10 "strings" 11 "sync" 12 "sync/atomic" 13 "time" 14 15 "github.com/rclone/rclone/backend/union/common" 16 "github.com/rclone/rclone/fs" 17 "github.com/rclone/rclone/fs/cache" 18 "github.com/rclone/rclone/fs/fspath" 19 "github.com/rclone/rclone/fs/operations" 20 ) 21 22 var ( 23 // ErrUsageFieldNotSupported stats the usage field is not supported by the backend 24 ErrUsageFieldNotSupported = errors.New("this usage field is not supported") 25 ) 26 27 // Fs is a wrap of any fs and its configs 28 type Fs struct { 29 fs.Fs 30 RootFs fs.Fs 31 RootPath string 32 Opt *common.Options 33 writable bool 34 creatable bool 35 usage *fs.Usage // Cache the usage 36 cacheTime time.Duration // cache duration 37 cacheExpiry atomic.Int64 // usage cache expiry time 38 cacheMutex sync.RWMutex 39 cacheOnce sync.Once 40 cacheUpdate bool // if the cache is updating 41 writeback bool // writeback to this upstream 42 writebackFs *Fs // if non zero, writeback to this upstream 43 } 44 45 // Directory describes a wrapped Directory 46 // 47 // This is a wrapped Directory which contains the upstream Fs 48 type Directory struct { 49 fs.Directory 50 f *Fs 51 } 52 53 // Object describes a wrapped Object 54 // 55 // This is a wrapped Object which contains the upstream Fs 56 type Object struct { 57 fs.Object 58 f *Fs 59 } 60 61 // Entry describe a wrapped fs.DirEntry interface with the 62 // information of upstream Fs 63 type Entry interface { 64 fs.DirEntry 65 UpstreamFs() *Fs 66 } 67 68 // New creates a new Fs based on the 69 // string formatted `type:root_path(:ro/:nc)` 70 func New(ctx context.Context, remote, root string, opt *common.Options) (*Fs, error) { 71 configName, fsPath, err := fspath.SplitFs(remote) 72 if err != nil { 73 return nil, err 74 } 75 f := &Fs{ 76 RootPath: strings.TrimRight(root, "/"), 77 Opt: opt, 78 writable: true, 79 creatable: true, 80 cacheTime: time.Duration(opt.CacheTime) * time.Second, 81 usage: &fs.Usage{}, 82 } 83 f.cacheExpiry.Store(time.Now().Unix()) 84 if strings.HasSuffix(fsPath, ":ro") { 85 f.writable = false 86 f.creatable = false 87 fsPath = fsPath[0 : len(fsPath)-3] 88 } else if strings.HasSuffix(fsPath, ":nc") { 89 f.writable = true 90 f.creatable = false 91 fsPath = fsPath[0 : len(fsPath)-3] 92 } else if strings.HasSuffix(fsPath, ":writeback") { 93 f.writeback = true 94 fsPath = fsPath[0 : len(fsPath)-len(":writeback")] 95 } 96 remote = configName + fsPath 97 rFs, err := cache.Get(ctx, remote) 98 if err != nil && err != fs.ErrorIsFile { 99 return nil, err 100 } 101 f.RootFs = rFs 102 rootString := fspath.JoinRootPath(remote, root) 103 myFs, err := cache.Get(ctx, rootString) 104 if err != nil && err != fs.ErrorIsFile { 105 return nil, err 106 } 107 f.Fs = myFs 108 cache.PinUntilFinalized(f.Fs, f) 109 return f, err 110 } 111 112 // Prepare the configured upstreams as a group 113 func Prepare(fses []*Fs) error { 114 writebacks := 0 115 var writebackFs *Fs 116 for _, f := range fses { 117 if f.writeback { 118 writebackFs = f 119 writebacks++ 120 } 121 } 122 if writebacks == 0 { 123 return nil 124 } else if writebacks > 1 { 125 return fmt.Errorf("can only have 1 :writeback not %d", writebacks) 126 } 127 for _, f := range fses { 128 if !f.writeback { 129 f.writebackFs = writebackFs 130 } 131 } 132 return nil 133 } 134 135 // WrapDirectory wraps an fs.Directory to include the info 136 // of the upstream Fs 137 func (f *Fs) WrapDirectory(e fs.Directory) *Directory { 138 if e == nil { 139 return nil 140 } 141 return &Directory{ 142 Directory: e, 143 f: f, 144 } 145 } 146 147 // WrapObject wraps an fs.Object to include the info 148 // of the upstream Fs 149 func (f *Fs) WrapObject(o fs.Object) *Object { 150 if o == nil { 151 return nil 152 } 153 return &Object{ 154 Object: o, 155 f: f, 156 } 157 } 158 159 // WrapEntry wraps an fs.DirEntry to include the info 160 // of the upstream Fs 161 func (f *Fs) WrapEntry(e fs.DirEntry) (Entry, error) { 162 switch e := e.(type) { 163 case fs.Object: 164 return f.WrapObject(e), nil 165 case fs.Directory: 166 return f.WrapDirectory(e), nil 167 default: 168 return nil, fmt.Errorf("unknown object type %T", e) 169 } 170 } 171 172 // UpstreamFs get the upstream Fs the entry is stored in 173 func (e *Directory) UpstreamFs() *Fs { 174 return e.f 175 } 176 177 // UpstreamFs get the upstream Fs the entry is stored in 178 func (o *Object) UpstreamFs() *Fs { 179 return o.f 180 } 181 182 // UnWrap returns the Object that this Object is wrapping or 183 // nil if it isn't wrapping anything 184 func (o *Object) UnWrap() fs.Object { 185 return o.Object 186 } 187 188 // IsCreatable return if the fs is allowed to create new objects 189 func (f *Fs) IsCreatable() bool { 190 return f.creatable 191 } 192 193 // IsWritable return if the fs is allowed to write 194 func (f *Fs) IsWritable() bool { 195 return f.writable 196 } 197 198 // Put in to the remote path with the modTime given of the given size 199 // 200 // May create the object even if it returns an error - if so 201 // will return the object and the error, otherwise will return 202 // nil and the error 203 func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 204 o, err := f.Fs.Put(ctx, in, src, options...) 205 if err != nil { 206 return o, err 207 } 208 f.cacheMutex.Lock() 209 defer f.cacheMutex.Unlock() 210 size := src.Size() 211 if f.usage.Used != nil { 212 *f.usage.Used += size 213 } 214 if f.usage.Free != nil { 215 *f.usage.Free -= size 216 } 217 if f.usage.Objects != nil { 218 *f.usage.Objects++ 219 } 220 return o, nil 221 } 222 223 // PutStream uploads to the remote path with the modTime given of indeterminate size 224 // 225 // May create the object even if it returns an error - if so 226 // will return the object and the error, otherwise will return 227 // nil and the error 228 func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 229 do := f.Features().PutStream 230 if do == nil { 231 return nil, fs.ErrorNotImplemented 232 } 233 o, err := do(ctx, in, src, options...) 234 if err != nil { 235 return o, err 236 } 237 f.cacheMutex.Lock() 238 defer f.cacheMutex.Unlock() 239 size := o.Size() 240 if f.usage.Used != nil { 241 *f.usage.Used += size 242 } 243 if f.usage.Free != nil { 244 *f.usage.Free -= size 245 } 246 if f.usage.Objects != nil { 247 *f.usage.Objects++ 248 } 249 return o, nil 250 } 251 252 // Update in to the object with the modTime given of the given size 253 // 254 // When called from outside an Fs by rclone, src.Size() will always be >= 0. 255 // But for unknown-sized objects (indicated by src.Size() == -1), Upload should either 256 // return an error or update the object properly (rather than e.g. calling panic). 257 func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error { 258 size := o.Size() 259 err := o.Object.Update(ctx, in, src, options...) 260 if err != nil { 261 return err 262 } 263 o.f.cacheMutex.Lock() 264 defer o.f.cacheMutex.Unlock() 265 delta := o.Size() - size 266 if delta <= 0 { 267 return nil 268 } 269 if o.f.usage.Used != nil { 270 *o.f.usage.Used += size 271 } 272 if o.f.usage.Free != nil { 273 *o.f.usage.Free -= size 274 } 275 return nil 276 } 277 278 // GetTier returns storage tier or class of the Object 279 func (o *Object) GetTier() string { 280 do, ok := o.Object.(fs.GetTierer) 281 if !ok { 282 return "" 283 } 284 return do.GetTier() 285 } 286 287 // ID returns the ID of the Object if known, or "" if not 288 func (o *Object) ID() string { 289 do, ok := o.Object.(fs.IDer) 290 if !ok { 291 return "" 292 } 293 return do.ID() 294 } 295 296 // MimeType returns the content type of the Object if known 297 func (o *Object) MimeType(ctx context.Context) (mimeType string) { 298 if do, ok := o.Object.(fs.MimeTyper); ok { 299 mimeType = do.MimeType(ctx) 300 } 301 return mimeType 302 } 303 304 // SetTier performs changing storage tier of the Object if 305 // multiple storage classes supported 306 func (o *Object) SetTier(tier string) error { 307 do, ok := o.Object.(fs.SetTierer) 308 if !ok { 309 return errors.New("underlying remote does not support SetTier") 310 } 311 return do.SetTier(tier) 312 } 313 314 // Metadata returns metadata for an object 315 // 316 // It should return nil if there is no Metadata 317 func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) { 318 do, ok := o.Object.(fs.Metadataer) 319 if !ok { 320 return nil, nil 321 } 322 return do.Metadata(ctx) 323 } 324 325 // SetMetadata sets metadata for an Object 326 // 327 // It should return fs.ErrorNotImplemented if it can't set metadata 328 func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error { 329 do, ok := o.Object.(fs.SetMetadataer) 330 if !ok { 331 return fs.ErrorNotImplemented 332 } 333 return do.SetMetadata(ctx, metadata) 334 } 335 336 // Metadata returns metadata for an DirEntry 337 // 338 // It should return nil if there is no Metadata 339 func (e *Directory) Metadata(ctx context.Context) (fs.Metadata, error) { 340 do, ok := e.Directory.(fs.Metadataer) 341 if !ok { 342 return nil, nil 343 } 344 return do.Metadata(ctx) 345 } 346 347 // SetMetadata sets metadata for an DirEntry 348 // 349 // It should return fs.ErrorNotImplemented if it can't set metadata 350 func (e *Directory) SetMetadata(ctx context.Context, metadata fs.Metadata) error { 351 do, ok := e.Directory.(fs.SetMetadataer) 352 if !ok { 353 return fs.ErrorNotImplemented 354 } 355 return do.SetMetadata(ctx, metadata) 356 } 357 358 // SetModTime sets the metadata on the DirEntry to set the modification date 359 // 360 // If there is any other metadata it does not overwrite it. 361 func (e *Directory) SetModTime(ctx context.Context, t time.Time) error { 362 do, ok := e.Directory.(fs.SetModTimer) 363 if !ok { 364 return fs.ErrorNotImplemented 365 } 366 return do.SetModTime(ctx, t) 367 } 368 369 // Writeback writes the object back and returns a new object 370 // 371 // If it returns nil, nil then the original object is OK 372 func (o *Object) Writeback(ctx context.Context) (*Object, error) { 373 if o.f.writebackFs == nil { 374 return nil, nil 375 } 376 newObj, err := operations.Copy(ctx, o.f.writebackFs.Fs, nil, o.Object.Remote(), o.Object) 377 if err != nil { 378 return nil, err 379 } 380 // newObj could be nil here 381 if newObj == nil { 382 fs.Errorf(o, "nil Object returned from operations.Copy") 383 return nil, nil 384 } 385 return &Object{ 386 Object: newObj, 387 f: o.f, 388 }, err 389 } 390 391 // About gets quota information from the Fs 392 func (f *Fs) About(ctx context.Context) (*fs.Usage, error) { 393 if f.cacheExpiry.Load() <= time.Now().Unix() { 394 err := f.updateUsage() 395 if err != nil { 396 return nil, ErrUsageFieldNotSupported 397 } 398 } 399 f.cacheMutex.RLock() 400 defer f.cacheMutex.RUnlock() 401 return f.usage, nil 402 } 403 404 // GetFreeSpace get the free space of the fs 405 // 406 // This is returned as 0..math.MaxInt64-1 leaving math.MaxInt64 as a sentinel 407 func (f *Fs) GetFreeSpace() (int64, error) { 408 if f.cacheExpiry.Load() <= time.Now().Unix() { 409 err := f.updateUsage() 410 if err != nil { 411 return math.MaxInt64 - 1, ErrUsageFieldNotSupported 412 } 413 } 414 f.cacheMutex.RLock() 415 defer f.cacheMutex.RUnlock() 416 if f.usage.Free == nil { 417 return math.MaxInt64 - 1, ErrUsageFieldNotSupported 418 } 419 return *f.usage.Free, nil 420 } 421 422 // GetUsedSpace get the used space of the fs 423 // 424 // This is returned as 0..math.MaxInt64-1 leaving math.MaxInt64 as a sentinel 425 func (f *Fs) GetUsedSpace() (int64, error) { 426 if f.cacheExpiry.Load() <= time.Now().Unix() { 427 err := f.updateUsage() 428 if err != nil { 429 return 0, ErrUsageFieldNotSupported 430 } 431 } 432 f.cacheMutex.RLock() 433 defer f.cacheMutex.RUnlock() 434 if f.usage.Used == nil { 435 return 0, ErrUsageFieldNotSupported 436 } 437 return *f.usage.Used, nil 438 } 439 440 // GetNumObjects get the number of objects of the fs 441 func (f *Fs) GetNumObjects() (int64, error) { 442 if f.cacheExpiry.Load() <= time.Now().Unix() { 443 err := f.updateUsage() 444 if err != nil { 445 return 0, ErrUsageFieldNotSupported 446 } 447 } 448 f.cacheMutex.RLock() 449 defer f.cacheMutex.RUnlock() 450 if f.usage.Objects == nil { 451 return 0, ErrUsageFieldNotSupported 452 } 453 return *f.usage.Objects, nil 454 } 455 456 func (f *Fs) updateUsage() (err error) { 457 if do := f.RootFs.Features().About; do == nil { 458 return ErrUsageFieldNotSupported 459 } 460 done := false 461 f.cacheOnce.Do(func() { 462 f.cacheMutex.Lock() 463 err = f.updateUsageCore(false) 464 f.cacheMutex.Unlock() 465 done = true 466 }) 467 if done { 468 return err 469 } 470 if !f.cacheUpdate { 471 f.cacheUpdate = true 472 go func() { 473 _ = f.updateUsageCore(true) 474 f.cacheUpdate = false 475 }() 476 } 477 return nil 478 } 479 480 func (f *Fs) updateUsageCore(lock bool) error { 481 // Run in background, should not be cancelled by user 482 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 483 defer cancel() 484 usage, err := f.RootFs.Features().About(ctx) 485 if err != nil { 486 f.cacheUpdate = false 487 if errors.Is(err, fs.ErrorDirNotFound) { 488 err = nil 489 } 490 return err 491 } 492 if lock { 493 f.cacheMutex.Lock() 494 defer f.cacheMutex.Unlock() 495 } 496 // Store usage 497 f.cacheExpiry.Store(time.Now().Add(f.cacheTime).Unix()) 498 f.usage = usage 499 return nil 500 } 501 502 // Check the interfaces are satisfied 503 var ( 504 _ fs.FullObject = (*Object)(nil) 505 _ fs.FullDirectory = (*Directory)(nil) 506 )