2016-07-25 18:18:56 +00:00
// Package crypt provides wrappers for Fs and Object which implement encryption
package crypt
import (
"fmt"
"io"
2016-12-19 14:09:59 +00:00
"strings"
2017-10-16 19:52:12 +00:00
"time"
2016-07-25 18:18:56 +00:00
"github.com/ncw/rclone/fs"
2018-08-20 14:29:35 +00:00
"github.com/ncw/rclone/fs/accounting"
2018-05-14 17:06:57 +00:00
"github.com/ncw/rclone/fs/config/configmap"
"github.com/ncw/rclone/fs/config/configstruct"
2018-01-18 20:19:55 +00:00
"github.com/ncw/rclone/fs/config/obscure"
2018-10-09 11:35:27 +00:00
"github.com/ncw/rclone/fs/fspath"
2018-01-12 16:30:54 +00:00
"github.com/ncw/rclone/fs/hash"
2016-07-25 18:18:56 +00:00
"github.com/pkg/errors"
)
2017-01-29 10:12:52 +00:00
// Globals
2016-07-25 18:18:56 +00:00
// Register with Fs
func init ( ) {
fs . Register ( & fs . RegInfo {
Name : "crypt" ,
Description : "Encrypt/Decrypt a remote" ,
NewFs : NewFs ,
Options : [ ] fs . Option { {
2018-05-14 17:06:57 +00:00
Name : "remote" ,
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)." ,
Required : true ,
2016-07-25 18:18:56 +00:00
} , {
2018-05-14 17:06:57 +00:00
Name : "filename_encryption" ,
Help : "How to encrypt the filenames." ,
Default : "standard" ,
2016-07-25 18:18:56 +00:00
Examples : [ ] fs . OptionExample {
{
2016-08-20 17:46:10 +00:00
Value : "off" ,
Help : "Don't encrypt the file names. Adds a \".bin\" extension only." ,
2016-07-25 18:18:56 +00:00
} , {
2016-08-20 17:46:10 +00:00
Value : "standard" ,
Help : "Encrypt the filenames see the docs for the details." ,
2017-03-12 18:14:36 +00:00
} , {
Value : "obfuscate" ,
Help : "Very simple filename obfuscation." ,
2016-07-25 18:18:56 +00:00
} ,
} ,
2017-11-06 07:35:53 +00:00
} , {
2018-05-14 17:06:57 +00:00
Name : "directory_name_encryption" ,
Help : "Option to either encrypt directory names or leave them intact." ,
Default : true ,
2017-11-06 07:35:53 +00:00
Examples : [ ] fs . OptionExample {
{
Value : "true" ,
Help : "Encrypt directory names." ,
} ,
{
Value : "false" ,
Help : "Don't encrypt directory names, leave them intact." ,
} ,
} ,
2016-07-25 18:18:56 +00:00
} , {
Name : "password" ,
Help : "Password or pass phrase for encryption." ,
IsPassword : true ,
2016-08-19 19:02:02 +00:00
} , {
Name : "password2" ,
Help : "Password or pass phrase for salt. Optional but recommended.\nShould be different to the previous password." ,
IsPassword : true ,
2018-05-14 17:06:57 +00:00
} , {
2018-10-01 17:36:15 +00:00
Name : "show_mapping" ,
Help : ` For all files listed show how the names encrypt .
If this flag is set then for each file that the remote is asked to
list , it will log ( at level INFO ) a line stating the decrypted file
name and the encrypted file name .
This is so you can work out which encrypted names are which decrypted
names just in case you need to do something with the encrypted file
names , or for debugging purposes . ` ,
2018-05-14 17:06:57 +00:00
Default : false ,
Hide : fs . OptionHideConfigurator ,
Advanced : true ,
2016-07-25 18:18:56 +00:00
} } ,
} )
}
2018-05-14 17:06:57 +00:00
// newCipherForConfig constructs a Cipher for the given config name
func newCipherForConfig ( opt * Options ) ( Cipher , error ) {
mode , err := NewNameEncryptionMode ( opt . FilenameEncryption )
2017-11-06 07:35:53 +00:00
if err != nil {
return nil , err
}
2018-05-14 17:06:57 +00:00
if opt . Password == "" {
2016-07-25 18:18:56 +00:00
return nil , errors . New ( "password not set in config file" )
}
2018-05-14 17:06:57 +00:00
password , err := obscure . Reveal ( opt . Password )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to decrypt password" )
}
2018-05-14 17:06:57 +00:00
var salt string
if opt . Password2 != "" {
salt , err = obscure . Reveal ( opt . Password2 )
2016-08-19 19:02:02 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to decrypt password2" )
}
}
2018-05-14 17:06:57 +00:00
cipher , err := newCipher ( mode , password , salt , opt . DirectoryNameEncryption )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to make cipher" )
}
2018-02-25 11:57:14 +00:00
return cipher , nil
}
2018-05-14 17:06:57 +00:00
// NewCipher constructs a Cipher for the given config
func NewCipher ( m configmap . Mapper ) ( Cipher , error ) {
// Parse config into Options struct
opt := new ( Options )
err := configstruct . Set ( m , opt )
if err != nil {
return nil , err
}
return newCipherForConfig ( opt )
}
2019-02-07 17:41:17 +00:00
// NewFs constructs an Fs from the path, container:path
2018-05-14 17:06:57 +00:00
func NewFs ( name , rpath string , m configmap . Mapper ) ( fs . Fs , error ) {
// Parse config into Options struct
opt := new ( Options )
err := configstruct . Set ( m , opt )
if err != nil {
return nil , err
}
cipher , err := newCipherForConfig ( opt )
2018-02-25 11:57:14 +00:00
if err != nil {
return nil , err
}
2018-05-14 17:06:57 +00:00
remote := opt . Remote
2016-12-19 14:09:59 +00:00
if strings . HasPrefix ( remote , name + ":" ) {
return nil , errors . New ( "can't point crypt remote at itself - check the value of the remote setting" )
}
2018-09-08 11:02:19 +00:00
wInfo , wName , wPath , wConfig , err := fs . ConfigFs ( remote )
if err != nil {
return nil , errors . Wrapf ( err , "failed to parse remote %q to wrap" , remote )
}
2016-08-20 17:46:10 +00:00
// Look for a file first
2018-10-09 11:35:27 +00:00
remotePath := fspath . JoinRootPath ( wPath , cipher . EncryptFileName ( rpath ) )
2018-09-08 11:02:19 +00:00
wrappedFs , err := wInfo . NewFs ( wName , remotePath , wConfig )
2016-08-20 17:46:10 +00:00
// if that didn't produce a file, look for a directory
if err != fs . ErrorIsFile {
2018-10-09 11:35:27 +00:00
remotePath = fspath . JoinRootPath ( wPath , cipher . EncryptDirName ( rpath ) )
2018-09-08 11:02:19 +00:00
wrappedFs , err = wInfo . NewFs ( wName , remotePath , wConfig )
2016-08-20 17:46:10 +00:00
}
2016-07-25 18:18:56 +00:00
if err != fs . ErrorIsFile && err != nil {
2018-09-08 11:02:19 +00:00
return nil , errors . Wrapf ( err , "failed to make remote %s:%q to wrap" , wName , remotePath )
2016-07-25 18:18:56 +00:00
}
f := & Fs {
2016-08-20 17:46:10 +00:00
Fs : wrappedFs ,
2016-09-09 07:38:18 +00:00
name : name ,
root : rpath ,
2018-05-14 17:06:57 +00:00
opt : * opt ,
2017-01-13 17:21:47 +00:00
cipher : cipher ,
2016-07-25 18:18:56 +00:00
}
2017-01-13 17:21:47 +00:00
// the features here are ones we could support, and they are
// ANDed with the ones from wrappedFs
f . features = ( & fs . Features {
2018-02-25 11:57:14 +00:00
CaseInsensitive : cipher . NameEncryptionMode ( ) == NameEncryptionOff ,
2017-08-09 14:27:43 +00:00
DuplicateFiles : true ,
ReadMimeType : false , // MimeTypes not supported with crypt
WriteMimeType : false ,
BucketBased : true ,
CanHaveEmptyDirectories : true ,
2017-12-06 15:14:34 +00:00
} ) . Fill ( f ) . Mask ( wrappedFs ) . WrapsFs ( f , wrappedFs )
2017-10-16 19:52:12 +00:00
2018-03-08 20:03:34 +00:00
doChangeNotify := wrappedFs . Features ( ) . ChangeNotify
if doChangeNotify != nil {
2018-08-25 19:28:57 +00:00
f . features . ChangeNotify = func ( notifyFunc func ( string , fs . EntryType ) , pollInterval <- chan time . Duration ) {
2018-03-08 20:03:34 +00:00
wrappedNotifyFunc := func ( path string , entryType fs . EntryType ) {
2017-10-16 19:52:12 +00:00
decrypted , err := f . DecryptFileName ( path )
if err != nil {
2018-03-08 20:03:34 +00:00
fs . Logf ( f , "ChangeNotify was unable to decrypt %q: %s" , path , err )
2017-10-16 19:52:12 +00:00
return
}
2018-03-08 20:03:34 +00:00
notifyFunc ( decrypted , entryType )
2017-10-16 19:52:12 +00:00
}
2018-08-25 19:28:57 +00:00
doChangeNotify ( wrappedNotifyFunc , pollInterval )
2017-10-16 19:52:12 +00:00
}
}
2016-07-25 18:18:56 +00:00
return f , err
}
2018-05-14 17:06:57 +00:00
// Options defines the configuration for this backend
type Options struct {
Remote string ` config:"remote" `
FilenameEncryption string ` config:"filename_encryption" `
DirectoryNameEncryption bool ` config:"directory_name_encryption" `
Password string ` config:"password" `
Password2 string ` config:"password2" `
ShowMapping bool ` config:"show_mapping" `
}
2016-07-25 18:18:56 +00:00
// Fs represents a wrapped fs.Fs
type Fs struct {
fs . Fs
2017-01-13 17:21:47 +00:00
name string
root string
2018-05-14 17:06:57 +00:00
opt Options
2017-01-13 17:21:47 +00:00
features * fs . Features // optional features
cipher Cipher
2016-09-09 07:38:18 +00:00
}
// Name of the remote (as passed into NewFs)
func ( f * Fs ) Name ( ) string {
return f . name
}
// Root of the remote (as passed into NewFs)
func ( f * Fs ) Root ( ) string {
return f . root
2016-07-25 18:18:56 +00:00
}
2017-01-13 17:21:47 +00:00
// Features returns the optional features of this Fs
func ( f * Fs ) Features ( ) * fs . Features {
return f . features
}
2016-07-25 18:18:56 +00:00
// String returns a description of the FS
func ( f * Fs ) String ( ) string {
2017-04-10 16:52:31 +00:00
return fmt . Sprintf ( "Encrypted drive '%s:%s'" , f . name , f . root )
2016-07-25 18:18:56 +00:00
}
2017-06-11 21:43:31 +00:00
// Encrypt an object file name to entries.
func ( f * Fs ) add ( entries * fs . DirEntries , obj fs . Object ) {
remote := obj . Remote ( )
decryptedRemote , err := f . cipher . DecryptFileName ( remote )
if err != nil {
fs . Debugf ( remote , "Skipping undecryptable file name: %v" , err )
return
}
2018-05-14 17:06:57 +00:00
if f . opt . ShowMapping {
2017-06-11 21:43:31 +00:00
fs . Logf ( decryptedRemote , "Encrypts to %q" , remote )
}
* entries = append ( * entries , f . newObject ( obj ) )
}
// Encrypt an directory file name to entries.
2017-06-30 12:37:29 +00:00
func ( f * Fs ) addDir ( entries * fs . DirEntries , dir fs . Directory ) {
remote := dir . Remote ( )
2017-06-11 21:43:31 +00:00
decryptedRemote , err := f . cipher . DecryptDirName ( remote )
if err != nil {
fs . Debugf ( remote , "Skipping undecryptable dir name: %v" , err )
return
}
2018-05-14 17:06:57 +00:00
if f . opt . ShowMapping {
2017-06-11 21:43:31 +00:00
fs . Logf ( decryptedRemote , "Encrypts to %q" , remote )
}
* entries = append ( * entries , f . newDir ( dir ) )
}
2017-06-15 15:45:21 +00:00
// Encrypt some directory entries. This alters entries returning it as newEntries.
2017-06-11 21:43:31 +00:00
func ( f * Fs ) encryptEntries ( entries fs . DirEntries ) ( newEntries fs . DirEntries , err error ) {
2017-06-15 15:45:21 +00:00
newEntries = entries [ : 0 ] // in place filter
2017-06-11 21:43:31 +00:00
for _ , entry := range entries {
switch x := entry . ( type ) {
case fs . Object :
f . add ( & newEntries , x )
2017-06-30 12:37:29 +00:00
case fs . Directory :
2017-06-11 21:43:31 +00:00
f . addDir ( & newEntries , x )
default :
return nil , errors . Errorf ( "Unknown object type %T" , entry )
}
}
return newEntries , nil
}
// List the objects and directories in dir into entries. The
// entries can be returned in any order but should be for a
// complete directory.
//
// dir should be "" to list the root, and should not have
// trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
func ( f * Fs ) List ( dir string ) ( entries fs . DirEntries , err error ) {
entries , err = f . Fs . List ( f . cipher . EncryptDirName ( dir ) )
if err != nil {
return nil , err
}
return f . encryptEntries ( entries )
}
// ListR lists the objects and directories of the Fs starting
// from dir recursively into out.
//
// dir should be "" to start from the root, and should not
// have trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
//
// It should call callback for each tranche of entries read.
// These need not be returned in any particular order. If
// callback returns an error then the listing will stop
// immediately.
//
// Don't implement this unless you have a more efficient way
// of listing recursively that doing a directory traversal.
func ( f * Fs ) ListR ( dir string , callback fs . ListRCallback ) ( err error ) {
return f . Fs . Features ( ) . ListR ( f . cipher . EncryptDirName ( dir ) , func ( entries fs . DirEntries ) error {
newEntries , err := f . encryptEntries ( entries )
if err != nil {
return err
}
return callback ( newEntries )
} )
2016-07-25 18:18:56 +00:00
}
// NewObject finds the Object at remote.
func ( f * Fs ) NewObject ( remote string ) ( fs . Object , error ) {
2016-08-20 17:46:10 +00:00
o , err := f . Fs . NewObject ( f . cipher . EncryptFileName ( remote ) )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
return f . newObject ( o ) , nil
}
2017-09-27 15:46:28 +00:00
type putFn func ( in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error )
// put implements Put or PutStream
func ( f * Fs ) put ( in io . Reader , src fs . ObjectInfo , options [ ] fs . OpenOption , put putFn ) ( fs . Object , error ) {
2018-05-12 11:01:14 +00:00
// Encrypt the data into wrappedIn
2016-07-25 18:18:56 +00:00
wrappedIn , err := f . cipher . EncryptData ( in )
if err != nil {
return nil , err
}
2018-05-12 11:01:14 +00:00
// Find a hash the destination supports to compute a hash of
// the encrypted data
ht := f . Fs . Hashes ( ) . GetOne ( )
var hasher * hash . MultiHasher
if ht != hash . None {
hasher , err = hash . NewMultiHasherTypes ( hash . NewHashSet ( ht ) )
if err != nil {
return nil , err
}
2018-08-20 14:29:35 +00:00
// unwrap the accounting
var wrap accounting . WrapFn
wrappedIn , wrap = accounting . UnWrap ( wrappedIn )
// add the hasher
2018-05-12 11:01:14 +00:00
wrappedIn = io . TeeReader ( wrappedIn , hasher )
2018-08-20 14:29:35 +00:00
// wrap the accounting back on
wrappedIn = wrap ( wrappedIn )
2018-05-12 11:01:14 +00:00
}
// Transfer the data
2017-09-27 15:46:28 +00:00
o , err := put ( wrappedIn , f . newObjectInfo ( src ) , options ... )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
2018-05-12 11:01:14 +00:00
// Check the hashes of the encrypted data if we were comparing them
if ht != hash . None && hasher != nil {
srcHash := hasher . Sums ( ) [ ht ]
var dstHash string
dstHash , err = o . Hash ( ht )
if err != nil {
return nil , errors . Wrap ( err , "failed to read destination hash" )
}
if srcHash != "" && dstHash != "" && srcHash != dstHash {
// remove object
err = o . Remove ( )
if err != nil {
fs . Errorf ( o , "Failed to remove corrupted object: %v" , err )
}
return nil , errors . Errorf ( "corrupted on transfer: %v crypted hash differ %q vs %q" , ht , srcHash , dstHash )
}
}
2016-07-25 18:18:56 +00:00
return f . newObject ( o ) , nil
}
2017-09-27 15:46:28 +00:00
// Put in to the remote path with the modTime given of the given size
//
// May create the object even if it returns an error - if so
// will return the object and the error, otherwise will return
// nil and the error
func ( f * Fs ) Put ( in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
return f . put ( in , src , options , f . Fs . Put )
}
2017-08-03 19:42:35 +00:00
// PutStream uploads to the remote path with the modTime given of indeterminate size
func ( f * Fs ) PutStream ( in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
2017-09-27 15:46:28 +00:00
return f . put ( in , src , options , f . Fs . Features ( ) . PutStream )
2017-08-03 19:42:35 +00:00
}
2016-07-25 18:18:56 +00:00
// Hashes returns the supported hash sets.
2018-01-12 16:30:54 +00:00
func ( f * Fs ) Hashes ( ) hash . Set {
2018-01-18 20:27:52 +00:00
return hash . Set ( hash . None )
2016-07-25 18:18:56 +00:00
}
2016-12-06 15:13:29 +00:00
// Mkdir makes the directory (container, bucket)
//
// Shouldn't return an error if it already exists
func ( f * Fs ) Mkdir ( dir string ) error {
return f . Fs . Mkdir ( f . cipher . EncryptDirName ( dir ) )
}
// Rmdir removes the directory (container, bucket) if empty
//
// Return an error if it doesn't exist or isn't empty
func ( f * Fs ) Rmdir ( dir string ) error {
return f . Fs . Rmdir ( f . cipher . EncryptDirName ( dir ) )
}
2016-07-25 18:18:56 +00:00
// Purge all files in the root and the root directory
//
// Implement this if you have a way of deleting all the files
// quicker than just running Remove() on the result of List()
//
// Return an error if it doesn't exist
func ( f * Fs ) Purge ( ) error {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . Purge
if do == nil {
2016-07-25 18:18:56 +00:00
return fs . ErrorCantPurge
}
2017-01-13 17:21:47 +00:00
return do ( )
2016-07-25 18:18:56 +00:00
}
// Copy src to this remote using server side copy operations.
//
// This is stored with the remote path given
//
// It returns the destination Object and a possible error
//
// Will only be called if src.Fs().Name() == f.Name()
//
// If it isn't possible then return fs.ErrorCantCopy
func ( f * Fs ) Copy ( src fs . Object , remote string ) ( fs . Object , error ) {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . Copy
if do == nil {
2016-07-25 18:18:56 +00:00
return nil , fs . ErrorCantCopy
}
o , ok := src . ( * Object )
if ! ok {
return nil , fs . ErrorCantCopy
}
2017-01-13 17:21:47 +00:00
oResult , err := do ( o . Object , f . cipher . EncryptFileName ( remote ) )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
return f . newObject ( oResult ) , nil
}
// Move src to this remote using server side move operations.
//
// This is stored with the remote path given
//
// It returns the destination Object and a possible error
//
// Will only be called if src.Fs().Name() == f.Name()
//
// If it isn't possible then return fs.ErrorCantMove
func ( f * Fs ) Move ( src fs . Object , remote string ) ( fs . Object , error ) {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . Move
if do == nil {
2016-07-25 18:18:56 +00:00
return nil , fs . ErrorCantMove
}
o , ok := src . ( * Object )
if ! ok {
2016-08-23 16:43:43 +00:00
return nil , fs . ErrorCantMove
2016-07-25 18:18:56 +00:00
}
2017-01-13 17:21:47 +00:00
oResult , err := do ( o . Object , f . cipher . EncryptFileName ( remote ) )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
return f . newObject ( oResult ) , nil
}
2017-02-05 21:20:56 +00:00
// DirMove moves src, srcRemote to this remote at dstRemote
// using server side move operations.
2016-08-23 16:43:43 +00:00
//
// Will only be called if src.Fs().Name() == f.Name()
//
// If it isn't possible then return fs.ErrorCantDirMove
//
// If destination exists then return fs.ErrorDirExists
2017-02-05 21:20:56 +00:00
func ( f * Fs ) DirMove ( src fs . Fs , srcRemote , dstRemote string ) error {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . DirMove
if do == nil {
2016-08-23 16:43:43 +00:00
return fs . ErrorCantDirMove
}
srcFs , ok := src . ( * Fs )
if ! ok {
2017-02-09 11:01:20 +00:00
fs . Debugf ( srcFs , "Can't move directory - not same remote type" )
2016-08-23 16:43:43 +00:00
return fs . ErrorCantDirMove
}
2017-02-05 21:20:56 +00:00
return do ( srcFs . Fs , f . cipher . EncryptDirName ( srcRemote ) , f . cipher . EncryptDirName ( dstRemote ) )
2017-01-13 17:21:47 +00:00
}
// PutUnchecked uploads the object
//
// This will create a duplicate if we upload a new file without
// checking to see if there is one already - use Put() for that.
2017-05-28 11:44:22 +00:00
func ( f * Fs ) PutUnchecked ( in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . PutUnchecked
if do == nil {
return nil , errors . New ( "can't PutUnchecked" )
}
wrappedIn , err := f . cipher . EncryptData ( in )
if err != nil {
return nil , err
}
o , err := do ( wrappedIn , f . newObjectInfo ( src ) )
if err != nil {
return nil , err
}
return f . newObject ( o ) , nil
}
// CleanUp the trash in the Fs
//
// Implement this if you have a way of emptying the trash or
// otherwise cleaning up old versions of files.
func ( f * Fs ) CleanUp ( ) error {
do := f . Fs . Features ( ) . CleanUp
if do == nil {
return errors . New ( "can't CleanUp" )
}
return do ( )
2016-08-23 16:43:43 +00:00
}
2018-04-16 21:19:25 +00:00
// About gets quota information from the Fs
func ( f * Fs ) About ( ) ( * fs . Usage , error ) {
do := f . Fs . Features ( ) . About
if do == nil {
return nil , errors . New ( "About not supported" )
}
return do ( )
}
2016-07-25 18:18:56 +00:00
// UnWrap returns the Fs that this Fs is wrapping
func ( f * Fs ) UnWrap ( ) fs . Fs {
return f . Fs
}
2018-01-08 08:20:01 +00:00
// EncryptFileName returns an encrypted file name
func ( f * Fs ) EncryptFileName ( fileName string ) string {
return f . cipher . EncryptFileName ( fileName )
}
2017-09-20 11:40:07 +00:00
// DecryptFileName returns a decrypted file name
func ( f * Fs ) DecryptFileName ( encryptedFileName string ) ( string , error ) {
return f . cipher . DecryptFileName ( encryptedFileName )
}
2017-02-12 16:30:18 +00:00
// ComputeHash takes the nonce from o, and encrypts the contents of
2019-02-07 17:41:17 +00:00
// src with it, and calculates the hash given by HashType on the fly
2017-02-12 16:30:18 +00:00
//
// Note that we break lots of encapsulation in this function.
2018-01-12 16:30:54 +00:00
func ( f * Fs ) ComputeHash ( o * Object , src fs . Object , hashType hash . Type ) ( hashStr string , err error ) {
2017-02-12 16:30:18 +00:00
// Read the nonce - opening the file is sufficient to read the nonce in
2018-02-19 17:37:07 +00:00
// use a limited read so we only read the header
in , err := o . Object . Open ( & fs . RangeOption { Start : 0 , End : int64 ( fileHeaderSize ) - 1 } )
2017-02-12 16:30:18 +00:00
if err != nil {
2018-02-19 17:37:07 +00:00
return "" , errors . Wrap ( err , "failed to open object to read nonce" )
2017-02-12 16:30:18 +00:00
}
2018-02-19 17:37:07 +00:00
d , err := f . cipher . ( * cipher ) . newDecrypter ( in )
if err != nil {
_ = in . Close ( )
return "" , errors . Wrap ( err , "failed to open object to read nonce" )
}
nonce := d . nonce
2017-02-12 16:30:18 +00:00
// fs.Debugf(o, "Read nonce % 2x", nonce)
// Check nonce isn't all zeros
isZero := true
for i := range nonce {
if nonce [ i ] != 0 {
isZero = false
}
}
if isZero {
fs . Errorf ( o , "empty nonce read" )
}
2018-02-19 17:37:07 +00:00
// Close d (and hence in) once we have read the nonce
err = d . Close ( )
2017-02-12 16:30:18 +00:00
if err != nil {
return "" , errors . Wrap ( err , "failed to close nonce read" )
}
// Open the src for input
in , err = src . Open ( )
if err != nil {
return "" , errors . Wrap ( err , "failed to open src" )
}
defer fs . CheckClose ( in , & err )
// Now encrypt the src with the nonce
out , err := f . cipher . ( * cipher ) . newEncrypter ( in , & nonce )
if err != nil {
return "" , errors . Wrap ( err , "failed to make encrypter" )
}
// pipe into hash
2018-02-19 17:37:07 +00:00
m , err := hash . NewMultiHasherTypes ( hash . NewHashSet ( hashType ) )
if err != nil {
return "" , errors . Wrap ( err , "failed to make hasher" )
}
2017-02-12 16:30:18 +00:00
_ , err = io . Copy ( m , out )
if err != nil {
return "" , errors . Wrap ( err , "failed to hash data" )
}
return m . Sums ( ) [ hashType ] , nil
}
2016-07-25 18:18:56 +00:00
// Object describes a wrapped for being read from the Fs
//
// This decrypts the remote name and decrypts the data
type Object struct {
fs . Object
f * Fs
}
func ( f * Fs ) newObject ( o fs . Object ) * Object {
return & Object {
Object : o ,
f : f ,
}
}
// Fs returns read only access to the Fs that this object is part of
func ( o * Object ) Fs ( ) fs . Info {
return o . f
}
// Return a string version
func ( o * Object ) String ( ) string {
if o == nil {
return "<nil>"
}
return o . Remote ( )
}
// Remote returns the remote path
func ( o * Object ) Remote ( ) string {
remote := o . Object . Remote ( )
2016-08-20 17:46:10 +00:00
decryptedName , err := o . f . cipher . DecryptFileName ( remote )
2016-07-25 18:18:56 +00:00
if err != nil {
2017-02-09 11:01:20 +00:00
fs . Debugf ( remote , "Undecryptable file name: %v" , err )
2016-07-25 18:18:56 +00:00
return remote
}
return decryptedName
}
// Size returns the size of the file
func ( o * Object ) Size ( ) int64 {
size , err := o . f . cipher . DecryptedSize ( o . Object . Size ( ) )
if err != nil {
2017-02-09 11:01:20 +00:00
fs . Debugf ( o , "Bad size for decrypt: %v" , err )
2016-07-25 18:18:56 +00:00
}
return size
}
// Hash returns the selected checksum of the file
// If no checksum is available it returns ""
2018-01-12 16:30:54 +00:00
func ( o * Object ) Hash ( ht hash . Type ) ( string , error ) {
2018-01-18 20:27:52 +00:00
return "" , hash . ErrUnsupported
2016-07-25 18:18:56 +00:00
}
2017-02-12 16:30:18 +00:00
// UnWrap returns the wrapped Object
func ( o * Object ) UnWrap ( ) fs . Object {
return o . Object
}
2016-07-25 18:18:56 +00:00
// Open opens the file for read. Call Close() on the returned io.ReadCloser
2016-10-20 16:47:33 +00:00
func ( o * Object ) Open ( options ... fs . OpenOption ) ( rc io . ReadCloser , err error ) {
2018-01-22 18:31:59 +00:00
var openOptions [ ] fs . OpenOption
var offset , limit int64 = 0 , - 1
2016-09-10 10:29:57 +00:00
for _ , option := range options {
switch x := option . ( type ) {
case * fs . SeekOption :
offset = x . Offset
2018-01-22 18:31:59 +00:00
case * fs . RangeOption :
offset , limit = x . Decode ( o . Size ( ) )
2016-09-10 10:29:57 +00:00
default :
2018-01-22 18:31:59 +00:00
// pass on Options to underlying open if appropriate
openOptions = append ( openOptions , option )
2016-09-10 10:29:57 +00:00
}
}
2018-01-22 18:31:59 +00:00
rc , err = o . f . cipher . DecryptDataSeek ( func ( underlyingOffset , underlyingLimit int64 ) ( io . ReadCloser , error ) {
if underlyingOffset == 0 && underlyingLimit < 0 {
2016-10-20 16:47:33 +00:00
// Open with no seek
2018-01-22 18:31:59 +00:00
return o . Object . Open ( openOptions ... )
}
// Open stream with a range of underlyingOffset, underlyingLimit
end := int64 ( - 1 )
if underlyingLimit >= 0 {
end = underlyingOffset + underlyingLimit - 1
if end >= o . Object . Size ( ) {
end = - 1
}
2016-10-20 16:47:33 +00:00
}
2018-01-22 18:31:59 +00:00
newOpenOptions := append ( openOptions , & fs . RangeOption { Start : underlyingOffset , End : end } )
return o . Object . Open ( newOpenOptions ... )
} , offset , limit )
2016-09-10 10:29:57 +00:00
if err != nil {
return nil , err
}
2018-01-22 18:31:59 +00:00
return rc , nil
2016-07-25 18:18:56 +00:00
}
// Update in to the object with the modTime given of the given size
2017-05-28 11:44:22 +00:00
func ( o * Object ) Update ( in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) error {
2018-05-12 11:01:14 +00:00
update := func ( in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
return o . Object , o . Object . Update ( in , src , options ... )
2016-07-25 18:18:56 +00:00
}
2018-05-12 11:01:14 +00:00
_ , err := o . f . put ( in , src , options , update )
return err
2016-07-25 18:18:56 +00:00
}
// newDir returns a dir with the Name decrypted
2017-06-30 12:37:29 +00:00
func ( f * Fs ) newDir ( dir fs . Directory ) fs . Directory {
2018-08-04 10:16:43 +00:00
newDir := fs . NewDirCopy ( dir )
2017-06-30 12:37:29 +00:00
remote := dir . Remote ( )
2016-08-20 17:46:10 +00:00
decryptedRemote , err := f . cipher . DecryptDirName ( remote )
2016-07-25 18:18:56 +00:00
if err != nil {
2017-02-09 11:01:20 +00:00
fs . Debugf ( remote , "Undecryptable dir name: %v" , err )
2016-07-25 18:18:56 +00:00
} else {
2018-08-04 10:16:43 +00:00
newDir . SetRemote ( decryptedRemote )
2016-07-25 18:18:56 +00:00
}
2018-08-04 10:16:43 +00:00
return newDir
2016-07-25 18:18:56 +00:00
}
// ObjectInfo describes a wrapped fs.ObjectInfo for being the source
//
// This encrypts the remote name and adjusts the size
type ObjectInfo struct {
fs . ObjectInfo
f * Fs
}
func ( f * Fs ) newObjectInfo ( src fs . ObjectInfo ) * ObjectInfo {
return & ObjectInfo {
ObjectInfo : src ,
f : f ,
}
}
// Fs returns read only access to the Fs that this object is part of
func ( o * ObjectInfo ) Fs ( ) fs . Info {
return o . f
}
// Remote returns the remote path
func ( o * ObjectInfo ) Remote ( ) string {
2016-08-20 17:46:10 +00:00
return o . f . cipher . EncryptFileName ( o . ObjectInfo . Remote ( ) )
2016-07-25 18:18:56 +00:00
}
// Size returns the size of the file
func ( o * ObjectInfo ) Size ( ) int64 {
2017-09-27 15:46:28 +00:00
size := o . ObjectInfo . Size ( )
if size < 0 {
return size
}
return o . f . cipher . EncryptedSize ( size )
2016-07-25 18:18:56 +00:00
}
2016-08-25 20:26:55 +00:00
// Hash returns the selected checksum of the file
// If no checksum is available it returns ""
2018-01-12 16:30:54 +00:00
func ( o * ObjectInfo ) Hash ( hash hash . Type ) ( string , error ) {
2016-08-25 20:26:55 +00:00
return "" , nil
}
2016-07-25 18:18:56 +00:00
// Check the interfaces are satisfied
var (
2018-01-31 14:22:49 +00:00
_ fs . Fs = ( * Fs ) ( nil )
_ fs . Purger = ( * Fs ) ( nil )
_ fs . Copier = ( * Fs ) ( nil )
_ fs . Mover = ( * Fs ) ( nil )
_ fs . DirMover = ( * Fs ) ( nil )
_ fs . PutUncheckeder = ( * Fs ) ( nil )
_ fs . PutStreamer = ( * Fs ) ( nil )
_ fs . CleanUpper = ( * Fs ) ( nil )
_ fs . UnWrapper = ( * Fs ) ( nil )
_ fs . ListRer = ( * Fs ) ( nil )
2018-04-16 21:19:25 +00:00
_ fs . Abouter = ( * Fs ) ( nil )
2018-01-31 14:22:49 +00:00
_ fs . ObjectInfo = ( * ObjectInfo ) ( nil )
_ fs . Object = ( * Object ) ( nil )
_ fs . ObjectUnWrapper = ( * Object ) ( nil )
2016-07-25 18:18:56 +00:00
)