github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }