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 }