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  }