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

     1  // Copyright 2018 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 libfs
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/kbfs/data"
    14  	"github.com/keybase/client/go/kbfs/libkbfs"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  )
    17  
    18  // ErrUnknownPrefetchStatus is returned when the prefetch status given by the
    19  // KBFSOps's NodeMetadata is invalid.
    20  var ErrUnknownPrefetchStatus = errors.New(
    21  	"Failed to determine prefetch status")
    22  
    23  // FileInfo is a wrapper around libkbfs.EntryInfo that implements the
    24  // os.FileInfo interface.
    25  type FileInfo struct {
    26  	fs   *FS
    27  	ei   data.EntryInfo
    28  	node libkbfs.Node
    29  	name string
    30  }
    31  
    32  var _ os.FileInfo = (*FileInfo)(nil)
    33  
    34  // Name implements the os.FileInfo interface for FileInfo.
    35  func (fi *FileInfo) Name() string {
    36  	return fi.name
    37  }
    38  
    39  // Size implements the os.FileInfo interface for FileInfo.
    40  func (fi *FileInfo) Size() int64 {
    41  	// TODO: deal with overflow?
    42  	return int64(fi.ei.Size)
    43  }
    44  
    45  // Mode implements the os.FileInfo interface for FileInfo.
    46  func (fi *FileInfo) Mode() os.FileMode {
    47  	mode, err := WritePermMode(
    48  		fi.fs.ctx, fi.node, os.FileMode(0), fi.fs.config.KBPKI(),
    49  		fi.fs.config, fi.fs.h)
    50  	if err != nil {
    51  		fi.fs.log.CWarningf(
    52  			fi.fs.ctx, "Couldn't get mode for file %s: %+v", fi.Name(), err)
    53  		mode = os.FileMode(0)
    54  	}
    55  
    56  	mode |= 0400
    57  	switch fi.ei.Type {
    58  	case data.Dir:
    59  		mode |= os.ModeDir | 0100
    60  	case data.Sym:
    61  		mode |= os.ModeSymlink
    62  	case data.Exec:
    63  		mode |= 0100
    64  	}
    65  	return mode
    66  }
    67  
    68  // ModTime implements the os.FileInfo interface for FileInfo.
    69  func (fi *FileInfo) ModTime() time.Time {
    70  	return time.Unix(0, fi.ei.Mtime)
    71  }
    72  
    73  // IsDir implements the os.FileInfo interface for FileInfo.
    74  func (fi *FileInfo) IsDir() bool {
    75  	return fi.ei.Type == data.Dir
    76  }
    77  
    78  // KBFSMetadataForSimpleFS contains the KBFS metadata needed to answer a
    79  // simpleFSStat call.
    80  type KBFSMetadataForSimpleFS struct {
    81  	LastWriter       keybase1.User
    82  	PrefetchStatus   keybase1.PrefetchStatus
    83  	PrefetchProgress libkbfs.PrefetchProgress
    84  }
    85  
    86  // KBFSMetadataForSimpleFSGetter is an interface for something that can return
    87  // the last KBFS writer and prefetch status of a directory entry.
    88  type KBFSMetadataForSimpleFSGetter interface {
    89  	KBFSMetadataForSimpleFS() (KBFSMetadataForSimpleFS, error)
    90  }
    91  
    92  // PrevRevisionsGetter is an interface for something that can return
    93  // the previous revisions of an entry.
    94  type PrevRevisionsGetter interface {
    95  	PrevRevisions() data.PrevRevisions
    96  }
    97  
    98  type fileInfoSys struct {
    99  	fi *FileInfo
   100  }
   101  
   102  var _ KBFSMetadataForSimpleFSGetter = fileInfoSys{}
   103  
   104  func (fis fileInfoSys) KBFSMetadataForSimpleFS() (
   105  	KBFSMetadataForSimpleFS, error) {
   106  	if fis.fi.node == nil {
   107  		// This won't return any last writer for symlinks themselves.
   108  		// TODO: if we want symlink last writers, we'll need to add a
   109  		// new interface to KBFSOps to get them.
   110  		return KBFSMetadataForSimpleFS{}, nil
   111  	}
   112  	md, err := fis.fi.fs.config.KBFSOps().GetNodeMetadata(
   113  		fis.fi.fs.ctx, fis.fi.node)
   114  	if err != nil {
   115  		return KBFSMetadataForSimpleFS{}, err
   116  	}
   117  
   118  	prefetchStatus := md.PrefetchStatus.ToProtocolStatus()
   119  	status := KBFSMetadataForSimpleFS{PrefetchStatus: prefetchStatus}
   120  	if md.PrefetchProgress != nil {
   121  		status.PrefetchProgress = *md.PrefetchProgress
   122  	}
   123  
   124  	lastWriterName := md.LastWriterUnverified
   125  	if lastWriterName == "" {
   126  		// This can happen in old, buggy team folders where the writer
   127  		// isn't properly set.  See KBFS-2939.
   128  		return status, nil
   129  	}
   130  
   131  	_, id, err := fis.fi.fs.config.KBPKI().Resolve(
   132  		fis.fi.fs.ctx, lastWriterName.String(),
   133  		fis.fi.fs.config.OfflineAvailabilityForID(
   134  			fis.fi.fs.root.GetFolderBranch().Tlf))
   135  	if err != nil {
   136  		return KBFSMetadataForSimpleFS{}, err
   137  	}
   138  	uid, err := id.AsUser()
   139  	if err != nil {
   140  		return KBFSMetadataForSimpleFS{}, err
   141  	}
   142  
   143  	status.LastWriter = keybase1.User{
   144  		Uid:      uid,
   145  		Username: lastWriterName.String(),
   146  	}
   147  	return status, nil
   148  }
   149  
   150  var _ PrevRevisionsGetter = fileInfoSys{}
   151  
   152  func (fis fileInfoSys) PrevRevisions() (revs data.PrevRevisions) {
   153  	return fis.fi.ei.PrevRevisions
   154  }
   155  
   156  func (fis fileInfoSys) EntryInfo() data.EntryInfo {
   157  	return fis.fi.ei
   158  }
   159  
   160  // Sys implements the os.FileInfo interface for FileInfo.
   161  func (fi *FileInfo) Sys() interface{} {
   162  	return fileInfoSys{fi}
   163  }
   164  
   165  // FileInfoFast always returns a returns a read-only mode, and doesn't populate
   166  // LastWriterUnverified. This allows us to avoid doing a Lookup on the entry,
   167  // which makes a big difference in ReadDir.
   168  type FileInfoFast struct {
   169  	ei   data.EntryInfo
   170  	name string
   171  }
   172  
   173  // Name implements the os.FileInfo interface.
   174  func (fif *FileInfoFast) Name() string {
   175  	return fif.name
   176  }
   177  
   178  // Size implements the os.FileInfo interface.
   179  func (fif *FileInfoFast) Size() int64 {
   180  	// TODO: deal with overflow?
   181  	return int64(fif.ei.Size)
   182  }
   183  
   184  // Mode implements the os.FileInfo interface.
   185  func (fif *FileInfoFast) Mode() os.FileMode {
   186  	mode := os.FileMode(0400)
   187  	switch fif.ei.Type {
   188  	case data.Dir:
   189  		mode |= os.ModeDir | 0100
   190  	case data.Sym:
   191  		mode |= os.ModeSymlink
   192  	case data.Exec:
   193  		mode |= 0100
   194  	}
   195  	return mode
   196  }
   197  
   198  // ModTime implements the os.FileInfo interface.
   199  func (fif *FileInfoFast) ModTime() time.Time {
   200  	return time.Unix(0, fif.ei.Mtime)
   201  }
   202  
   203  // IsDir implements the os.FileInfo interface.
   204  func (fif *FileInfoFast) IsDir() bool {
   205  	return fif.ei.Type == data.Dir
   206  }
   207  
   208  // Sys implements the os.FileInfo interface.
   209  func (fif *FileInfoFast) Sys() interface{} {
   210  	return fif
   211  }
   212  
   213  type ctxFastModeKey struct{}
   214  
   215  // EnableFastMode returns a context.Context based on ctx that will test to true
   216  // with IsFastModeEnabled.
   217  func EnableFastMode(ctx context.Context) context.Context {
   218  	return context.WithValue(ctx, ctxFastModeKey{}, true)
   219  }
   220  
   221  // IsFastModeEnabled returns true if fast mode should be enabled. In fast mode,
   222  // *FS doesn't populate LastWriterUnverified, and always returns read-only
   223  // info. All *FS created under this ctx will also be in fast mode.
   224  func IsFastModeEnabled(ctx context.Context) bool {
   225  	v, ok := ctx.Value(ctxFastModeKey{}).(bool)
   226  	return ok && v
   227  }