github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/lib/bucket/bucket.go (about) 1 // Package bucket is contains utilities for managing bucket based backends 2 package bucket 3 4 import ( 5 "errors" 6 "strings" 7 "sync" 8 ) 9 10 var ( 11 // ErrAlreadyDeleted is returned when an already deleted 12 // bucket is passed to Remove 13 ErrAlreadyDeleted = errors.New("bucket already deleted") 14 ) 15 16 // Split takes an absolute path which includes the bucket and 17 // splits it into a bucket and a path in that bucket 18 // bucketPath 19 func Split(absPath string) (bucket, bucketPath string) { 20 // No bucket 21 if absPath == "" { 22 return "", "" 23 } 24 slash := strings.IndexRune(absPath, '/') 25 // Bucket but no path 26 if slash < 0 { 27 return absPath, "" 28 } 29 return absPath[:slash], absPath[slash+1:] 30 } 31 32 // Cache stores whether buckets are available and their IDs 33 type Cache struct { 34 mu sync.Mutex // mutex to protect created and deleted 35 status map[string]bool // true if we have created the container, false if deleted 36 createMu sync.Mutex // mutex to protect against simultaneous Remove 37 removeMu sync.Mutex // mutex to protect against simultaneous Create 38 } 39 40 // NewCache creates an empty Cache 41 func NewCache() *Cache { 42 return &Cache{ 43 status: make(map[string]bool, 1), 44 } 45 } 46 47 // MarkOK marks the bucket as being present 48 func (c *Cache) MarkOK(bucket string) { 49 if bucket != "" { 50 c.mu.Lock() 51 c.status[bucket] = true 52 c.mu.Unlock() 53 } 54 } 55 56 // MarkDeleted marks the bucket as being deleted 57 func (c *Cache) MarkDeleted(bucket string) { 58 if bucket != "" { 59 c.mu.Lock() 60 c.status[bucket] = false 61 c.mu.Unlock() 62 } 63 } 64 65 type ( 66 // ExistsFn should be passed to Create to see if a bucket 67 // exists or not 68 ExistsFn func() (found bool, err error) 69 70 // CreateFn should be passed to Create to make a bucket 71 CreateFn func() error 72 ) 73 74 // Create the bucket with create() if it doesn't exist 75 // 76 // If exists is set then if the bucket has been deleted it will call 77 // exists() to see if it still exists. 78 // 79 // If f returns an error we assume the bucket was not created 80 func (c *Cache) Create(bucket string, create CreateFn, exists ExistsFn) (err error) { 81 // if we are at the root, then it is OK 82 if bucket == "" { 83 return nil 84 } 85 86 c.createMu.Lock() 87 defer c.createMu.Unlock() 88 c.mu.Lock() 89 defer c.mu.Unlock() 90 91 // if have exists fuction and bucket has been deleted, check 92 // it still exists 93 if created, ok := c.status[bucket]; ok && !created && exists != nil { 94 found, err := exists() 95 if err == nil { 96 c.status[bucket] = found 97 } 98 if err != nil || found { 99 return err 100 } 101 } 102 103 // If bucket already exists then it is OK 104 if created, ok := c.status[bucket]; ok && created { 105 return nil 106 } 107 108 // Create the bucket 109 c.mu.Unlock() 110 err = create() 111 c.mu.Lock() 112 if err != nil { 113 return err 114 } 115 116 // Mark OK if successful 117 c.status[bucket] = true 118 return nil 119 } 120 121 // Remove the bucket with f if it exists 122 // 123 // If f returns an error we assume the bucket was not removed 124 // 125 // If the bucket has already been deleted it returns ErrAlreadyDeleted 126 func (c *Cache) Remove(bucket string, f func() error) error { 127 // if we are at the root, then it is OK 128 if bucket == "" { 129 return nil 130 } 131 132 c.removeMu.Lock() 133 defer c.removeMu.Unlock() 134 c.mu.Lock() 135 defer c.mu.Unlock() 136 137 // If bucket already deleted then it is OK 138 if created, ok := c.status[bucket]; ok && !created { 139 return ErrAlreadyDeleted 140 } 141 142 // Remove the bucket 143 c.mu.Unlock() 144 err := f() 145 c.mu.Lock() 146 if err != nil { 147 return err 148 } 149 150 // Mark removed if successful 151 c.status[bucket] = false 152 return err 153 } 154 155 // IsDeleted returns true if the bucket has definitely been deleted by 156 // us, false otherwise. 157 func (c *Cache) IsDeleted(bucket string) bool { 158 c.mu.Lock() 159 created, ok := c.status[bucket] 160 c.mu.Unlock() 161 // if status unknown then return false 162 if !ok { 163 return false 164 } 165 return !created 166 }