github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/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  	"fmt"
     7  	"io"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/ncw/rclone/fs"
    12  	"github.com/ncw/rclone/fs/accounting"
    13  	"github.com/ncw/rclone/fs/config/configmap"
    14  	"github.com/ncw/rclone/fs/config/configstruct"
    15  	"github.com/ncw/rclone/fs/config/obscure"
    16  	"github.com/ncw/rclone/fs/fspath"
    17  	"github.com/ncw/rclone/fs/hash"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  // Globals
    22  // Register with Fs
    23  func init() {
    24  	fs.Register(&fs.RegInfo{
    25  		Name:        "crypt",
    26  		Description: "Encrypt/Decrypt a remote",
    27  		NewFs:       NewFs,
    28  		Options: []fs.Option{{
    29  			Name:     "remote",
    30  			Help:     "Remote to encrypt/decrypt.\nNormally should contain a ':' and a path, eg \"myremote:path/to/dir\",\n\"myremote:bucket\" or maybe \"myremote:\" (not recommended).",
    31  			Required: true,
    32  		}, {
    33  			Name:    "filename_encryption",
    34  			Help:    "How to encrypt the filenames.",
    35  			Default: "standard",
    36  			Examples: []fs.OptionExample{
    37  				{
    38  					Value: "off",
    39  					Help:  "Don't encrypt the file names.  Adds a \".bin\" extension only.",
    40  				}, {
    41  					Value: "standard",
    42  					Help:  "Encrypt the filenames see the docs for the details.",
    43  				}, {
    44  					Value: "obfuscate",
    45  					Help:  "Very simple filename obfuscation.",
    46  				},
    47  			},
    48  		}, {
    49  			Name:    "directory_name_encryption",
    50  			Help:    "Option to either encrypt directory names or leave them intact.",
    51  			Default: true,
    52  			Examples: []fs.OptionExample{
    53  				{
    54  					Value: "true",
    55  					Help:  "Encrypt directory names.",
    56  				},
    57  				{
    58  					Value: "false",
    59  					Help:  "Don't encrypt directory names, leave them intact.",
    60  				},
    61  			},
    62  		}, {
    63  			Name:       "password",
    64  			Help:       "Password or pass phrase for encryption.",
    65  			IsPassword: true,
    66  		}, {
    67  			Name:       "password2",
    68  			Help:       "Password or pass phrase for salt. Optional but recommended.\nShould be different to the previous password.",
    69  			IsPassword: true,
    70  		}, {
    71  			Name: "show_mapping",
    72  			Help: `For all files listed show how the names encrypt.
    73  
    74  If this flag is set then for each file that the remote is asked to
    75  list, it will log (at level INFO) a line stating the decrypted file
    76  name and the encrypted file name.
    77  
    78  This is so you can work out which encrypted names are which decrypted
    79  names just in case you need to do something with the encrypted file
    80  names, or for debugging purposes.`,
    81  			Default:  false,
    82  			Hide:     fs.OptionHideConfigurator,
    83  			Advanced: true,
    84  		}},
    85  	})
    86  }
    87  
    88  // newCipherForConfig constructs a Cipher for the given config name
    89  func newCipherForConfig(opt *Options) (Cipher, error) {
    90  	mode, err := NewNameEncryptionMode(opt.FilenameEncryption)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	if opt.Password == "" {
    95  		return nil, errors.New("password not set in config file")
    96  	}
    97  	password, err := obscure.Reveal(opt.Password)
    98  	if err != nil {
    99  		return nil, errors.Wrap(err, "failed to decrypt password")
   100  	}
   101  	var salt string
   102  	if opt.Password2 != "" {
   103  		salt, err = obscure.Reveal(opt.Password2)
   104  		if err != nil {
   105  			return nil, errors.Wrap(err, "failed to decrypt password2")
   106  		}
   107  	}
   108  	cipher, err := newCipher(mode, password, salt, opt.DirectoryNameEncryption)
   109  	if err != nil {
   110  		return nil, errors.Wrap(err, "failed to make cipher")
   111  	}
   112  	return cipher, nil
   113  }
   114  
   115  // NewCipher constructs a Cipher for the given config
   116  func NewCipher(m configmap.Mapper) (Cipher, error) {
   117  	// Parse config into Options struct
   118  	opt := new(Options)
   119  	err := configstruct.Set(m, opt)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return newCipherForConfig(opt)
   124  }
   125  
   126  // NewFs constructs an Fs from the path, container:path
   127  func NewFs(name, rpath string, m configmap.Mapper) (fs.Fs, error) {
   128  	// Parse config into Options struct
   129  	opt := new(Options)
   130  	err := configstruct.Set(m, opt)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	cipher, err := newCipherForConfig(opt)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	remote := opt.Remote
   139  	if strings.HasPrefix(remote, name+":") {
   140  		return nil, errors.New("can't point crypt remote at itself - check the value of the remote setting")
   141  	}
   142  	wInfo, wName, wPath, wConfig, err := fs.ConfigFs(remote)
   143  	if err != nil {
   144  		return nil, errors.Wrapf(err, "failed to parse remote %q to wrap", remote)
   145  	}
   146  	// Look for a file first
   147  	remotePath := fspath.JoinRootPath(wPath, cipher.EncryptFileName(rpath))
   148  	wrappedFs, err := wInfo.NewFs(wName, remotePath, wConfig)
   149  	// if that didn't produce a file, look for a directory
   150  	if err != fs.ErrorIsFile {
   151  		remotePath = fspath.JoinRootPath(wPath, cipher.EncryptDirName(rpath))
   152  		wrappedFs, err = wInfo.NewFs(wName, remotePath, wConfig)
   153  	}
   154  	if err != fs.ErrorIsFile && err != nil {
   155  		return nil, errors.Wrapf(err, "failed to make remote %s:%q to wrap", wName, remotePath)
   156  	}
   157  	f := &Fs{
   158  		Fs:     wrappedFs,
   159  		name:   name,
   160  		root:   rpath,
   161  		opt:    *opt,
   162  		cipher: cipher,
   163  	}
   164  	// the features here are ones we could support, and they are
   165  	// ANDed with the ones from wrappedFs
   166  	f.features = (&fs.Features{
   167  		CaseInsensitive:         cipher.NameEncryptionMode() == NameEncryptionOff,
   168  		DuplicateFiles:          true,
   169  		ReadMimeType:            false, // MimeTypes not supported with crypt
   170  		WriteMimeType:           false,
   171  		BucketBased:             true,
   172  		CanHaveEmptyDirectories: true,
   173  		SetTier:                 true,
   174  		GetTier:                 true,
   175  	}).Fill(f).Mask(wrappedFs).WrapsFs(f, wrappedFs)
   176  
   177  	return f, err
   178  }
   179  
   180  // Options defines the configuration for this backend
   181  type Options struct {
   182  	Remote                  string `config:"remote"`
   183  	FilenameEncryption      string `config:"filename_encryption"`
   184  	DirectoryNameEncryption bool   `config:"directory_name_encryption"`
   185  	Password                string `config:"password"`
   186  	Password2               string `config:"password2"`
   187  	ShowMapping             bool   `config:"show_mapping"`
   188  }
   189  
   190  // Fs represents a wrapped fs.Fs
   191  type Fs struct {
   192  	fs.Fs
   193  	wrapper  fs.Fs
   194  	name     string
   195  	root     string
   196  	opt      Options
   197  	features *fs.Features // optional features
   198  	cipher   Cipher
   199  }
   200  
   201  // Name of the remote (as passed into NewFs)
   202  func (f *Fs) Name() string {
   203  	return f.name
   204  }
   205  
   206  // Root of the remote (as passed into NewFs)
   207  func (f *Fs) Root() string {
   208  	return f.root
   209  }
   210  
   211  // Features returns the optional features of this Fs
   212  func (f *Fs) Features() *fs.Features {
   213  	return f.features
   214  }
   215  
   216  // String returns a description of the FS
   217  func (f *Fs) String() string {
   218  	return fmt.Sprintf("Encrypted drive '%s:%s'", f.name, f.root)
   219  }
   220  
   221  // Encrypt an object file name to entries.
   222  func (f *Fs) add(entries *fs.DirEntries, obj fs.Object) {
   223  	remote := obj.Remote()
   224  	decryptedRemote, err := f.cipher.DecryptFileName(remote)
   225  	if err != nil {
   226  		fs.Debugf(remote, "Skipping undecryptable file name: %v", err)
   227  		return
   228  	}
   229  	if f.opt.ShowMapping {
   230  		fs.Logf(decryptedRemote, "Encrypts to %q", remote)
   231  	}
   232  	*entries = append(*entries, f.newObject(obj))
   233  }
   234  
   235  // Encrypt an directory file name to entries.
   236  func (f *Fs) addDir(ctx context.Context, entries *fs.DirEntries, dir fs.Directory) {
   237  	remote := dir.Remote()
   238  	decryptedRemote, err := f.cipher.DecryptDirName(remote)
   239  	if err != nil {
   240  		fs.Debugf(remote, "Skipping undecryptable dir name: %v", err)
   241  		return
   242  	}
   243  	if f.opt.ShowMapping {
   244  		fs.Logf(decryptedRemote, "Encrypts to %q", remote)
   245  	}
   246  	*entries = append(*entries, f.newDir(ctx, dir))
   247  }
   248  
   249  // Encrypt some directory entries.  This alters entries returning it as newEntries.
   250  func (f *Fs) encryptEntries(ctx context.Context, entries fs.DirEntries) (newEntries fs.DirEntries, err error) {
   251  	newEntries = entries[:0] // in place filter
   252  	for _, entry := range entries {
   253  		switch x := entry.(type) {
   254  		case fs.Object:
   255  			f.add(&newEntries, x)
   256  		case fs.Directory:
   257  			f.addDir(ctx, &newEntries, x)
   258  		default:
   259  			return nil, errors.Errorf("Unknown object type %T", entry)
   260  		}
   261  	}
   262  	return newEntries, nil
   263  }
   264  
   265  // List the objects and directories in dir into entries.  The
   266  // entries can be returned in any order but should be for a
   267  // complete directory.
   268  //
   269  // dir should be "" to list the root, and should not have
   270  // trailing slashes.
   271  //
   272  // This should return ErrDirNotFound if the directory isn't
   273  // found.
   274  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   275  	entries, err = f.Fs.List(ctx, f.cipher.EncryptDirName(dir))
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	return f.encryptEntries(ctx, entries)
   280  }
   281  
   282  // ListR lists the objects and directories of the Fs starting
   283  // from dir recursively into out.
   284  //
   285  // dir should be "" to start from the root, and should not
   286  // have trailing slashes.
   287  //
   288  // This should return ErrDirNotFound if the directory isn't
   289  // found.
   290  //
   291  // It should call callback for each tranche of entries read.
   292  // These need not be returned in any particular order.  If
   293  // callback returns an error then the listing will stop
   294  // immediately.
   295  //
   296  // Don't implement this unless you have a more efficient way
   297  // of listing recursively that doing a directory traversal.
   298  func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) {
   299  	return f.Fs.Features().ListR(ctx, f.cipher.EncryptDirName(dir), func(entries fs.DirEntries) error {
   300  		newEntries, err := f.encryptEntries(ctx, entries)
   301  		if err != nil {
   302  			return err
   303  		}
   304  		return callback(newEntries)
   305  	})
   306  }
   307  
   308  // NewObject finds the Object at remote.
   309  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   310  	o, err := f.Fs.NewObject(ctx, f.cipher.EncryptFileName(remote))
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	return f.newObject(o), nil
   315  }
   316  
   317  type putFn func(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error)
   318  
   319  // put implements Put or PutStream
   320  func (f *Fs) put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options []fs.OpenOption, put putFn) (fs.Object, error) {
   321  	// Encrypt the data into wrappedIn
   322  	wrappedIn, err := f.cipher.EncryptData(in)
   323  	if err != nil {
   324  		return nil, err
   325  	}
   326  
   327  	// Find a hash the destination supports to compute a hash of
   328  	// the encrypted data
   329  	ht := f.Fs.Hashes().GetOne()
   330  	var hasher *hash.MultiHasher
   331  	if ht != hash.None {
   332  		hasher, err = hash.NewMultiHasherTypes(hash.NewHashSet(ht))
   333  		if err != nil {
   334  			return nil, err
   335  		}
   336  		// unwrap the accounting
   337  		var wrap accounting.WrapFn
   338  		wrappedIn, wrap = accounting.UnWrap(wrappedIn)
   339  		// add the hasher
   340  		wrappedIn = io.TeeReader(wrappedIn, hasher)
   341  		// wrap the accounting back on
   342  		wrappedIn = wrap(wrappedIn)
   343  	}
   344  
   345  	// Transfer the data
   346  	o, err := put(ctx, wrappedIn, f.newObjectInfo(src), options...)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	// Check the hashes of the encrypted data if we were comparing them
   352  	if ht != hash.None && hasher != nil {
   353  		srcHash := hasher.Sums()[ht]
   354  		var dstHash string
   355  		dstHash, err = o.Hash(ctx, ht)
   356  		if err != nil {
   357  			return nil, errors.Wrap(err, "failed to read destination hash")
   358  		}
   359  		if srcHash != "" && dstHash != "" && srcHash != dstHash {
   360  			// remove object
   361  			err = o.Remove(ctx)
   362  			if err != nil {
   363  				fs.Errorf(o, "Failed to remove corrupted object: %v", err)
   364  			}
   365  			return nil, errors.Errorf("corrupted on transfer: %v crypted hash differ %q vs %q", ht, srcHash, dstHash)
   366  		}
   367  	}
   368  
   369  	return f.newObject(o), nil
   370  }
   371  
   372  // Put in to the remote path with the modTime given of the given size
   373  //
   374  // May create the object even if it returns an error - if so
   375  // will return the object and the error, otherwise will return
   376  // nil and the error
   377  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   378  	return f.put(ctx, in, src, options, f.Fs.Put)
   379  }
   380  
   381  // PutStream uploads to the remote path with the modTime given of indeterminate size
   382  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   383  	return f.put(ctx, in, src, options, f.Fs.Features().PutStream)
   384  }
   385  
   386  // Hashes returns the supported hash sets.
   387  func (f *Fs) Hashes() hash.Set {
   388  	return hash.Set(hash.None)
   389  }
   390  
   391  // Mkdir makes the directory (container, bucket)
   392  //
   393  // Shouldn't return an error if it already exists
   394  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   395  	return f.Fs.Mkdir(ctx, f.cipher.EncryptDirName(dir))
   396  }
   397  
   398  // Rmdir removes the directory (container, bucket) if empty
   399  //
   400  // Return an error if it doesn't exist or isn't empty
   401  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   402  	return f.Fs.Rmdir(ctx, f.cipher.EncryptDirName(dir))
   403  }
   404  
   405  // Purge all files in the root and the root directory
   406  //
   407  // Implement this if you have a way of deleting all the files
   408  // quicker than just running Remove() on the result of List()
   409  //
   410  // Return an error if it doesn't exist
   411  func (f *Fs) Purge(ctx context.Context) error {
   412  	do := f.Fs.Features().Purge
   413  	if do == nil {
   414  		return fs.ErrorCantPurge
   415  	}
   416  	return do(ctx)
   417  }
   418  
   419  // Copy src to this remote using server side copy operations.
   420  //
   421  // This is stored with the remote path given
   422  //
   423  // It returns the destination Object and a possible error
   424  //
   425  // Will only be called if src.Fs().Name() == f.Name()
   426  //
   427  // If it isn't possible then return fs.ErrorCantCopy
   428  func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   429  	do := f.Fs.Features().Copy
   430  	if do == nil {
   431  		return nil, fs.ErrorCantCopy
   432  	}
   433  	o, ok := src.(*Object)
   434  	if !ok {
   435  		return nil, fs.ErrorCantCopy
   436  	}
   437  	oResult, err := do(ctx, o.Object, f.cipher.EncryptFileName(remote))
   438  	if err != nil {
   439  		return nil, err
   440  	}
   441  	return f.newObject(oResult), nil
   442  }
   443  
   444  // Move src to this remote using server side move operations.
   445  //
   446  // This is stored with the remote path given
   447  //
   448  // It returns the destination Object and a possible error
   449  //
   450  // Will only be called if src.Fs().Name() == f.Name()
   451  //
   452  // If it isn't possible then return fs.ErrorCantMove
   453  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   454  	do := f.Fs.Features().Move
   455  	if do == nil {
   456  		return nil, fs.ErrorCantMove
   457  	}
   458  	o, ok := src.(*Object)
   459  	if !ok {
   460  		return nil, fs.ErrorCantMove
   461  	}
   462  	oResult, err := do(ctx, o.Object, f.cipher.EncryptFileName(remote))
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  	return f.newObject(oResult), nil
   467  }
   468  
   469  // DirMove moves src, srcRemote to this remote at dstRemote
   470  // using server side move operations.
   471  //
   472  // Will only be called if src.Fs().Name() == f.Name()
   473  //
   474  // If it isn't possible then return fs.ErrorCantDirMove
   475  //
   476  // If destination exists then return fs.ErrorDirExists
   477  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
   478  	do := f.Fs.Features().DirMove
   479  	if do == nil {
   480  		return fs.ErrorCantDirMove
   481  	}
   482  	srcFs, ok := src.(*Fs)
   483  	if !ok {
   484  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   485  		return fs.ErrorCantDirMove
   486  	}
   487  	return do(ctx, srcFs.Fs, f.cipher.EncryptDirName(srcRemote), f.cipher.EncryptDirName(dstRemote))
   488  }
   489  
   490  // PutUnchecked uploads the object
   491  //
   492  // This will create a duplicate if we upload a new file without
   493  // checking to see if there is one already - use Put() for that.
   494  func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   495  	do := f.Fs.Features().PutUnchecked
   496  	if do == nil {
   497  		return nil, errors.New("can't PutUnchecked")
   498  	}
   499  	wrappedIn, err := f.cipher.EncryptData(in)
   500  	if err != nil {
   501  		return nil, err
   502  	}
   503  	o, err := do(ctx, wrappedIn, f.newObjectInfo(src))
   504  	if err != nil {
   505  		return nil, err
   506  	}
   507  	return f.newObject(o), nil
   508  }
   509  
   510  // CleanUp the trash in the Fs
   511  //
   512  // Implement this if you have a way of emptying the trash or
   513  // otherwise cleaning up old versions of files.
   514  func (f *Fs) CleanUp(ctx context.Context) error {
   515  	do := f.Fs.Features().CleanUp
   516  	if do == nil {
   517  		return errors.New("can't CleanUp")
   518  	}
   519  	return do(ctx)
   520  }
   521  
   522  // About gets quota information from the Fs
   523  func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
   524  	do := f.Fs.Features().About
   525  	if do == nil {
   526  		return nil, errors.New("About not supported")
   527  	}
   528  	return do(ctx)
   529  }
   530  
   531  // UnWrap returns the Fs that this Fs is wrapping
   532  func (f *Fs) UnWrap() fs.Fs {
   533  	return f.Fs
   534  }
   535  
   536  // WrapFs returns the Fs that is wrapping this Fs
   537  func (f *Fs) WrapFs() fs.Fs {
   538  	return f.wrapper
   539  }
   540  
   541  // SetWrapper sets the Fs that is wrapping this Fs
   542  func (f *Fs) SetWrapper(wrapper fs.Fs) {
   543  	f.wrapper = wrapper
   544  }
   545  
   546  // EncryptFileName returns an encrypted file name
   547  func (f *Fs) EncryptFileName(fileName string) string {
   548  	return f.cipher.EncryptFileName(fileName)
   549  }
   550  
   551  // DecryptFileName returns a decrypted file name
   552  func (f *Fs) DecryptFileName(encryptedFileName string) (string, error) {
   553  	return f.cipher.DecryptFileName(encryptedFileName)
   554  }
   555  
   556  // ComputeHash takes the nonce from o, and encrypts the contents of
   557  // src with it, and calculates the hash given by HashType on the fly
   558  //
   559  // Note that we break lots of encapsulation in this function.
   560  func (f *Fs) ComputeHash(ctx context.Context, o *Object, src fs.Object, hashType hash.Type) (hashStr string, err error) {
   561  	// Read the nonce - opening the file is sufficient to read the nonce in
   562  	// use a limited read so we only read the header
   563  	in, err := o.Object.Open(ctx, &fs.RangeOption{Start: 0, End: int64(fileHeaderSize) - 1})
   564  	if err != nil {
   565  		return "", errors.Wrap(err, "failed to open object to read nonce")
   566  	}
   567  	d, err := f.cipher.(*cipher).newDecrypter(in)
   568  	if err != nil {
   569  		_ = in.Close()
   570  		return "", errors.Wrap(err, "failed to open object to read nonce")
   571  	}
   572  	nonce := d.nonce
   573  	// fs.Debugf(o, "Read nonce % 2x", nonce)
   574  
   575  	// Check nonce isn't all zeros
   576  	isZero := true
   577  	for i := range nonce {
   578  		if nonce[i] != 0 {
   579  			isZero = false
   580  		}
   581  	}
   582  	if isZero {
   583  		fs.Errorf(o, "empty nonce read")
   584  	}
   585  
   586  	// Close d (and hence in) once we have read the nonce
   587  	err = d.Close()
   588  	if err != nil {
   589  		return "", errors.Wrap(err, "failed to close nonce read")
   590  	}
   591  
   592  	// Open the src for input
   593  	in, err = src.Open(ctx)
   594  	if err != nil {
   595  		return "", errors.Wrap(err, "failed to open src")
   596  	}
   597  	defer fs.CheckClose(in, &err)
   598  
   599  	// Now encrypt the src with the nonce
   600  	out, err := f.cipher.(*cipher).newEncrypter(in, &nonce)
   601  	if err != nil {
   602  		return "", errors.Wrap(err, "failed to make encrypter")
   603  	}
   604  
   605  	// pipe into hash
   606  	m, err := hash.NewMultiHasherTypes(hash.NewHashSet(hashType))
   607  	if err != nil {
   608  		return "", errors.Wrap(err, "failed to make hasher")
   609  	}
   610  	_, err = io.Copy(m, out)
   611  	if err != nil {
   612  		return "", errors.Wrap(err, "failed to hash data")
   613  	}
   614  
   615  	return m.Sums()[hashType], nil
   616  }
   617  
   618  // MergeDirs merges the contents of all the directories passed
   619  // in into the first one and rmdirs the other directories.
   620  func (f *Fs) MergeDirs(ctx context.Context, dirs []fs.Directory) error {
   621  	do := f.Fs.Features().MergeDirs
   622  	if do == nil {
   623  		return errors.New("MergeDirs not supported")
   624  	}
   625  	out := make([]fs.Directory, len(dirs))
   626  	for i, dir := range dirs {
   627  		out[i] = fs.NewDirCopy(ctx, dir).SetRemote(f.cipher.EncryptDirName(dir.Remote()))
   628  	}
   629  	return do(ctx, out)
   630  }
   631  
   632  // DirCacheFlush resets the directory cache - used in testing
   633  // as an optional interface
   634  func (f *Fs) DirCacheFlush() {
   635  	do := f.Fs.Features().DirCacheFlush
   636  	if do != nil {
   637  		do()
   638  	}
   639  }
   640  
   641  // PublicLink generates a public link to the remote path (usually readable by anyone)
   642  func (f *Fs) PublicLink(ctx context.Context, remote string) (string, error) {
   643  	do := f.Fs.Features().PublicLink
   644  	if do == nil {
   645  		return "", errors.New("PublicLink not supported")
   646  	}
   647  	o, err := f.NewObject(ctx, remote)
   648  	if err != nil {
   649  		// assume it is a directory
   650  		return do(ctx, f.cipher.EncryptDirName(remote))
   651  	}
   652  	return do(ctx, o.(*Object).Object.Remote())
   653  }
   654  
   655  // ChangeNotify calls the passed function with a path
   656  // that has had changes. If the implementation
   657  // uses polling, it should adhere to the given interval.
   658  func (f *Fs) ChangeNotify(ctx context.Context, notifyFunc func(string, fs.EntryType), pollIntervalChan <-chan time.Duration) {
   659  	do := f.Fs.Features().ChangeNotify
   660  	if do == nil {
   661  		return
   662  	}
   663  	wrappedNotifyFunc := func(path string, entryType fs.EntryType) {
   664  		// fs.Debugf(f, "ChangeNotify: path %q entryType %d", path, entryType)
   665  		var (
   666  			err       error
   667  			decrypted string
   668  		)
   669  		switch entryType {
   670  		case fs.EntryDirectory:
   671  			decrypted, err = f.cipher.DecryptDirName(path)
   672  		case fs.EntryObject:
   673  			decrypted, err = f.cipher.DecryptFileName(path)
   674  		default:
   675  			fs.Errorf(path, "crypt ChangeNotify: ignoring unknown EntryType %d", entryType)
   676  			return
   677  		}
   678  		if err != nil {
   679  			fs.Logf(f, "ChangeNotify was unable to decrypt %q: %s", path, err)
   680  			return
   681  		}
   682  		notifyFunc(decrypted, entryType)
   683  	}
   684  	do(ctx, wrappedNotifyFunc, pollIntervalChan)
   685  }
   686  
   687  // Object describes a wrapped for being read from the Fs
   688  //
   689  // This decrypts the remote name and decrypts the data
   690  type Object struct {
   691  	fs.Object
   692  	f *Fs
   693  }
   694  
   695  func (f *Fs) newObject(o fs.Object) *Object {
   696  	return &Object{
   697  		Object: o,
   698  		f:      f,
   699  	}
   700  }
   701  
   702  // Fs returns read only access to the Fs that this object is part of
   703  func (o *Object) Fs() fs.Info {
   704  	return o.f
   705  }
   706  
   707  // Return a string version
   708  func (o *Object) String() string {
   709  	if o == nil {
   710  		return "<nil>"
   711  	}
   712  	return o.Remote()
   713  }
   714  
   715  // Remote returns the remote path
   716  func (o *Object) Remote() string {
   717  	remote := o.Object.Remote()
   718  	decryptedName, err := o.f.cipher.DecryptFileName(remote)
   719  	if err != nil {
   720  		fs.Debugf(remote, "Undecryptable file name: %v", err)
   721  		return remote
   722  	}
   723  	return decryptedName
   724  }
   725  
   726  // Size returns the size of the file
   727  func (o *Object) Size() int64 {
   728  	size, err := o.f.cipher.DecryptedSize(o.Object.Size())
   729  	if err != nil {
   730  		fs.Debugf(o, "Bad size for decrypt: %v", err)
   731  	}
   732  	return size
   733  }
   734  
   735  // Hash returns the selected checksum of the file
   736  // If no checksum is available it returns ""
   737  func (o *Object) Hash(ctx context.Context, ht hash.Type) (string, error) {
   738  	return "", hash.ErrUnsupported
   739  }
   740  
   741  // UnWrap returns the wrapped Object
   742  func (o *Object) UnWrap() fs.Object {
   743  	return o.Object
   744  }
   745  
   746  // Open opens the file for read.  Call Close() on the returned io.ReadCloser
   747  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (rc io.ReadCloser, err error) {
   748  	var openOptions []fs.OpenOption
   749  	var offset, limit int64 = 0, -1
   750  	for _, option := range options {
   751  		switch x := option.(type) {
   752  		case *fs.SeekOption:
   753  			offset = x.Offset
   754  		case *fs.RangeOption:
   755  			offset, limit = x.Decode(o.Size())
   756  		default:
   757  			// pass on Options to underlying open if appropriate
   758  			openOptions = append(openOptions, option)
   759  		}
   760  	}
   761  	rc, err = o.f.cipher.DecryptDataSeek(ctx, func(ctx context.Context, underlyingOffset, underlyingLimit int64) (io.ReadCloser, error) {
   762  		if underlyingOffset == 0 && underlyingLimit < 0 {
   763  			// Open with no seek
   764  			return o.Object.Open(ctx, openOptions...)
   765  		}
   766  		// Open stream with a range of underlyingOffset, underlyingLimit
   767  		end := int64(-1)
   768  		if underlyingLimit >= 0 {
   769  			end = underlyingOffset + underlyingLimit - 1
   770  			if end >= o.Object.Size() {
   771  				end = -1
   772  			}
   773  		}
   774  		newOpenOptions := append(openOptions, &fs.RangeOption{Start: underlyingOffset, End: end})
   775  		return o.Object.Open(ctx, newOpenOptions...)
   776  	}, offset, limit)
   777  	if err != nil {
   778  		return nil, err
   779  	}
   780  	return rc, nil
   781  }
   782  
   783  // Update in to the object with the modTime given of the given size
   784  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
   785  	update := func(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   786  		return o.Object, o.Object.Update(ctx, in, src, options...)
   787  	}
   788  	_, err := o.f.put(ctx, in, src, options, update)
   789  	return err
   790  }
   791  
   792  // newDir returns a dir with the Name decrypted
   793  func (f *Fs) newDir(ctx context.Context, dir fs.Directory) fs.Directory {
   794  	newDir := fs.NewDirCopy(ctx, dir)
   795  	remote := dir.Remote()
   796  	decryptedRemote, err := f.cipher.DecryptDirName(remote)
   797  	if err != nil {
   798  		fs.Debugf(remote, "Undecryptable dir name: %v", err)
   799  	} else {
   800  		newDir.SetRemote(decryptedRemote)
   801  	}
   802  	return newDir
   803  }
   804  
   805  // ObjectInfo describes a wrapped fs.ObjectInfo for being the source
   806  //
   807  // This encrypts the remote name and adjusts the size
   808  type ObjectInfo struct {
   809  	fs.ObjectInfo
   810  	f *Fs
   811  }
   812  
   813  func (f *Fs) newObjectInfo(src fs.ObjectInfo) *ObjectInfo {
   814  	return &ObjectInfo{
   815  		ObjectInfo: src,
   816  		f:          f,
   817  	}
   818  }
   819  
   820  // Fs returns read only access to the Fs that this object is part of
   821  func (o *ObjectInfo) Fs() fs.Info {
   822  	return o.f
   823  }
   824  
   825  // Remote returns the remote path
   826  func (o *ObjectInfo) Remote() string {
   827  	return o.f.cipher.EncryptFileName(o.ObjectInfo.Remote())
   828  }
   829  
   830  // Size returns the size of the file
   831  func (o *ObjectInfo) Size() int64 {
   832  	size := o.ObjectInfo.Size()
   833  	if size < 0 {
   834  		return size
   835  	}
   836  	return o.f.cipher.EncryptedSize(size)
   837  }
   838  
   839  // Hash returns the selected checksum of the file
   840  // If no checksum is available it returns ""
   841  func (o *ObjectInfo) Hash(ctx context.Context, hash hash.Type) (string, error) {
   842  	return "", nil
   843  }
   844  
   845  // ID returns the ID of the Object if known, or "" if not
   846  func (o *Object) ID() string {
   847  	do, ok := o.Object.(fs.IDer)
   848  	if !ok {
   849  		return ""
   850  	}
   851  	return do.ID()
   852  }
   853  
   854  // SetTier performs changing storage tier of the Object if
   855  // multiple storage classes supported
   856  func (o *Object) SetTier(tier string) error {
   857  	do, ok := o.Object.(fs.SetTierer)
   858  	if !ok {
   859  		return errors.New("crypt: underlying remote does not support SetTier")
   860  	}
   861  	return do.SetTier(tier)
   862  }
   863  
   864  // GetTier returns storage tier or class of the Object
   865  func (o *Object) GetTier() string {
   866  	do, ok := o.Object.(fs.GetTierer)
   867  	if !ok {
   868  		return ""
   869  	}
   870  	return do.GetTier()
   871  }
   872  
   873  // Check the interfaces are satisfied
   874  var (
   875  	_ fs.Fs              = (*Fs)(nil)
   876  	_ fs.Purger          = (*Fs)(nil)
   877  	_ fs.Copier          = (*Fs)(nil)
   878  	_ fs.Mover           = (*Fs)(nil)
   879  	_ fs.DirMover        = (*Fs)(nil)
   880  	_ fs.PutUncheckeder  = (*Fs)(nil)
   881  	_ fs.PutStreamer     = (*Fs)(nil)
   882  	_ fs.CleanUpper      = (*Fs)(nil)
   883  	_ fs.UnWrapper       = (*Fs)(nil)
   884  	_ fs.ListRer         = (*Fs)(nil)
   885  	_ fs.Abouter         = (*Fs)(nil)
   886  	_ fs.Wrapper         = (*Fs)(nil)
   887  	_ fs.MergeDirser     = (*Fs)(nil)
   888  	_ fs.DirCacheFlusher = (*Fs)(nil)
   889  	_ fs.ChangeNotifier  = (*Fs)(nil)
   890  	_ fs.PublicLinker    = (*Fs)(nil)
   891  	_ fs.ObjectInfo      = (*ObjectInfo)(nil)
   892  	_ fs.Object          = (*Object)(nil)
   893  	_ fs.ObjectUnWrapper = (*Object)(nil)
   894  	_ fs.IDer            = (*Object)(nil)
   895  	_ fs.SetTierer       = (*Object)(nil)
   896  	_ fs.GetTierer       = (*Object)(nil)
   897  )