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  }