github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/bserver_disk.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  	"errors"
     9  	"fmt"
    10  	"math"
    11  	"os"
    12  	"path/filepath"
    13  	"sync"
    14  
    15  	"github.com/keybase/client/go/kbfs/ioutil"
    16  	"github.com/keybase/client/go/kbfs/kbfsblock"
    17  	"github.com/keybase/client/go/kbfs/kbfscodec"
    18  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    19  	"github.com/keybase/client/go/kbfs/tlf"
    20  	"github.com/keybase/client/go/logger"
    21  	"github.com/keybase/client/go/protocol/keybase1"
    22  	"golang.org/x/net/context"
    23  )
    24  
    25  type blockServerDiskTlfStorage struct {
    26  	lock sync.RWMutex
    27  	// store is nil after it is shut down in Shutdown().
    28  	store *blockDiskStore
    29  }
    30  
    31  // BlockServerDisk implements the BlockServer interface by just
    32  // storing blocks in a local disk store.
    33  type BlockServerDisk struct {
    34  	codec        kbfscodec.Codec
    35  	log          logger.Logger
    36  	dirPath      string
    37  	shutdownFunc func(logger.Logger)
    38  
    39  	tlfStorageLock sync.RWMutex
    40  	// tlfStorage is nil after Shutdown() is called.
    41  	tlfStorage map[tlf.ID]*blockServerDiskTlfStorage
    42  }
    43  
    44  var _ blockServerLocal = (*BlockServerDisk)(nil)
    45  
    46  // newBlockServerDisk constructs a new BlockServerDisk that stores
    47  // its data in the given directory.
    48  func newBlockServerDisk(
    49  	codec kbfscodec.Codec, log logger.Logger,
    50  	dirPath string, shutdownFunc func(logger.Logger)) *BlockServerDisk {
    51  	bserv := &BlockServerDisk{
    52  		codec, log, dirPath, shutdownFunc, sync.RWMutex{},
    53  		make(map[tlf.ID]*blockServerDiskTlfStorage),
    54  	}
    55  	return bserv
    56  }
    57  
    58  // NewBlockServerDir constructs a new BlockServerDisk that stores
    59  // its data in the given directory.
    60  func NewBlockServerDir(codec kbfscodec.Codec,
    61  	log logger.Logger, dirPath string) *BlockServerDisk {
    62  	return newBlockServerDisk(codec, log, dirPath, nil)
    63  }
    64  
    65  // NewBlockServerTempDir constructs a new BlockServerDisk that stores its
    66  // data in a temp directory which is cleaned up on shutdown.
    67  func NewBlockServerTempDir(codec kbfscodec.Codec,
    68  	log logger.Logger) (*BlockServerDisk, error) {
    69  	tempdir, err := ioutil.TempDir(os.TempDir(), "kbfs_bserver_tmp")
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return newBlockServerDisk(codec, log, tempdir, func(log logger.Logger) {
    74  		err := ioutil.RemoveAll(tempdir)
    75  		if err != nil {
    76  			log.Warning("error removing %s: %s", tempdir, err)
    77  		}
    78  	}), nil
    79  }
    80  
    81  var errBlockServerDiskShutdown = errors.New("BlockServerDisk is shutdown")
    82  
    83  func (b *BlockServerDisk) getStorage(tlfID tlf.ID) (
    84  	*blockServerDiskTlfStorage, error) {
    85  	storage, err := func() (*blockServerDiskTlfStorage, error) {
    86  		b.tlfStorageLock.RLock()
    87  		defer b.tlfStorageLock.RUnlock()
    88  		if b.tlfStorage == nil {
    89  			return nil, errBlockServerDiskShutdown
    90  		}
    91  		return b.tlfStorage[tlfID], nil
    92  	}()
    93  
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	if storage != nil {
    99  		return storage, nil
   100  	}
   101  
   102  	b.tlfStorageLock.Lock()
   103  	defer b.tlfStorageLock.Unlock()
   104  	if b.tlfStorage == nil {
   105  		return nil, errBlockServerDiskShutdown
   106  	}
   107  
   108  	storage, ok := b.tlfStorage[tlfID]
   109  	if ok {
   110  		return storage, nil
   111  	}
   112  
   113  	path := filepath.Join(b.dirPath, tlfID.String())
   114  	store := makeBlockDiskStore(b.codec, path)
   115  
   116  	storage = &blockServerDiskTlfStorage{
   117  		store: store,
   118  	}
   119  
   120  	b.tlfStorage[tlfID] = storage
   121  	return storage, nil
   122  }
   123  
   124  // FastForwardBackoff implements the BlockServer interface.
   125  func (b *BlockServerDisk) FastForwardBackoff() {}
   126  
   127  // Get implements the BlockServer interface for BlockServerDisk.
   128  func (b *BlockServerDisk) Get(
   129  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   130  	context kbfsblock.Context, _ DiskBlockCacheType) (
   131  	data []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf, err error) {
   132  	if err := checkContext(ctx); err != nil {
   133  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err
   134  	}
   135  
   136  	defer func() {
   137  		err = translateToBlockServerError(err)
   138  	}()
   139  	b.log.CDebugf(ctx, "BlockServerDisk.Get id=%s tlfID=%s context=%s",
   140  		id, tlfID, context)
   141  	tlfStorage, err := b.getStorage(tlfID)
   142  	if err != nil {
   143  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err
   144  	}
   145  
   146  	tlfStorage.lock.RLock()
   147  	defer tlfStorage.lock.RUnlock()
   148  	if tlfStorage.store == nil {
   149  		return nil, kbfscrypto.BlockCryptKeyServerHalf{},
   150  			errBlockServerDiskShutdown
   151  	}
   152  
   153  	data, keyServerHalf, err := tlfStorage.store.getDataWithContext(
   154  		ctx, id, context)
   155  	if err != nil {
   156  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, err
   157  	}
   158  	return data, keyServerHalf, nil
   159  }
   160  
   161  // GetEncodedSizes implements the BlockServer interface for
   162  // BlockServerDisk.
   163  func (b *BlockServerDisk) GetEncodedSizes(
   164  	ctx context.Context, tlfID tlf.ID, ids []kbfsblock.ID,
   165  	contexts []kbfsblock.Context) (
   166  	sizes []uint32, statuses []keybase1.BlockStatus, err error) {
   167  	if err := checkContext(ctx); err != nil {
   168  		return nil, nil, err
   169  	}
   170  
   171  	defer func() {
   172  		err = translateToBlockServerError(err)
   173  	}()
   174  	b.log.CDebugf(ctx,
   175  		"BlockServerDisk.GetEncodedSizes id=%s tlfID=%s context=%s",
   176  		ids, tlfID, contexts)
   177  	tlfStorage, err := b.getStorage(tlfID)
   178  	if err != nil {
   179  		return nil, nil, err
   180  	}
   181  
   182  	tlfStorage.lock.RLock()
   183  	defer tlfStorage.lock.RUnlock()
   184  	if tlfStorage.store == nil {
   185  		return nil, nil, errBlockServerDiskShutdown
   186  	}
   187  
   188  	sizes = make([]uint32, len(ids))
   189  	statuses = make([]keybase1.BlockStatus, len(ids))
   190  	for i, id := range ids {
   191  		context := contexts[i]
   192  		hasContext, refStatus, err := tlfStorage.store.hasContext(
   193  			ctx, id, context)
   194  		if err != nil {
   195  			return nil, nil, err
   196  		}
   197  		if !hasContext {
   198  			sizes[i] = 0
   199  			statuses[i] = keybase1.BlockStatus_UNKNOWN
   200  			continue
   201  		}
   202  
   203  		size64, err := tlfStorage.store.getDataSize(ctx, id)
   204  		if err != nil {
   205  			return nil, nil, err
   206  		}
   207  		sizes[i] = uint32(size64)
   208  		statuses[i] = refStatus.toBlockStatus()
   209  	}
   210  	return sizes, statuses, nil
   211  }
   212  
   213  // Put implements the BlockServer interface for BlockServerDisk.
   214  func (b *BlockServerDisk) Put(
   215  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   216  	context kbfsblock.Context, buf []byte,
   217  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   218  	_ DiskBlockCacheType) (err error) {
   219  	if err := checkContext(ctx); err != nil {
   220  		return err
   221  	}
   222  
   223  	defer func() {
   224  		err = translateToBlockServerError(err)
   225  	}()
   226  	b.log.CDebugf(ctx, "BlockServerDisk.Put id=%s tlfID=%s context=%s size=%d",
   227  		id, tlfID, context, len(buf))
   228  
   229  	if context.GetRefNonce() != kbfsblock.ZeroRefNonce {
   230  		return errors.New("can't Put() a block with a non-zero refnonce")
   231  	}
   232  
   233  	tlfStorage, err := b.getStorage(tlfID)
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	tlfStorage.lock.Lock()
   239  	defer tlfStorage.lock.Unlock()
   240  	if tlfStorage.store == nil {
   241  		return errBlockServerDiskShutdown
   242  	}
   243  
   244  	_, err = tlfStorage.store.put(ctx, true, id, context, buf, serverHalf)
   245  	if err != nil {
   246  		return err
   247  	}
   248  	err = tlfStorage.store.addReference(ctx, id, context, "tag")
   249  	if err != nil {
   250  		return err
   251  	}
   252  	return nil
   253  }
   254  
   255  // PutAgain implements the BlockServer interface for BlockServerDisk.
   256  func (b *BlockServerDisk) PutAgain(
   257  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   258  	context kbfsblock.Context, buf []byte,
   259  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   260  	_ DiskBlockCacheType) (err error) {
   261  	if err := checkContext(ctx); err != nil {
   262  		return err
   263  	}
   264  	defer func() {
   265  		err = translateToBlockServerError(err)
   266  	}()
   267  	b.log.CDebugf(ctx, "BlockServerDisk.PutAgain id=%s tlfID=%s context=%s size=%d",
   268  		id, tlfID, context, len(buf))
   269  
   270  	tlfStorage, err := b.getStorage(tlfID)
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	tlfStorage.lock.Lock()
   276  	defer tlfStorage.lock.Unlock()
   277  	if tlfStorage.store == nil {
   278  		return errBlockServerDiskShutdown
   279  	}
   280  
   281  	_, err = tlfStorage.store.put(ctx, false, id, context, buf, serverHalf)
   282  	if err != nil {
   283  		return err
   284  	}
   285  	err = tlfStorage.store.addReference(ctx, id, context, "tag")
   286  	if err != nil {
   287  		return err
   288  	}
   289  	return nil
   290  }
   291  
   292  // AddBlockReference implements the BlockServer interface for BlockServerDisk.
   293  func (b *BlockServerDisk) AddBlockReference(ctx context.Context, tlfID tlf.ID,
   294  	id kbfsblock.ID, context kbfsblock.Context) error {
   295  	if err := checkContext(ctx); err != nil {
   296  		return err
   297  	}
   298  
   299  	b.log.CDebugf(ctx, "BlockServerDisk.AddBlockReference id=%s "+
   300  		"tlfID=%s context=%s", id, tlfID, context)
   301  	tlfStorage, err := b.getStorage(tlfID)
   302  	if err != nil {
   303  		return err
   304  	}
   305  
   306  	tlfStorage.lock.Lock()
   307  	defer tlfStorage.lock.Unlock()
   308  	if tlfStorage.store == nil {
   309  		return errBlockServerDiskShutdown
   310  	}
   311  
   312  	hasRef, err := tlfStorage.store.hasAnyRef(ctx, id)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	if !hasRef {
   317  		return kbfsblock.ServerErrorBlockNonExistent{Msg: fmt.Sprintf("Block ID %s "+
   318  			"doesn't exist and cannot be referenced.", id)}
   319  	}
   320  
   321  	hasNonArchivedRef, err := tlfStorage.store.hasNonArchivedRef(ctx, id)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	if !hasNonArchivedRef {
   326  		return kbfsblock.ServerErrorBlockArchived{Msg: fmt.Sprintf("Block ID %s has "+
   327  			"been archived and cannot be referenced.", id)}
   328  	}
   329  
   330  	return tlfStorage.store.addReference(ctx, id, context, "")
   331  }
   332  
   333  // RemoveBlockReferences implements the BlockServer interface for
   334  // BlockServerDisk.
   335  func (b *BlockServerDisk) RemoveBlockReferences(ctx context.Context,
   336  	tlfID tlf.ID, contexts kbfsblock.ContextMap) (
   337  	liveCounts map[kbfsblock.ID]int, err error) {
   338  	if err := checkContext(ctx); err != nil {
   339  		return nil, err
   340  	}
   341  
   342  	defer func() {
   343  		err = translateToBlockServerError(err)
   344  	}()
   345  	b.log.CDebugf(ctx, "BlockServerDisk.RemoveBlockReference "+
   346  		"tlfID=%s contexts=%v", tlfID, contexts)
   347  	tlfStorage, err := b.getStorage(tlfID)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  
   352  	tlfStorage.lock.Lock()
   353  	defer tlfStorage.lock.Unlock()
   354  	if tlfStorage.store == nil {
   355  		return nil, errBlockServerDiskShutdown
   356  	}
   357  
   358  	liveCounts = make(map[kbfsblock.ID]int)
   359  	for id, idContexts := range contexts {
   360  		liveCount, err := tlfStorage.store.removeReferences(
   361  			ctx, id, idContexts, "")
   362  		if err != nil {
   363  			return nil, err
   364  		}
   365  		liveCounts[id] = liveCount
   366  
   367  		if liveCount == 0 {
   368  			err := tlfStorage.store.remove(ctx, id)
   369  			if err != nil {
   370  				return nil, err
   371  			}
   372  		}
   373  	}
   374  
   375  	return liveCounts, nil
   376  }
   377  
   378  // ArchiveBlockReferences implements the BlockServer interface for
   379  // BlockServerDisk.
   380  func (b *BlockServerDisk) ArchiveBlockReferences(ctx context.Context,
   381  	tlfID tlf.ID, contexts kbfsblock.ContextMap) (err error) {
   382  	if err := checkContext(ctx); err != nil {
   383  		return err
   384  	}
   385  
   386  	defer func() {
   387  		err = translateToBlockServerError(err)
   388  	}()
   389  	b.log.CDebugf(ctx, "BlockServerDisk.ArchiveBlockReferences "+
   390  		"tlfID=%s contexts=%v", tlfID, contexts)
   391  	tlfStorage, err := b.getStorage(tlfID)
   392  	if err != nil {
   393  		return err
   394  	}
   395  
   396  	tlfStorage.lock.Lock()
   397  	defer tlfStorage.lock.Unlock()
   398  	if tlfStorage.store == nil {
   399  		return errBlockServerDiskShutdown
   400  	}
   401  
   402  	for id, idContexts := range contexts {
   403  		for _, context := range idContexts {
   404  			hasContext, _, err := tlfStorage.store.hasContext(ctx, id, context)
   405  			if err != nil {
   406  				return err
   407  			}
   408  			if !hasContext {
   409  				return kbfsblock.ServerErrorBlockNonExistent{
   410  					Msg: fmt.Sprintf(
   411  						"Block ID %s (context %s) doesn't "+
   412  							"exist and cannot be archived.",
   413  						id, context),
   414  				}
   415  			}
   416  		}
   417  	}
   418  
   419  	return tlfStorage.store.archiveReferences(ctx, contexts, "")
   420  }
   421  
   422  // GetLiveBlockReferences implements the BlockServer interface for
   423  // BlockServerDisk.
   424  func (b *BlockServerDisk) GetLiveBlockReferences(
   425  	ctx context.Context, tlfID tlf.ID, contexts kbfsblock.ContextMap) (
   426  	liveCounts map[kbfsblock.ID]int, err error) {
   427  	if err := checkContext(ctx); err != nil {
   428  		return nil, err
   429  	}
   430  
   431  	defer func() {
   432  		err = translateToBlockServerError(err)
   433  	}()
   434  	b.log.CDebugf(ctx, "BlockServerDisk.GetLiveBlockReferences "+
   435  		"tlfID=%s contexts=%v", tlfID, contexts)
   436  	tlfStorage, err := b.getStorage(tlfID)
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	tlfStorage.lock.Lock()
   442  	defer tlfStorage.lock.Unlock()
   443  	if tlfStorage.store == nil {
   444  		return nil, errBlockServerDiskShutdown
   445  	}
   446  
   447  	liveCounts = make(map[kbfsblock.ID]int)
   448  	for id := range contexts {
   449  		liveCount, err := tlfStorage.store.getLiveCount(ctx, id)
   450  		if err != nil {
   451  			return nil, err
   452  		}
   453  		liveCounts[id] = liveCount
   454  	}
   455  
   456  	return liveCounts, nil
   457  }
   458  
   459  // getAllRefsForTest implements the blockServerLocal interface for
   460  // BlockServerDisk.
   461  func (b *BlockServerDisk) getAllRefsForTest(ctx context.Context, tlfID tlf.ID) (
   462  	map[kbfsblock.ID]blockRefMap, error) {
   463  	tlfStorage, err := b.getStorage(tlfID)
   464  	if err != nil {
   465  		return nil, err
   466  	}
   467  
   468  	tlfStorage.lock.RLock()
   469  	defer tlfStorage.lock.RUnlock()
   470  	if tlfStorage.store == nil {
   471  		return nil, errBlockServerDiskShutdown
   472  	}
   473  
   474  	return tlfStorage.store.getAllRefsForTest()
   475  }
   476  
   477  // IsUnflushed implements the BlockServer interface for BlockServerDisk.
   478  func (b *BlockServerDisk) IsUnflushed(ctx context.Context, tlfID tlf.ID,
   479  	_ kbfsblock.ID) (bool, error) {
   480  	if err := checkContext(ctx); err != nil {
   481  		return false, err
   482  	}
   483  
   484  	tlfStorage, err := b.getStorage(tlfID)
   485  	if err != nil {
   486  		return false, err
   487  	}
   488  
   489  	tlfStorage.lock.RLock()
   490  	defer tlfStorage.lock.RUnlock()
   491  	if tlfStorage.store == nil {
   492  		return false, errBlockServerDiskShutdown
   493  	}
   494  
   495  	return false, nil
   496  }
   497  
   498  // Shutdown implements the BlockServer interface for BlockServerDisk.
   499  func (b *BlockServerDisk) Shutdown(ctx context.Context) {
   500  	tlfStorage := func() map[tlf.ID]*blockServerDiskTlfStorage {
   501  		b.tlfStorageLock.Lock()
   502  		defer b.tlfStorageLock.Unlock()
   503  		// Make further accesses error out.
   504  		tlfStorage := b.tlfStorage
   505  		b.tlfStorage = nil
   506  		return tlfStorage
   507  	}()
   508  
   509  	for _, s := range tlfStorage {
   510  		func() {
   511  			s.lock.Lock()
   512  			defer s.lock.Unlock()
   513  			if s.store == nil {
   514  				// Already shutdown.
   515  				return
   516  			}
   517  
   518  			// Make further accesses error out.
   519  			s.store = nil
   520  		}()
   521  	}
   522  
   523  	if b.shutdownFunc != nil {
   524  		b.shutdownFunc(b.log)
   525  	}
   526  }
   527  
   528  // RefreshAuthToken implements the BlockServer interface for BlockServerDisk.
   529  func (b *BlockServerDisk) RefreshAuthToken(_ context.Context) {}
   530  
   531  // GetUserQuotaInfo implements the BlockServer interface for BlockServerDisk.
   532  func (b *BlockServerDisk) GetUserQuotaInfo(ctx context.Context) (info *kbfsblock.QuotaInfo, err error) {
   533  	if err := checkContext(ctx); err != nil {
   534  		return nil, err
   535  	}
   536  
   537  	// Return a dummy value here.
   538  	return &kbfsblock.QuotaInfo{Limit: math.MaxInt64}, nil
   539  }
   540  
   541  // GetTeamQuotaInfo implements the BlockServer interface for BlockServerDisk.
   542  func (b *BlockServerDisk) GetTeamQuotaInfo(
   543  	ctx context.Context, _ keybase1.TeamID) (
   544  	info *kbfsblock.QuotaInfo, err error) {
   545  	if err := checkContext(ctx); err != nil {
   546  		return nil, err
   547  	}
   548  
   549  	// TODO: check team membership and return error if not a reader?
   550  
   551  	// Return a dummy value here.
   552  	return &kbfsblock.QuotaInfo{Limit: math.MaxInt64}, nil
   553  }