github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfuse/fs_darwin.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 darwin
     6  // +build darwin
     7  
     8  package libfuse
     9  
    10  import (
    11  	"fmt"
    12  	"os"
    13  	"path/filepath"
    14  	"strconv"
    15  
    16  	"bazil.org/fuse"
    17  	"bazil.org/fuse/fs"
    18  	"github.com/keybase/client/go/kbfs/idutil"
    19  	"github.com/keybase/client/go/kbfs/libkbfs"
    20  	kbname "github.com/keybase/client/go/kbun"
    21  	"github.com/keybase/client/go/libkb"
    22  	"github.com/keybase/client/go/protocol/keybase1"
    23  	"github.com/keybase/client/go/utils"
    24  	"golang.org/x/net/context"
    25  )
    26  
    27  const (
    28  	// TrashDirName is the .Trashes special directory that macOS uses for Trash
    29  	// on non boot volumes.
    30  	TrashDirName = ".Trashes"
    31  
    32  	// FSEventsDirName is the .fseventsd directory that macOS always tries to get.
    33  	// TODO: find out what this is for.
    34  	FSEventsDirName = ".fseventsd"
    35  
    36  	// DSStoreFileName is the .DS_Store file
    37  	// TODO: find out if this is necessary
    38  	DSStoreFileName = ".DS_Store"
    39  )
    40  
    41  // mountRootSpecialPaths defines automatically handled special paths.
    42  // TrashDirName is notably missing here since we use the *Trash type to handle
    43  // it.
    44  var mountRootSpecialPaths = map[string]bool{
    45  	FSEventsDirName: true,
    46  	DSStoreFileName: true,
    47  }
    48  
    49  var platformRootDirs = []fuse.Dirent{
    50  	{
    51  		Type: fuse.DT_Dir,
    52  		Name: TrashDirName,
    53  	},
    54  	{
    55  		Type: fuse.DT_Dir,
    56  		Name: FSEventsDirName,
    57  	},
    58  	{
    59  		Type: fuse.DT_File,
    60  		Name: DSStoreFileName,
    61  	},
    62  }
    63  
    64  func (r *Root) platformLookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
    65  	switch req.Name {
    66  	case VolIconFileName:
    67  		return newExternalBundleResourceFile("KeybaseFolder.icns")
    68  	case ExtendedAttributeSelfFileName:
    69  		return newExternalBundleResourceFile("ExtendedAttributeFinderInfo.bin")
    70  	}
    71  
    72  	if r.private.fs.platformParams.UseLocal {
    73  		if mountRootSpecialPaths[req.Name] {
    74  			session, err := idutil.GetCurrentSessionIfPossible(ctx, r.private.fs.config.KBPKI(), false)
    75  			if err != nil {
    76  				return nil, err
    77  			}
    78  			return &Alias{realPath: fmt.Sprintf("private/%s/.darwin/%s", session.Name, req.Name)}, nil
    79  		}
    80  
    81  		if req.Name == TrashDirName {
    82  			session, err := idutil.GetCurrentSessionIfPossible(ctx, r.private.fs.config.KBPKI(), false)
    83  			if err != nil {
    84  				return nil, err
    85  			}
    86  			return &Trash{
    87  				fs:         r.private.fs,
    88  				kbusername: session.Name,
    89  			}, nil
    90  		}
    91  	}
    92  
    93  	return nil, nil
    94  }
    95  
    96  func newExternalBundleResourceFile(path string) (*SpecialReadFile, error) {
    97  	bpath, err := bundleResourcePath(path)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return newExternalFile(bpath)
   102  }
   103  
   104  func bundleResourcePath(path string) (string, error) {
   105  	if libkb.RuntimeGroup() != keybase1.RuntimeGroup_DARWINLIKE {
   106  		return "", fmt.Errorf("Bundle resource path only available on macOS/darwin")
   107  	}
   108  	execPath, err := utils.BinPath()
   109  	if err != nil {
   110  		return "", err
   111  	}
   112  	return filepath.Join(execPath, "..", "..", "..", "Resources", path), nil
   113  }
   114  
   115  // Trash is a mock .Trashes directory. It implements a /keybase/.Trashes that
   116  // has a $UID inside, which symlinks to a directory within the user's own
   117  // private TLF. Since rename doesn't work across different TLFs, this would be
   118  // a Trash that only works for stuff in user's own private TLF.
   119  //
   120  // TODO: implement per-TLF "trash" location, and have this type figure
   121  // out how to concatenate files from different TLF's trash together, and
   122  // disseminates renames into different TLF's trash.
   123  type Trash struct {
   124  	fs         *FS
   125  	kbusername kbname.NormalizedUsername
   126  }
   127  
   128  // Lookup implements the fs.NodeRequestLookuper interface for *Trash
   129  func (t *Trash) Lookup(ctx context.Context,
   130  	req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
   131  	if req.Name == strconv.Itoa(os.Getuid()) {
   132  		return &Alias{
   133  			realPath: fmt.Sprintf("../private/%s/.trash", t.kbusername),
   134  		}, nil
   135  	}
   136  	return nil, fuse.ENOENT
   137  }
   138  
   139  // Attr implements the fs.Node interface for *Trash
   140  func (t *Trash) Attr(ctx context.Context, a *fuse.Attr) error {
   141  	a.Mode = os.ModeDir | 0755
   142  	return nil
   143  }
   144  
   145  // ReadDirAll implements the fs.NodeReadDirAller interface for *Trash
   146  func (t *Trash) ReadDirAll(ctx context.Context) (res []fuse.Dirent, err error) {
   147  	t.fs.log.CDebugf(ctx, "Trash ReadDirAll")
   148  	defer func() { err = t.fs.processError(ctx, libkbfs.ReadMode, err) }()
   149  
   150  	return []fuse.Dirent{
   151  		{
   152  			Type: fuse.DT_Link,
   153  			Name: strconv.Itoa(os.Getuid()),
   154  		},
   155  	}, nil
   156  }