github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/md_util.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "fmt" 9 "sort" 10 "time" 11 12 "github.com/keybase/client/go/kbfs/data" 13 "github.com/keybase/client/go/kbfs/idutil" 14 "github.com/keybase/client/go/kbfs/kbfscodec" 15 "github.com/keybase/client/go/kbfs/kbfscrypto" 16 "github.com/keybase/client/go/kbfs/kbfsmd" 17 "github.com/keybase/client/go/kbfs/libkey" 18 "github.com/keybase/client/go/kbfs/tlf" 19 "github.com/keybase/client/go/kbfs/tlfhandle" 20 kbname "github.com/keybase/client/go/kbun" 21 "github.com/keybase/client/go/libkb" 22 "github.com/keybase/client/go/logger" 23 "github.com/keybase/client/go/protocol/keybase1" 24 "github.com/pkg/errors" 25 "golang.org/x/net/context" 26 ) 27 28 type mdRange struct { 29 start kbfsmd.Revision 30 end kbfsmd.Revision 31 } 32 33 func makeRekeyReadErrorHelper( 34 err error, kmd libkey.KeyMetadata, resolvedHandle *tlfhandle.Handle, 35 uid keybase1.UID, username kbname.NormalizedUsername) error { 36 if resolvedHandle.Type() == tlf.Public { 37 panic("makeRekeyReadError called on public folder") 38 } 39 // If the user is not a legitimate reader of the folder, this is a 40 // normal read access error. 41 if !resolvedHandle.IsReader(uid) { 42 return tlfhandle.NewReadAccessError( 43 resolvedHandle, username, resolvedHandle.GetCanonicalPath()) 44 } 45 46 // Otherwise, this folder needs to be rekeyed for this device. 47 tlfName := resolvedHandle.GetCanonicalName() 48 hasKeys, hasKeyErr := kmd.HasKeyForUser(uid) 49 if (hasKeyErr == nil) && hasKeys { 50 return NeedSelfRekeyError{tlfName, err} 51 } 52 return NeedOtherRekeyError{tlfName, err} 53 } 54 55 func makeRekeyReadError( 56 ctx context.Context, err error, kbpki KBPKI, 57 syncGetter syncedTlfGetterSetter, kmd libkey.KeyMetadata, 58 uid keybase1.UID, username kbname.NormalizedUsername) error { 59 h := kmd.GetTlfHandle() 60 resolvedHandle, resolveErr := h.ResolveAgain(ctx, kbpki, nil, syncGetter) 61 if resolveErr != nil { 62 // Ignore error and pretend h is already fully 63 // resolved. 64 resolvedHandle = h 65 } 66 return makeRekeyReadErrorHelper(err, kmd, resolvedHandle, uid, username) 67 } 68 69 // Helper which returns nil if the md block is uninitialized or readable by 70 // the current user. Otherwise an appropriate read access error is returned. 71 func isReadableOrError( 72 ctx context.Context, kbpki KBPKI, syncGetter syncedTlfGetterSetter, 73 md ReadOnlyRootMetadata) error { 74 if !md.IsInitialized() || md.IsReadable() { 75 return nil 76 } 77 // this should only be the case if we're a new device not yet 78 // added to the set of reader/writer keys. 79 session, err := kbpki.GetCurrentSession(ctx) 80 if err != nil { 81 return err 82 } 83 err = errors.Errorf("%s is not readable by %s (uid:%s)", md.TlfID(), 84 session.Name, session.UID) 85 return makeRekeyReadError( 86 ctx, err, kbpki, syncGetter, md, session.UID, session.Name) 87 } 88 89 func getMDRange(ctx context.Context, config Config, id tlf.ID, bid kbfsmd.BranchID, 90 start kbfsmd.Revision, end kbfsmd.Revision, mStatus kbfsmd.MergeStatus, 91 lockBeforeGet *keybase1.LockID) (rmds []ImmutableRootMetadata, err error) { 92 // The range is invalid. Don't treat as an error though; it just 93 // indicates that we don't yet know about any revisions. 94 if start < kbfsmd.RevisionInitial || end < kbfsmd.RevisionInitial { 95 return nil, nil 96 } 97 98 mdcache := config.MDCache() 99 var toDownload []mdRange 100 101 // Fetch one at a time, and figure out what ranges to fetch as you 102 // go. 103 minSlot := int(end-start) + 1 104 maxSlot := -1 105 for i := start; i <= end; i++ { 106 irmd, err := mdcache.Get(id, i, bid) 107 if err != nil { 108 if len(toDownload) == 0 || 109 toDownload[len(toDownload)-1].end != i-1 { 110 toDownload = append(toDownload, mdRange{i, i}) 111 } 112 toDownload[len(toDownload)-1].end = i 113 irmd = ImmutableRootMetadata{} 114 } else { 115 slot := len(rmds) 116 if slot < minSlot { 117 minSlot = slot 118 } 119 if slot > maxSlot { 120 maxSlot = slot 121 } 122 } 123 rmds = append(rmds, irmd) 124 } 125 126 // Try to fetch the rest from the server. TODO: parallelize me. 127 for _, r := range toDownload { 128 var fetchedRmds []ImmutableRootMetadata 129 switch mStatus { 130 case kbfsmd.Merged: 131 fetchedRmds, err = config.MDOps().GetRange( 132 ctx, id, r.start, r.end, lockBeforeGet) 133 case kbfsmd.Unmerged: 134 fetchedRmds, err = config.MDOps().GetUnmergedRange( 135 ctx, id, bid, r.start, r.end) 136 default: 137 panic(fmt.Sprintf("Unknown merged type: %s", mStatus)) 138 } 139 if err != nil { 140 return nil, err 141 } 142 143 for _, rmd := range fetchedRmds { 144 slot := int(rmd.Revision() - start) 145 if slot < minSlot { 146 minSlot = slot 147 } 148 if slot > maxSlot { 149 maxSlot = slot 150 } 151 152 rmds[slot] = rmd 153 154 // We don't cache the MD here, since it's already done in 155 // `MDOpsStandard` for MDs that come from a remote server. 156 // MDs that come from the local journal don't get cached 157 // as part of a get, to avoid races as in KBFS-2224. 158 } 159 } 160 161 if minSlot > maxSlot { 162 return nil, nil 163 } 164 165 rmds = rmds[minSlot : maxSlot+1] 166 // check to make sure there are no holes 167 for i, rmd := range rmds { 168 if rmd == (ImmutableRootMetadata{}) { 169 return nil, fmt.Errorf("No %s MD found for revision %d", 170 mStatus, int(start)+minSlot+i) 171 } 172 } 173 174 return rmds, nil 175 } 176 177 // GetSingleMD returns an MD that is required to exist. 178 func GetSingleMD( 179 ctx context.Context, config Config, id tlf.ID, bid kbfsmd.BranchID, 180 rev kbfsmd.Revision, mStatus kbfsmd.MergeStatus, 181 lockBeforeGet *keybase1.LockID) (ImmutableRootMetadata, error) { 182 rmds, err := getMDRange( 183 ctx, config, id, bid, rev, rev, mStatus, lockBeforeGet) 184 if err != nil { 185 return ImmutableRootMetadata{}, err 186 } 187 188 if len(rmds) != 1 { 189 return ImmutableRootMetadata{}, 190 fmt.Errorf("Single expected revision %d not found", rev) 191 } 192 return rmds[0], nil 193 } 194 195 // MakeCopyWithDecryptedPrivateData makes a copy of the given IRMD, 196 // decrypting it with the given IRMD with keys. 197 func MakeCopyWithDecryptedPrivateData( 198 ctx context.Context, config Config, 199 irmdToDecrypt, irmdWithKeys ImmutableRootMetadata, uid keybase1.UID) ( 200 rmdDecrypted ImmutableRootMetadata, err error) { 201 pmd, err := decryptMDPrivateData( 202 ctx, config.Codec(), config.Crypto(), 203 config.BlockCache(), config.BlockOps(), 204 config.KeyManager(), config.KBPKI(), config, config.Mode(), uid, 205 irmdToDecrypt.GetSerializedPrivateMetadata(), 206 irmdToDecrypt, irmdWithKeys, config.MakeLogger("")) 207 if err != nil { 208 return ImmutableRootMetadata{}, err 209 } 210 211 rmdCopy, err := irmdToDecrypt.deepCopy(config.Codec()) 212 if err != nil { 213 return ImmutableRootMetadata{}, err 214 } 215 rmdCopy.data = pmd 216 return MakeImmutableRootMetadata(rmdCopy, 217 irmdToDecrypt.LastModifyingWriterVerifyingKey(), 218 irmdToDecrypt.MdID(), 219 irmdToDecrypt.LocalTimestamp(), 220 irmdToDecrypt.putToServer), nil 221 } 222 223 func getMergedMDUpdatesWithEnd(ctx context.Context, config Config, id tlf.ID, 224 startRev kbfsmd.Revision, endRev kbfsmd.Revision, 225 lockBeforeGet *keybase1.LockID) ( 226 mergedRmds []ImmutableRootMetadata, err error) { 227 // We don't yet know about any revisions yet, so there's no range 228 // to get. 229 if startRev < kbfsmd.RevisionInitial { 230 return nil, nil 231 } 232 233 start := startRev 234 for { 235 end := start + maxMDsAtATime - 1 // range is inclusive 236 if endRev != kbfsmd.RevisionUninitialized && end > endRev { 237 end = endRev 238 } 239 if end < start { 240 break 241 } 242 rmds, err := getMDRange(ctx, config, id, kbfsmd.NullBranchID, 243 start, end, kbfsmd.Merged, lockBeforeGet) 244 if err != nil { 245 return nil, err 246 } 247 248 if len(mergedRmds) > 0 && len(rmds) > 0 { 249 // Make sure the first new one is a valid successor of the 250 // last one. 251 lastRmd := mergedRmds[len(mergedRmds)-1] 252 err = lastRmd.CheckValidSuccessor( 253 lastRmd.mdID, rmds[0].ReadOnlyRootMetadata) 254 if err != nil { 255 return nil, err 256 } 257 } 258 259 mergedRmds = append(mergedRmds, rmds...) 260 261 // TODO: limit the number of MDs we're allowed to hold in 262 // memory at any one time? 263 if len(rmds) < maxMDsAtATime { 264 break 265 } 266 start = end + 1 267 } 268 269 var uid keybase1.UID 270 // Check the readability of each MD. Because rekeys can append a 271 // MD revision with the new key, older revisions might not be 272 // readable until the newer revision, containing the key for this 273 // device, is processed. 274 for i, rmd := range mergedRmds { 275 if err := isReadableOrError(ctx, config.KBPKI(), config, rmd.ReadOnly()); err != nil { 276 // The right secret key for the given rmd's 277 // key generation may only be present in the 278 // most recent rmd. 279 latestRmd := mergedRmds[len(mergedRmds)-1] 280 281 if uid == keybase1.UID("") { 282 session, err := config.KBPKI().GetCurrentSession(ctx) 283 if err != nil { 284 return nil, err 285 } 286 uid = session.UID 287 } 288 289 irmdCopy, err := MakeCopyWithDecryptedPrivateData( 290 ctx, config, rmd, latestRmd, uid) 291 if err != nil { 292 return nil, err 293 } 294 // Overwrite the cached copy with the new copy. Unlike in 295 // `getMDRange`, it's safe to put this into the cache 296 // blindly, since updates coming from our local journal 297 // would always be readable, and thus not subject to this 298 // rewrite. 299 if err := config.MDCache().Put(irmdCopy); err != nil { 300 return nil, err 301 } 302 mergedRmds[i] = irmdCopy 303 } 304 } 305 return mergedRmds, nil 306 } 307 308 // getMergedMDUpdates returns a slice of all the merged MDs for a TLF, 309 // starting from the given startRev. The returned MDs are the same 310 // instances that are stored in the MD cache, so they should be 311 // modified with care. 312 // 313 // TODO: Accept a parameter to express that we want copies of the MDs 314 // instead of the cached versions. 315 func getMergedMDUpdates(ctx context.Context, config Config, id tlf.ID, 316 startRev kbfsmd.Revision, lockBeforeGet *keybase1.LockID) ( 317 mergedRmds []ImmutableRootMetadata, err error) { 318 return getMergedMDUpdatesWithEnd( 319 ctx, config, id, startRev, kbfsmd.RevisionUninitialized, lockBeforeGet) 320 } 321 322 // getUnmergedMDUpdates returns a slice of the unmerged MDs for a TLF 323 // and unmerged branch, between the merge point for that branch and 324 // startRev (inclusive). The returned MDs are the same instances that 325 // are stored in the MD cache, so they should be modified with care. 326 // If bid is kbfsmd.NullBranchID, it returns an empty MD list. 327 // 328 // TODO: Accept a parameter to express that we want copies of the MDs 329 // instead of the cached versions. 330 func getUnmergedMDUpdates(ctx context.Context, config Config, id tlf.ID, 331 bid kbfsmd.BranchID, startRev kbfsmd.Revision) ( 332 currHead kbfsmd.Revision, unmergedRmds []ImmutableRootMetadata, 333 err error) { 334 if bid == kbfsmd.NullBranchID { 335 // We're not really unmerged, so there's nothing to do. 336 // TODO: require the caller to avoid making this call if the 337 // bid isn't set (and change the mdserver behavior in that 338 // case as well). 339 return startRev, nil, nil 340 } 341 342 // We don't yet know about any revisions yet, so there's no range 343 // to get. 344 if startRev < kbfsmd.RevisionInitial { 345 return kbfsmd.RevisionUninitialized, nil, nil 346 } 347 348 // walk backwards until we find one that is merged 349 currHead = startRev 350 for { 351 // first look up all unmerged MD revisions older than my current head 352 startRev := currHead - maxMDsAtATime + 1 // (kbfsmd.Revision is signed) 353 if startRev < kbfsmd.RevisionInitial { 354 startRev = kbfsmd.RevisionInitial 355 } 356 357 rmds, err := getMDRange(ctx, config, id, bid, startRev, currHead, 358 kbfsmd.Unmerged, nil) 359 if err != nil { 360 return kbfsmd.RevisionUninitialized, nil, err 361 } 362 363 if len(unmergedRmds) > 0 && len(rmds) > 0 { 364 // Make sure the first old one is a valid successor of the 365 // last new one. 366 lastRmd := rmds[len(rmds)-1] 367 err = lastRmd.CheckValidSuccessor( 368 lastRmd.mdID, unmergedRmds[0].ReadOnlyRootMetadata) 369 if err != nil { 370 return kbfsmd.RevisionUninitialized, nil, err 371 } 372 } 373 374 numNew := len(rmds) 375 // prepend to keep the ordering correct 376 unmergedRmds = append(rmds, unmergedRmds...) 377 378 // on the next iteration, start apply the previous root 379 if numNew > 0 { 380 currHead = rmds[0].Revision() - 1 381 } 382 if currHead < kbfsmd.RevisionInitial { 383 return kbfsmd.RevisionUninitialized, nil, 384 errors.New("ran out of MD updates to unstage") 385 } 386 // TODO: limit the number of MDs we're allowed to hold in 387 // memory at any one time? 388 if numNew < maxMDsAtATime { 389 break 390 } 391 } 392 return currHead, unmergedRmds, nil 393 } 394 395 // GetMDRevisionByTime returns the revision number of the earliest 396 // merged MD of `handle` with a server timestamp greater or equal to 397 // `serverTime`. 398 func GetMDRevisionByTime( 399 ctx context.Context, config Config, handle *tlfhandle.Handle, 400 serverTime time.Time) (kbfsmd.Revision, error) { 401 id := handle.TlfID() 402 if id == tlf.NullID { 403 return kbfsmd.RevisionUninitialized, errors.Errorf( 404 "No ID set in handle %s", handle.GetCanonicalPath()) 405 } 406 407 md, err := config.MDOps().GetForTLFByTime(ctx, id, serverTime) 408 if err != nil { 409 return kbfsmd.RevisionUninitialized, err 410 } 411 412 return md.Revision(), nil 413 } 414 415 // encryptMDPrivateData encrypts the private data of the given 416 // RootMetadata and makes other modifications to prepare it for 417 // signing (see signMD below). After this function is called, the 418 // MetadataID of the RootMetadata's BareRootMetadata can be computed. 419 func encryptMDPrivateData( 420 ctx context.Context, codec kbfscodec.Codec, crypto cryptoPure, 421 signer kbfscrypto.Signer, ekg encryptionKeyGetter, me keybase1.UID, 422 rmd *RootMetadata) error { 423 err := rmd.data.checkValid() 424 if err != nil { 425 return err 426 } 427 428 brmd := rmd.bareMd 429 privateData := rmd.data 430 431 if brmd.TypeForKeying() == tlf.PublicKeying || 432 !brmd.IsWriterMetadataCopiedSet() { 433 // Record the last writer to modify this writer metadata 434 brmd.SetLastModifyingWriter(me) 435 436 if brmd.TypeForKeying() == tlf.PublicKeying { 437 // Encode the private metadata 438 encodedPrivateMetadata, err := codec.Encode(privateData) 439 if err != nil { 440 return err 441 } 442 brmd.SetSerializedPrivateMetadata(encodedPrivateMetadata) 443 } else if !brmd.IsWriterMetadataCopiedSet() { 444 // Encrypt and encode the private metadata 445 k, err := ekg.GetTLFCryptKeyForEncryption(ctx, rmd) 446 if err != nil { 447 return err 448 } 449 encryptedPrivateMetadata, err := crypto.EncryptPrivateMetadata(privateData, k) 450 if err != nil { 451 return err 452 } 453 encodedEncryptedPrivateMetadata, err := codec.Encode(encryptedPrivateMetadata) 454 if err != nil { 455 return err 456 } 457 brmd.SetSerializedPrivateMetadata(encodedEncryptedPrivateMetadata) 458 } 459 460 // Sign the writer metadata internally. This has to be 461 // done here, instead of in signMD, since the 462 // MetadataID may depend on it. 463 err := brmd.SignWriterMetadataInternally(ctx, codec, signer) 464 if err != nil { 465 return err 466 } 467 } 468 469 // Record the last user to modify this metadata 470 brmd.SetLastModifyingUser(me) 471 472 return nil 473 } 474 475 func getFileBlockForMD( 476 ctx context.Context, bcache data.BlockCacheSimple, bops BlockOps, 477 ptr data.BlockPointer, tlfID tlf.ID, rmdWithKeys libkey.KeyMetadata) ( 478 *data.FileBlock, error) { 479 // We don't have a convenient way to fetch the block from here via 480 // folderBlockOps, so just go directly via the 481 // BlockCache/BlockOps. No locking around the blocks is needed 482 // since these change blocks are read-only. 483 block, err := bcache.Get(ptr) 484 if err != nil { 485 block = data.NewFileBlock() 486 // TODO: eventually we should plumb the correct branch name 487 // here, but that would impact a huge number of functions that 488 // fetch MD. For now, the worst thing that can happen is that 489 // MD blocks for historical MD revisions sneak their way into 490 // the sync cache. 491 branch := data.MasterBranch 492 if err := bops.Get( 493 ctx, rmdWithKeys, ptr, block, data.TransientEntry, 494 branch); err != nil { 495 return nil, err 496 } 497 } 498 499 fblock, ok := block.(*data.FileBlock) 500 if !ok { 501 return nil, NotFileBlockError{ptr, data.MasterBranch, data.Path{}} 502 } 503 return fblock, nil 504 } 505 506 func reembedBlockChanges(ctx context.Context, codec kbfscodec.Codec, 507 bcache data.BlockCacheSimple, bops BlockOps, mode InitMode, tlfID tlf.ID, 508 pmd *PrivateMetadata, rmdWithKeys libkey.KeyMetadata, 509 log logger.Logger) error { 510 info := pmd.Changes.Info 511 if info.BlockPointer == data.ZeroPtr { 512 return nil 513 } 514 515 if !mode.BlockManagementEnabled() { 516 // Leave the block changes unembedded -- they aren't needed in 517 // minimal mode since there's no node cache, and thus there 518 // are no Nodes that needs to be updated due to BlockChange 519 // pointers in those blocks. 520 log.CDebugf(ctx, "Skipping block change reembedding in mode: %s", 521 mode.Type()) 522 return nil 523 } 524 525 // Treat the unembedded block change like a file so we can reuse 526 // the file reading code. 527 file := data.Path{ 528 FolderBranch: data.FolderBranch{ 529 Tlf: tlfID, 530 Branch: data.MasterBranch, 531 }, 532 Path: []data.PathNode{{ 533 BlockPointer: info.BlockPointer, 534 Name: data.NewPathPartString( 535 fmt.Sprintf("<MD with block change pointer %s>", 536 info.BlockPointer), nil), 537 }}, 538 } 539 getter := func(ctx context.Context, kmd libkey.KeyMetadata, ptr data.BlockPointer, 540 p data.Path, rtype data.BlockReqType) (*data.FileBlock, bool, error) { 541 block, err := getFileBlockForMD(ctx, bcache, bops, ptr, tlfID, kmd) 542 if err != nil { 543 return nil, false, err 544 } 545 return block, false, nil 546 } 547 cacher := func(_ context.Context, ptr data.BlockPointer, block data.Block) error { 548 return nil 549 } 550 // Reading doesn't use crypto or the block splitter, so for now 551 // just pass in nil. Also, reading doesn't depend on the UID, so 552 // it's ok to be empty. 553 var id keybase1.UserOrTeamID 554 fd := data.NewFileData( 555 file, id, nil, rmdWithKeys, getter, cacher, log, 556 libkb.NewVDebugLog(log) /* one-off, short-lived, unconfigured vlog */) 557 558 buf, err := fd.GetBytes(ctx, 0, -1) 559 if err != nil { 560 return err 561 } 562 563 var unembeddedChanges BlockChanges 564 err = codec.Decode(buf, &unembeddedChanges) 565 if err != nil { 566 return err 567 } 568 569 // We rely on at most one of Info or Ops being non-empty in 570 // crChains.addOps. 571 if unembeddedChanges.Info.IsInitialized() { 572 return errors.New("Unembedded BlockChangesInfo unexpectedly has an initialized Info") 573 } 574 575 // The changes block pointers are implicit ref blocks. 576 unembeddedChanges.Ops[0].AddRefBlock(info.BlockPointer) 577 iptrs, err := fd.GetIndirectFileBlockInfos(ctx) 578 if err != nil { 579 return err 580 } 581 for _, iptr := range iptrs { 582 unembeddedChanges.Ops[0].AddRefBlock(iptr.BlockPointer) 583 } 584 585 pmd.Changes = unembeddedChanges 586 pmd.cachedChanges.Info = info 587 return nil 588 } 589 590 func reembedBlockChangesIntoCopyIfNeeded( 591 ctx context.Context, codec kbfscodec.Codec, 592 bcache data.BlockCacheSimple, bops BlockOps, mode InitMode, 593 rmd ImmutableRootMetadata, log logger.Logger) ( 594 ImmutableRootMetadata, error) { 595 if rmd.data.Changes.Ops != nil { 596 return rmd, nil 597 } 598 599 // This might be necessary if the MD was retrieved from the 600 // cache in between putting it to the server (with unembedded 601 // block changes), and re-loading the block changes back into 602 // the MD and re-inserting into the cache. 603 log.CDebugf(ctx, 604 "Reembedding block changes for revision %d", rmd.Revision()) 605 rmdCopy, err := rmd.deepCopy(codec) 606 if err != nil { 607 return ImmutableRootMetadata{}, err 608 } 609 err = reembedBlockChanges( 610 ctx, codec, bcache, bops, mode, rmd.TlfID(), 611 &rmdCopy.data, rmd, log) 612 if err != nil { 613 return ImmutableRootMetadata{}, err 614 } 615 return MakeImmutableRootMetadata( 616 rmdCopy, rmd.lastWriterVerifyingKey, rmd.mdID, 617 rmd.localTimestamp, rmd.putToServer), nil 618 } 619 620 func getMDObfuscationSecret( 621 ctx context.Context, keyGetter mdDecryptionKeyGetter, 622 kmd libkey.KeyMetadata) (data.NodeObfuscatorSecret, error) { 623 if kmd.TlfID().Type() == tlf.Public { 624 return nil, nil 625 } 626 key, err := keyGetter.GetFirstTLFCryptKey(ctx, kmd) 627 if err != nil { 628 return nil, err 629 } 630 secret, err := key.DeriveSecret(obfuscatorDerivationString) 631 if err != nil { 632 return nil, err 633 } 634 return data.NodeObfuscatorSecret(secret), nil 635 } 636 637 func makeMDObfuscatorFromSecret( 638 secret data.NodeObfuscatorSecret, mode InitMode) data.Obfuscator { 639 if !mode.DoLogObfuscation() { 640 return nil 641 } 642 643 if secret == nil { 644 return nil 645 } 646 return data.NewNodeObfuscator(secret) 647 } 648 649 // decryptMDPrivateData does not use uid if the handle is a public one. 650 func decryptMDPrivateData(ctx context.Context, codec kbfscodec.Codec, 651 crypto Crypto, bcache data.BlockCache, bops BlockOps, 652 keyGetter mdDecryptionKeyGetter, teamChecker kbfsmd.TeamMembershipChecker, 653 osg idutil.OfflineStatusGetter, mode InitMode, uid keybase1.UID, 654 serializedPrivateMetadata []byte, rmdToDecrypt, rmdWithKeys libkey.KeyMetadata, 655 log logger.Logger) (PrivateMetadata, error) { 656 handle := rmdToDecrypt.GetTlfHandle() 657 658 var pmd PrivateMetadata 659 keyedForDevice := true 660 if handle.TypeForKeying() == tlf.PublicKeying { 661 if err := codec.Decode(serializedPrivateMetadata, 662 &pmd); err != nil { 663 return PrivateMetadata{}, err 664 } 665 } else { 666 // decrypt the root data for non-public directories 667 var encryptedPrivateMetadata kbfscrypto.EncryptedPrivateMetadata 668 if err := codec.Decode(serializedPrivateMetadata, 669 &encryptedPrivateMetadata); err != nil { 670 return PrivateMetadata{}, err 671 } 672 673 k, err := keyGetter.GetTLFCryptKeyForMDDecryption(ctx, 674 rmdToDecrypt, rmdWithKeys) 675 676 if err != nil { 677 log.CDebugf(ctx, "Couldn't get crypt key for %s (%s): %+v", 678 handle.GetCanonicalPath(), rmdToDecrypt.TlfID(), err) 679 isReader, readerErr := isReaderFromHandle( 680 ctx, handle, teamChecker, osg, uid) 681 if readerErr != nil { 682 return PrivateMetadata{}, readerErr 683 } 684 _, isSelfRekeyError := err.(NeedSelfRekeyError) 685 _, isOtherRekeyError := err.(NeedOtherRekeyError) 686 if isReader && (isOtherRekeyError || isSelfRekeyError) { 687 // Rekey errors are expected if this client is a 688 // valid folder participant but doesn't have the 689 // shared crypt key. 690 keyedForDevice = false 691 } else { 692 return PrivateMetadata{}, err 693 } 694 } else { 695 pmd, err = crypto.DecryptPrivateMetadata( 696 encryptedPrivateMetadata, k) 697 if err != nil { 698 log.CDebugf( 699 ctx, "Failed to decrypt MD for id=%s, keygen=%d", 700 rmdToDecrypt.TlfID(), rmdToDecrypt.LatestKeyGeneration()) 701 return PrivateMetadata{}, err 702 } 703 } 704 } 705 706 // Re-embed the block changes if it's needed. 707 err := reembedBlockChanges( 708 ctx, codec, bcache, bops, mode, rmdWithKeys.TlfID(), 709 &pmd, rmdWithKeys, log) 710 if err != nil { 711 log.CDebugf( 712 ctx, "Failed to re-embed block changes for id=%s, keygen=%d, info pointer=%v", 713 rmdToDecrypt.TlfID(), rmdToDecrypt.LatestKeyGeneration(), 714 pmd.Changes.Info) 715 return PrivateMetadata{}, err 716 } 717 718 var obfuscator data.Obfuscator 719 if keyedForDevice { 720 secret, err := getMDObfuscationSecret(ctx, keyGetter, rmdWithKeys) 721 if err != nil { 722 return PrivateMetadata{}, err 723 } 724 obfuscator = makeMDObfuscatorFromSecret(secret, mode) 725 } 726 for _, op := range pmd.Changes.Ops { 727 // Add a temporary path with an obfuscator. When we 728 // deserialize the ops from the raw byte buffer of the MD 729 // object, they don't have proper `data.Path`s set in them yet 730 // -- that's an unexported field. The path is required for 731 // obfuscation, so here we're just making sure that they all 732 // have one. In places where a perfectly-accurate path is 733 // required (like in conflict resolution), the code there will 734 // need to add a proper path. Note that here the level of the 735 // obfuscator might be wrong, and so might result in 736 // inconsistent suffixes for obfuscated names that conflict. 737 if !op.getFinalPath().IsValid() { 738 op.setFinalPath(data.Path{Path: []data.PathNode{{ 739 BlockPointer: data.ZeroPtr, 740 Name: data.NewPathPartString("", obfuscator), 741 }}}) 742 } 743 } 744 745 return pmd, nil 746 } 747 748 func getOpsSafe(config Config, id tlf.ID) (*folderBranchOps, error) { 749 kbfsOps := config.KBFSOps() 750 kbfsOpsStandard, ok := kbfsOps.(*KBFSOpsStandard) 751 if !ok { 752 return nil, errors.New("Not KBFSOpsStandard") 753 } 754 755 return kbfsOpsStandard.getOpsNoAdd(context.TODO(), data.FolderBranch{ 756 Tlf: id, 757 Branch: data.MasterBranch, 758 }), nil 759 } 760 761 func getOps(config Config, id tlf.ID) *folderBranchOps { 762 ops, err := getOpsSafe(config, id) 763 if err != nil { 764 panic(err) 765 } 766 return ops 767 } 768 769 // ChangeType indicates what kind of change is being referenced. 770 type ChangeType int 771 772 const ( 773 // ChangeTypeWrite is a change to a file (could be a create or a 774 // write to an existing file). 775 ChangeTypeWrite ChangeType = iota 776 // ChangeTypeRename is a rename of an existing file or directory. 777 ChangeTypeRename 778 // ChangeTypeDelete is a delete of an existing file or directory. 779 ChangeTypeDelete 780 ) 781 782 func (ct ChangeType) String() string { 783 switch ct { 784 case ChangeTypeWrite: 785 return "write" 786 case ChangeTypeRename: 787 return "rename" 788 case ChangeTypeDelete: 789 return "delete" 790 default: 791 return "unknown" 792 } 793 } 794 795 // ChangeItem describes a single change to a file or directory between 796 // revisions. 797 type ChangeItem struct { 798 Type ChangeType 799 CurrPath data.Path // Full path to the node created/renamed/deleted 800 UnrefsForDelete []data.BlockPointer 801 IsNew bool 802 OldPtr data.BlockPointer 803 } 804 805 func (ci *ChangeItem) addUnrefs(chains *crChains, op op) error { 806 // Find the original block pointers for each unref. 807 unrefs := op.Unrefs() 808 ci.UnrefsForDelete = make([]data.BlockPointer, len(unrefs)) 809 for i, unref := range unrefs { 810 ptr, err := chains.originalFromMostRecentOrSame(unref) 811 if err != nil { 812 return err 813 } 814 ci.UnrefsForDelete[i] = ptr 815 } 816 return nil 817 } 818 819 func (ci ChangeItem) String() string { 820 return fmt.Sprintf( 821 "{type: %s, currPath: %s}", ci.Type, ci.CurrPath.CanonicalPathString()) 822 } 823 824 // GetChangesBetweenRevisions returns a list of all the changes 825 // between the two given revisions (after `oldRev`, up to and 826 // including `newRev`). Also returns the sum of all the newly ref'd 827 // block sizes (in bytes), as a crude estimate of how big this change 828 // set is. 829 func GetChangesBetweenRevisions( 830 ctx context.Context, config Config, id tlf.ID, 831 oldRev, newRev kbfsmd.Revision) ( 832 changes []*ChangeItem, refSize uint64, err error) { 833 if newRev <= oldRev { 834 return nil, 0, errors.Errorf( 835 "Can't get changes between %d and %d", oldRev, newRev) 836 } 837 838 rmds, err := getMDRange( 839 ctx, config, id, kbfsmd.NullBranchID, oldRev+1, newRev, 840 kbfsmd.Merged, nil) 841 if err != nil { 842 return nil, 0, err 843 } 844 845 fbo, err := getOpsSafe(config, id) 846 if err != nil { 847 return nil, 0, err 848 } 849 850 chains, err := newCRChainsForIRMDs( 851 ctx, config.Codec(), config, rmds, &fbo.blocks, true) 852 if err != nil { 853 return nil, 0, err 854 } 855 err = fbo.blocks.populateChainPaths( 856 ctx, config.MakeLogger(""), chains, true) 857 if err != nil { 858 return nil, 0, err 859 } 860 861 // The crChains creation process splits up a rename op into 862 // a delete and a create. Turn them back into a rename. 863 opsCount := 0 864 for _, rmd := range rmds { 865 opsCount += len(rmd.data.Changes.Ops) 866 } 867 ops := make([]op, opsCount) 868 soFar := 0 869 for _, rmd := range rmds { 870 for i, op := range rmd.data.Changes.Ops { 871 ops[soFar+i] = op.deepCopy() 872 } 873 soFar += len(rmd.data.Changes.Ops) 874 refSize += rmd.RefBytes() 875 } 876 err = chains.revertRenames(ops) 877 if err != nil { 878 return nil, 0, err 879 } 880 881 // Create the change items for each chain. Use the following 882 // simplifications: 883 // * Creates become writes, and use the full path to the created file/dir. 884 // * Deletes use the original blockpointer from the start of the chain 885 // for the deleted file's path. 886 items := make(map[string][]*ChangeItem) 887 numItems := 0 888 for _, chain := range chains.byMostRecent { 889 for _, op := range chain.ops { 890 newItem := true 891 item := &ChangeItem{ 892 CurrPath: op.getFinalPath(), 893 } 894 switch realOp := op.(type) { 895 case *createOp: 896 item.Type = ChangeTypeWrite 897 // Don't force there to be a pointer for the new node, 898 // since it could be a symlink. 899 item.CurrPath = item.CurrPath.ChildPathNoPtr( 900 realOp.obfuscatedNewName(), fbo.makeObfuscator()) 901 902 // If the write was processed first, re-use that item. 903 itemSlice, ok := items[item.CurrPath.CanonicalPathString()] 904 if ok { 905 for _, existingItem := range itemSlice { 906 if existingItem.Type == ChangeTypeWrite { 907 newItem = false 908 item = existingItem 909 } 910 } 911 } 912 item.IsNew = true 913 case *syncOp: 914 // If the create was processed first, reuse that item. 915 itemSlice, ok := items[item.CurrPath.CanonicalPathString()] 916 if ok { 917 for _, existingItem := range itemSlice { 918 if existingItem.Type == ChangeTypeWrite { 919 newItem = false 920 item = existingItem 921 item.CurrPath.Path[len(item.CurrPath.Path)-1]. 922 BlockPointer = chain.mostRecent 923 break 924 } 925 } 926 } 927 item.Type = ChangeTypeWrite 928 item.OldPtr = chain.original 929 case *renameOp: 930 item.Type = ChangeTypeRename 931 err := item.addUnrefs(chains, op) 932 if err != nil { 933 return nil, 0, err 934 } 935 // Don't force there to be a pointer for the node, 936 // since it could be a symlink. 937 item.CurrPath = item.CurrPath.ChildPathNoPtr( 938 realOp.obfuscatedNewName(), fbo.makeObfuscator()) 939 case *rmOp: 940 item.Type = ChangeTypeDelete 941 // Find the original block pointers for each unref. 942 err := item.addUnrefs(chains, op) 943 if err != nil { 944 return nil, 0, err 945 } 946 unrefs := op.Unrefs() 947 if len(unrefs) > 0 { 948 unref := unrefs[0] 949 ptr, err := chains.originalFromMostRecentOrSame(unref) 950 if err != nil { 951 return nil, 0, err 952 } 953 item.CurrPath = item.CurrPath.ChildPath( 954 realOp.obfuscatedOldName(), ptr, fbo.makeObfuscator()) 955 } 956 } 957 958 if newItem { 959 pString := item.CurrPath.CanonicalPathString() 960 items[pString] = append(items[pString], item) 961 numItems++ 962 963 // Add in an update for every directory whose blockpointer 964 // was updated. 965 currPath := item.CurrPath 966 for currPath.HasValidParent() { 967 currPath = *currPath.ParentPath() 968 pString := currPath.CanonicalPathString() 969 itemSlice, ok := items[pString] 970 needsUpdate := true 971 if ok { 972 for _, existingItem := range itemSlice { 973 if existingItem.Type == ChangeTypeWrite { 974 needsUpdate = false 975 break 976 } 977 } 978 } 979 if !needsUpdate { 980 break 981 } 982 oldPtr, err := chains.originalFromMostRecentOrSame( 983 currPath.TailPointer()) 984 if err != nil { 985 return nil, 0, err 986 } 987 item := &ChangeItem{ 988 Type: ChangeTypeWrite, 989 CurrPath: currPath, 990 OldPtr: oldPtr, 991 } 992 items[pString] = append(items[pString], item) 993 } 994 } 995 } 996 } 997 998 changes = make([]*ChangeItem, 0, numItems) 999 for _, itemSlice := range items { 1000 changes = append(changes, itemSlice...) 1001 } 1002 1003 // Renames should always go at the end, since if there's a pointer 1004 // change for the renamed thing (e.g., because it was a directory 1005 // that changed or a file that was written), we need to process 1006 // that pointer change before the rename. 1007 sort.SliceStable(changes, func(i, j int) bool { 1008 if changes[i].Type != ChangeTypeRename && 1009 changes[j].Type == ChangeTypeRename { 1010 return true 1011 } 1012 return false 1013 }) 1014 1015 return changes, refSize, nil 1016 }