github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfuse/tlf.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  //go:build !windows
     6  // +build !windows
     7  
     8  package libfuse
     9  
    10  import (
    11  	"os"
    12  	"sync"
    13  	"time"
    14  
    15  	"bazil.org/fuse"
    16  	"bazil.org/fuse/fs"
    17  	"github.com/keybase/client/go/kbfs/data"
    18  	"github.com/keybase/client/go/kbfs/libfs"
    19  	"github.com/keybase/client/go/kbfs/libkbfs"
    20  	"github.com/keybase/client/go/kbfs/tlf"
    21  	"github.com/keybase/client/go/kbfs/tlfhandle"
    22  	"github.com/keybase/client/go/libkb"
    23  	"github.com/keybase/client/go/logger"
    24  	"golang.org/x/net/context"
    25  )
    26  
    27  // TLF represents the root directory of a TLF. It wraps a lazy-loaded
    28  // Dir.
    29  type TLF struct {
    30  	folder *Folder
    31  	inode  uint64
    32  
    33  	dirLock sync.RWMutex
    34  	dir     *Dir
    35  
    36  	// We never set quarantine on TLF roots, so don't bother with getting a dir
    37  	// and calling the method on it. Instead, just use this dumb handler to
    38  	// always return fuse.ENOTSUP.
    39  	NoXattrHandler
    40  }
    41  
    42  func newTLF(ctx context.Context, fl *FolderList, h *tlfhandle.Handle,
    43  	name tlf.PreferredName) *TLF {
    44  	folder := newFolder(ctx, fl, h, name)
    45  	tlf := &TLF{
    46  		folder: folder,
    47  		inode:  fl.fs.assignInode(),
    48  	}
    49  	return tlf
    50  }
    51  
    52  var _ DirInterface = (*TLF)(nil)
    53  
    54  func (tlf *TLF) getStoredDir() *Dir {
    55  	tlf.dirLock.RLock()
    56  	defer tlf.dirLock.RUnlock()
    57  	return tlf.dir
    58  }
    59  
    60  func (tlf *TLF) clearStoredDir() {
    61  	tlf.dirLock.Lock()
    62  	defer tlf.dirLock.Unlock()
    63  	tlf.dir = nil
    64  }
    65  
    66  func (tlf *TLF) log() logger.Logger {
    67  	return tlf.folder.fs.log
    68  }
    69  
    70  func (tlf *TLF) vlog() *libkb.VDebugLog {
    71  	return tlf.folder.fs.vlog
    72  }
    73  
    74  func (tlf *TLF) loadDirHelper(
    75  	ctx context.Context, mode libkbfs.ErrorModeType, branch data.BranchName,
    76  	filterErr bool) (dir *Dir, exitEarly bool, err error) {
    77  	dir = tlf.getStoredDir()
    78  	if dir != nil {
    79  		return dir, false, nil
    80  	}
    81  
    82  	tlf.dirLock.Lock()
    83  	defer tlf.dirLock.Unlock()
    84  	// Need to check for nilness again to avoid racing with other
    85  	// calls to loadDir().
    86  	if tlf.dir != nil {
    87  		return tlf.dir, false, nil
    88  	}
    89  
    90  	tlf.log().CDebugf(ctx, "Loading root directory for folder %s "+
    91  		"(type: %s, filter error: %t)", tlf.folder.name(),
    92  		tlf.folder.list.tlfType, filterErr)
    93  	defer func() {
    94  		if filterErr {
    95  			exitEarly, err = libfs.FilterTLFEarlyExitError(ctx, err, tlf.log(), tlf.folder.name())
    96  		}
    97  		err = tlf.folder.processError(ctx, mode, err)
    98  	}()
    99  
   100  	handle, err := tlf.folder.resolve(ctx)
   101  	if err != nil {
   102  		return nil, false, err
   103  	}
   104  
   105  	if branch == data.MasterBranch {
   106  		conflictBranch, isLocalConflictBranch :=
   107  			data.MakeConflictBranchName(handle)
   108  		if isLocalConflictBranch {
   109  			branch = conflictBranch
   110  		}
   111  	}
   112  
   113  	var rootNode libkbfs.Node
   114  	if filterErr {
   115  		rootNode, _, err = tlf.folder.fs.config.KBFSOps().GetRootNode(
   116  			ctx, handle, branch)
   117  		if err != nil {
   118  			return nil, false, err
   119  		}
   120  		// If not fake an empty directory.
   121  		if rootNode == nil {
   122  			return nil, false, libfs.TlfDoesNotExist{}
   123  		}
   124  	} else {
   125  		rootNode, _, err = tlf.folder.fs.config.KBFSOps().GetOrCreateRootNode(
   126  			ctx, handle, branch)
   127  		if err != nil {
   128  			return nil, false, err
   129  		}
   130  	}
   131  
   132  	err = tlf.folder.setFolderBranch(rootNode.GetFolderBranch())
   133  	if err != nil {
   134  		return nil, false, err
   135  	}
   136  
   137  	tlf.folder.nodes[rootNode.GetID()] = tlf
   138  	tlf.dir = newDirWithInode(tlf.folder, rootNode, tlf.inode)
   139  
   140  	return tlf.dir, false, nil
   141  }
   142  
   143  func (tlf *TLF) loadDir(ctx context.Context) (*Dir, error) {
   144  	dir, _, err := tlf.loadDirHelper(
   145  		ctx, libkbfs.WriteMode, data.MasterBranch, false)
   146  	return dir, err
   147  }
   148  
   149  // loadDirAllowNonexistent loads a TLF if it's not already loaded.  If
   150  // the TLF doesn't yet exist, it still returns a nil error and
   151  // indicates that the calling function should pretend it's an empty
   152  // folder.
   153  func (tlf *TLF) loadDirAllowNonexistent(ctx context.Context) (
   154  	*Dir, bool, error) {
   155  	return tlf.loadDirHelper(ctx, libkbfs.ReadMode, data.MasterBranch, true)
   156  }
   157  
   158  func (tlf *TLF) loadArchivedDir(
   159  	ctx context.Context, branch data.BranchName) (*Dir, bool, error) {
   160  	// Always filter errors for archive TLF directories, so that we
   161  	// don't try to initialize them.
   162  	return tlf.loadDirHelper(ctx, libkbfs.ReadMode, branch, true)
   163  }
   164  
   165  // Access implements the fs.NodeAccesser interface for *TLF.
   166  func (tlf *TLF) Access(ctx context.Context, r *fuse.AccessRequest) error {
   167  	return tlf.folder.access(ctx, r)
   168  }
   169  
   170  // Attr implements the fs.Node interface for TLF.
   171  func (tlf *TLF) Attr(ctx context.Context, a *fuse.Attr) error {
   172  	dir := tlf.getStoredDir()
   173  	a.Inode = tlf.inode
   174  	if dir == nil {
   175  		tlf.vlog().CLogf(
   176  			ctx, libkb.VLog1, "Faking Attr for TLF %s", tlf.folder.name())
   177  		// Have a low non-zero value for Valid to avoid being
   178  		// swamped with requests, while still not showing
   179  		// stale data for too long if we end up loading the
   180  		// dir.
   181  		a.Valid = 1 * time.Second
   182  		a.Mode = os.ModeDir | 0500
   183  		a.Uid = uint32(os.Getuid())
   184  		return nil
   185  	}
   186  
   187  	return dir.Attr(ctx, a)
   188  }
   189  
   190  // tlfLoadAvoidingLookupNames specifies a set of directory entry names
   191  // that should NOT cause a TLF to be fully loaded and identified on a
   192  // lookup.  If the directory is not yet loaded and one of these names
   193  // are looked-up, then ENOENT will be returned automatically.  This is
   194  // to avoid unnecessary loading and tracker popups when listing the
   195  // folder list directories.  For example, when macOS finder opens
   196  // `/keybase/private`, it looks up `.localized` in every TLF
   197  // subdirectory to see if it should translate the TLF folder name or
   198  // not, which can cause a tracker popup storm (see KBFS-2649).
   199  var tlfLoadAvoidingLookupNames = map[string]bool{
   200  	".localized": true,
   201  	"Contents":   true,
   202  }
   203  
   204  // Lookup implements the fs.NodeRequestLookuper interface for TLF.
   205  func (tlf *TLF) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
   206  	if tlfLoadAvoidingLookupNames[req.Name] {
   207  		dir := tlf.getStoredDir()
   208  		if dir == nil {
   209  			tlf.vlog().CLogf(
   210  				ctx, libkb.VLog1, "Avoiding TLF loading for name %s", req.Name)
   211  			return nil, fuse.ENOENT
   212  		}
   213  		return dir.Lookup(ctx, req, resp)
   214  	}
   215  
   216  	dir, exitEarly, err := tlf.loadDirAllowNonexistent(ctx)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	if exitEarly {
   221  		if node := handleTLFSpecialFile(
   222  			req.Name, tlf.folder, &resp.EntryValid); node != nil {
   223  			return node, nil
   224  		}
   225  		return nil, fuse.ENOENT
   226  	}
   227  
   228  	branch, isArchivedBranch := libfs.BranchNameFromArchiveRefDir(req.Name)
   229  	if isArchivedBranch {
   230  		archivedTLF := newTLF(ctx,
   231  			tlf.folder.list, tlf.folder.h, tlf.folder.hPreferredName)
   232  		_, _, err := archivedTLF.loadArchivedDir(ctx, branch)
   233  		if err != nil {
   234  			return nil, err
   235  		}
   236  		return archivedTLF, nil
   237  	}
   238  
   239  	linkTarget, isArchivedTimeLink, err := libfs.LinkTargetFromTimeString(
   240  		ctx, tlf.folder.fs.config, tlf.folder.h, req.Name)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	if isArchivedTimeLink {
   245  		return &Alias{
   246  			realPath: linkTarget,
   247  			inode:    0,
   248  		}, nil
   249  	}
   250  
   251  	_, isRelTimeLink, err := libfs.FileDataFromRelativeTimeString(
   252  		ctx, tlf.folder.fs.config, tlf.folder.h, req.Name)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	if isRelTimeLink {
   257  		return NewArchiveRelTimeFile(
   258  			tlf.folder.fs, tlf.folder.h, req.Name, &resp.EntryValid), nil
   259  	}
   260  
   261  	return dir.Lookup(ctx, req, resp)
   262  }
   263  
   264  // Create implements the fs.NodeCreater interface for TLF.
   265  func (tlf *TLF) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
   266  	dir, err := tlf.loadDir(ctx)
   267  	if err != nil {
   268  		return nil, nil, err
   269  	}
   270  	return dir.Create(ctx, req, resp)
   271  }
   272  
   273  // Mkdir implements the fs.NodeMkdirer interface for TLF.
   274  func (tlf *TLF) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (_ fs.Node, err error) {
   275  	dir, err := tlf.loadDir(ctx)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	return dir.Mkdir(ctx, req)
   280  }
   281  
   282  // Symlink implements the fs.NodeSymlinker interface for TLF.
   283  func (tlf *TLF) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (
   284  	fs.Node, error) {
   285  	dir, err := tlf.loadDir(ctx)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	return dir.Symlink(ctx, req)
   290  }
   291  
   292  var _ fs.NodeLinker = (*TLF)(nil)
   293  
   294  // Link implements the fs.NodeLinker interface for TLF.
   295  func (tlf *TLF) Link(
   296  	_ context.Context, _ *fuse.LinkRequest, _ fs.Node) (fs.Node, error) {
   297  	return nil, fuse.ENOTSUP
   298  }
   299  
   300  // Rename implements the fs.NodeRenamer interface for TLF.
   301  func (tlf *TLF) Rename(ctx context.Context, req *fuse.RenameRequest,
   302  	newDir fs.Node) error {
   303  	dir, err := tlf.loadDir(ctx)
   304  	if err != nil {
   305  		return err
   306  	}
   307  	return dir.Rename(ctx, req, newDir)
   308  }
   309  
   310  // Remove implements the fs.NodeRemover interface for TLF.
   311  func (tlf *TLF) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
   312  	dir, err := tlf.loadDir(ctx)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	return dir.Remove(ctx, req)
   317  }
   318  
   319  // ReadDirAll implements the fs.NodeReadDirAller interface for TLF.
   320  func (tlf *TLF) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
   321  	dir, exitEarly, err := tlf.loadDirAllowNonexistent(ctx)
   322  	if err != nil || exitEarly {
   323  		return nil, err
   324  	}
   325  	return dir.ReadDirAll(ctx)
   326  }
   327  
   328  // Forget kernel reference to this node.
   329  func (tlf *TLF) Forget() {
   330  	dir := tlf.getStoredDir()
   331  	if dir != nil {
   332  		dir.Forget()
   333  	} else {
   334  		tlf.folder.list.forgetFolder(string(tlf.folder.name()))
   335  	}
   336  }
   337  
   338  // Setattr implements the fs.NodeSetattrer interface for TLF.
   339  func (tlf *TLF) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
   340  	dir, err := tlf.loadDir(ctx)
   341  	if err != nil {
   342  		return err
   343  	}
   344  	return dir.Setattr(ctx, req, resp)
   345  }
   346  
   347  // Fsync implements the fs.NodeFsyncer interface for TLF.
   348  func (tlf *TLF) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) {
   349  	dir := tlf.getStoredDir()
   350  	if dir == nil {
   351  		// The directory hasn't been loaded yet, so there's nothing to do.
   352  		return nil
   353  	}
   354  
   355  	return dir.Fsync(ctx, req)
   356  }
   357  
   358  var _ fs.Handle = (*TLF)(nil)
   359  
   360  var _ fs.NodeOpener = (*TLF)(nil)
   361  
   362  // Open implements the fs.NodeOpener interface for TLF.
   363  func (tlf *TLF) Open(ctx context.Context, req *fuse.OpenRequest,
   364  	resp *fuse.OpenResponse) (fs.Handle, error) {
   365  	// Explicitly load the directory when a TLF is opened, because
   366  	// some OSX programs like ls have a bug that doesn't report errors
   367  	// on a ReadDirAll.
   368  	_, _, err := tlf.loadDirAllowNonexistent(ctx)
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	return tlf, nil
   373  }
   374  
   375  func (tlf *TLF) openFileCount() int64 {
   376  	return tlf.folder.openFileCount()
   377  }