github.com/artpar/rclone@v1.67.3/backend/crypt/crypt.go (about)

     1  // Package crypt provides wrappers for Fs and Object which implement encryption
     2  package crypt
     3  
     4  import (
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"path"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/artpar/rclone/fs"
    14  	"github.com/artpar/rclone/fs/accounting"
    15  	"github.com/artpar/rclone/fs/cache"
    16  	"github.com/artpar/rclone/fs/config/configmap"
    17  	"github.com/artpar/rclone/fs/config/configstruct"
    18  	"github.com/artpar/rclone/fs/config/obscure"
    19  	"github.com/artpar/rclone/fs/fspath"
    20  	"github.com/artpar/rclone/fs/hash"
    21  )
    22  
    23  // Globals
    24  // Register with Fs
    25  func init() {
    26  	fs.Register(&fs.RegInfo{
    27  		Name:        "crypt",
    28  		Description: "Encrypt/Decrypt a remote",
    29  		NewFs:       NewFs,
    30  		CommandHelp: commandHelp,
    31  		MetadataInfo: &fs.MetadataInfo{
    32  			Help: `Any metadata supported by the underlying remote is read and written.`,
    33  		},
    34  		Options: []fs.Option{{
    35  			Name:     "remote",
    36  			Help:     "Remote to encrypt/decrypt.\n\nNormally should contain a ':' and a path, e.g. \"myremote:path/to/dir\",\n\"myremote:bucket\" or maybe \"myremote:\" (not recommended).",
    37  			Required: true,
    38  		}, {
    39  			Name:    "filename_encryption",
    40  			Help:    "How to encrypt the filenames.",
    41  			Default: "standard",
    42  			Examples: []fs.OptionExample{
    43  				{
    44  					Value: "standard",
    45  					Help:  "Encrypt the filenames.\nSee the docs for the details.",
    46  				}, {
    47  					Value: "obfuscate",
    48  					Help:  "Very simple filename obfuscation.",
    49  				}, {
    50  					Value: "off",
    51  					Help:  "Don't encrypt the file names.\nAdds a \".bin\", or \"suffix\" extension only.",
    52  				},
    53  			},
    54  		}, {
    55  			Name: "directory_name_encryption",
    56  			Help: `Option to either encrypt directory names or leave them intact.
    57  
    58  NB If filename_encryption is "off" then this option will do nothing.`,
    59  			Default: true,
    60  			Examples: []fs.OptionExample{
    61  				{
    62  					Value: "true",
    63  					Help:  "Encrypt directory names.",
    64  				},
    65  				{
    66  					Value: "false",
    67  					Help:  "Don't encrypt directory names, leave them intact.",
    68  				},
    69  			},
    70  		}, {
    71  			Name:       "password",
    72  			Help:       "Password or pass phrase for encryption.",
    73  			IsPassword: true,
    74  			Required:   true,
    75  		}, {
    76  			Name:       "password2",
    77  			Help:       "Password or pass phrase for salt.\n\nOptional but recommended.\nShould be different to the previous password.",
    78  			IsPassword: true,
    79  		}, {
    80  			Name:    "server_side_across_configs",
    81  			Default: false,
    82  			Help: `Deprecated: use --server-side-across-configs instead.
    83  
    84  Allow server-side operations (e.g. copy) to work across different crypt configs.
    85  
    86  Normally this option is not what you want, but if you have two crypts
    87  pointing to the same backend you can use it.
    88  
    89  This can be used, for example, to change file name encryption type
    90  without re-uploading all the data. Just make two crypt backends
    91  pointing to two different directories with the single changed
    92  parameter and use rclone move to move the files between the crypt
    93  remotes.`,
    94  			Advanced: true,
    95  		}, {
    96  			Name: "show_mapping",
    97  			Help: `For all files listed show how the names encrypt.
    98  
    99  If this flag is set then for each file that the remote is asked to
   100  list, it will log (at level INFO) a line stating the decrypted file
   101  name and the encrypted file name.
   102  
   103  This is so you can work out which encrypted names are which decrypted
   104  names just in case you need to do something with the encrypted file
   105  names, or for debugging purposes.`,
   106  			Default:  false,
   107  			Hide:     fs.OptionHideConfigurator,
   108  			Advanced: true,
   109  		}, {
   110  			Name:     "no_data_encryption",
   111  			Help:     "Option to either encrypt file data or leave it unencrypted.",
   112  			Default:  false,
   113  			Advanced: true,
   114  			Examples: []fs.OptionExample{
   115  				{
   116  					Value: "true",
   117  					Help:  "Don't encrypt file data, leave it unencrypted.",
   118  				},
   119  				{
   120  					Value: "false",
   121  					Help:  "Encrypt file data.",
   122  				},
   123  			},
   124  		}, {
   125  			Name: "pass_bad_blocks",
   126  			Help: `If set this will pass bad blocks through as all 0.
   127  
   128  This should not be set in normal operation, it should only be set if
   129  trying to recover an encrypted file with errors and it is desired to
   130  recover as much of the file as possible.`,
   131  			Default:  false,
   132  			Advanced: true,
   133  		}, {
   134  			Name: "strict_names",
   135  			Help: `If set, this will raise an error when crypt comes across a filename that can't be decrypted.
   136  
   137  (By default, rclone will just log a NOTICE and continue as normal.)
   138  This can happen if encrypted and unencrypted files are stored in the same
   139  directory (which is not recommended.) It may also indicate a more serious
   140  problem that should be investigated.`,
   141  			Default:  false,
   142  			Advanced: true,
   143  		}, {
   144  			Name: "filename_encoding",
   145  			Help: `How to encode the encrypted filename to text string.
   146  
   147  This option could help with shortening the encrypted filename. The 
   148  suitable option would depend on the way your remote count the filename
   149  length and if it's case sensitive.`,
   150  			Default: "base32",
   151  			Examples: []fs.OptionExample{
   152  				{
   153  					Value: "base32",
   154  					Help:  "Encode using base32. Suitable for all remote.",
   155  				},
   156  				{
   157  					Value: "base64",
   158  					Help:  "Encode using base64. Suitable for case sensitive remote.",
   159  				},
   160  				{
   161  					Value: "base32768",
   162  					Help:  "Encode using base32768. Suitable if your remote counts UTF-16 or\nUnicode codepoint instead of UTF-8 byte length. (Eg. Onedrive, Dropbox)",
   163  				},
   164  			},
   165  			Advanced: true,
   166  		}, {
   167  			Name: "suffix",
   168  			Help: `If this is set it will override the default suffix of ".bin".
   169  
   170  Setting suffix to "none" will result in an empty suffix. This may be useful 
   171  when the path length is critical.`,
   172  			Default:  ".bin",
   173  			Advanced: true,
   174  		}},
   175  	})
   176  }
   177  
   178  // newCipherForConfig constructs a Cipher for the given config name
   179  func newCipherForConfig(opt *Options) (*Cipher, error) {
   180  	mode, err := NewNameEncryptionMode(opt.FilenameEncryption)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	if opt.Password == "" {
   185  		return nil, errors.New("password not set in config file")
   186  	}
   187  	password, err := obscure.Reveal(opt.Password)
   188  	if err != nil {
   189  		return nil, fmt.Errorf("failed to decrypt password: %w", err)
   190  	}
   191  	var salt string
   192  	if opt.Password2 != "" {
   193  		salt, err = obscure.Reveal(opt.Password2)
   194  		if err != nil {
   195  			return nil, fmt.Errorf("failed to decrypt password2: %w", err)
   196  		}
   197  	}
   198  	enc, err := NewNameEncoding(opt.FilenameEncoding)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	cipher, err := newCipher(mode, password, salt, opt.DirectoryNameEncryption, enc)
   203  	if err != nil {
   204  		return nil, fmt.Errorf("failed to make cipher: %w", err)
   205  	}
   206  	cipher.setEncryptedSuffix(opt.Suffix)
   207  	cipher.setPassBadBlocks(opt.PassBadBlocks)
   208  	return cipher, nil
   209  }
   210  
   211  // NewCipher constructs a Cipher for the given config
   212  func NewCipher(m configmap.Mapper) (*Cipher, error) {
   213  	// Parse config into Options struct
   214  	opt := new(Options)
   215  	err := configstruct.Set(m, opt)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	return newCipherForConfig(opt)
   220  }
   221  
   222  // NewFs constructs an Fs from the path, container:path
   223  func NewFs(ctx context.Context, name, rpath string, m configmap.Mapper) (fs.Fs, error) {
   224  	// Parse config into Options struct
   225  	opt := new(Options)
   226  	err := configstruct.Set(m, opt)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	cipher, err := newCipherForConfig(opt)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	remote := opt.Remote
   235  	if strings.HasPrefix(remote, name+":") {
   236  		return nil, errors.New("can't point crypt remote at itself - check the value of the remote setting")
   237  	}
   238  	// Make sure to remove trailing . referring to the current dir
   239  	if path.Base(rpath) == "." {
   240  		rpath = strings.TrimSuffix(rpath, ".")
   241  	}
   242  	// Look for a file first
   243  	var wrappedFs fs.Fs
   244  	if rpath == "" {
   245  		wrappedFs, err = cache.Get(ctx, remote)
   246  	} else {
   247  		remotePath := fspath.JoinRootPath(remote, cipher.EncryptFileName(rpath))
   248  		wrappedFs, err = cache.Get(ctx, remotePath)
   249  		// if that didn't produce a file, look for a directory
   250  		if err != fs.ErrorIsFile {
   251  			remotePath = fspath.JoinRootPath(remote, cipher.EncryptDirName(rpath))
   252  			wrappedFs, err = cache.Get(ctx, remotePath)
   253  		}
   254  	}
   255  	if err != fs.ErrorIsFile && err != nil {
   256  		return nil, fmt.Errorf("failed to make remote %q to wrap: %w", remote, err)
   257  	}
   258  	f := &Fs{
   259  		Fs:     wrappedFs,
   260  		name:   name,
   261  		root:   rpath,
   262  		opt:    *opt,
   263  		cipher: cipher,
   264  	}
   265  	cache.PinUntilFinalized(f.Fs, f)
   266  	// Correct root if definitely pointing to a file
   267  	if err == fs.ErrorIsFile {
   268  		f.root = path.Dir(f.root)
   269  		if f.root == "." || f.root == "/" {
   270  			f.root = ""
   271  		}
   272  	}
   273  	// the features here are ones we could support, and they are
   274  	// ANDed with the ones from wrappedFs
   275  	f.features = (&fs.Features{
   276  		CaseInsensitive:          !cipher.dirNameEncrypt || cipher.NameEncryptionMode() == NameEncryptionOff,
   277  		DuplicateFiles:           true,
   278  		ReadMimeType:             false, // MimeTypes not supported with crypt
   279  		WriteMimeType:            false,
   280  		BucketBased:              true,
   281  		CanHaveEmptyDirectories:  true,
   282  		SetTier:                  true,
   283  		GetTier:                  true,
   284  		ServerSideAcrossConfigs:  opt.ServerSideAcrossConfigs,
   285  		ReadMetadata:             true,
   286  		WriteMetadata:            true,
   287  		UserMetadata:             true,
   288  		ReadDirMetadata:          true,
   289  		WriteDirMetadata:         true,
   290  		WriteDirSetModTime:       true,
   291  		UserDirMetadata:          true,
   292  		DirModTimeUpdatesOnWrite: true,
   293  		PartialUploads:           true,
   294  	}).Fill(ctx, f).Mask(ctx, wrappedFs).WrapsFs(f, wrappedFs)
   295  
   296  	return f, err
   297  }
   298  
   299  // Options defines the configuration for this backend
   300  type Options struct {
   301  	Remote                  string `config:"remote"`
   302  	FilenameEncryption      string `config:"filename_encryption"`
   303  	DirectoryNameEncryption bool   `config:"directory_name_encryption"`
   304  	NoDataEncryption        bool   `config:"no_data_encryption"`
   305  	Password                string `config:"password"`
   306  	Password2               string `config:"password2"`
   307  	ServerSideAcrossConfigs bool   `config:"server_side_across_configs"`
   308  	ShowMapping             bool   `config:"show_mapping"`
   309  	PassBadBlocks           bool   `config:"pass_bad_blocks"`
   310  	FilenameEncoding        string `config:"filename_encoding"`
   311  	Suffix                  string `config:"suffix"`
   312  	StrictNames             bool   `config:"strict_names"`
   313  }
   314  
   315  // Fs represents a wrapped fs.Fs
   316  type Fs struct {
   317  	fs.Fs
   318  	wrapper  fs.Fs
   319  	name     string
   320  	root     string
   321  	opt      Options
   322  	features *fs.Features // optional features
   323  	cipher   *Cipher
   324  }
   325  
   326  // Name of the remote (as passed into NewFs)
   327  func (f *Fs) Name() string {
   328  	return f.name
   329  }
   330  
   331  // Root of the remote (as passed into NewFs)
   332  func (f *Fs) Root() string {
   333  	return f.root
   334  }
   335  
   336  // Features returns the optional features of this Fs
   337  func (f *Fs) Features() *fs.Features {
   338  	return f.features
   339  }
   340  
   341  // String returns a description of the FS
   342  func (f *Fs) String() string {
   343  	return fmt.Sprintf("Encrypted drive '%s:%s'", f.name, f.root)
   344  }
   345  
   346  // Encrypt an object file name to entries.
   347  func (f *Fs) add(entries *fs.DirEntries, obj fs.Object) error {
   348  	remote := obj.Remote()
   349  	decryptedRemote, err := f.cipher.DecryptFileName(remote)
   350  	if err != nil {
   351  		if f.opt.StrictNames {
   352  			return fmt.Errorf("%s: undecryptable file name detected: %v", remote, err)
   353  		}
   354  		fs.Logf(remote, "Skipping undecryptable file name: %v", err)
   355  		return nil
   356  	}
   357  	if f.opt.ShowMapping {
   358  		fs.Logf(decryptedRemote, "Encrypts to %q", remote)
   359  	}
   360  	*entries = append(*entries, f.newObject(obj))
   361  	return nil
   362  }
   363  
   364  // Encrypt a directory file name to entries.
   365  func (f *Fs) addDir(ctx context.Context, entries *fs.DirEntries, dir fs.Directory) error {
   366  	remote := dir.Remote()
   367  	decryptedRemote, err := f.cipher.DecryptDirName(remote)
   368  	if err != nil {
   369  		if f.opt.StrictNames {
   370  			return fmt.Errorf("%s: undecryptable dir name detected: %v", remote, err)
   371  		}
   372  		fs.Logf(remote, "Skipping undecryptable dir name: %v", err)
   373  		return nil
   374  	}
   375  	if f.opt.ShowMapping {
   376  		fs.Logf(decryptedRemote, "Encrypts to %q", remote)
   377  	}
   378  	*entries = append(*entries, f.newDir(ctx, dir))
   379  	return nil
   380  }
   381  
   382  // Encrypt some directory entries.  This alters entries returning it as newEntries.
   383  func (f *Fs) encryptEntries(ctx context.Context, entries fs.DirEntries) (newEntries fs.DirEntries, err error) {
   384  	newEntries = entries[:0] // in place filter
   385  	errors := 0
   386  	var firsterr error
   387  	for _, entry := range entries {
   388  		switch x := entry.(type) {
   389  		case fs.Object:
   390  			err = f.add(&newEntries, x)
   391  		case fs.Directory:
   392  			err = f.addDir(ctx, &newEntries, x)
   393  		default:
   394  			return nil, fmt.Errorf("unknown object type %T", entry)
   395  		}
   396  		if err != nil {
   397  			errors++
   398  			if firsterr == nil {
   399  				firsterr = err
   400  			}
   401  		}
   402  	}
   403  	if firsterr != nil {
   404  		return nil, fmt.Errorf("there were %v undecryptable name errors. first error: %v", errors, firsterr)
   405  	}
   406  	return newEntries, nil
   407  }
   408  
   409  // List the objects and directories in dir into entries.  The
   410  // entries can be returned in any order but should be for a
   411  // complete directory.
   412  //
   413  // dir should be "" to list the root, and should not have
   414  // trailing slashes.
   415  //
   416  // This should return ErrDirNotFound if the directory isn't
   417  // found.
   418  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   419  	entries, err = f.Fs.List(ctx, f.cipher.EncryptDirName(dir))
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	return f.encryptEntries(ctx, entries)
   424  }
   425  
   426  // ListR lists the objects and directories of the Fs starting
   427  // from dir recursively into out.
   428  //
   429  // dir should be "" to start from the root, and should not
   430  // have trailing slashes.
   431  //
   432  // This should return ErrDirNotFound if the directory isn't
   433  // found.
   434  //
   435  // It should call callback for each tranche of entries read.
   436  // These need not be returned in any particular order.  If
   437  // callback returns an error then the listing will stop
   438  // immediately.
   439  //
   440  // Don't implement this unless you have a more efficient way
   441  // of listing recursively that doing a directory traversal.
   442  func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) {
   443  	return f.Fs.Features().ListR(ctx, f.cipher.EncryptDirName(dir), func(entries fs.DirEntries) error {
   444  		newEntries, err := f.encryptEntries(ctx, entries)
   445  		if err != nil {
   446  			return err
   447  		}
   448  		return callback(newEntries)
   449  	})
   450  }
   451  
   452  // NewObject finds the Object at remote.
   453  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   454  	o, err := f.Fs.NewObject(ctx, f.cipher.EncryptFileName(remote))
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  	return f.newObject(o), nil
   459  }
   460  
   461  type putFn func(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error)
   462  
   463  // put implements Put or PutStream
   464  func (f *Fs) put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options []fs.OpenOption, put putFn) (fs.Object, error) {
   465  	ci := fs.GetConfig(ctx)
   466  
   467  	if f.opt.NoDataEncryption {
   468  		o, err := put(ctx, in, f.newObjectInfo(src, nonce{}), options...)
   469  		if err == nil && o != nil {
   470  			o = f.newObject(o)
   471  		}
   472  		return o, err
   473  	}
   474  
   475  	// Encrypt the data into wrappedIn
   476  	wrappedIn, encrypter, err := f.cipher.encryptData(in)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	// Find a hash the destination supports to compute a hash of
   482  	// the encrypted data
   483  	ht := f.Fs.Hashes().GetOne()
   484  	if ci.IgnoreChecksum {
   485  		ht = hash.None
   486  	}
   487  	var hasher *hash.MultiHasher
   488  	if ht != hash.None {
   489  		hasher, err = hash.NewMultiHasherTypes(hash.NewHashSet(ht))
   490  		if err != nil {
   491  			return nil, err
   492  		}
   493  		// unwrap the accounting
   494  		var wrap accounting.WrapFn
   495  		wrappedIn, wrap = accounting.UnWrap(wrappedIn)
   496  		// add the hasher
   497  		wrappedIn = io.TeeReader(wrappedIn, hasher)
   498  		// wrap the accounting back on
   499  		wrappedIn = wrap(wrappedIn)
   500  	}
   501  
   502  	// Transfer the data
   503  	o, err := put(ctx, wrappedIn, f.newObjectInfo(src, encrypter.nonce), options...)
   504  	if err != nil {
   505  		return nil, err
   506  	}
   507  
   508  	// Check the hashes of the encrypted data if we were comparing them
   509  	if ht != hash.None && hasher != nil {
   510  		srcHash := hasher.Sums()[ht]
   511  		var dstHash string
   512  		dstHash, err = o.Hash(ctx, ht)
   513  		if err != nil {
   514  			return nil, fmt.Errorf("failed to read destination hash: %w", err)
   515  		}
   516  		if srcHash != "" && dstHash != "" {
   517  			if srcHash != dstHash {
   518  				// remove object
   519  				err = o.Remove(ctx)
   520  				if err != nil {
   521  					fs.Errorf(o, "Failed to remove corrupted object: %v", err)
   522  				}
   523  				return nil, fmt.Errorf("corrupted on transfer: %v encrypted hashes differ src(%s) %q vs dst(%s) %q", ht, f.Fs, srcHash, o.Fs(), dstHash)
   524  			}
   525  			fs.Debugf(src, "%v = %s OK", ht, srcHash)
   526  		}
   527  	}
   528  
   529  	return f.newObject(o), nil
   530  }
   531  
   532  // Put in to the remote path with the modTime given of the given size
   533  //
   534  // May create the object even if it returns an error - if so
   535  // will return the object and the error, otherwise will return
   536  // nil and the error
   537  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   538  	return f.put(ctx, in, src, options, f.Fs.Put)
   539  }
   540  
   541  // PutStream uploads to the remote path with the modTime given of indeterminate size
   542  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   543  	return f.put(ctx, in, src, options, f.Fs.Features().PutStream)
   544  }
   545  
   546  // Hashes returns the supported hash sets.
   547  func (f *Fs) Hashes() hash.Set {
   548  	return hash.Set(hash.None)
   549  }
   550  
   551  // Mkdir makes the directory (container, bucket)
   552  //
   553  // Shouldn't return an error if it already exists
   554  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   555  	return f.Fs.Mkdir(ctx, f.cipher.EncryptDirName(dir))
   556  }
   557  
   558  // MkdirMetadata makes the root directory of the Fs object
   559  func (f *Fs) MkdirMetadata(ctx context.Context, dir string, metadata fs.Metadata) (fs.Directory, error) {
   560  	do := f.Fs.Features().MkdirMetadata
   561  	if do == nil {
   562  		return nil, fs.ErrorNotImplemented
   563  	}
   564  	newDir, err := do(ctx, f.cipher.EncryptDirName(dir), metadata)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  	var entries = make(fs.DirEntries, 0, 1)
   569  	err = f.addDir(ctx, &entries, newDir)
   570  	if err != nil {
   571  		return nil, err
   572  	}
   573  	newDir, ok := entries[0].(fs.Directory)
   574  	if !ok {
   575  		return nil, fmt.Errorf("internal error: expecting %T to be fs.Directory", entries[0])
   576  	}
   577  	return newDir, nil
   578  }
   579  
   580  // DirSetModTime sets the directory modtime for dir
   581  func (f *Fs) DirSetModTime(ctx context.Context, dir string, modTime time.Time) error {
   582  	do := f.Fs.Features().DirSetModTime
   583  	if do == nil {
   584  		return fs.ErrorNotImplemented
   585  	}
   586  	return do(ctx, f.cipher.EncryptDirName(dir), modTime)
   587  }
   588  
   589  // Rmdir removes the directory (container, bucket) if empty
   590  //
   591  // Return an error if it doesn't exist or isn't empty
   592  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   593  	return f.Fs.Rmdir(ctx, f.cipher.EncryptDirName(dir))
   594  }
   595  
   596  // Purge all files in the directory specified
   597  //
   598  // Implement this if you have a way of deleting all the files
   599  // quicker than just running Remove() on the result of List()
   600  //
   601  // Return an error if it doesn't exist
   602  func (f *Fs) Purge(ctx context.Context, dir string) error {
   603  	do := f.Fs.Features().Purge
   604  	if do == nil {
   605  		return fs.ErrorCantPurge
   606  	}
   607  	return do(ctx, f.cipher.EncryptDirName(dir))
   608  }
   609  
   610  // Copy src to this remote using server-side copy operations.
   611  //
   612  // This is stored with the remote path given.
   613  //
   614  // It returns the destination Object and a possible error.
   615  //
   616  // Will only be called if src.Fs().Name() == f.Name()
   617  //
   618  // If it isn't possible then return fs.ErrorCantCopy
   619  func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   620  	do := f.Fs.Features().Copy
   621  	if do == nil {
   622  		return nil, fs.ErrorCantCopy
   623  	}
   624  	o, ok := src.(*Object)
   625  	if !ok {
   626  		return nil, fs.ErrorCantCopy
   627  	}
   628  	oResult, err := do(ctx, o.Object, f.cipher.EncryptFileName(remote))
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  	return f.newObject(oResult), nil
   633  }
   634  
   635  // Move src to this remote using server-side move operations.
   636  //
   637  // This is stored with the remote path given.
   638  //
   639  // It returns the destination Object and a possible error.
   640  //
   641  // Will only be called if src.Fs().Name() == f.Name()
   642  //
   643  // If it isn't possible then return fs.ErrorCantMove
   644  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   645  	do := f.Fs.Features().Move
   646  	if do == nil {
   647  		return nil, fs.ErrorCantMove
   648  	}
   649  	o, ok := src.(*Object)
   650  	if !ok {
   651  		return nil, fs.ErrorCantMove
   652  	}
   653  	oResult, err := do(ctx, o.Object, f.cipher.EncryptFileName(remote))
   654  	if err != nil {
   655  		return nil, err
   656  	}
   657  	return f.newObject(oResult), nil
   658  }
   659  
   660  // DirMove moves src, srcRemote to this remote at dstRemote
   661  // using server-side move operations.
   662  //
   663  // Will only be called if src.Fs().Name() == f.Name()
   664  //
   665  // If it isn't possible then return fs.ErrorCantDirMove
   666  //
   667  // If destination exists then return fs.ErrorDirExists
   668  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
   669  	do := f.Fs.Features().DirMove
   670  	if do == nil {
   671  		return fs.ErrorCantDirMove
   672  	}
   673  	srcFs, ok := src.(*Fs)
   674  	if !ok {
   675  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   676  		return fs.ErrorCantDirMove
   677  	}
   678  	return do(ctx, srcFs.Fs, f.cipher.EncryptDirName(srcRemote), f.cipher.EncryptDirName(dstRemote))
   679  }
   680  
   681  // PutUnchecked uploads the object
   682  //
   683  // This will create a duplicate if we upload a new file without
   684  // checking to see if there is one already - use Put() for that.
   685  func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   686  	do := f.Fs.Features().PutUnchecked
   687  	if do == nil {
   688  		return nil, errors.New("can't PutUnchecked")
   689  	}
   690  	wrappedIn, encrypter, err := f.cipher.encryptData(in)
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  	o, err := do(ctx, wrappedIn, f.newObjectInfo(src, encrypter.nonce))
   695  	if err != nil {
   696  		return nil, err
   697  	}
   698  	return f.newObject(o), nil
   699  }
   700  
   701  // CleanUp the trash in the Fs
   702  //
   703  // Implement this if you have a way of emptying the trash or
   704  // otherwise cleaning up old versions of files.
   705  func (f *Fs) CleanUp(ctx context.Context) error {
   706  	do := f.Fs.Features().CleanUp
   707  	if do == nil {
   708  		return errors.New("not supported by underlying remote")
   709  	}
   710  	return do(ctx)
   711  }
   712  
   713  // About gets quota information from the Fs
   714  func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
   715  	do := f.Fs.Features().About
   716  	if do == nil {
   717  		return nil, errors.New("not supported by underlying remote")
   718  	}
   719  	return do(ctx)
   720  }
   721  
   722  // UnWrap returns the Fs that this Fs is wrapping
   723  func (f *Fs) UnWrap() fs.Fs {
   724  	return f.Fs
   725  }
   726  
   727  // WrapFs returns the Fs that is wrapping this Fs
   728  func (f *Fs) WrapFs() fs.Fs {
   729  	return f.wrapper
   730  }
   731  
   732  // SetWrapper sets the Fs that is wrapping this Fs
   733  func (f *Fs) SetWrapper(wrapper fs.Fs) {
   734  	f.wrapper = wrapper
   735  }
   736  
   737  // EncryptFileName returns an encrypted file name
   738  func (f *Fs) EncryptFileName(fileName string) string {
   739  	return f.cipher.EncryptFileName(fileName)
   740  }
   741  
   742  // DecryptFileName returns a decrypted file name
   743  func (f *Fs) DecryptFileName(encryptedFileName string) (string, error) {
   744  	return f.cipher.DecryptFileName(encryptedFileName)
   745  }
   746  
   747  // computeHashWithNonce takes the nonce and encrypts the contents of
   748  // src with it, and calculates the hash given by HashType on the fly
   749  //
   750  // Note that we break lots of encapsulation in this function.
   751  func (f *Fs) computeHashWithNonce(ctx context.Context, nonce nonce, src fs.Object, hashType hash.Type) (hashStr string, err error) {
   752  	// Open the src for input
   753  	in, err := src.Open(ctx)
   754  	if err != nil {
   755  		return "", fmt.Errorf("failed to open src: %w", err)
   756  	}
   757  	defer fs.CheckClose(in, &err)
   758  
   759  	// Now encrypt the src with the nonce
   760  	out, err := f.cipher.newEncrypter(in, &nonce)
   761  	if err != nil {
   762  		return "", fmt.Errorf("failed to make encrypter: %w", err)
   763  	}
   764  
   765  	// pipe into hash
   766  	m, err := hash.NewMultiHasherTypes(hash.NewHashSet(hashType))
   767  	if err != nil {
   768  		return "", fmt.Errorf("failed to make hasher: %w", err)
   769  	}
   770  	_, err = io.Copy(m, out)
   771  	if err != nil {
   772  		return "", fmt.Errorf("failed to hash data: %w", err)
   773  	}
   774  
   775  	return m.Sums()[hashType], nil
   776  }
   777  
   778  // ComputeHash takes the nonce from o, and encrypts the contents of
   779  // src with it, and calculates the hash given by HashType on the fly
   780  //
   781  // Note that we break lots of encapsulation in this function.
   782  func (f *Fs) ComputeHash(ctx context.Context, o *Object, src fs.Object, hashType hash.Type) (hashStr string, err error) {
   783  	if f.opt.NoDataEncryption {
   784  		return src.Hash(ctx, hashType)
   785  	}
   786  
   787  	// Read the nonce - opening the file is sufficient to read the nonce in
   788  	// use a limited read so we only read the header
   789  	in, err := o.Object.Open(ctx, &fs.RangeOption{Start: 0, End: int64(fileHeaderSize) - 1})
   790  	if err != nil {
   791  		return "", fmt.Errorf("failed to open object to read nonce: %w", err)
   792  	}
   793  	d, err := f.cipher.newDecrypter(in)
   794  	if err != nil {
   795  		_ = in.Close()
   796  		return "", fmt.Errorf("failed to open object to read nonce: %w", err)
   797  	}
   798  	nonce := d.nonce
   799  	// fs.Debugf(o, "Read nonce % 2x", nonce)
   800  
   801  	// Check nonce isn't all zeros
   802  	isZero := true
   803  	for i := range nonce {
   804  		if nonce[i] != 0 {
   805  			isZero = false
   806  		}
   807  	}
   808  	if isZero {
   809  		fs.Errorf(o, "empty nonce read")
   810  	}
   811  
   812  	// Close d (and hence in) once we have read the nonce
   813  	err = d.Close()
   814  	if err != nil {
   815  		return "", fmt.Errorf("failed to close nonce read: %w", err)
   816  	}
   817  
   818  	return f.computeHashWithNonce(ctx, nonce, src, hashType)
   819  }
   820  
   821  // MergeDirs merges the contents of all the directories passed
   822  // in into the first one and rmdirs the other directories.
   823  func (f *Fs) MergeDirs(ctx context.Context, dirs []fs.Directory) error {
   824  	do := f.Fs.Features().MergeDirs
   825  	if do == nil {
   826  		return errors.New("MergeDirs not supported")
   827  	}
   828  	out := make([]fs.Directory, len(dirs))
   829  	for i, dir := range dirs {
   830  		out[i] = fs.NewDirWrapper(f.cipher.EncryptDirName(dir.Remote()), dir)
   831  	}
   832  	return do(ctx, out)
   833  }
   834  
   835  // DirCacheFlush resets the directory cache - used in testing
   836  // as an optional interface
   837  func (f *Fs) DirCacheFlush() {
   838  	do := f.Fs.Features().DirCacheFlush
   839  	if do != nil {
   840  		do()
   841  	}
   842  }
   843  
   844  // PublicLink generates a public link to the remote path (usually readable by anyone)
   845  func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration, unlink bool) (string, error) {
   846  	do := f.Fs.Features().PublicLink
   847  	if do == nil {
   848  		return "", errors.New("PublicLink not supported")
   849  	}
   850  	o, err := f.NewObject(ctx, remote)
   851  	if err != nil {
   852  		// assume it is a directory
   853  		return do(ctx, f.cipher.EncryptDirName(remote), expire, unlink)
   854  	}
   855  	return do(ctx, o.(*Object).Object.Remote(), expire, unlink)
   856  }
   857  
   858  // ChangeNotify calls the passed function with a path
   859  // that has had changes. If the implementation
   860  // uses polling, it should adhere to the given interval.
   861  func (f *Fs) ChangeNotify(ctx context.Context, notifyFunc func(string, fs.EntryType), pollIntervalChan <-chan time.Duration) {
   862  	do := f.Fs.Features().ChangeNotify
   863  	if do == nil {
   864  		return
   865  	}
   866  	wrappedNotifyFunc := func(path string, entryType fs.EntryType) {
   867  		// fs.Debugf(f, "ChangeNotify: path %q entryType %d", path, entryType)
   868  		var (
   869  			err       error
   870  			decrypted string
   871  		)
   872  		switch entryType {
   873  		case fs.EntryDirectory:
   874  			decrypted, err = f.cipher.DecryptDirName(path)
   875  		case fs.EntryObject:
   876  			decrypted, err = f.cipher.DecryptFileName(path)
   877  		default:
   878  			fs.Errorf(path, "crypt ChangeNotify: ignoring unknown EntryType %d", entryType)
   879  			return
   880  		}
   881  		if err != nil {
   882  			fs.Logf(f, "ChangeNotify was unable to decrypt %q: %s", path, err)
   883  			return
   884  		}
   885  		notifyFunc(decrypted, entryType)
   886  	}
   887  	do(ctx, wrappedNotifyFunc, pollIntervalChan)
   888  }
   889  
   890  var commandHelp = []fs.CommandHelp{
   891  	{
   892  		Name:  "encode",
   893  		Short: "Encode the given filename(s)",
   894  		Long: `This encodes the filenames given as arguments returning a list of
   895  strings of the encoded results.
   896  
   897  Usage Example:
   898  
   899      rclone backend encode crypt: file1 [file2...]
   900      rclone rc backend/command command=encode fs=crypt: file1 [file2...]
   901  `,
   902  	},
   903  	{
   904  		Name:  "decode",
   905  		Short: "Decode the given filename(s)",
   906  		Long: `This decodes the filenames given as arguments returning a list of
   907  strings of the decoded results. It will return an error if any of the
   908  inputs are invalid.
   909  
   910  Usage Example:
   911  
   912      rclone backend decode crypt: encryptedfile1 [encryptedfile2...]
   913      rclone rc backend/command command=decode fs=crypt: encryptedfile1 [encryptedfile2...]
   914  `,
   915  	},
   916  }
   917  
   918  // Command the backend to run a named command
   919  //
   920  // The command run is name
   921  // args may be used to read arguments from
   922  // opts may be used to read optional arguments from
   923  //
   924  // The result should be capable of being JSON encoded
   925  // If it is a string or a []string it will be shown to the user
   926  // otherwise it will be JSON encoded and shown to the user like that
   927  func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[string]string) (out interface{}, err error) {
   928  	switch name {
   929  	case "decode":
   930  		out := make([]string, 0, len(arg))
   931  		for _, encryptedFileName := range arg {
   932  			fileName, err := f.DecryptFileName(encryptedFileName)
   933  			if err != nil {
   934  				return out, fmt.Errorf("failed to decrypt: %s: %w", encryptedFileName, err)
   935  			}
   936  			out = append(out, fileName)
   937  		}
   938  		return out, nil
   939  	case "encode":
   940  		out := make([]string, 0, len(arg))
   941  		for _, fileName := range arg {
   942  			encryptedFileName := f.EncryptFileName(fileName)
   943  			out = append(out, encryptedFileName)
   944  		}
   945  		return out, nil
   946  	default:
   947  		return nil, fs.ErrorCommandNotFound
   948  	}
   949  }
   950  
   951  // Object describes a wrapped for being read from the Fs
   952  //
   953  // This decrypts the remote name and decrypts the data
   954  type Object struct {
   955  	fs.Object
   956  	f *Fs
   957  }
   958  
   959  func (f *Fs) newObject(o fs.Object) *Object {
   960  	return &Object{
   961  		Object: o,
   962  		f:      f,
   963  	}
   964  }
   965  
   966  // Fs returns read only access to the Fs that this object is part of
   967  func (o *Object) Fs() fs.Info {
   968  	return o.f
   969  }
   970  
   971  // Return a string version
   972  func (o *Object) String() string {
   973  	if o == nil {
   974  		return "<nil>"
   975  	}
   976  	return o.Remote()
   977  }
   978  
   979  // Remote returns the remote path
   980  func (o *Object) Remote() string {
   981  	remote := o.Object.Remote()
   982  	decryptedName, err := o.f.cipher.DecryptFileName(remote)
   983  	if err != nil {
   984  		fs.Debugf(remote, "Undecryptable file name: %v", err)
   985  		return remote
   986  	}
   987  	return decryptedName
   988  }
   989  
   990  // Size returns the size of the file
   991  func (o *Object) Size() int64 {
   992  	size := o.Object.Size()
   993  	if !o.f.opt.NoDataEncryption {
   994  		var err error
   995  		size, err = o.f.cipher.DecryptedSize(size)
   996  		if err != nil {
   997  			fs.Debugf(o, "Bad size for decrypt: %v", err)
   998  		}
   999  	}
  1000  	return size
  1001  }
  1002  
  1003  // Hash returns the selected checksum of the file
  1004  // If no checksum is available it returns ""
  1005  func (o *Object) Hash(ctx context.Context, ht hash.Type) (string, error) {
  1006  	return "", hash.ErrUnsupported
  1007  }
  1008  
  1009  // UnWrap returns the wrapped Object
  1010  func (o *Object) UnWrap() fs.Object {
  1011  	return o.Object
  1012  }
  1013  
  1014  // Open opens the file for read.  Call Close() on the returned io.ReadCloser
  1015  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (rc io.ReadCloser, err error) {
  1016  	if o.f.opt.NoDataEncryption {
  1017  		return o.Object.Open(ctx, options...)
  1018  	}
  1019  
  1020  	var openOptions []fs.OpenOption
  1021  	var offset, limit int64 = 0, -1
  1022  	for _, option := range options {
  1023  		switch x := option.(type) {
  1024  		case *fs.SeekOption:
  1025  			offset = x.Offset
  1026  		case *fs.RangeOption:
  1027  			offset, limit = x.Decode(o.Size())
  1028  		default:
  1029  			// pass on Options to underlying open if appropriate
  1030  			openOptions = append(openOptions, option)
  1031  		}
  1032  	}
  1033  	rc, err = o.f.cipher.DecryptDataSeek(ctx, func(ctx context.Context, underlyingOffset, underlyingLimit int64) (io.ReadCloser, error) {
  1034  		if underlyingOffset == 0 && underlyingLimit < 0 {
  1035  			// Open with no seek
  1036  			return o.Object.Open(ctx, openOptions...)
  1037  		}
  1038  		// Open stream with a range of underlyingOffset, underlyingLimit
  1039  		end := int64(-1)
  1040  		if underlyingLimit >= 0 {
  1041  			end = underlyingOffset + underlyingLimit - 1
  1042  			if end >= o.Object.Size() {
  1043  				end = -1
  1044  			}
  1045  		}
  1046  		newOpenOptions := append(openOptions, &fs.RangeOption{Start: underlyingOffset, End: end})
  1047  		return o.Object.Open(ctx, newOpenOptions...)
  1048  	}, offset, limit)
  1049  	if err != nil {
  1050  		return nil, err
  1051  	}
  1052  	return rc, nil
  1053  }
  1054  
  1055  // Update in to the object with the modTime given of the given size
  1056  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
  1057  	update := func(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
  1058  		return o.Object, o.Object.Update(ctx, in, src, options...)
  1059  	}
  1060  	_, err := o.f.put(ctx, in, src, options, update)
  1061  	return err
  1062  }
  1063  
  1064  // newDir returns a dir with the Name decrypted
  1065  func (f *Fs) newDir(ctx context.Context, dir fs.Directory) fs.Directory {
  1066  	remote := dir.Remote()
  1067  	decryptedRemote, err := f.cipher.DecryptDirName(remote)
  1068  	if err != nil {
  1069  		fs.Debugf(remote, "Undecryptable dir name: %v", err)
  1070  	} else {
  1071  		remote = decryptedRemote
  1072  	}
  1073  	newDir := fs.NewDirWrapper(remote, dir)
  1074  	return newDir
  1075  }
  1076  
  1077  // UserInfo returns info about the connected user
  1078  func (f *Fs) UserInfo(ctx context.Context) (map[string]string, error) {
  1079  	do := f.Fs.Features().UserInfo
  1080  	if do == nil {
  1081  		return nil, fs.ErrorNotImplemented
  1082  	}
  1083  	return do(ctx)
  1084  }
  1085  
  1086  // Disconnect the current user
  1087  func (f *Fs) Disconnect(ctx context.Context) error {
  1088  	do := f.Fs.Features().Disconnect
  1089  	if do == nil {
  1090  		return fs.ErrorNotImplemented
  1091  	}
  1092  	return do(ctx)
  1093  }
  1094  
  1095  // Shutdown the backend, closing any background tasks and any
  1096  // cached connections.
  1097  func (f *Fs) Shutdown(ctx context.Context) error {
  1098  	do := f.Fs.Features().Shutdown
  1099  	if do == nil {
  1100  		return nil
  1101  	}
  1102  	return do(ctx)
  1103  }
  1104  
  1105  // ObjectInfo describes a wrapped fs.ObjectInfo for being the source
  1106  //
  1107  // This encrypts the remote name and adjusts the size
  1108  type ObjectInfo struct {
  1109  	fs.ObjectInfo
  1110  	f     *Fs
  1111  	nonce nonce
  1112  }
  1113  
  1114  func (f *Fs) newObjectInfo(src fs.ObjectInfo, nonce nonce) *ObjectInfo {
  1115  	return &ObjectInfo{
  1116  		ObjectInfo: src,
  1117  		f:          f,
  1118  		nonce:      nonce,
  1119  	}
  1120  }
  1121  
  1122  // Fs returns read only access to the Fs that this object is part of
  1123  func (o *ObjectInfo) Fs() fs.Info {
  1124  	return o.f
  1125  }
  1126  
  1127  // Remote returns the remote path
  1128  func (o *ObjectInfo) Remote() string {
  1129  	return o.f.cipher.EncryptFileName(o.ObjectInfo.Remote())
  1130  }
  1131  
  1132  // Size returns the size of the file
  1133  func (o *ObjectInfo) Size() int64 {
  1134  	size := o.ObjectInfo.Size()
  1135  	if size < 0 {
  1136  		return size
  1137  	}
  1138  	if o.f.opt.NoDataEncryption {
  1139  		return size
  1140  	}
  1141  	return o.f.cipher.EncryptedSize(size)
  1142  }
  1143  
  1144  // Hash returns the selected checksum of the file
  1145  // If no checksum is available it returns ""
  1146  func (o *ObjectInfo) Hash(ctx context.Context, hash hash.Type) (string, error) {
  1147  	var srcObj fs.Object
  1148  	var ok bool
  1149  	// Get the underlying object if there is one
  1150  	if srcObj, ok = o.ObjectInfo.(fs.Object); ok {
  1151  		// Prefer direct interface assertion
  1152  	} else if do, ok := o.ObjectInfo.(*fs.OverrideRemote); ok {
  1153  		// Unwrap if it is an operations.OverrideRemote
  1154  		srcObj = do.UnWrap()
  1155  	} else {
  1156  		// Otherwise don't unwrap any further
  1157  		return "", nil
  1158  	}
  1159  	// if this is wrapping a local object then we work out the hash
  1160  	if srcObj.Fs().Features().IsLocal {
  1161  		// Read the data and encrypt it to calculate the hash
  1162  		fs.Debugf(o, "Computing %v hash of encrypted source", hash)
  1163  		return o.f.computeHashWithNonce(ctx, o.nonce, srcObj, hash)
  1164  	}
  1165  	return "", nil
  1166  }
  1167  
  1168  // GetTier returns storage tier or class of the Object
  1169  func (o *ObjectInfo) GetTier() string {
  1170  	do, ok := o.ObjectInfo.(fs.GetTierer)
  1171  	if !ok {
  1172  		return ""
  1173  	}
  1174  	return do.GetTier()
  1175  }
  1176  
  1177  // ID returns the ID of the Object if known, or "" if not
  1178  func (o *ObjectInfo) ID() string {
  1179  	do, ok := o.ObjectInfo.(fs.IDer)
  1180  	if !ok {
  1181  		return ""
  1182  	}
  1183  	return do.ID()
  1184  }
  1185  
  1186  // Metadata returns metadata for an object
  1187  //
  1188  // It should return nil if there is no Metadata
  1189  func (o *ObjectInfo) Metadata(ctx context.Context) (fs.Metadata, error) {
  1190  	do, ok := o.ObjectInfo.(fs.Metadataer)
  1191  	if !ok {
  1192  		return nil, nil
  1193  	}
  1194  	return do.Metadata(ctx)
  1195  }
  1196  
  1197  // MimeType returns the content type of the Object if
  1198  // known, or "" if not
  1199  //
  1200  // This is deliberately unsupported so we don't leak mime type info by
  1201  // default.
  1202  func (o *ObjectInfo) MimeType(ctx context.Context) string {
  1203  	return ""
  1204  }
  1205  
  1206  // UnWrap returns the Object that this Object is wrapping or
  1207  // nil if it isn't wrapping anything
  1208  func (o *ObjectInfo) UnWrap() fs.Object {
  1209  	return fs.UnWrapObjectInfo(o.ObjectInfo)
  1210  }
  1211  
  1212  // ID returns the ID of the Object if known, or "" if not
  1213  func (o *Object) ID() string {
  1214  	do, ok := o.Object.(fs.IDer)
  1215  	if !ok {
  1216  		return ""
  1217  	}
  1218  	return do.ID()
  1219  }
  1220  
  1221  // SetTier performs changing storage tier of the Object if
  1222  // multiple storage classes supported
  1223  func (o *Object) SetTier(tier string) error {
  1224  	do, ok := o.Object.(fs.SetTierer)
  1225  	if !ok {
  1226  		return errors.New("crypt: underlying remote does not support SetTier")
  1227  	}
  1228  	return do.SetTier(tier)
  1229  }
  1230  
  1231  // GetTier returns storage tier or class of the Object
  1232  func (o *Object) GetTier() string {
  1233  	do, ok := o.Object.(fs.GetTierer)
  1234  	if !ok {
  1235  		return ""
  1236  	}
  1237  	return do.GetTier()
  1238  }
  1239  
  1240  // Metadata returns metadata for an object
  1241  //
  1242  // It should return nil if there is no Metadata
  1243  func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
  1244  	do, ok := o.Object.(fs.Metadataer)
  1245  	if !ok {
  1246  		return nil, nil
  1247  	}
  1248  	return do.Metadata(ctx)
  1249  }
  1250  
  1251  // MimeType returns the content type of the Object if
  1252  // known, or "" if not
  1253  //
  1254  // This is deliberately unsupported so we don't leak mime type info by
  1255  // default.
  1256  func (o *Object) MimeType(ctx context.Context) string {
  1257  	return ""
  1258  }
  1259  
  1260  // Check the interfaces are satisfied
  1261  var (
  1262  	_ fs.Fs              = (*Fs)(nil)
  1263  	_ fs.Purger          = (*Fs)(nil)
  1264  	_ fs.Copier          = (*Fs)(nil)
  1265  	_ fs.Mover           = (*Fs)(nil)
  1266  	_ fs.DirMover        = (*Fs)(nil)
  1267  	_ fs.Commander       = (*Fs)(nil)
  1268  	_ fs.PutUncheckeder  = (*Fs)(nil)
  1269  	_ fs.PutStreamer     = (*Fs)(nil)
  1270  	_ fs.CleanUpper      = (*Fs)(nil)
  1271  	_ fs.UnWrapper       = (*Fs)(nil)
  1272  	_ fs.ListRer         = (*Fs)(nil)
  1273  	_ fs.Abouter         = (*Fs)(nil)
  1274  	_ fs.Wrapper         = (*Fs)(nil)
  1275  	_ fs.MergeDirser     = (*Fs)(nil)
  1276  	_ fs.DirSetModTimer  = (*Fs)(nil)
  1277  	_ fs.MkdirMetadataer = (*Fs)(nil)
  1278  	_ fs.DirCacheFlusher = (*Fs)(nil)
  1279  	_ fs.ChangeNotifier  = (*Fs)(nil)
  1280  	_ fs.PublicLinker    = (*Fs)(nil)
  1281  	_ fs.UserInfoer      = (*Fs)(nil)
  1282  	_ fs.Disconnecter    = (*Fs)(nil)
  1283  	_ fs.Shutdowner      = (*Fs)(nil)
  1284  	_ fs.FullObjectInfo  = (*ObjectInfo)(nil)
  1285  	_ fs.FullObject      = (*Object)(nil)
  1286  )