github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfstool/md_dump.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 7 "github.com/keybase/client/go/kbfs/kbfsmd" 8 "github.com/keybase/client/go/kbfs/libkbfs" 9 "github.com/keybase/client/go/kbfs/tlf" 10 "golang.org/x/net/context" 11 ) 12 13 func mdDumpImmutableRMD(ctx context.Context, config libkbfs.Config, 14 replacements replacementMap, 15 irmd libkbfs.ImmutableRootMetadata) error { 16 err := mdDumpFillReplacements( 17 ctx, config.Codec(), config.KeybaseService(), config, "md dump", 18 irmd.GetBareRootMetadata(), irmd.Extra(), replacements) 19 if err != nil { 20 printError("md dump", err) 21 } 22 23 fmt.Print("Immutable metadata\n") 24 fmt.Print("------------------\n") 25 26 fmt.Printf("MD ID: %s\n", irmd.MdID()) 27 fmt.Printf("Local timestamp: %s\n", irmd.LocalTimestamp()) 28 fmt.Printf("Last modifying device (verifying key): %s\n", 29 mdDumpReplaceAll(irmd.LastModifyingWriterVerifyingKey().String(), replacements)) 30 fmt.Print("\n") 31 32 return mdDumpReadOnlyRMDWithReplacements( 33 ctx, config.Codec(), replacements, irmd.ReadOnly()) 34 } 35 36 func mdDumpChunk(ctx context.Context, config libkbfs.Config, 37 replacements replacementMap, tlfStr, branchStr string, 38 tlfID tlf.ID, branchID kbfsmd.BranchID, 39 start, stop kbfsmd.Revision) error { 40 min := start 41 max := stop 42 reversed := false 43 if start > stop { 44 min = stop 45 max = start 46 reversed = true 47 } 48 49 irmds, err := mdGet(ctx, config, tlfID, branchID, min, max) 50 if err != nil { 51 return err 52 } 53 54 if reversed { 55 irmds = reverseIRMDList(irmds) 56 } 57 58 fmt.Printf("%d results for %q:\n\n", len(irmds), 59 mdJoinInput(tlfStr, branchStr, start.String(), stop.String())) 60 61 for _, irmd := range irmds { 62 err = mdDumpImmutableRMD(ctx, config, replacements, irmd) 63 if err != nil { 64 return err 65 } 66 fmt.Print("\n") 67 } 68 69 return nil 70 } 71 72 func mdDumpInput(ctx context.Context, config libkbfs.Config, 73 replacements replacementMap, input string) error { 74 tlfStr, branchStr, startStr, stopStr, err := mdSplitInput(input) 75 if err != nil { 76 return err 77 } 78 79 tlfID, branchID, start, stop, err := 80 mdParseInput(ctx, config, tlfStr, branchStr, startStr, stopStr) 81 if err != nil { 82 return err 83 } 84 85 // TODO: Ideally we'd fetch MDs concurrently with dumping 86 // them, but this works well enough for now. 87 // 88 // TODO: Make maxChunkSize configurable. 89 90 const maxChunkSize = 100 91 92 if start <= stop { 93 for chunkStart := start; chunkStart <= stop; chunkStart += maxChunkSize { 94 chunkStop := chunkStart + maxChunkSize - 1 95 if chunkStop > stop { 96 chunkStop = stop 97 } 98 err = mdDumpChunk(ctx, config, replacements, tlfStr, branchStr, tlfID, branchID, chunkStart, chunkStop) 99 if err != nil { 100 return err 101 } 102 } 103 } else { 104 for chunkStart := start; chunkStart >= stop; chunkStart -= maxChunkSize { 105 chunkStop := chunkStart - maxChunkSize + 1 106 if chunkStop < stop { 107 chunkStop = stop 108 } 109 110 err = mdDumpChunk(ctx, config, replacements, tlfStr, branchStr, tlfID, branchID, chunkStart, chunkStop) 111 if err != nil { 112 return err 113 } 114 } 115 } 116 117 return nil 118 } 119 120 const mdDumpUsageStr = `Usage: 121 kbfstool md dump input [inputs...] 122 123 Each input must be in the following format: 124 125 TLF 126 TLF:Branch 127 TLF^RevisionRange 128 TLF:Branch^RevisionRange 129 130 where TLF can be: 131 132 - a TLF ID string (32 hex digits), or 133 - a keybase TLF path (e.g., "/keybase/public/user1,user2", or 134 "/keybase/private/user1,assertion2"); 135 136 Branch can be: 137 138 - a Branch ID string (32 hex digits), 139 - the string "device", which indicates the unmerged branch for the 140 current device, or the master branch if there is no unmerged branch, 141 - the string "master", which is a shorthand for 142 the ID of the master branch "00000000000000000000000000000000", or 143 - omitted, in which case it is treated as if it were the string "device"; 144 145 and RevisionRange can be in the following format: 146 147 Revision 148 Revision-Revision 149 150 where Revision can be: 151 152 - a hex number prefixed with "0x", 153 - a decimal number with no prefix, 154 - the string "latest", which indicates the latest revision for the 155 branch, or 156 - omitted, in which case it is treated as if it were the string "latest". 157 158 If a single revision "rev" is specified, it's treated as if the range 159 "rev-rev" was specified. If a range "rev1-rev2" is specified, then the 160 revisions are dumped in ascending order if rev1 <= rev2, and descending 161 order if rev1 > rev2. 162 ` 163 164 func mdDump(ctx context.Context, config libkbfs.Config, args []string) (exitStatus int) { 165 flags := flag.NewFlagSet("kbfs md dump", flag.ContinueOnError) 166 err := flags.Parse(args) 167 if err != nil { 168 printError("md dump", err) 169 return 1 170 } 171 172 inputs := flags.Args() 173 if len(inputs) < 1 { 174 fmt.Print(mdDumpUsageStr) 175 return 1 176 } 177 178 replacements := make(replacementMap) 179 180 for _, input := range inputs { 181 err := mdDumpInput(ctx, config, replacements, input) 182 if err != nil { 183 printError("md dump", err) 184 return 1 185 } 186 } 187 188 return 0 189 }