github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/mdserver_tlf_storage.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"path/filepath"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/kbfs/ioutil"
    13  	"github.com/keybase/client/go/kbfs/kbfscodec"
    14  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    15  	"github.com/keybase/client/go/kbfs/kbfsmd"
    16  	"github.com/keybase/client/go/kbfs/tlf"
    17  	"github.com/keybase/client/go/protocol/keybase1"
    18  	"github.com/pkg/errors"
    19  	"golang.org/x/net/context"
    20  )
    21  
    22  // mdServerTlfStorage stores an ordered list of metadata IDs for each
    23  // branch of a single TLF, along with the associated metadata objects,
    24  // in flat files on disk.
    25  //
    26  // The directory layout looks like:
    27  //
    28  // dir/md_branch_journals/00..00/EARLIEST
    29  // dir/md_branch_journals/00..00/LATEST
    30  // dir/md_branch_journals/00..00/0...001
    31  // dir/md_branch_journals/00..00/0...002
    32  // dir/md_branch_journals/00..00/0...fff
    33  // dir/md_branch_journals/5f..3d/EARLIEST
    34  // dir/md_branch_journals/5f..3d/LATEST
    35  // dir/md_branch_journals/5f..3d/0...0ff
    36  // dir/md_branch_journals/5f..3d/0...100
    37  // dir/md_branch_journals/5f..3d/0...fff
    38  // dir/mds/0100/0...01
    39  // ...
    40  // dir/mds/01ff/f...ff
    41  // dir/wkbv3/0100...01
    42  // ...
    43  // dir/wkbv3/0100...ff
    44  // dir/rkbv3/0100...01
    45  // ...
    46  // dir/rkbv3/0100...ff
    47  //
    48  // Each branch has its own subdirectory with a journal; the journal
    49  // ordinals are just Revisions, and the journal entries are
    50  // just MdIDs. (Branches are usually temporary, so no need to splay
    51  // them.)
    52  //
    53  // The Metadata objects are stored separately in dir/mds. Each block
    54  // has its own subdirectory with its ID as a name. The MD
    55  // subdirectories are splayed over (# of possible hash types) * 256
    56  // subdirectories -- one byte for the hash type (currently only one)
    57  // plus the first byte of the hash data -- using the first four
    58  // characters of the name to keep the number of directories in dir
    59  // itself to a manageable number, similar to git.
    60  //
    61  // Writer (reader) key bundles for V3 metadata objects are stored
    62  // separately in dir/wkbv3 (dir/rkbv3). The number of bundles is
    63  // small, so no need to splay them.
    64  type mdServerTlfStorage struct {
    65  	tlfID          tlf.ID
    66  	codec          kbfscodec.Codec
    67  	clock          Clock
    68  	teamMemChecker kbfsmd.TeamMembershipChecker
    69  	mdVer          kbfsmd.MetadataVer
    70  	dir            string
    71  
    72  	// Protects any IO operations in dir or any of its children,
    73  	// as well as branchJournals and its contents.
    74  	lock           sync.RWMutex
    75  	branchJournals map[kbfsmd.BranchID]mdIDJournal
    76  }
    77  
    78  func makeMDServerTlfStorage(tlfID tlf.ID, codec kbfscodec.Codec,
    79  	clock Clock, teamMemChecker kbfsmd.TeamMembershipChecker,
    80  	mdVer kbfsmd.MetadataVer, dir string) *mdServerTlfStorage {
    81  	journal := &mdServerTlfStorage{
    82  		tlfID:          tlfID,
    83  		codec:          codec,
    84  		clock:          clock,
    85  		teamMemChecker: teamMemChecker,
    86  		mdVer:          mdVer,
    87  		dir:            dir,
    88  		branchJournals: make(map[kbfsmd.BranchID]mdIDJournal),
    89  	}
    90  	return journal
    91  }
    92  
    93  // The functions below are for building various paths.
    94  
    95  func (s *mdServerTlfStorage) branchJournalsPath() string {
    96  	return filepath.Join(s.dir, "md_branch_journals")
    97  }
    98  
    99  func (s *mdServerTlfStorage) mdsPath() string {
   100  	return filepath.Join(s.dir, "mds")
   101  }
   102  
   103  func (s *mdServerTlfStorage) writerKeyBundleV3Path(
   104  	id kbfsmd.TLFWriterKeyBundleID) string {
   105  	return filepath.Join(s.dir, "wkbv3", id.String())
   106  }
   107  
   108  func (s *mdServerTlfStorage) readerKeyBundleV3Path(
   109  	id kbfsmd.TLFReaderKeyBundleID) string {
   110  	return filepath.Join(s.dir, "rkbv3", id.String())
   111  }
   112  
   113  func (s *mdServerTlfStorage) mdPath(id kbfsmd.ID) string {
   114  	idStr := id.String()
   115  	return filepath.Join(s.mdsPath(), idStr[:4], idStr[4:])
   116  }
   117  
   118  // serializedRMDS is the structure stored in mdPath(id).
   119  type serializedRMDS struct {
   120  	EncodedRMDS []byte
   121  	Timestamp   time.Time
   122  	Version     kbfsmd.MetadataVer
   123  }
   124  
   125  // getMDReadLocked verifies the MD data (but not the signature) for
   126  // the given ID and returns it.
   127  //
   128  // TODO: Verify signature?
   129  func (s *mdServerTlfStorage) getMDReadLocked(id kbfsmd.ID) (
   130  	*RootMetadataSigned, error) {
   131  	// Read file.
   132  
   133  	var srmds serializedRMDS
   134  	err := kbfscodec.DeserializeFromFile(s.codec, s.mdPath(id), &srmds)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	rmds, err := DecodeRootMetadataSigned(
   140  		s.codec, s.tlfID, srmds.Version, s.mdVer, srmds.EncodedRMDS,
   141  		srmds.Timestamp)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	// Check integrity.
   147  
   148  	mdID, err := kbfsmd.MakeID(s.codec, rmds.MD)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	if id != mdID {
   154  		return nil, errors.Errorf(
   155  			"Metadata ID mismatch: expected %s, got %s",
   156  			id, mdID)
   157  	}
   158  
   159  	return rmds, nil
   160  }
   161  
   162  func (s *mdServerTlfStorage) putMDLocked(
   163  	rmds *RootMetadataSigned) (kbfsmd.ID, error) {
   164  	id, err := kbfsmd.MakeID(s.codec, rmds.MD)
   165  	if err != nil {
   166  		return kbfsmd.ID{}, err
   167  	}
   168  
   169  	_, err = s.getMDReadLocked(id)
   170  	switch {
   171  	case ioutil.IsNotExist(err):
   172  		// Continue on.
   173  	case err != nil:
   174  		return kbfsmd.ID{}, err
   175  	default:
   176  		// Entry exists, so nothing else to do.
   177  		return id, nil
   178  	}
   179  
   180  	encodedRMDS, err := kbfsmd.EncodeRootMetadataSigned(s.codec, &rmds.RootMetadataSigned)
   181  	if err != nil {
   182  		return kbfsmd.ID{}, err
   183  	}
   184  
   185  	srmds := serializedRMDS{
   186  		EncodedRMDS: encodedRMDS,
   187  		// Pretend the timestamp went over RPC, so we get the same
   188  		// resolution level as a real server.
   189  		Timestamp: keybase1.FromTime(keybase1.ToTime(s.clock.Now())),
   190  		Version:   rmds.MD.Version(),
   191  	}
   192  
   193  	err = kbfscodec.SerializeToFileIfNotExist(s.codec, srmds, s.mdPath(id))
   194  	if err != nil {
   195  		return kbfsmd.ID{}, err
   196  	}
   197  
   198  	return id, nil
   199  }
   200  
   201  func (s *mdServerTlfStorage) getOrCreateBranchJournalLocked(
   202  	bid kbfsmd.BranchID) (mdIDJournal, error) {
   203  	j, ok := s.branchJournals[bid]
   204  	if ok {
   205  		return j, nil
   206  	}
   207  
   208  	dir := filepath.Join(s.branchJournalsPath(), bid.String())
   209  	err := ioutil.MkdirAll(dir, 0700)
   210  	if err != nil {
   211  		return mdIDJournal{}, err
   212  	}
   213  
   214  	j, err = makeMdIDJournal(s.codec, dir)
   215  	if err != nil {
   216  		return mdIDJournal{}, err
   217  	}
   218  	s.branchJournals[bid] = j
   219  	return j, nil
   220  }
   221  
   222  func (s *mdServerTlfStorage) getHeadForTLFReadLocked(bid kbfsmd.BranchID) (
   223  	rmds *RootMetadataSigned, err error) {
   224  	j, err := s.getOrCreateBranchJournalLocked(bid)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	entry, exists, err := j.getLatestEntry()
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	if !exists {
   233  		return nil, nil
   234  	}
   235  	return s.getMDReadLocked(entry.ID)
   236  }
   237  
   238  func (s *mdServerTlfStorage) checkGetParamsReadLocked(
   239  	ctx context.Context, currentUID keybase1.UID, bid kbfsmd.BranchID) error {
   240  	mergedMasterHead, err := s.getHeadForTLFReadLocked(kbfsmd.NullBranchID)
   241  	if err != nil {
   242  		return kbfsmd.ServerError{Err: err}
   243  	}
   244  
   245  	if mergedMasterHead != nil {
   246  		extra, err := getExtraMetadata(
   247  			s.getKeyBundlesReadLocked, mergedMasterHead.MD)
   248  		if err != nil {
   249  			return kbfsmd.ServerError{Err: err}
   250  		}
   251  		ok, err := isReader(
   252  			ctx, s.teamMemChecker, currentUID, mergedMasterHead.MD, extra)
   253  		if err != nil {
   254  			return kbfsmd.ServerError{Err: err}
   255  		}
   256  		if !ok {
   257  			return kbfsmd.ServerErrorUnauthorized{}
   258  		}
   259  	}
   260  
   261  	return nil
   262  }
   263  
   264  func (s *mdServerTlfStorage) getRangeReadLocked(
   265  	ctx context.Context, currentUID keybase1.UID, bid kbfsmd.BranchID,
   266  	start, stop kbfsmd.Revision) (
   267  	[]*RootMetadataSigned, error) {
   268  	err := s.checkGetParamsReadLocked(ctx, currentUID, bid)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	j, ok := s.branchJournals[bid]
   274  	if !ok {
   275  		return nil, nil
   276  	}
   277  
   278  	realStart, entries, err := j.getEntryRange(start, stop)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	var rmdses []*RootMetadataSigned
   283  	for i, entry := range entries {
   284  		expectedRevision := realStart + kbfsmd.Revision(i)
   285  		rmds, err := s.getMDReadLocked(entry.ID)
   286  		if err != nil {
   287  			return nil, kbfsmd.ServerError{Err: err}
   288  		}
   289  		if expectedRevision != rmds.MD.RevisionNumber() {
   290  			panic(errors.Errorf("expected revision %v, got %v",
   291  				expectedRevision, rmds.MD.RevisionNumber()))
   292  		}
   293  		rmdses = append(rmdses, rmds)
   294  	}
   295  
   296  	return rmdses, nil
   297  }
   298  
   299  func (s *mdServerTlfStorage) putExtraMetadataLocked(rmds *RootMetadataSigned,
   300  	extra kbfsmd.ExtraMetadata) error {
   301  	if extra == nil {
   302  		return nil
   303  	}
   304  
   305  	extraV3, ok := extra.(*kbfsmd.ExtraMetadataV3)
   306  	if !ok {
   307  		return errors.New("Invalid extra metadata")
   308  	}
   309  
   310  	if extraV3.IsWriterKeyBundleNew() {
   311  		wkbID := rmds.MD.GetTLFWriterKeyBundleID()
   312  		if wkbID == (kbfsmd.TLFWriterKeyBundleID{}) {
   313  			panic("writer key bundle ID is empty")
   314  		}
   315  		err := kbfscodec.SerializeToFileIfNotExist(
   316  			s.codec, extraV3.GetWriterKeyBundle(), s.writerKeyBundleV3Path(wkbID))
   317  		if err != nil {
   318  			return err
   319  		}
   320  	}
   321  
   322  	if extraV3.IsReaderKeyBundleNew() {
   323  		rkbID := rmds.MD.GetTLFReaderKeyBundleID()
   324  		if rkbID == (kbfsmd.TLFReaderKeyBundleID{}) {
   325  			panic("reader key bundle ID is empty")
   326  		}
   327  		err := kbfscodec.SerializeToFileIfNotExist(
   328  			s.codec, extraV3.GetReaderKeyBundle(), s.readerKeyBundleV3Path(rkbID))
   329  		if err != nil {
   330  			return err
   331  		}
   332  	}
   333  	return nil
   334  }
   335  
   336  type errMDServerTlfStorageShutdown struct{}
   337  
   338  func (e errMDServerTlfStorageShutdown) Error() string {
   339  	return "mdServerTlfStorage is shutdown"
   340  }
   341  
   342  func (s *mdServerTlfStorage) checkShutdownReadLocked() error {
   343  	if s.branchJournals == nil {
   344  		return errors.WithStack(errMDServerTlfStorageShutdown{})
   345  	}
   346  	return nil
   347  }
   348  
   349  // All functions below are public functions.
   350  
   351  func (s *mdServerTlfStorage) journalLength(bid kbfsmd.BranchID) (uint64, error) {
   352  	s.lock.RLock()
   353  	defer s.lock.RUnlock()
   354  	err := s.checkShutdownReadLocked()
   355  	if err != nil {
   356  		return 0, err
   357  	}
   358  
   359  	j, ok := s.branchJournals[bid]
   360  	if !ok {
   361  		return 0, nil
   362  	}
   363  
   364  	return j.length(), nil
   365  }
   366  
   367  func (s *mdServerTlfStorage) getForTLF(
   368  	ctx context.Context, currentUID keybase1.UID, bid kbfsmd.BranchID) (
   369  	*RootMetadataSigned, error) {
   370  	s.lock.RLock()
   371  	defer s.lock.RUnlock()
   372  	err := s.checkShutdownReadLocked()
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  
   377  	err = s.checkGetParamsReadLocked(ctx, currentUID, bid)
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  
   382  	rmds, err := s.getHeadForTLFReadLocked(bid)
   383  	if err != nil {
   384  		return nil, kbfsmd.ServerError{Err: err}
   385  	}
   386  	return rmds, nil
   387  }
   388  
   389  func (s *mdServerTlfStorage) getRange(
   390  	ctx context.Context, currentUID keybase1.UID, bid kbfsmd.BranchID,
   391  	start, stop kbfsmd.Revision) (
   392  	[]*RootMetadataSigned, error) {
   393  	s.lock.RLock()
   394  	defer s.lock.RUnlock()
   395  	err := s.checkShutdownReadLocked()
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  
   400  	return s.getRangeReadLocked(ctx, currentUID, bid, start, stop)
   401  }
   402  
   403  func (s *mdServerTlfStorage) put(ctx context.Context,
   404  	currentUID keybase1.UID, currentVerifyingKey kbfscrypto.VerifyingKey,
   405  	rmds *RootMetadataSigned, extra kbfsmd.ExtraMetadata) (
   406  	recordBranchID bool, err error) {
   407  	s.lock.Lock()
   408  	defer s.lock.Unlock()
   409  	err = s.checkShutdownReadLocked()
   410  	if err != nil {
   411  		return false, err
   412  	}
   413  
   414  	err = rmds.IsValidAndSigned(
   415  		ctx, s.codec, s.teamMemChecker, extra,
   416  		keybase1.OfflineAvailability_NONE)
   417  	if err != nil {
   418  		return false, kbfsmd.ServerErrorBadRequest{Reason: err.Error()}
   419  	}
   420  
   421  	err = rmds.IsLastModifiedBy(currentUID, currentVerifyingKey)
   422  	if err != nil {
   423  		return false, kbfsmd.ServerErrorBadRequest{Reason: err.Error()}
   424  	}
   425  
   426  	// Check permissions
   427  
   428  	mergedMasterHead, err := s.getHeadForTLFReadLocked(kbfsmd.NullBranchID)
   429  	if err != nil {
   430  		return false, kbfsmd.ServerError{Err: err}
   431  	}
   432  
   433  	// TODO: Figure out nil case.
   434  	if mergedMasterHead != nil {
   435  		prevExtra, err := getExtraMetadata(
   436  			s.getKeyBundlesReadLocked, mergedMasterHead.MD)
   437  		if err != nil {
   438  			return false, kbfsmd.ServerError{Err: err}
   439  		}
   440  		ok, err := isWriterOrValidRekey(
   441  			ctx, s.teamMemChecker, s.codec, currentUID, currentVerifyingKey,
   442  			mergedMasterHead.MD, rmds.MD,
   443  			prevExtra, extra)
   444  		if err != nil {
   445  			return false, kbfsmd.ServerError{Err: err}
   446  		}
   447  		if !ok {
   448  			return false, kbfsmd.ServerErrorUnauthorized{}
   449  		}
   450  	}
   451  
   452  	bid := rmds.MD.BID()
   453  	mStatus := rmds.MD.MergedStatus()
   454  
   455  	head, err := s.getHeadForTLFReadLocked(bid)
   456  	if err != nil {
   457  		return false, kbfsmd.ServerError{Err: err}
   458  	}
   459  
   460  	if mStatus == kbfsmd.Unmerged && head == nil {
   461  		// currHead for unmerged history might be on the main branch
   462  		prevRev := rmds.MD.RevisionNumber() - 1
   463  		rmdses, err := s.getRangeReadLocked(
   464  			ctx, currentUID, kbfsmd.NullBranchID, prevRev, prevRev)
   465  		if err != nil {
   466  			return false, kbfsmd.ServerError{Err: err}
   467  		}
   468  		if len(rmdses) != 1 {
   469  			return false, kbfsmd.ServerError{
   470  				Err: errors.Errorf("Expected 1 MD block got %d", len(rmdses)),
   471  			}
   472  		}
   473  		head = rmdses[0]
   474  		recordBranchID = true
   475  	}
   476  
   477  	// Consistency checks
   478  	if head != nil {
   479  		headID, err := kbfsmd.MakeID(s.codec, head.MD)
   480  		if err != nil {
   481  			return false, kbfsmd.ServerError{Err: err}
   482  		}
   483  
   484  		err = head.MD.CheckValidSuccessorForServer(headID, rmds.MD)
   485  		if err != nil {
   486  			return false, err
   487  		}
   488  	}
   489  
   490  	id, err := s.putMDLocked(rmds)
   491  	if err != nil {
   492  		return false, kbfsmd.ServerError{Err: err}
   493  	}
   494  
   495  	err = s.putExtraMetadataLocked(rmds, extra)
   496  	if err != nil {
   497  		return false, kbfsmd.ServerError{Err: err}
   498  	}
   499  
   500  	j, err := s.getOrCreateBranchJournalLocked(bid)
   501  	if err != nil {
   502  		return false, err
   503  	}
   504  
   505  	err = j.append(rmds.MD.RevisionNumber(), mdIDJournalEntry{ID: id})
   506  	if err != nil {
   507  		return false, kbfsmd.ServerError{Err: err}
   508  	}
   509  
   510  	return recordBranchID, nil
   511  }
   512  
   513  func (s *mdServerTlfStorage) getKeyBundlesReadLocked(tlfID tlf.ID,
   514  	wkbID kbfsmd.TLFWriterKeyBundleID, rkbID kbfsmd.TLFReaderKeyBundleID) (
   515  	*kbfsmd.TLFWriterKeyBundleV3, *kbfsmd.TLFReaderKeyBundleV3, error) {
   516  	err := s.checkShutdownReadLocked()
   517  	if err != nil {
   518  		return nil, nil, err
   519  	}
   520  
   521  	var wkb *kbfsmd.TLFWriterKeyBundleV3
   522  	if wkbID != (kbfsmd.TLFWriterKeyBundleID{}) {
   523  		foundWKB, err := kbfsmd.DeserializeTLFWriterKeyBundleV3(
   524  			s.codec, s.writerKeyBundleV3Path(wkbID))
   525  		if err != nil {
   526  			return nil, nil, err
   527  		}
   528  
   529  		err = kbfsmd.CheckWKBID(s.codec, wkbID, foundWKB)
   530  		if err != nil {
   531  			return nil, nil, err
   532  		}
   533  		wkb = &foundWKB
   534  	}
   535  
   536  	var rkb *kbfsmd.TLFReaderKeyBundleV3
   537  	if rkbID != (kbfsmd.TLFReaderKeyBundleID{}) {
   538  		foundRKB, err := kbfsmd.DeserializeTLFReaderKeyBundleV3(
   539  			s.codec, s.readerKeyBundleV3Path(rkbID))
   540  		if err != nil {
   541  			return nil, nil, err
   542  		}
   543  
   544  		err = kbfsmd.CheckRKBID(s.codec, rkbID, foundRKB)
   545  		if err != nil {
   546  			return nil, nil, err
   547  		}
   548  		rkb = &foundRKB
   549  	}
   550  
   551  	return wkb, rkb, nil
   552  }
   553  
   554  func (s *mdServerTlfStorage) getKeyBundles(tlfID tlf.ID,
   555  	wkbID kbfsmd.TLFWriterKeyBundleID, rkbID kbfsmd.TLFReaderKeyBundleID) (
   556  	*kbfsmd.TLFWriterKeyBundleV3, *kbfsmd.TLFReaderKeyBundleV3, error) {
   557  	s.lock.RLock()
   558  	defer s.lock.RUnlock()
   559  	return s.getKeyBundlesReadLocked(tlfID, wkbID, rkbID)
   560  
   561  }
   562  
   563  func (s *mdServerTlfStorage) shutdown() {
   564  	s.lock.Lock()
   565  	defer s.lock.Unlock()
   566  	s.branchJournals = nil
   567  }