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 )