github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfstool/md_reset.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "flag" 6 "fmt" 7 "os" 8 "strings" 9 10 "github.com/keybase/client/go/kbfs/data" 11 "github.com/keybase/client/go/kbfs/libkbfs" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "golang.org/x/net/context" 14 ) 15 16 func mdResetOne( 17 ctx context.Context, config libkbfs.Config, tlfPath string, 18 replacements replacementMap, checkValid, dryRun, force bool) error { 19 irmd, err := mdGetMergedHeadForWriter(ctx, config, tlfPath) 20 if err != nil { 21 return err 22 } 23 24 // This function is loosely adapted from 25 // folderBranchOps.initMDLocked. 26 27 if checkValid { 28 rootPtr := irmd.Data().Dir.BlockPointer 29 if rootPtr.Ref().IsValid() { 30 var dirBlock data.DirBlock 31 err = config.BlockOps().Get( 32 ctx, irmd, rootPtr, &dirBlock, data.NoCacheEntry, 33 data.MasterBranch) 34 if err == nil { 35 fmt.Printf("Got no error when getting root block %s; not doing anything\n", rootPtr) 36 return nil 37 } 38 fmt.Printf("Got error %s when getting root block %s, so revision %d is broken. Making successor...\n", 39 err, rootPtr, irmd.Revision()) 40 } else { 41 // This happens in the wild, but only for folders used 42 // for journal-related testing early on. 43 fmt.Printf("Root block pointer is invalid, so revision %d is broken. Making successor...\n", 44 irmd.Revision()) 45 } 46 } 47 48 rmdNext, err := irmd.MakeSuccessor(ctx, config.MetadataVersion(), 49 config.Codec(), config.KeyManager(), 50 config.KBPKI(), config.KBPKI(), config, irmd.MdID(), true) 51 if err != nil { 52 return err 53 } 54 55 // TODO: Add an option to scan for and use the last known good 56 // root block. 57 _, info, readyBlockData, err := 58 libkbfs.ResetRootBlock(ctx, config, rmdNext) 59 if err != nil { 60 return err 61 } 62 63 fmt.Printf( 64 "Will put an empty root block for tlfID=%s with blockInfo=%s and bufLen=%d\n", 65 rmdNext.TlfID(), info, readyBlockData.GetEncodedSize()) 66 fmt.Print("Will put MD:\n") 67 err = mdDumpReadOnlyRMD(ctx, config, "md reset", replacements, rmdNext.ReadOnly()) 68 if err != nil { 69 return err 70 } 71 72 if dryRun { 73 fmt.Print("Dry-run set; not doing anything\n") 74 return nil 75 } 76 77 if !force { 78 fmt.Print("Are you sure you want to continue? [y/N]: ") 79 response, err := bufio.NewReader(os.Stdin).ReadString('\n') 80 if err != nil { 81 return err 82 } 83 response = strings.ToLower(strings.TrimSpace(response)) 84 if response != "y" { 85 fmt.Printf("Didn't confirm; not doing anything\n") 86 return nil 87 } 88 } 89 90 fmt.Printf("Putting block %s...\n", info) 91 92 err = libkbfs.PutBlockCheckLimitErrs( 93 ctx, config.BlockServer(), config.Reporter(), 94 rmdNext.TlfID(), info.BlockPointer, readyBlockData, 95 irmd.GetTlfHandle().GetCanonicalName(), libkbfs.DiskBlockAnyCache) 96 if err != nil { 97 return err 98 } 99 100 // Assume there's no need to unembed the block changes. 101 102 fmt.Printf("Putting revision %d...\n", rmdNext.Revision()) 103 104 session, err := config.KBPKI().GetCurrentSession(ctx) 105 if err != nil { 106 return err 107 } 108 109 newIrmd, err := config.MDOps().Put( 110 ctx, rmdNext, session.VerifyingKey, nil, keybase1.MDPriorityNormal, nil) 111 if err != nil { 112 return err 113 } 114 115 fmt.Printf("New MD has revision %d\n", newIrmd.Revision()) 116 117 return nil 118 } 119 120 const mdResetUsageStr = `Usage: 121 kbfstool md reset /keybase/[public|private]/user1,assertion2 122 123 ` 124 125 func mdReset(ctx context.Context, config libkbfs.Config, args []string) (exitStatus int) { 126 flags := flag.NewFlagSet("kbfs md reset", flag.ContinueOnError) 127 checkValid := flags.Bool("c", true, "If set, don't do anything if the existing root block is valid") 128 dryRun := flags.Bool("d", false, "Dry run: don't actually do anything.") 129 force := flags.Bool("f", false, "If set, skip confirmation prompt.") 130 err := flags.Parse(args) 131 if err != nil { 132 printError("md reset", err) 133 return 1 134 } 135 136 inputs := flags.Args() 137 if len(inputs) != 1 { 138 fmt.Print(mdResetUsageStr) 139 return 1 140 } 141 142 replacements := make(replacementMap) 143 144 err = mdResetOne(ctx, config, inputs[0], replacements, *checkValid, *dryRun, *force) 145 if err != nil { 146 printError("md reset", err) 147 return 1 148 } 149 150 fmt.Print("\n") 151 152 return 0 153 }