github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfstool/md_common.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "github.com/keybase/client/go/kbfs/fsrpc" 10 "github.com/keybase/client/go/kbfs/idutil" 11 "github.com/keybase/client/go/kbfs/kbfsmd" 12 "github.com/keybase/client/go/kbfs/libkbfs" 13 "github.com/keybase/client/go/kbfs/tlf" 14 "github.com/keybase/client/go/kbfs/tlfhandle" 15 "github.com/keybase/client/go/protocol/keybase1" 16 "github.com/pkg/errors" 17 "golang.org/x/net/context" 18 ) 19 20 var mdInputRegexp = regexp.MustCompile( 21 `^(.+?)(?::(.*?))?(?:\^(.*?)(?:-(.*?))?)?$`) 22 23 func mdSplitInput(input string) ( 24 tlfStr, branchStr, startStr, stopStr string, err error) { 25 matches := mdInputRegexp.FindStringSubmatch(input) 26 if matches == nil { 27 return "", "", "", "", fmt.Errorf("Could not parse %q", input) 28 } 29 30 return matches[1], matches[2], matches[3], matches[4], nil 31 } 32 33 func mdJoinInput(tlfStr, branchStr, startStr, stopStr string) string { 34 s := tlfStr 35 if branchStr != "" { 36 s += ":" + branchStr 37 } 38 if startStr != "" || stopStr != "" { 39 s += "^" + startStr 40 if stopStr != "" { 41 s += "-" + stopStr 42 } 43 } 44 return s 45 } 46 47 func mdParseInput(ctx context.Context, config libkbfs.Config, 48 tlfStr, branchStr, startStr, stopStr string) ( 49 tlfID tlf.ID, branchID kbfsmd.BranchID, start, stop kbfsmd.Revision, err error) { 50 tlfID, err = getTlfID(ctx, config, tlfStr) 51 if err != nil { 52 return tlf.ID{}, kbfsmd.BranchID{}, kbfsmd.RevisionUninitialized, 53 kbfsmd.RevisionUninitialized, err 54 } 55 56 branchID, err = getBranchID(ctx, config, tlfID, branchStr) 57 if err != nil { 58 return tlf.ID{}, kbfsmd.BranchID{}, kbfsmd.RevisionUninitialized, 59 kbfsmd.RevisionUninitialized, err 60 } 61 62 start, err = getRevision(ctx, config, tlfID, branchID, startStr) 63 if err != nil { 64 return tlf.ID{}, kbfsmd.BranchID{}, kbfsmd.RevisionUninitialized, 65 kbfsmd.RevisionUninitialized, err 66 } 67 68 // TODO: Chunk the range between start and stop. 69 70 stop = start 71 if stopStr != "" { 72 stop, err = getRevision(ctx, config, tlfID, branchID, stopStr) 73 if err != nil { 74 return tlf.ID{}, kbfsmd.BranchID{}, kbfsmd.RevisionUninitialized, 75 kbfsmd.RevisionUninitialized, err 76 } 77 } 78 79 return tlfID, branchID, start, stop, nil 80 } 81 82 func parseTLFPath(ctx context.Context, kbpki libkbfs.KBPKI, 83 mdOps libkbfs.MDOps, osg idutil.OfflineStatusGetter, tlfStr string) ( 84 *tlfhandle.Handle, error) { 85 p, err := fsrpc.NewPath(tlfStr) 86 if err != nil { 87 return nil, err 88 } 89 if p.PathType != fsrpc.TLFPathType { 90 return nil, fmt.Errorf("%q is not a TLF path", tlfStr) 91 } 92 if len(p.TLFComponents) > 0 { 93 return nil, fmt.Errorf( 94 "%q is not the root path of a TLF", tlfStr) 95 } 96 return fsrpc.ParseTlfHandle(ctx, kbpki, mdOps, osg, p.TLFName, p.TLFType) 97 } 98 99 func getTlfID( 100 ctx context.Context, config libkbfs.Config, tlfStr string) ( 101 tlf.ID, error) { 102 _, err := kbfsmd.ParseID(tlfStr) 103 if err == nil { 104 return tlf.ID{}, errors.New("Cannot handle metadata IDs") 105 } 106 107 tlfID, err := tlf.ParseID(tlfStr) 108 if err == nil { 109 return tlfID, nil 110 } else if _, ok := errors.Cause(err).(tlf.InvalidIDError); !ok { 111 return tlf.ID{}, err 112 } 113 114 handle, err := parseTLFPath( 115 ctx, config.KBPKI(), config.MDOps(), config, tlfStr) 116 if err != nil { 117 return tlf.ID{}, err 118 } 119 120 return handle.TlfID(), nil 121 } 122 123 func getBranchID(ctx context.Context, config libkbfs.Config, 124 tlfID tlf.ID, branchStr string) (kbfsmd.BranchID, error) { 125 if branchStr == "master" { 126 return kbfsmd.NullBranchID, nil 127 } 128 129 if len(branchStr) == 0 || branchStr == "device" { 130 irmd, err := config.MDOps().GetUnmergedForTLF( 131 ctx, tlfID, kbfsmd.NullBranchID) 132 if err != nil { 133 return kbfsmd.NullBranchID, err 134 } 135 if irmd == (libkbfs.ImmutableRootMetadata{}) { 136 return kbfsmd.NullBranchID, nil 137 } 138 return irmd.BID(), nil 139 } 140 141 return kbfsmd.ParseBranchID(branchStr) 142 } 143 144 func getRevision(ctx context.Context, config libkbfs.Config, 145 tlfID tlf.ID, branchID kbfsmd.BranchID, 146 revisionStr string) (kbfsmd.Revision, error) { 147 if len(revisionStr) == 0 || revisionStr == "latest" { 148 if branchID == kbfsmd.NullBranchID { 149 irmd, err := config.MDOps().GetForTLF(ctx, tlfID, nil) 150 if err != nil { 151 return kbfsmd.RevisionUninitialized, err 152 } 153 return irmd.Revision(), nil 154 } 155 156 irmd, err := config.MDOps().GetUnmergedForTLF( 157 ctx, tlfID, branchID) 158 if err != nil { 159 return kbfsmd.RevisionUninitialized, err 160 } 161 return irmd.Revision(), nil 162 } 163 164 base := 10 165 if strings.HasPrefix(revisionStr, "0x") { 166 base = 16 167 revisionStr = strings.TrimPrefix(revisionStr, "0x") 168 } 169 u, err := strconv.ParseUint(revisionStr, base, 64) 170 if err != nil { 171 return kbfsmd.RevisionUninitialized, err 172 } 173 return kbfsmd.Revision(u), nil 174 } 175 176 func reverseIRMDList(irmds []libkbfs.ImmutableRootMetadata) []libkbfs.ImmutableRootMetadata { 177 irmdsReversed := make([]libkbfs.ImmutableRootMetadata, len(irmds)) 178 for i := range irmds { 179 irmdsReversed[i] = irmds[len(irmds)-1-i] 180 } 181 return irmdsReversed 182 } 183 184 func mdGet(ctx context.Context, config libkbfs.Config, tlfID tlf.ID, 185 branchID kbfsmd.BranchID, start, stop kbfsmd.Revision) ( 186 irmds []libkbfs.ImmutableRootMetadata, err error) { 187 if start > stop { 188 panic("start unexpectedly greater than stop") 189 } 190 191 if branchID == kbfsmd.NullBranchID { 192 irmds, err = config.MDOps().GetRange(ctx, tlfID, start, stop, nil) 193 } else { 194 irmds, err = config.MDOps().GetUnmergedRange( 195 ctx, tlfID, branchID, start, stop) 196 } 197 if err != nil { 198 return nil, err 199 } 200 201 var latestIRMD libkbfs.ImmutableRootMetadata 202 var uid keybase1.UID 203 for i, irmd := range irmds { 204 if !irmd.IsReadable() { 205 if latestIRMD == (libkbfs.ImmutableRootMetadata{}) { 206 if branchID == kbfsmd.NullBranchID { 207 latestIRMD, err = config.MDOps().GetForTLF(ctx, tlfID, nil) 208 } else { 209 latestIRMD, err = config.MDOps().GetUnmergedForTLF(ctx, tlfID, branchID) 210 } 211 if err != nil { 212 return nil, err 213 } 214 } 215 216 if uid == keybase1.UID("") { 217 session, err := config.KBPKI().GetCurrentSession(ctx) 218 if err != nil { 219 return nil, err 220 } 221 uid = session.UID 222 } 223 224 irmdCopy, err := libkbfs.MakeCopyWithDecryptedPrivateData( 225 ctx, config, irmd, latestIRMD, uid) 226 if err != nil { 227 return nil, err 228 } 229 irmds[i] = irmdCopy 230 } 231 } 232 233 return irmds, nil 234 } 235 236 func mdGetMergedHeadForWriter(ctx context.Context, config libkbfs.Config, 237 tlfPath string) (libkbfs.ImmutableRootMetadata, error) { 238 handle, err := parseTLFPath( 239 ctx, config.KBPKI(), config.MDOps(), config, tlfPath) 240 if err != nil { 241 return libkbfs.ImmutableRootMetadata{}, err 242 } 243 244 session, err := config.KBPKI().GetCurrentSession(ctx) 245 if err != nil { 246 return libkbfs.ImmutableRootMetadata{}, err 247 } 248 249 // Make sure we're a writer before doing anything else. 250 isWriter, err := libkbfs.IsWriterFromHandle( 251 ctx, handle, config.KBPKI(), config, session.UID, session.VerifyingKey) 252 if err != nil { 253 return libkbfs.ImmutableRootMetadata{}, err 254 } 255 if !isWriter { 256 return libkbfs.ImmutableRootMetadata{}, 257 tlfhandle.NewWriteAccessError( 258 handle, session.Name, handle.GetCanonicalPath()) 259 } 260 261 fmt.Printf("Looking for unmerged branch...\n") 262 263 tlfID := handle.TlfID() 264 if tlfID == tlf.NullID { 265 return libkbfs.ImmutableRootMetadata{}, errors.New("No TLF ID") 266 } 267 268 unmergedIRMD, err := config.MDOps().GetUnmergedForTLF( 269 ctx, tlfID, kbfsmd.NullBranchID) 270 if err != nil { 271 return libkbfs.ImmutableRootMetadata{}, err 272 } 273 274 if unmergedIRMD != (libkbfs.ImmutableRootMetadata{}) { 275 return libkbfs.ImmutableRootMetadata{}, fmt.Errorf( 276 "%s has unmerged data; try unstaging it first", 277 tlfPath) 278 } 279 280 fmt.Printf("Getting latest metadata...\n") 281 282 irmd, err := config.MDOps().GetForTLF(ctx, tlfID, nil) 283 if err != nil { 284 return libkbfs.ImmutableRootMetadata{}, err 285 } 286 287 if irmd == (libkbfs.ImmutableRootMetadata{}) { 288 fmt.Printf("No TLF found for %q\n", tlfPath) 289 return libkbfs.ImmutableRootMetadata{}, nil 290 } 291 292 return irmd, nil 293 }