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  }