github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/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 // Join path1 and path2 33 // 34 // Like path.Join but does not clean the path - useful to preserve trailing / 35 func Join(path1, path2 string) string { 36 if path1 == "" { 37 return path2 38 } 39 if path2 == "" { 40 return path1 41 } 42 return strings.TrimSuffix(path1, "/") + "/" + strings.TrimPrefix(path2, "/") 43 } 44 45 // Cache stores whether buckets are available and their IDs 46 type Cache struct { 47 mu sync.Mutex // mutex to protect created and deleted 48 status map[string]bool // true if we have created the container, false if deleted 49 createMu sync.Mutex // mutex to protect against simultaneous Remove 50 removeMu sync.Mutex // mutex to protect against simultaneous Create 51 } 52 53 // NewCache creates an empty Cache 54 func NewCache() *Cache { 55 return &Cache{ 56 status: make(map[string]bool, 1), 57 } 58 } 59 60 // MarkOK marks the bucket as being present 61 func (c *Cache) MarkOK(bucket string) { 62 if bucket != "" { 63 c.mu.Lock() 64 c.status[bucket] = true 65 c.mu.Unlock() 66 } 67 } 68 69 // MarkDeleted marks the bucket as being deleted 70 func (c *Cache) MarkDeleted(bucket string) { 71 if bucket != "" { 72 c.mu.Lock() 73 c.status[bucket] = false 74 c.mu.Unlock() 75 } 76 } 77 78 type ( 79 // ExistsFn should be passed to Create to see if a bucket 80 // exists or not 81 ExistsFn func() (found bool, err error) 82 83 // CreateFn should be passed to Create to make a bucket 84 CreateFn func() error 85 ) 86 87 // Create the bucket with create() if it doesn't exist 88 // 89 // If exists is set then if the bucket has been deleted it will call 90 // exists() to see if it still exists. 91 // 92 // If f returns an error we assume the bucket was not created 93 func (c *Cache) Create(bucket string, create CreateFn, exists ExistsFn) (err error) { 94 // if we are at the root, then it is OK 95 if bucket == "" { 96 return nil 97 } 98 99 c.createMu.Lock() 100 defer c.createMu.Unlock() 101 c.mu.Lock() 102 defer c.mu.Unlock() 103 104 // if have exists function and bucket has been deleted, check 105 // it still exists 106 if created, ok := c.status[bucket]; ok && !created && exists != nil { 107 found, err := exists() 108 if err == nil { 109 c.status[bucket] = found 110 } 111 if err != nil || found { 112 return err 113 } 114 } 115 116 // If bucket already exists then it is OK 117 if created, ok := c.status[bucket]; ok && created { 118 return nil 119 } 120 121 // Create the bucket 122 c.mu.Unlock() 123 err = create() 124 c.mu.Lock() 125 if err != nil { 126 return err 127 } 128 129 // Mark OK if successful 130 c.status[bucket] = true 131 return nil 132 } 133 134 // Remove the bucket with f if it exists 135 // 136 // If f returns an error we assume the bucket was not removed. 137 // 138 // If the bucket has already been deleted it returns ErrAlreadyDeleted 139 func (c *Cache) Remove(bucket string, f func() error) error { 140 // if we are at the root, then it is OK 141 if bucket == "" { 142 return nil 143 } 144 145 c.removeMu.Lock() 146 defer c.removeMu.Unlock() 147 c.mu.Lock() 148 defer c.mu.Unlock() 149 150 // If bucket already deleted then it is OK 151 if created, ok := c.status[bucket]; ok && !created { 152 return ErrAlreadyDeleted 153 } 154 155 // Remove the bucket 156 c.mu.Unlock() 157 err := f() 158 c.mu.Lock() 159 if err != nil { 160 return err 161 } 162 163 // Mark removed if successful 164 c.status[bucket] = false 165 return err 166 } 167 168 // IsDeleted returns true if the bucket has definitely been deleted by 169 // us, false otherwise. 170 func (c *Cache) IsDeleted(bucket string) bool { 171 c.mu.Lock() 172 created, ok := c.status[bucket] 173 c.mu.Unlock() 174 // if status unknown then return false 175 if !ok { 176 return false 177 } 178 return !created 179 }