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