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