github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/swift/swift.go (about)

     1  // Package swift provides an interface to the Swift object storage system
     2  package swift
     3  
     4  import (
     5  	"bufio"
     6  	"bytes"
     7  	"context"
     8  	"fmt"
     9  	"io"
    10  	"net/url"
    11  	"path"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/ncw/swift"
    17  	"github.com/pkg/errors"
    18  	"github.com/rclone/rclone/fs"
    19  	"github.com/rclone/rclone/fs/config"
    20  	"github.com/rclone/rclone/fs/config/configmap"
    21  	"github.com/rclone/rclone/fs/config/configstruct"
    22  	"github.com/rclone/rclone/fs/fserrors"
    23  	"github.com/rclone/rclone/fs/fshttp"
    24  	"github.com/rclone/rclone/fs/hash"
    25  	"github.com/rclone/rclone/fs/operations"
    26  	"github.com/rclone/rclone/fs/walk"
    27  	"github.com/rclone/rclone/lib/bucket"
    28  	"github.com/rclone/rclone/lib/encoder"
    29  	"github.com/rclone/rclone/lib/pacer"
    30  	"github.com/rclone/rclone/lib/readers"
    31  )
    32  
    33  // Constants
    34  const (
    35  	directoryMarkerContentType = "application/directory" // content type of directory marker objects
    36  	listChunks                 = 1000                    // chunk size to read directory listings
    37  	defaultChunkSize           = 5 * fs.GibiByte
    38  	minSleep                   = 10 * time.Millisecond // In case of error, start at 10ms sleep.
    39  )
    40  
    41  // SharedOptions are shared between swift and hubic
    42  var SharedOptions = []fs.Option{{
    43  	Name: "chunk_size",
    44  	Help: `Above this size files will be chunked into a _segments container.
    45  
    46  Above this size files will be chunked into a _segments container.  The
    47  default for this is 5GB which is its maximum value.`,
    48  	Default:  defaultChunkSize,
    49  	Advanced: true,
    50  }, {
    51  	Name: "no_chunk",
    52  	Help: `Don't chunk files during streaming upload.
    53  
    54  When doing streaming uploads (eg using rcat or mount) setting this
    55  flag will cause the swift backend to not upload chunked files.
    56  
    57  This will limit the maximum upload size to 5GB. However non chunked
    58  files are easier to deal with and have an MD5SUM.
    59  
    60  Rclone will still chunk files bigger than chunk_size when doing normal
    61  copy operations.`,
    62  	Default:  false,
    63  	Advanced: true,
    64  }, {
    65  	Name:     config.ConfigEncoding,
    66  	Help:     config.ConfigEncodingHelp,
    67  	Advanced: true,
    68  	Default: (encoder.EncodeInvalidUtf8 |
    69  		encoder.EncodeSlash),
    70  }}
    71  
    72  // Register with Fs
    73  func init() {
    74  	fs.Register(&fs.RegInfo{
    75  		Name:        "swift",
    76  		Description: "OpenStack Swift (Rackspace Cloud Files, Memset Memstore, OVH)",
    77  		NewFs:       NewFs,
    78  		Options: append([]fs.Option{{
    79  			Name:    "env_auth",
    80  			Help:    "Get swift credentials from environment variables in standard OpenStack form.",
    81  			Default: false,
    82  			Examples: []fs.OptionExample{
    83  				{
    84  					Value: "false",
    85  					Help:  "Enter swift credentials in the next step",
    86  				}, {
    87  					Value: "true",
    88  					Help:  "Get swift credentials from environment vars. Leave other fields blank if using this.",
    89  				},
    90  			},
    91  		}, {
    92  			Name: "user",
    93  			Help: "User name to log in (OS_USERNAME).",
    94  		}, {
    95  			Name: "key",
    96  			Help: "API key or password (OS_PASSWORD).",
    97  		}, {
    98  			Name: "auth",
    99  			Help: "Authentication URL for server (OS_AUTH_URL).",
   100  			Examples: []fs.OptionExample{{
   101  				Help:  "Rackspace US",
   102  				Value: "https://auth.api.rackspacecloud.com/v1.0",
   103  			}, {
   104  				Help:  "Rackspace UK",
   105  				Value: "https://lon.auth.api.rackspacecloud.com/v1.0",
   106  			}, {
   107  				Help:  "Rackspace v2",
   108  				Value: "https://identity.api.rackspacecloud.com/v2.0",
   109  			}, {
   110  				Help:  "Memset Memstore UK",
   111  				Value: "https://auth.storage.memset.com/v1.0",
   112  			}, {
   113  				Help:  "Memset Memstore UK v2",
   114  				Value: "https://auth.storage.memset.com/v2.0",
   115  			}, {
   116  				Help:  "OVH",
   117  				Value: "https://auth.cloud.ovh.net/v3",
   118  			}},
   119  		}, {
   120  			Name: "user_id",
   121  			Help: "User ID to log in - optional - most swift systems use user and leave this blank (v3 auth) (OS_USER_ID).",
   122  		}, {
   123  			Name: "domain",
   124  			Help: "User domain - optional (v3 auth) (OS_USER_DOMAIN_NAME)",
   125  		}, {
   126  			Name: "tenant",
   127  			Help: "Tenant name - optional for v1 auth, this or tenant_id required otherwise (OS_TENANT_NAME or OS_PROJECT_NAME)",
   128  		}, {
   129  			Name: "tenant_id",
   130  			Help: "Tenant ID - optional for v1 auth, this or tenant required otherwise (OS_TENANT_ID)",
   131  		}, {
   132  			Name: "tenant_domain",
   133  			Help: "Tenant domain - optional (v3 auth) (OS_PROJECT_DOMAIN_NAME)",
   134  		}, {
   135  			Name: "region",
   136  			Help: "Region name - optional (OS_REGION_NAME)",
   137  		}, {
   138  			Name: "storage_url",
   139  			Help: "Storage URL - optional (OS_STORAGE_URL)",
   140  		}, {
   141  			Name: "auth_token",
   142  			Help: "Auth Token from alternate authentication - optional (OS_AUTH_TOKEN)",
   143  		}, {
   144  			Name: "application_credential_id",
   145  			Help: "Application Credential ID (OS_APPLICATION_CREDENTIAL_ID)",
   146  		}, {
   147  			Name: "application_credential_name",
   148  			Help: "Application Credential Name (OS_APPLICATION_CREDENTIAL_NAME)",
   149  		}, {
   150  			Name: "application_credential_secret",
   151  			Help: "Application Credential Secret (OS_APPLICATION_CREDENTIAL_SECRET)",
   152  		}, {
   153  			Name:    "auth_version",
   154  			Help:    "AuthVersion - optional - set to (1,2,3) if your auth URL has no version (ST_AUTH_VERSION)",
   155  			Default: 0,
   156  		}, {
   157  			Name:    "endpoint_type",
   158  			Help:    "Endpoint type to choose from the service catalogue (OS_ENDPOINT_TYPE)",
   159  			Default: "public",
   160  			Examples: []fs.OptionExample{{
   161  				Help:  "Public (default, choose this if not sure)",
   162  				Value: "public",
   163  			}, {
   164  				Help:  "Internal (use internal service net)",
   165  				Value: "internal",
   166  			}, {
   167  				Help:  "Admin",
   168  				Value: "admin",
   169  			}},
   170  		}, {
   171  			Name: "storage_policy",
   172  			Help: `The storage policy to use when creating a new container
   173  
   174  This applies the specified storage policy when creating a new
   175  container. The policy cannot be changed afterwards. The allowed
   176  configuration values and their meaning depend on your Swift storage
   177  provider.`,
   178  			Default: "",
   179  			Examples: []fs.OptionExample{{
   180  				Help:  "Default",
   181  				Value: "",
   182  			}, {
   183  				Help:  "OVH Public Cloud Storage",
   184  				Value: "pcs",
   185  			}, {
   186  				Help:  "OVH Public Cloud Archive",
   187  				Value: "pca",
   188  			}},
   189  		}}, SharedOptions...),
   190  	})
   191  }
   192  
   193  // Options defines the configuration for this backend
   194  type Options struct {
   195  	EnvAuth                     bool                 `config:"env_auth"`
   196  	User                        string               `config:"user"`
   197  	Key                         string               `config:"key"`
   198  	Auth                        string               `config:"auth"`
   199  	UserID                      string               `config:"user_id"`
   200  	Domain                      string               `config:"domain"`
   201  	Tenant                      string               `config:"tenant"`
   202  	TenantID                    string               `config:"tenant_id"`
   203  	TenantDomain                string               `config:"tenant_domain"`
   204  	Region                      string               `config:"region"`
   205  	StorageURL                  string               `config:"storage_url"`
   206  	AuthToken                   string               `config:"auth_token"`
   207  	AuthVersion                 int                  `config:"auth_version"`
   208  	ApplicationCredentialID     string               `config:"application_credential_id"`
   209  	ApplicationCredentialName   string               `config:"application_credential_name"`
   210  	ApplicationCredentialSecret string               `config:"application_credential_secret"`
   211  	StoragePolicy               string               `config:"storage_policy"`
   212  	EndpointType                string               `config:"endpoint_type"`
   213  	ChunkSize                   fs.SizeSuffix        `config:"chunk_size"`
   214  	NoChunk                     bool                 `config:"no_chunk"`
   215  	Enc                         encoder.MultiEncoder `config:"encoding"`
   216  }
   217  
   218  // Fs represents a remote swift server
   219  type Fs struct {
   220  	name             string            // name of this remote
   221  	root             string            // the path we are working on if any
   222  	features         *fs.Features      // optional features
   223  	opt              Options           // options for this backend
   224  	c                *swift.Connection // the connection to the swift server
   225  	rootContainer    string            // container part of root (if any)
   226  	rootDirectory    string            // directory part of root (if any)
   227  	cache            *bucket.Cache     // cache of container status
   228  	noCheckContainer bool              // don't check the container before creating it
   229  	pacer            *fs.Pacer         // To pace the API calls
   230  }
   231  
   232  // Object describes a swift object
   233  //
   234  // Will definitely have info but maybe not meta
   235  type Object struct {
   236  	fs           *Fs    // what this object is part of
   237  	remote       string // The remote path
   238  	size         int64
   239  	lastModified time.Time
   240  	contentType  string
   241  	md5          string
   242  	headers      swift.Headers // The object headers if known
   243  }
   244  
   245  // ------------------------------------------------------------
   246  
   247  // Name of the remote (as passed into NewFs)
   248  func (f *Fs) Name() string {
   249  	return f.name
   250  }
   251  
   252  // Root of the remote (as passed into NewFs)
   253  func (f *Fs) Root() string {
   254  	return f.root
   255  }
   256  
   257  // String converts this Fs to a string
   258  func (f *Fs) String() string {
   259  	if f.rootContainer == "" {
   260  		return fmt.Sprintf("Swift root")
   261  	}
   262  	if f.rootDirectory == "" {
   263  		return fmt.Sprintf("Swift container %s", f.rootContainer)
   264  	}
   265  	return fmt.Sprintf("Swift container %s path %s", f.rootContainer, f.rootDirectory)
   266  }
   267  
   268  // Features returns the optional features of this Fs
   269  func (f *Fs) Features() *fs.Features {
   270  	return f.features
   271  }
   272  
   273  // retryErrorCodes is a slice of error codes that we will retry
   274  var retryErrorCodes = []int{
   275  	401, // Unauthorized (eg "Token has expired")
   276  	408, // Request Timeout
   277  	409, // Conflict - various states that could be resolved on a retry
   278  	429, // Rate exceeded.
   279  	500, // Get occasional 500 Internal Server Error
   280  	503, // Service Unavailable/Slow Down - "Reduce your request rate"
   281  	504, // Gateway Time-out
   282  }
   283  
   284  // shouldRetry returns a boolean as to whether this err deserves to be
   285  // retried.  It returns the err as a convenience
   286  func shouldRetry(err error) (bool, error) {
   287  	// If this is a swift.Error object extract the HTTP error code
   288  	if swiftError, ok := err.(*swift.Error); ok {
   289  		for _, e := range retryErrorCodes {
   290  			if swiftError.StatusCode == e {
   291  				return true, err
   292  			}
   293  		}
   294  	}
   295  	// Check for generic failure conditions
   296  	return fserrors.ShouldRetry(err), err
   297  }
   298  
   299  // shouldRetryHeaders returns a boolean as to whether this err
   300  // deserves to be retried.  It reads the headers passed in looking for
   301  // `Retry-After`. It returns the err as a convenience
   302  func shouldRetryHeaders(headers swift.Headers, err error) (bool, error) {
   303  	if swiftError, ok := err.(*swift.Error); ok && swiftError.StatusCode == 429 {
   304  		if value := headers["Retry-After"]; value != "" {
   305  			retryAfter, parseErr := strconv.Atoi(value)
   306  			if parseErr != nil {
   307  				fs.Errorf(nil, "Failed to parse Retry-After: %q: %v", value, parseErr)
   308  			} else {
   309  				duration := time.Second * time.Duration(retryAfter)
   310  				if duration <= 60*time.Second {
   311  					// Do a short sleep immediately
   312  					fs.Debugf(nil, "Sleeping for %v to obey Retry-After", duration)
   313  					time.Sleep(duration)
   314  					return true, err
   315  				}
   316  				// Delay a long sleep for a retry
   317  				return false, fserrors.NewErrorRetryAfter(duration)
   318  			}
   319  		}
   320  	}
   321  	return shouldRetry(err)
   322  }
   323  
   324  // parsePath parses a remote 'url'
   325  func parsePath(path string) (root string) {
   326  	root = strings.Trim(path, "/")
   327  	return
   328  }
   329  
   330  // split returns container and containerPath from the rootRelativePath
   331  // relative to f.root
   332  func (f *Fs) split(rootRelativePath string) (container, containerPath string) {
   333  	container, containerPath = bucket.Split(path.Join(f.root, rootRelativePath))
   334  	return f.opt.Enc.FromStandardName(container), f.opt.Enc.FromStandardPath(containerPath)
   335  }
   336  
   337  // split returns container and containerPath from the object
   338  func (o *Object) split() (container, containerPath string) {
   339  	return o.fs.split(o.remote)
   340  }
   341  
   342  // swiftConnection makes a connection to swift
   343  func swiftConnection(opt *Options, name string) (*swift.Connection, error) {
   344  	c := &swift.Connection{
   345  		// Keep these in the same order as the Config for ease of checking
   346  		UserName:                    opt.User,
   347  		ApiKey:                      opt.Key,
   348  		AuthUrl:                     opt.Auth,
   349  		UserId:                      opt.UserID,
   350  		Domain:                      opt.Domain,
   351  		Tenant:                      opt.Tenant,
   352  		TenantId:                    opt.TenantID,
   353  		TenantDomain:                opt.TenantDomain,
   354  		Region:                      opt.Region,
   355  		StorageUrl:                  opt.StorageURL,
   356  		AuthToken:                   opt.AuthToken,
   357  		AuthVersion:                 opt.AuthVersion,
   358  		ApplicationCredentialId:     opt.ApplicationCredentialID,
   359  		ApplicationCredentialName:   opt.ApplicationCredentialName,
   360  		ApplicationCredentialSecret: opt.ApplicationCredentialSecret,
   361  		EndpointType:                swift.EndpointType(opt.EndpointType),
   362  		ConnectTimeout:              10 * fs.Config.ConnectTimeout, // Use the timeouts in the transport
   363  		Timeout:                     10 * fs.Config.Timeout,        // Use the timeouts in the transport
   364  		Transport:                   fshttp.NewTransport(fs.Config),
   365  	}
   366  	if opt.EnvAuth {
   367  		err := c.ApplyEnvironment()
   368  		if err != nil {
   369  			return nil, errors.Wrap(err, "failed to read environment variables")
   370  		}
   371  	}
   372  	StorageUrl, AuthToken := c.StorageUrl, c.AuthToken // nolint
   373  	if !c.Authenticated() {
   374  		if (c.ApplicationCredentialId != "" || c.ApplicationCredentialName != "") && c.ApplicationCredentialSecret == "" {
   375  			if c.UserName == "" && c.UserId == "" {
   376  				return nil, errors.New("user name or user id not found for authentication (and no storage_url+auth_token is provided)")
   377  			}
   378  			if c.ApiKey == "" {
   379  				return nil, errors.New("key not found")
   380  			}
   381  		}
   382  		if c.AuthUrl == "" {
   383  			return nil, errors.New("auth not found")
   384  		}
   385  		err := c.Authenticate() // fills in c.StorageUrl and c.AuthToken
   386  		if err != nil {
   387  			return nil, err
   388  		}
   389  	}
   390  	// Make sure we re-auth with the AuthToken and StorageUrl
   391  	// provided by wrapping the existing auth, so we can just
   392  	// override one or the other or both.
   393  	if StorageUrl != "" || AuthToken != "" {
   394  		// Re-write StorageURL and AuthToken if they are being
   395  		// overridden as c.Authenticate above will have
   396  		// overwritten them.
   397  		if StorageUrl != "" {
   398  			c.StorageUrl = StorageUrl
   399  		}
   400  		if AuthToken != "" {
   401  			c.AuthToken = AuthToken
   402  		}
   403  		c.Auth = newAuth(c.Auth, StorageUrl, AuthToken)
   404  	}
   405  	return c, nil
   406  }
   407  
   408  func checkUploadChunkSize(cs fs.SizeSuffix) error {
   409  	const minChunkSize = fs.Byte
   410  	if cs < minChunkSize {
   411  		return errors.Errorf("%s is less than %s", cs, minChunkSize)
   412  	}
   413  	return nil
   414  }
   415  
   416  func (f *Fs) setUploadChunkSize(cs fs.SizeSuffix) (old fs.SizeSuffix, err error) {
   417  	err = checkUploadChunkSize(cs)
   418  	if err == nil {
   419  		old, f.opt.ChunkSize = f.opt.ChunkSize, cs
   420  	}
   421  	return
   422  }
   423  
   424  // setRoot changes the root of the Fs
   425  func (f *Fs) setRoot(root string) {
   426  	f.root = parsePath(root)
   427  	f.rootContainer, f.rootDirectory = bucket.Split(f.root)
   428  }
   429  
   430  // NewFsWithConnection constructs an Fs from the path, container:path
   431  // and authenticated connection.
   432  //
   433  // if noCheckContainer is set then the Fs won't check the container
   434  // exists before creating it.
   435  func NewFsWithConnection(opt *Options, name, root string, c *swift.Connection, noCheckContainer bool) (fs.Fs, error) {
   436  	f := &Fs{
   437  		name:             name,
   438  		opt:              *opt,
   439  		c:                c,
   440  		noCheckContainer: noCheckContainer,
   441  		pacer:            fs.NewPacer(pacer.NewS3(pacer.MinSleep(minSleep))),
   442  		cache:            bucket.NewCache(),
   443  	}
   444  	f.setRoot(root)
   445  	f.features = (&fs.Features{
   446  		ReadMimeType:      true,
   447  		WriteMimeType:     true,
   448  		BucketBased:       true,
   449  		BucketBasedRootOK: true,
   450  	}).Fill(f)
   451  	if f.rootContainer != "" && f.rootDirectory != "" {
   452  		// Check to see if the object exists - ignoring directory markers
   453  		var info swift.Object
   454  		var err error
   455  		encodedDirectory := f.opt.Enc.FromStandardPath(f.rootDirectory)
   456  		err = f.pacer.Call(func() (bool, error) {
   457  			var rxHeaders swift.Headers
   458  			info, rxHeaders, err = f.c.Object(f.rootContainer, encodedDirectory)
   459  			return shouldRetryHeaders(rxHeaders, err)
   460  		})
   461  		if err == nil && info.ContentType != directoryMarkerContentType {
   462  			newRoot := path.Dir(f.root)
   463  			if newRoot == "." {
   464  				newRoot = ""
   465  			}
   466  			f.setRoot(newRoot)
   467  			// return an error with an fs which points to the parent
   468  			return f, fs.ErrorIsFile
   469  		}
   470  	}
   471  	return f, nil
   472  }
   473  
   474  // NewFs constructs an Fs from the path, container:path
   475  func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
   476  	// Parse config into Options struct
   477  	opt := new(Options)
   478  	err := configstruct.Set(m, opt)
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  	err = checkUploadChunkSize(opt.ChunkSize)
   483  	if err != nil {
   484  		return nil, errors.Wrap(err, "swift: chunk size")
   485  	}
   486  
   487  	c, err := swiftConnection(opt, name)
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  	return NewFsWithConnection(opt, name, root, c, false)
   492  }
   493  
   494  // Return an Object from a path
   495  //
   496  // If it can't be found it returns the error fs.ErrorObjectNotFound.
   497  func (f *Fs) newObjectWithInfo(remote string, info *swift.Object) (fs.Object, error) {
   498  	o := &Object{
   499  		fs:     f,
   500  		remote: remote,
   501  	}
   502  	// Note that due to a quirk of swift, dynamic large objects are
   503  	// returned as 0 bytes in the listing.  Correct this here by
   504  	// making sure we read the full metadata for all 0 byte files.
   505  	// We don't read the metadata for directory marker objects.
   506  	if info != nil && info.Bytes == 0 && info.ContentType != "application/directory" {
   507  		info = nil
   508  	}
   509  	if info != nil {
   510  		// Set info but not headers
   511  		err := o.decodeMetaData(info)
   512  		if err != nil {
   513  			return nil, err
   514  		}
   515  	} else {
   516  		err := o.readMetaData() // reads info and headers, returning an error
   517  		if err != nil {
   518  			return nil, err
   519  		}
   520  	}
   521  	return o, nil
   522  }
   523  
   524  // NewObject finds the Object at remote.  If it can't be found it
   525  // returns the error fs.ErrorObjectNotFound.
   526  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   527  	return f.newObjectWithInfo(remote, nil)
   528  }
   529  
   530  // listFn is called from list and listContainerRoot to handle an object.
   531  type listFn func(remote string, object *swift.Object, isDirectory bool) error
   532  
   533  // listContainerRoot lists the objects into the function supplied from
   534  // the container and directory supplied.  The remote has prefix
   535  // removed from it and if addContainer is set then it adds the
   536  // container to the start.
   537  //
   538  // Set recurse to read sub directories
   539  func (f *Fs) listContainerRoot(container, directory, prefix string, addContainer bool, recurse bool, fn listFn) error {
   540  	if prefix != "" && !strings.HasSuffix(prefix, "/") {
   541  		prefix += "/"
   542  	}
   543  	if directory != "" && !strings.HasSuffix(directory, "/") {
   544  		directory += "/"
   545  	}
   546  	// Options for ObjectsWalk
   547  	opts := swift.ObjectsOpts{
   548  		Prefix: directory,
   549  		Limit:  listChunks,
   550  	}
   551  	if !recurse {
   552  		opts.Delimiter = '/'
   553  	}
   554  	return f.c.ObjectsWalk(container, &opts, func(opts *swift.ObjectsOpts) (interface{}, error) {
   555  		var objects []swift.Object
   556  		var err error
   557  		err = f.pacer.Call(func() (bool, error) {
   558  			objects, err = f.c.Objects(container, opts)
   559  			return shouldRetry(err)
   560  		})
   561  		if err == nil {
   562  			for i := range objects {
   563  				object := &objects[i]
   564  				isDirectory := false
   565  				if !recurse {
   566  					isDirectory = strings.HasSuffix(object.Name, "/")
   567  				}
   568  				remote := f.opt.Enc.ToStandardPath(object.Name)
   569  				if !strings.HasPrefix(remote, prefix) {
   570  					fs.Logf(f, "Odd name received %q", remote)
   571  					continue
   572  				}
   573  				if remote == prefix {
   574  					// If we have zero length directory markers ending in / then swift
   575  					// will return them in the listing for the directory which causes
   576  					// duplicate directories.  Ignore them here.
   577  					continue
   578  				}
   579  				remote = remote[len(prefix):]
   580  				if addContainer {
   581  					remote = path.Join(container, remote)
   582  				}
   583  				err = fn(remote, object, isDirectory)
   584  				if err != nil {
   585  					break
   586  				}
   587  			}
   588  		}
   589  		return objects, err
   590  	})
   591  }
   592  
   593  type addEntryFn func(fs.DirEntry) error
   594  
   595  // list the objects into the function supplied
   596  func (f *Fs) list(container, directory, prefix string, addContainer bool, recurse bool, fn addEntryFn) error {
   597  	err := f.listContainerRoot(container, directory, prefix, addContainer, recurse, func(remote string, object *swift.Object, isDirectory bool) (err error) {
   598  		if isDirectory {
   599  			remote = strings.TrimRight(remote, "/")
   600  			d := fs.NewDir(remote, time.Time{}).SetSize(object.Bytes)
   601  			err = fn(d)
   602  		} else {
   603  			// newObjectWithInfo does a full metadata read on 0 size objects which might be dynamic large objects
   604  			var o fs.Object
   605  			o, err = f.newObjectWithInfo(remote, object)
   606  			if err != nil {
   607  				return err
   608  			}
   609  			if o.Storable() {
   610  				err = fn(o)
   611  			}
   612  		}
   613  		return err
   614  	})
   615  	if err == swift.ContainerNotFound {
   616  		err = fs.ErrorDirNotFound
   617  	}
   618  	return err
   619  }
   620  
   621  // listDir lists a single directory
   622  func (f *Fs) listDir(container, directory, prefix string, addContainer bool) (entries fs.DirEntries, err error) {
   623  	if container == "" {
   624  		return nil, fs.ErrorListBucketRequired
   625  	}
   626  	// List the objects
   627  	err = f.list(container, directory, prefix, addContainer, false, func(entry fs.DirEntry) error {
   628  		entries = append(entries, entry)
   629  		return nil
   630  	})
   631  	if err != nil {
   632  		return nil, err
   633  	}
   634  	// container must be present if listing succeeded
   635  	f.cache.MarkOK(container)
   636  	return entries, nil
   637  }
   638  
   639  // listContainers lists the containers
   640  func (f *Fs) listContainers(ctx context.Context) (entries fs.DirEntries, err error) {
   641  	var containers []swift.Container
   642  	err = f.pacer.Call(func() (bool, error) {
   643  		containers, err = f.c.ContainersAll(nil)
   644  		return shouldRetry(err)
   645  	})
   646  	if err != nil {
   647  		return nil, errors.Wrap(err, "container listing failed")
   648  	}
   649  	for _, container := range containers {
   650  		f.cache.MarkOK(container.Name)
   651  		d := fs.NewDir(f.opt.Enc.ToStandardName(container.Name), time.Time{}).SetSize(container.Bytes).SetItems(container.Count)
   652  		entries = append(entries, d)
   653  	}
   654  	return entries, nil
   655  }
   656  
   657  // List the objects and directories in dir into entries.  The
   658  // entries can be returned in any order but should be for a
   659  // complete directory.
   660  //
   661  // dir should be "" to list the root, and should not have
   662  // trailing slashes.
   663  //
   664  // This should return ErrDirNotFound if the directory isn't
   665  // found.
   666  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   667  	container, directory := f.split(dir)
   668  	if container == "" {
   669  		if directory != "" {
   670  			return nil, fs.ErrorListBucketRequired
   671  		}
   672  		return f.listContainers(ctx)
   673  	}
   674  	return f.listDir(container, directory, f.rootDirectory, f.rootContainer == "")
   675  }
   676  
   677  // ListR lists the objects and directories of the Fs starting
   678  // from dir recursively into out.
   679  //
   680  // dir should be "" to start from the root, and should not
   681  // have trailing slashes.
   682  //
   683  // This should return ErrDirNotFound if the directory isn't
   684  // found.
   685  //
   686  // It should call callback for each tranche of entries read.
   687  // These need not be returned in any particular order.  If
   688  // callback returns an error then the listing will stop
   689  // immediately.
   690  //
   691  // Don't implement this unless you have a more efficient way
   692  // of listing recursively than doing a directory traversal.
   693  func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) {
   694  	container, directory := f.split(dir)
   695  	list := walk.NewListRHelper(callback)
   696  	listR := func(container, directory, prefix string, addContainer bool) error {
   697  		return f.list(container, directory, prefix, addContainer, true, func(entry fs.DirEntry) error {
   698  			return list.Add(entry)
   699  		})
   700  	}
   701  	if container == "" {
   702  		entries, err := f.listContainers(ctx)
   703  		if err != nil {
   704  			return err
   705  		}
   706  		for _, entry := range entries {
   707  			err = list.Add(entry)
   708  			if err != nil {
   709  				return err
   710  			}
   711  			container := entry.Remote()
   712  			err = listR(container, "", f.rootDirectory, true)
   713  			if err != nil {
   714  				return err
   715  			}
   716  			// container must be present if listing succeeded
   717  			f.cache.MarkOK(container)
   718  		}
   719  	} else {
   720  		err = listR(container, directory, f.rootDirectory, f.rootContainer == "")
   721  		if err != nil {
   722  			return err
   723  		}
   724  		// container must be present if listing succeeded
   725  		f.cache.MarkOK(container)
   726  	}
   727  	return list.Flush()
   728  }
   729  
   730  // About gets quota information
   731  func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
   732  	var containers []swift.Container
   733  	var err error
   734  	err = f.pacer.Call(func() (bool, error) {
   735  		containers, err = f.c.ContainersAll(nil)
   736  		return shouldRetry(err)
   737  	})
   738  	if err != nil {
   739  		return nil, errors.Wrap(err, "container listing failed")
   740  	}
   741  	var total, objects int64
   742  	for _, c := range containers {
   743  		total += c.Bytes
   744  		objects += c.Count
   745  	}
   746  	usage := &fs.Usage{
   747  		Used:    fs.NewUsageValue(total),   // bytes in use
   748  		Objects: fs.NewUsageValue(objects), // objects in use
   749  	}
   750  	return usage, nil
   751  }
   752  
   753  // Put the object into the container
   754  //
   755  // Copy the reader in to the new object which is returned
   756  //
   757  // The new object may have been created if an error is returned
   758  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   759  	// Temporary Object under construction
   760  	fs := &Object{
   761  		fs:      f,
   762  		remote:  src.Remote(),
   763  		headers: swift.Headers{}, // Empty object headers to stop readMetaData being called
   764  	}
   765  	return fs, fs.Update(ctx, in, src, options...)
   766  }
   767  
   768  // PutStream uploads to the remote path with the modTime given of indeterminate size
   769  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   770  	return f.Put(ctx, in, src, options...)
   771  }
   772  
   773  // Mkdir creates the container if it doesn't exist
   774  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   775  	container, _ := f.split(dir)
   776  	return f.makeContainer(ctx, container)
   777  }
   778  
   779  // makeContainer creates the container if it doesn't exist
   780  func (f *Fs) makeContainer(ctx context.Context, container string) error {
   781  	return f.cache.Create(container, func() error {
   782  		// Check to see if container exists first
   783  		var err error = swift.ContainerNotFound
   784  		if !f.noCheckContainer {
   785  			err = f.pacer.Call(func() (bool, error) {
   786  				var rxHeaders swift.Headers
   787  				_, rxHeaders, err = f.c.Container(container)
   788  				return shouldRetryHeaders(rxHeaders, err)
   789  			})
   790  		}
   791  		if err == swift.ContainerNotFound {
   792  			headers := swift.Headers{}
   793  			if f.opt.StoragePolicy != "" {
   794  				headers["X-Storage-Policy"] = f.opt.StoragePolicy
   795  			}
   796  			err = f.pacer.Call(func() (bool, error) {
   797  				err = f.c.ContainerCreate(container, headers)
   798  				return shouldRetry(err)
   799  			})
   800  			if err == nil {
   801  				fs.Infof(f, "Container %q created", container)
   802  			}
   803  		}
   804  		return err
   805  	}, nil)
   806  }
   807  
   808  // Rmdir deletes the container if the fs is at the root
   809  //
   810  // Returns an error if it isn't empty
   811  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   812  	container, directory := f.split(dir)
   813  	if container == "" || directory != "" {
   814  		return nil
   815  	}
   816  	err := f.cache.Remove(container, func() error {
   817  		err := f.pacer.Call(func() (bool, error) {
   818  			err := f.c.ContainerDelete(container)
   819  			return shouldRetry(err)
   820  		})
   821  		if err == nil {
   822  			fs.Infof(f, "Container %q removed", container)
   823  		}
   824  		return err
   825  	})
   826  	return err
   827  }
   828  
   829  // Precision of the remote
   830  func (f *Fs) Precision() time.Duration {
   831  	return time.Nanosecond
   832  }
   833  
   834  // Purge deletes all the files and directories
   835  //
   836  // Implemented here so we can make sure we delete directory markers
   837  func (f *Fs) Purge(ctx context.Context) error {
   838  	// Delete all the files including the directory markers
   839  	toBeDeleted := make(chan fs.Object, fs.Config.Transfers)
   840  	delErr := make(chan error, 1)
   841  	go func() {
   842  		delErr <- operations.DeleteFiles(ctx, toBeDeleted)
   843  	}()
   844  	err := f.list(f.rootContainer, f.rootDirectory, f.rootDirectory, f.rootContainer == "", true, func(entry fs.DirEntry) error {
   845  		if o, ok := entry.(*Object); ok {
   846  			toBeDeleted <- o
   847  		}
   848  		return nil
   849  	})
   850  	close(toBeDeleted)
   851  	delError := <-delErr
   852  	if err == nil {
   853  		err = delError
   854  	}
   855  	if err != nil {
   856  		return err
   857  	}
   858  	return f.Rmdir(ctx, "")
   859  }
   860  
   861  // Copy src to this remote using server side copy operations.
   862  //
   863  // This is stored with the remote path given
   864  //
   865  // It returns the destination Object and a possible error
   866  //
   867  // Will only be called if src.Fs().Name() == f.Name()
   868  //
   869  // If it isn't possible then return fs.ErrorCantCopy
   870  func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   871  	dstContainer, dstPath := f.split(remote)
   872  	err := f.makeContainer(ctx, dstContainer)
   873  	if err != nil {
   874  		return nil, err
   875  	}
   876  	srcObj, ok := src.(*Object)
   877  	if !ok {
   878  		fs.Debugf(src, "Can't copy - not same remote type")
   879  		return nil, fs.ErrorCantCopy
   880  	}
   881  	srcContainer, srcPath := srcObj.split()
   882  	err = f.pacer.Call(func() (bool, error) {
   883  		var rxHeaders swift.Headers
   884  		rxHeaders, err = f.c.ObjectCopy(srcContainer, srcPath, dstContainer, dstPath, nil)
   885  		return shouldRetryHeaders(rxHeaders, err)
   886  	})
   887  	if err != nil {
   888  		return nil, err
   889  	}
   890  	return f.NewObject(ctx, remote)
   891  }
   892  
   893  // Hashes returns the supported hash sets.
   894  func (f *Fs) Hashes() hash.Set {
   895  	return hash.Set(hash.MD5)
   896  }
   897  
   898  // ------------------------------------------------------------
   899  
   900  // Fs returns the parent Fs
   901  func (o *Object) Fs() fs.Info {
   902  	return o.fs
   903  }
   904  
   905  // Return a string version
   906  func (o *Object) String() string {
   907  	if o == nil {
   908  		return "<nil>"
   909  	}
   910  	return o.remote
   911  }
   912  
   913  // Remote returns the remote path
   914  func (o *Object) Remote() string {
   915  	return o.remote
   916  }
   917  
   918  // Hash returns the Md5sum of an object returning a lowercase hex string
   919  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
   920  	if t != hash.MD5 {
   921  		return "", hash.ErrUnsupported
   922  	}
   923  	isDynamicLargeObject, err := o.isDynamicLargeObject()
   924  	if err != nil {
   925  		return "", err
   926  	}
   927  	isStaticLargeObject, err := o.isStaticLargeObject()
   928  	if err != nil {
   929  		return "", err
   930  	}
   931  	if isDynamicLargeObject || isStaticLargeObject {
   932  		fs.Debugf(o, "Returning empty Md5sum for swift large object")
   933  		return "", nil
   934  	}
   935  	return strings.ToLower(o.md5), nil
   936  }
   937  
   938  // hasHeader checks for the header passed in returning false if the
   939  // object isn't found.
   940  func (o *Object) hasHeader(header string) (bool, error) {
   941  	err := o.readMetaData()
   942  	if err != nil {
   943  		if err == fs.ErrorObjectNotFound {
   944  			return false, nil
   945  		}
   946  		return false, err
   947  	}
   948  	_, isDynamicLargeObject := o.headers[header]
   949  	return isDynamicLargeObject, nil
   950  }
   951  
   952  // isDynamicLargeObject checks for X-Object-Manifest header
   953  func (o *Object) isDynamicLargeObject() (bool, error) {
   954  	return o.hasHeader("X-Object-Manifest")
   955  }
   956  
   957  // isStaticLargeObjectFile checks for the X-Static-Large-Object header
   958  func (o *Object) isStaticLargeObject() (bool, error) {
   959  	return o.hasHeader("X-Static-Large-Object")
   960  }
   961  
   962  func (o *Object) isInContainerVersioning(container string) (bool, error) {
   963  	_, headers, err := o.fs.c.Container(container)
   964  	if err != nil {
   965  		return false, err
   966  	}
   967  	xHistoryLocation := headers["X-History-Location"]
   968  	if len(xHistoryLocation) > 0 {
   969  		return true, nil
   970  	}
   971  	return false, nil
   972  }
   973  
   974  // Size returns the size of an object in bytes
   975  func (o *Object) Size() int64 {
   976  	return o.size
   977  }
   978  
   979  // decodeMetaData sets the metadata in the object from a swift.Object
   980  //
   981  // Sets
   982  //  o.lastModified
   983  //  o.size
   984  //  o.md5
   985  //  o.contentType
   986  func (o *Object) decodeMetaData(info *swift.Object) (err error) {
   987  	o.lastModified = info.LastModified
   988  	o.size = info.Bytes
   989  	o.md5 = info.Hash
   990  	o.contentType = info.ContentType
   991  	return nil
   992  }
   993  
   994  // readMetaData gets the metadata if it hasn't already been fetched
   995  //
   996  // it also sets the info
   997  //
   998  // it returns fs.ErrorObjectNotFound if the object isn't found
   999  func (o *Object) readMetaData() (err error) {
  1000  	if o.headers != nil {
  1001  		return nil
  1002  	}
  1003  	var info swift.Object
  1004  	var h swift.Headers
  1005  	container, containerPath := o.split()
  1006  	err = o.fs.pacer.Call(func() (bool, error) {
  1007  		info, h, err = o.fs.c.Object(container, containerPath)
  1008  		return shouldRetryHeaders(h, err)
  1009  	})
  1010  	if err != nil {
  1011  		if err == swift.ObjectNotFound {
  1012  			return fs.ErrorObjectNotFound
  1013  		}
  1014  		return err
  1015  	}
  1016  	o.headers = h
  1017  	err = o.decodeMetaData(&info)
  1018  	if err != nil {
  1019  		return err
  1020  	}
  1021  	return nil
  1022  }
  1023  
  1024  // ModTime returns the modification time of the object
  1025  //
  1026  //
  1027  // It attempts to read the objects mtime and if that isn't present the
  1028  // LastModified returned in the http headers
  1029  func (o *Object) ModTime(ctx context.Context) time.Time {
  1030  	if fs.Config.UseServerModTime {
  1031  		return o.lastModified
  1032  	}
  1033  	err := o.readMetaData()
  1034  	if err != nil {
  1035  		fs.Debugf(o, "Failed to read metadata: %s", err)
  1036  		return o.lastModified
  1037  	}
  1038  	modTime, err := o.headers.ObjectMetadata().GetModTime()
  1039  	if err != nil {
  1040  		// fs.Logf(o, "Failed to read mtime from object: %v", err)
  1041  		return o.lastModified
  1042  	}
  1043  	return modTime
  1044  }
  1045  
  1046  // SetModTime sets the modification time of the local fs object
  1047  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
  1048  	err := o.readMetaData()
  1049  	if err != nil {
  1050  		return err
  1051  	}
  1052  	meta := o.headers.ObjectMetadata()
  1053  	meta.SetModTime(modTime)
  1054  	newHeaders := meta.ObjectHeaders()
  1055  	for k, v := range newHeaders {
  1056  		o.headers[k] = v
  1057  	}
  1058  	// Include any other metadata from request
  1059  	for k, v := range o.headers {
  1060  		if strings.HasPrefix(k, "X-Object-") {
  1061  			newHeaders[k] = v
  1062  		}
  1063  	}
  1064  	container, containerPath := o.split()
  1065  	return o.fs.pacer.Call(func() (bool, error) {
  1066  		err = o.fs.c.ObjectUpdate(container, containerPath, newHeaders)
  1067  		return shouldRetry(err)
  1068  	})
  1069  }
  1070  
  1071  // Storable returns if this object is storable
  1072  //
  1073  // It compares the Content-Type to directoryMarkerContentType - that
  1074  // makes it a directory marker which is not storable.
  1075  func (o *Object) Storable() bool {
  1076  	return o.contentType != directoryMarkerContentType
  1077  }
  1078  
  1079  // Open an object for read
  1080  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
  1081  	fs.FixRangeOption(options, o.size)
  1082  	headers := fs.OpenOptionHeaders(options)
  1083  	_, isRanging := headers["Range"]
  1084  	container, containerPath := o.split()
  1085  	err = o.fs.pacer.Call(func() (bool, error) {
  1086  		var rxHeaders swift.Headers
  1087  		in, rxHeaders, err = o.fs.c.ObjectOpen(container, containerPath, !isRanging, headers)
  1088  		return shouldRetryHeaders(rxHeaders, err)
  1089  	})
  1090  	return
  1091  }
  1092  
  1093  // min returns the smallest of x, y
  1094  func min(x, y int64) int64 {
  1095  	if x < y {
  1096  		return x
  1097  	}
  1098  	return y
  1099  }
  1100  
  1101  // removeSegments removes any old segments from o
  1102  //
  1103  // if except is passed in then segments with that prefix won't be deleted
  1104  func (o *Object) removeSegments(except string) error {
  1105  	segmentsContainer, prefix, err := o.getSegmentsDlo()
  1106  	err = o.fs.listContainerRoot(segmentsContainer, prefix, "", false, true, func(remote string, object *swift.Object, isDirectory bool) error {
  1107  		if isDirectory {
  1108  			return nil
  1109  		}
  1110  		if except != "" && strings.HasPrefix(remote, except) {
  1111  			// fs.Debugf(o, "Ignoring current segment file %q in container %q", segmentsRoot+remote, segmentsContainer)
  1112  			return nil
  1113  		}
  1114  		fs.Debugf(o, "Removing segment file %q in container %q", remote, segmentsContainer)
  1115  		var err error
  1116  		return o.fs.pacer.Call(func() (bool, error) {
  1117  			err = o.fs.c.ObjectDelete(segmentsContainer, remote)
  1118  			return shouldRetry(err)
  1119  		})
  1120  	})
  1121  	if err != nil {
  1122  		return err
  1123  	}
  1124  	// remove the segments container if empty, ignore errors
  1125  	err = o.fs.pacer.Call(func() (bool, error) {
  1126  		err = o.fs.c.ContainerDelete(segmentsContainer)
  1127  		if err == swift.ContainerNotFound || err == swift.ContainerNotEmpty {
  1128  			return false, err
  1129  		}
  1130  		return shouldRetry(err)
  1131  	})
  1132  	if err == nil {
  1133  		fs.Debugf(o, "Removed empty container %q", segmentsContainer)
  1134  	}
  1135  	return nil
  1136  }
  1137  
  1138  func (o *Object) getSegmentsDlo() (segmentsContainer string, prefix string, err error) {
  1139  	if err = o.readMetaData(); err != nil {
  1140  		return
  1141  	}
  1142  	dirManifest := o.headers["X-Object-Manifest"]
  1143  	dirManifest, err = url.PathUnescape(dirManifest)
  1144  	if err != nil {
  1145  		return
  1146  	}
  1147  	delimiter := strings.Index(dirManifest, "/")
  1148  	if len(dirManifest) == 0 || delimiter < 0 {
  1149  		err = errors.New("Missing or wrong structure of manifest of Dynamic large object")
  1150  		return
  1151  	}
  1152  	return dirManifest[:delimiter], dirManifest[delimiter+1:], nil
  1153  }
  1154  
  1155  // urlEncode encodes a string so that it is a valid URL
  1156  //
  1157  // We don't use any of Go's standard methods as we need `/` not
  1158  // encoded but we need '&' encoded.
  1159  func urlEncode(str string) string {
  1160  	var buf bytes.Buffer
  1161  	for i := 0; i < len(str); i++ {
  1162  		c := str[i]
  1163  		if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '/' || c == '.' {
  1164  			_ = buf.WriteByte(c)
  1165  		} else {
  1166  			_, _ = buf.WriteString(fmt.Sprintf("%%%02X", c))
  1167  		}
  1168  	}
  1169  	return buf.String()
  1170  }
  1171  
  1172  // updateChunks updates the existing object using chunks to a separate
  1173  // container.  It returns a string which prefixes current segments.
  1174  func (o *Object) updateChunks(in0 io.Reader, headers swift.Headers, size int64, contentType string) (string, error) {
  1175  	container, containerPath := o.split()
  1176  	segmentsContainer := container + "_segments"
  1177  	// Create the segmentsContainer if it doesn't exist
  1178  	var err error
  1179  	err = o.fs.pacer.Call(func() (bool, error) {
  1180  		var rxHeaders swift.Headers
  1181  		_, rxHeaders, err = o.fs.c.Container(segmentsContainer)
  1182  		return shouldRetryHeaders(rxHeaders, err)
  1183  	})
  1184  	if err == swift.ContainerNotFound {
  1185  		headers := swift.Headers{}
  1186  		if o.fs.opt.StoragePolicy != "" {
  1187  			headers["X-Storage-Policy"] = o.fs.opt.StoragePolicy
  1188  		}
  1189  		err = o.fs.pacer.Call(func() (bool, error) {
  1190  			err = o.fs.c.ContainerCreate(segmentsContainer, headers)
  1191  			return shouldRetry(err)
  1192  		})
  1193  	}
  1194  	if err != nil {
  1195  		return "", err
  1196  	}
  1197  	// Upload the chunks
  1198  	left := size
  1199  	i := 0
  1200  	uniquePrefix := fmt.Sprintf("%s/%d", swift.TimeToFloatString(time.Now()), size)
  1201  	segmentsPath := path.Join(containerPath, uniquePrefix)
  1202  	in := bufio.NewReader(in0)
  1203  	segmentInfos := make([]string, 0, ((size / int64(o.fs.opt.ChunkSize)) + 1))
  1204  	for {
  1205  		// can we read at least one byte?
  1206  		if _, err := in.Peek(1); err != nil {
  1207  			if left > 0 {
  1208  				return "", err // read less than expected
  1209  			}
  1210  			fs.Debugf(o, "Uploading segments into %q seems done (%v)", segmentsContainer, err)
  1211  			break
  1212  		}
  1213  		n := int64(o.fs.opt.ChunkSize)
  1214  		if size != -1 {
  1215  			n = min(left, n)
  1216  			headers["Content-Length"] = strconv.FormatInt(n, 10) // set Content-Length as we know it
  1217  			left -= n
  1218  		}
  1219  		segmentReader := io.LimitReader(in, n)
  1220  		segmentPath := fmt.Sprintf("%s/%08d", segmentsPath, i)
  1221  		fs.Debugf(o, "Uploading segment file %q into %q", segmentPath, segmentsContainer)
  1222  		err = o.fs.pacer.CallNoRetry(func() (bool, error) {
  1223  			var rxHeaders swift.Headers
  1224  			rxHeaders, err = o.fs.c.ObjectPut(segmentsContainer, segmentPath, segmentReader, true, "", "", headers)
  1225  			if err == nil {
  1226  				segmentInfos = append(segmentInfos, segmentPath)
  1227  			}
  1228  			return shouldRetryHeaders(rxHeaders, err)
  1229  		})
  1230  		if err != nil {
  1231  			deleteChunks(o, segmentsContainer, segmentInfos)
  1232  			segmentInfos = nil
  1233  			return "", err
  1234  		}
  1235  		i++
  1236  	}
  1237  	// Upload the manifest
  1238  	headers["X-Object-Manifest"] = urlEncode(fmt.Sprintf("%s/%s", segmentsContainer, segmentsPath))
  1239  	headers["Content-Length"] = "0" // set Content-Length as we know it
  1240  	emptyReader := bytes.NewReader(nil)
  1241  	err = o.fs.pacer.Call(func() (bool, error) {
  1242  		var rxHeaders swift.Headers
  1243  		rxHeaders, err = o.fs.c.ObjectPut(container, containerPath, emptyReader, true, "", contentType, headers)
  1244  		return shouldRetryHeaders(rxHeaders, err)
  1245  	})
  1246  	if err != nil {
  1247  		deleteChunks(o, segmentsContainer, segmentInfos)
  1248  		segmentInfos = nil
  1249  	}
  1250  	return uniquePrefix + "/", err
  1251  }
  1252  
  1253  func deleteChunks(o *Object, segmentsContainer string, segmentInfos []string) {
  1254  	if segmentInfos != nil && len(segmentInfos) > 0 {
  1255  		for _, v := range segmentInfos {
  1256  			fs.Debugf(o, "Delete segment file %q on %q", v, segmentsContainer)
  1257  			e := o.fs.c.ObjectDelete(segmentsContainer, v)
  1258  			if e != nil {
  1259  				fs.Errorf(o, "Error occurred in delete segment file %q on %q, error: %q", v, segmentsContainer, e)
  1260  			}
  1261  		}
  1262  	}
  1263  }
  1264  
  1265  // Update the object with the contents of the io.Reader, modTime and size
  1266  //
  1267  // The new object may have been created if an error is returned
  1268  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
  1269  	container, containerPath := o.split()
  1270  	if container == "" {
  1271  		return fserrors.FatalError(errors.New("can't upload files to the root"))
  1272  	}
  1273  	err := o.fs.makeContainer(ctx, container)
  1274  	if err != nil {
  1275  		return err
  1276  	}
  1277  	size := src.Size()
  1278  	modTime := src.ModTime(ctx)
  1279  
  1280  	// Note whether this is a dynamic large object before starting
  1281  	isDynamicLargeObject, err := o.isDynamicLargeObject()
  1282  	if err != nil {
  1283  		return err
  1284  	}
  1285  
  1286  	// Set the mtime
  1287  	m := swift.Metadata{}
  1288  	m.SetModTime(modTime)
  1289  	contentType := fs.MimeType(ctx, src)
  1290  	headers := m.ObjectHeaders()
  1291  	fs.OpenOptionAddHeaders(options, headers)
  1292  	uniquePrefix := ""
  1293  	if size > int64(o.fs.opt.ChunkSize) || (size == -1 && !o.fs.opt.NoChunk) {
  1294  		uniquePrefix, err = o.updateChunks(in, headers, size, contentType)
  1295  		if err != nil {
  1296  			return err
  1297  		}
  1298  		o.headers = nil // wipe old metadata
  1299  	} else {
  1300  		var inCount *readers.CountingReader
  1301  		if size >= 0 {
  1302  			headers["Content-Length"] = strconv.FormatInt(size, 10) // set Content-Length if we know it
  1303  		} else {
  1304  			// otherwise count the size for later
  1305  			inCount = readers.NewCountingReader(in)
  1306  			in = inCount
  1307  		}
  1308  		var rxHeaders swift.Headers
  1309  		err = o.fs.pacer.CallNoRetry(func() (bool, error) {
  1310  			rxHeaders, err = o.fs.c.ObjectPut(container, containerPath, in, true, "", contentType, headers)
  1311  			return shouldRetryHeaders(rxHeaders, err)
  1312  		})
  1313  		if err != nil {
  1314  			return err
  1315  		}
  1316  		// set Metadata since ObjectPut checked the hash and length so we know the
  1317  		// object has been safely uploaded
  1318  		o.lastModified = modTime
  1319  		o.size = size
  1320  		o.md5 = rxHeaders["ETag"]
  1321  		o.contentType = contentType
  1322  		o.headers = headers
  1323  		if inCount != nil {
  1324  			// update the size if streaming from the reader
  1325  			o.size = int64(inCount.BytesRead())
  1326  		}
  1327  	}
  1328  
  1329  	// If file was a dynamic large object then remove old/all segments
  1330  	if isDynamicLargeObject {
  1331  		err = o.removeSegments(uniquePrefix)
  1332  		if err != nil {
  1333  			fs.Logf(o, "Failed to remove old segments - carrying on with upload: %v", err)
  1334  		}
  1335  	}
  1336  
  1337  	// Read the metadata from the newly created object if necessary
  1338  	return o.readMetaData()
  1339  }
  1340  
  1341  // Remove an object
  1342  func (o *Object) Remove(ctx context.Context) (err error) {
  1343  	container, containerPath := o.split()
  1344  
  1345  	// Remove file/manifest first
  1346  	err = o.fs.pacer.Call(func() (bool, error) {
  1347  		err = o.fs.c.ObjectDelete(container, containerPath)
  1348  		return shouldRetry(err)
  1349  	})
  1350  	if err != nil {
  1351  		return err
  1352  	}
  1353  	isDynamicLargeObject, err := o.isDynamicLargeObject()
  1354  	if err != nil {
  1355  		return err
  1356  	}
  1357  	// ...then segments if required
  1358  	if isDynamicLargeObject {
  1359  		isInContainerVersioning, err := o.isInContainerVersioning(container)
  1360  		if err != nil {
  1361  			return err
  1362  		}
  1363  		if !isInContainerVersioning {
  1364  			err = o.removeSegments("")
  1365  			if err != nil {
  1366  				return err
  1367  			}
  1368  		}
  1369  	}
  1370  	return nil
  1371  }
  1372  
  1373  // MimeType of an Object if known, "" otherwise
  1374  func (o *Object) MimeType(ctx context.Context) string {
  1375  	return o.contentType
  1376  }
  1377  
  1378  // Check the interfaces are satisfied
  1379  var (
  1380  	_ fs.Fs          = &Fs{}
  1381  	_ fs.Purger      = &Fs{}
  1382  	_ fs.PutStreamer = &Fs{}
  1383  	_ fs.Copier      = &Fs{}
  1384  	_ fs.ListRer     = &Fs{}
  1385  	_ fs.Object      = &Object{}
  1386  	_ fs.MimeTyper   = &Object{}
  1387  )