github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/memory/memory.go (about) 1 // Package memory provides an interface to an in memory object storage system 2 package memory 3 4 import ( 5 "bytes" 6 "context" 7 "crypto/md5" 8 "encoding/hex" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "path" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/pkg/errors" 18 "github.com/rclone/rclone/fs" 19 "github.com/rclone/rclone/fs/config/configmap" 20 "github.com/rclone/rclone/fs/config/configstruct" 21 "github.com/rclone/rclone/fs/hash" 22 "github.com/rclone/rclone/fs/walk" 23 "github.com/rclone/rclone/lib/bucket" 24 ) 25 26 var ( 27 hashType = hash.MD5 28 // the object storage is persistent 29 buckets = newBucketsInfo() 30 ) 31 32 // Register with Fs 33 func init() { 34 fs.Register(&fs.RegInfo{ 35 Name: "memory", 36 Description: "In memory object storage system.", 37 NewFs: NewFs, 38 Options: []fs.Option{}, 39 }) 40 } 41 42 // Options defines the configuration for this backend 43 type Options struct { 44 } 45 46 // Fs represents a remote memory server 47 type Fs struct { 48 name string // name of this remote 49 root string // the path we are working on if any 50 opt Options // parsed config options 51 rootBucket string // bucket part of root (if any) 52 rootDirectory string // directory part of root (if any) 53 features *fs.Features // optional features 54 } 55 56 // bucketsInfo holds info about all the buckets 57 type bucketsInfo struct { 58 mu sync.RWMutex 59 buckets map[string]*bucketInfo 60 } 61 62 func newBucketsInfo() *bucketsInfo { 63 return &bucketsInfo{ 64 buckets: make(map[string]*bucketInfo, 16), 65 } 66 } 67 68 // getBucket gets a names bucket or nil 69 func (bi *bucketsInfo) getBucket(name string) (b *bucketInfo) { 70 bi.mu.RLock() 71 b = bi.buckets[name] 72 bi.mu.RUnlock() 73 return b 74 } 75 76 // makeBucket returns the bucket or makes it 77 func (bi *bucketsInfo) makeBucket(name string) (b *bucketInfo) { 78 bi.mu.Lock() 79 defer bi.mu.Unlock() 80 b = bi.buckets[name] 81 if b != nil { 82 return b 83 } 84 b = newBucketInfo() 85 bi.buckets[name] = b 86 return b 87 } 88 89 // deleteBucket deleted the bucket or returns an error 90 func (bi *bucketsInfo) deleteBucket(name string) error { 91 bi.mu.Lock() 92 defer bi.mu.Unlock() 93 b := bi.buckets[name] 94 if b == nil { 95 return fs.ErrorDirNotFound 96 } 97 if !b.isEmpty() { 98 return fs.ErrorDirectoryNotEmpty 99 } 100 delete(bi.buckets, name) 101 return nil 102 } 103 104 // getObjectData gets an object from (bucketName, bucketPath) or nil 105 func (bi *bucketsInfo) getObjectData(bucketName, bucketPath string) (od *objectData) { 106 b := bi.getBucket(bucketName) 107 if b == nil { 108 return nil 109 } 110 return b.getObjectData(bucketPath) 111 } 112 113 // updateObjectData updates an object from (bucketName, bucketPath) 114 func (bi *bucketsInfo) updateObjectData(bucketName, bucketPath string, od *objectData) { 115 b := bi.makeBucket(bucketName) 116 b.mu.Lock() 117 b.objects[bucketPath] = od 118 b.mu.Unlock() 119 } 120 121 // removeObjectData removes an object from (bucketName, bucketPath) returning true if removed 122 func (bi *bucketsInfo) removeObjectData(bucketName, bucketPath string) (removed bool) { 123 b := bi.getBucket(bucketName) 124 if b != nil { 125 b.mu.Lock() 126 od := b.objects[bucketPath] 127 if od != nil { 128 delete(b.objects, bucketPath) 129 removed = true 130 } 131 b.mu.Unlock() 132 } 133 return removed 134 } 135 136 // bucketInfo holds info about a single bucket 137 type bucketInfo struct { 138 mu sync.RWMutex 139 objects map[string]*objectData 140 } 141 142 func newBucketInfo() *bucketInfo { 143 return &bucketInfo{ 144 objects: make(map[string]*objectData, 16), 145 } 146 } 147 148 // getBucket gets a names bucket or nil 149 func (bi *bucketInfo) getObjectData(name string) (od *objectData) { 150 bi.mu.RLock() 151 od = bi.objects[name] 152 bi.mu.RUnlock() 153 return od 154 } 155 156 // getBucket gets a names bucket or nil 157 func (bi *bucketInfo) isEmpty() (empty bool) { 158 bi.mu.RLock() 159 empty = len(bi.objects) == 0 160 bi.mu.RUnlock() 161 return empty 162 } 163 164 // the object data and metadata 165 type objectData struct { 166 modTime time.Time 167 hash string 168 mimeType string 169 data []byte 170 } 171 172 // Object describes a memory object 173 type Object struct { 174 fs *Fs // what this object is part of 175 remote string // The remote path 176 od *objectData // the object data 177 } 178 179 // ------------------------------------------------------------ 180 181 // Name of the remote (as passed into NewFs) 182 func (f *Fs) Name() string { 183 return f.name 184 } 185 186 // Root of the remote (as passed into NewFs) 187 func (f *Fs) Root() string { 188 return f.root 189 } 190 191 // String converts this Fs to a string 192 func (f *Fs) String() string { 193 return fmt.Sprintf("Memory root '%s'", f.root) 194 } 195 196 // Features returns the optional features of this Fs 197 func (f *Fs) Features() *fs.Features { 198 return f.features 199 } 200 201 // parsePath parses a remote 'url' 202 func parsePath(path string) (root string) { 203 root = strings.Trim(path, "/") 204 return 205 } 206 207 // split returns bucket and bucketPath from the rootRelativePath 208 // relative to f.root 209 func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) { 210 return bucket.Split(path.Join(f.root, rootRelativePath)) 211 } 212 213 // split returns bucket and bucketPath from the object 214 func (o *Object) split() (bucket, bucketPath string) { 215 return o.fs.split(o.remote) 216 } 217 218 // setRoot changes the root of the Fs 219 func (f *Fs) setRoot(root string) { 220 f.root = parsePath(root) 221 f.rootBucket, f.rootDirectory = bucket.Split(f.root) 222 } 223 224 // NewFs contstructs an Fs from the path, bucket:path 225 func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { 226 // Parse config into Options struct 227 opt := new(Options) 228 err := configstruct.Set(m, opt) 229 if err != nil { 230 return nil, err 231 } 232 root = strings.Trim(root, "/") 233 f := &Fs{ 234 name: name, 235 root: root, 236 opt: *opt, 237 } 238 f.setRoot(root) 239 f.features = (&fs.Features{ 240 ReadMimeType: true, 241 WriteMimeType: true, 242 BucketBased: true, 243 BucketBasedRootOK: true, 244 }).Fill(f) 245 if f.rootBucket != "" && f.rootDirectory != "" { 246 od := buckets.getObjectData(f.rootBucket, f.rootDirectory) 247 if od != nil { 248 newRoot := path.Dir(f.root) 249 if newRoot == "." { 250 newRoot = "" 251 } 252 f.setRoot(newRoot) 253 // return an error with an fs which points to the parent 254 err = fs.ErrorIsFile 255 } 256 } 257 return f, err 258 } 259 260 // newObject makes an object from a remote and an objectData 261 func (f *Fs) newObject(remote string, od *objectData) *Object { 262 return &Object{fs: f, remote: remote, od: od} 263 } 264 265 // NewObject finds the Object at remote. If it can't be found 266 // it returns the error fs.ErrorObjectNotFound. 267 func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) { 268 bucket, bucketPath := f.split(remote) 269 od := buckets.getObjectData(bucket, bucketPath) 270 if od == nil { 271 return nil, fs.ErrorObjectNotFound 272 } 273 return f.newObject(remote, od), nil 274 } 275 276 // listFn is called from list to handle an object. 277 type listFn func(remote string, entry fs.DirEntry, isDirectory bool) error 278 279 // list the buckets to fn 280 func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBucket bool, recurse bool, fn listFn) (err error) { 281 if prefix != "" { 282 prefix += "/" 283 } 284 if directory != "" { 285 directory += "/" 286 } 287 b := buckets.getBucket(bucket) 288 if b == nil { 289 return fs.ErrorDirNotFound 290 } 291 b.mu.RLock() 292 defer b.mu.RUnlock() 293 dirs := make(map[string]struct{}) 294 for absPath, od := range b.objects { 295 if strings.HasPrefix(absPath, directory) { 296 remote := absPath[len(prefix):] 297 if !recurse { 298 localPath := absPath[len(directory):] 299 slash := strings.IndexRune(localPath, '/') 300 if slash >= 0 { 301 // send a directory if have a slash 302 dir := directory + localPath[:slash] 303 if addBucket { 304 dir = path.Join(bucket, dir) 305 } 306 _, found := dirs[dir] 307 if !found { 308 err = fn(dir, fs.NewDir(dir, time.Time{}), true) 309 if err != nil { 310 return err 311 } 312 dirs[dir] = struct{}{} 313 } 314 continue // don't send this file if not recursing 315 } 316 } 317 // send an object 318 if addBucket { 319 remote = path.Join(bucket, remote) 320 } 321 err = fn(remote, f.newObject(remote, od), false) 322 if err != nil { 323 return err 324 } 325 } 326 } 327 return nil 328 } 329 330 // listDir lists the bucket to the entries 331 func (f *Fs) listDir(ctx context.Context, bucket, directory, prefix string, addBucket bool) (entries fs.DirEntries, err error) { 332 // List the objects and directories 333 err = f.list(ctx, bucket, directory, prefix, addBucket, false, func(remote string, entry fs.DirEntry, isDirectory bool) error { 334 entries = append(entries, entry) 335 return nil 336 }) 337 return entries, err 338 } 339 340 // listBuckets lists the buckets to entries 341 func (f *Fs) listBuckets(ctx context.Context) (entries fs.DirEntries, err error) { 342 buckets.mu.RLock() 343 defer buckets.mu.RUnlock() 344 for name := range buckets.buckets { 345 entries = append(entries, fs.NewDir(name, time.Time{})) 346 } 347 return entries, nil 348 } 349 350 // List the objects and directories in dir into entries. The 351 // entries can be returned in any order but should be for a 352 // complete directory. 353 // 354 // dir should be "" to list the root, and should not have 355 // trailing slashes. 356 // 357 // This should return ErrDirNotFound if the directory isn't 358 // found. 359 func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { 360 // defer fslog.Trace(dir, "")("entries = %q, err = %v", &entries, &err) 361 bucket, directory := f.split(dir) 362 if bucket == "" { 363 if directory != "" { 364 return nil, fs.ErrorListBucketRequired 365 } 366 return f.listBuckets(ctx) 367 } 368 return f.listDir(ctx, bucket, directory, f.rootDirectory, f.rootBucket == "") 369 } 370 371 // ListR lists the objects and directories of the Fs starting 372 // from dir recursively into out. 373 // 374 // dir should be "" to start from the root, and should not 375 // have trailing slashes. 376 // 377 // This should return ErrDirNotFound if the directory isn't 378 // found. 379 // 380 // It should call callback for each tranche of entries read. 381 // These need not be returned in any particular order. If 382 // callback returns an error then the listing will stop 383 // immediately. 384 // 385 // Don't implement this unless you have a more efficient way 386 // of listing recursively that doing a directory traversal. 387 func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) { 388 bucket, directory := f.split(dir) 389 list := walk.NewListRHelper(callback) 390 listR := func(bucket, directory, prefix string, addBucket bool) error { 391 return f.list(ctx, bucket, directory, prefix, addBucket, true, func(remote string, entry fs.DirEntry, isDirectory bool) error { 392 return list.Add(entry) 393 }) 394 } 395 if bucket == "" { 396 entries, err := f.listBuckets(ctx) 397 if err != nil { 398 return err 399 } 400 for _, entry := range entries { 401 err = list.Add(entry) 402 if err != nil { 403 return err 404 } 405 bucket := entry.Remote() 406 err = listR(bucket, "", f.rootDirectory, true) 407 if err != nil { 408 return err 409 } 410 } 411 } else { 412 err = listR(bucket, directory, f.rootDirectory, f.rootBucket == "") 413 if err != nil { 414 return err 415 } 416 } 417 return list.Flush() 418 } 419 420 // Put the object into the bucket 421 // 422 // Copy the reader in to the new object which is returned 423 // 424 // The new object may have been created if an error is returned 425 func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 426 // Temporary Object under construction 427 fs := &Object{ 428 fs: f, 429 remote: src.Remote(), 430 od: &objectData{ 431 modTime: src.ModTime(ctx), 432 }, 433 } 434 return fs, fs.Update(ctx, in, src, options...) 435 } 436 437 // PutStream uploads to the remote path with the modTime given of indeterminate size 438 func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 439 return f.Put(ctx, in, src, options...) 440 } 441 442 // Mkdir creates the bucket if it doesn't exist 443 func (f *Fs) Mkdir(ctx context.Context, dir string) error { 444 bucket, _ := f.split(dir) 445 buckets.makeBucket(bucket) 446 return nil 447 } 448 449 // Rmdir deletes the bucket if the fs is at the root 450 // 451 // Returns an error if it isn't empty 452 func (f *Fs) Rmdir(ctx context.Context, dir string) error { 453 bucket, directory := f.split(dir) 454 if bucket == "" || directory != "" { 455 return nil 456 } 457 return buckets.deleteBucket(bucket) 458 } 459 460 // Precision of the remote 461 func (f *Fs) Precision() time.Duration { 462 return time.Nanosecond 463 } 464 465 // Copy src to this remote using server side copy operations. 466 // 467 // This is stored with the remote path given 468 // 469 // It returns the destination Object and a possible error 470 // 471 // Will only be called if src.Fs().Name() == f.Name() 472 // 473 // If it isn't possible then return fs.ErrorCantCopy 474 func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) { 475 dstBucket, dstPath := f.split(remote) 476 _ = buckets.makeBucket(dstBucket) 477 srcObj, ok := src.(*Object) 478 if !ok { 479 fs.Debugf(src, "Can't copy - not same remote type") 480 return nil, fs.ErrorCantCopy 481 } 482 srcBucket, srcPath := srcObj.split() 483 od := buckets.getObjectData(srcBucket, srcPath) 484 if od == nil { 485 return nil, fs.ErrorObjectNotFound 486 } 487 buckets.updateObjectData(dstBucket, dstPath, od) 488 return f.NewObject(ctx, remote) 489 } 490 491 // Hashes returns the supported hash sets. 492 func (f *Fs) Hashes() hash.Set { 493 return hash.Set(hashType) 494 } 495 496 // ------------------------------------------------------------ 497 498 // Fs returns the parent Fs 499 func (o *Object) Fs() fs.Info { 500 return o.fs 501 } 502 503 // Return a string version 504 func (o *Object) String() string { 505 if o == nil { 506 return "<nil>" 507 } 508 return o.Remote() 509 } 510 511 // Remote returns the remote path 512 func (o *Object) Remote() string { 513 return o.remote 514 } 515 516 // Hash returns the hash of an object returning a lowercase hex string 517 func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) { 518 if t != hashType { 519 return "", hash.ErrUnsupported 520 } 521 if o.od.hash == "" { 522 sum := md5.Sum(o.od.data) 523 o.od.hash = hex.EncodeToString(sum[:]) 524 } 525 return o.od.hash, nil 526 } 527 528 // Size returns the size of an object in bytes 529 func (o *Object) Size() int64 { 530 return int64(len(o.od.data)) 531 } 532 533 // ModTime returns the modification time of the object 534 // 535 // It attempts to read the objects mtime and if that isn't present the 536 // LastModified returned in the http headers 537 // 538 // SHA-1 will also be updated once the request has completed. 539 func (o *Object) ModTime(ctx context.Context) (result time.Time) { 540 return o.od.modTime 541 } 542 543 // SetModTime sets the modification time of the local fs object 544 func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error { 545 o.od.modTime = modTime 546 return nil 547 } 548 549 // Storable returns if this object is storable 550 func (o *Object) Storable() bool { 551 return true 552 } 553 554 // Open an object for read 555 func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) { 556 var offset, limit int64 = 0, -1 557 for _, option := range options { 558 switch x := option.(type) { 559 case *fs.RangeOption: 560 offset, limit = x.Decode(int64(len(o.od.data))) 561 case *fs.SeekOption: 562 offset = x.Offset 563 default: 564 if option.Mandatory() { 565 fs.Logf(o, "Unsupported mandatory option: %v", option) 566 } 567 } 568 } 569 if offset > int64(len(o.od.data)) { 570 offset = int64(len(o.od.data)) 571 } 572 data := o.od.data[offset:] 573 if limit >= 0 { 574 if limit > int64(len(data)) { 575 limit = int64(len(data)) 576 } 577 data = data[:limit] 578 } 579 return ioutil.NopCloser(bytes.NewBuffer(data)), nil 580 } 581 582 // Update the object with the contents of the io.Reader, modTime and size 583 // 584 // The new object may have been created if an error is returned 585 func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) { 586 bucket, bucketPath := o.split() 587 data, err := ioutil.ReadAll(in) 588 if err != nil { 589 return errors.Wrap(err, "failed to update memory object") 590 } 591 o.od = &objectData{ 592 data: data, 593 hash: "", 594 modTime: src.ModTime(ctx), 595 mimeType: fs.MimeType(ctx, o), 596 } 597 buckets.updateObjectData(bucket, bucketPath, o.od) 598 return nil 599 } 600 601 // Remove an object 602 func (o *Object) Remove(ctx context.Context) error { 603 bucket, bucketPath := o.split() 604 removed := buckets.removeObjectData(bucket, bucketPath) 605 if !removed { 606 return fs.ErrorObjectNotFound 607 } 608 return nil 609 } 610 611 // MimeType of an Object if known, "" otherwise 612 func (o *Object) MimeType(ctx context.Context) string { 613 return o.od.mimeType 614 } 615 616 // Check the interfaces are satisfied 617 var ( 618 _ fs.Fs = &Fs{} 619 _ fs.Copier = &Fs{} 620 _ fs.PutStreamer = &Fs{} 621 _ fs.ListRer = &Fs{} 622 _ fs.Object = &Object{} 623 _ fs.MimeTyper = &Object{} 624 )