github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfuse/quarantine_darwin.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 //go:build !windows 6 // +build !windows 7 8 package libfuse 9 10 import ( 11 "fmt" 12 "strconv" 13 "time" 14 15 "bazil.org/fuse" 16 "github.com/keybase/client/go/kbfs/idutil" 17 "github.com/keybase/client/go/kbfs/libcontext" 18 "github.com/keybase/client/go/kbfs/libkbfs" 19 "github.com/pkg/errors" 20 ldberrors "github.com/syndtr/goleveldb/leveldb/errors" 21 "golang.org/x/net/context" 22 ) 23 24 const quarantineXattrName = "com.apple.quarantine" 25 26 // There are much more than this, but a call using Apple's library to set 27 // kLSQuarantineTypeOtherDownload produces this flag. 28 const _kLSQuarantineTypeOtherDownload = "0081;" 29 30 const quarantineAgentName = ";Keybase;" 31 32 func makeQuarantine(timestamp time.Time) []byte { 33 // Omit the identifier in the end. Example: 34 // []byte("0081;5bdb56a3;Keybase;") 35 return append( 36 strconv.AppendInt( 37 []byte(_kLSQuarantineTypeOtherDownload), timestamp.Unix(), 16), 38 quarantineAgentName...) 39 } 40 41 // QuarantineXattrHandler implements bazil.org/fuse/fs.NodeGetxattrer, 42 // bazil.org/fuse/fs.NodeSetxattrer, and bazil.org/fuse/fs.NodeRemovexattrer, 43 // that only handles a single xattr quarantineXattrName (com.apple.quarantine). 44 // For all other requests, we return fuse.ENOTSUP which causes the OS to handle 45 // it by creating and interacting with ._ files. 46 type QuarantineXattrHandler struct { 47 node libkbfs.Node 48 folder *Folder 49 } 50 51 // NewQuarantineXattrHandler returns a handler that handles 52 // com.apple.quarantine, but returns fuse.ENOTSUP for all other xattrs. 53 func NewQuarantineXattrHandler(node libkbfs.Node, folder *Folder, 54 ) XattrHandler { 55 return &QuarantineXattrHandler{ 56 node: node, 57 folder: folder, 58 } 59 } 60 61 var _ XattrHandler = (*QuarantineXattrHandler)(nil) 62 63 // Getxattr implements the fs.NodeGetxattrer interface. 64 func (h *QuarantineXattrHandler) Getxattr(ctx context.Context, 65 req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) (err error) { 66 ctx = h.folder.fs.config.MaybeStartTrace(ctx, "QuarantineXattrHandler.Getxattr", 67 fmt.Sprintf("%s %s", h.node.GetBasename(), req.Name)) 68 defer func() { h.folder.fs.config.MaybeFinishTrace(ctx, err) }() 69 70 h.folder.fs.log.CDebugf(ctx, 71 "QuarantineXattrHandler Getxattr %s %s", h.node.GetBasename(), req.Name) 72 defer func() { err = h.folder.processError(ctx, libkbfs.ReadMode, err) }() 73 74 if req.Name != quarantineXattrName { 75 // The request is not about quarantine. Let the OS fallback to ._ file 76 // based method. 77 return fuse.ENOTSUP 78 } 79 80 xattr, err := h.folder.fs.config.XattrStore().GetXattr( 81 ctx, h.node.GetBlockID(), libkbfs.XattrAppleQuarantine) 82 switch errors.Cause(err) { 83 case nil: 84 if len(xattr) == 0 { 85 return fuse.ENOATTR 86 } 87 resp.Xattr = xattr 88 return nil 89 case ldberrors.ErrNotFound: 90 // We don't have an Xattr value stored locally, so just use a 91 // quarantine value. 92 93 // Stat on the node to get the Mtime. 94 95 // This fits in situation 1 as described in 96 // libkbfs/delayed_cancellation.go 97 if err = libcontext.EnableDelayedCancellationWithGracePeriod(ctx, 98 h.folder.fs.config.DelayedCancellationGracePeriod()); err != nil { 99 return err 100 } 101 de, err := h.folder.fs.config.KBFSOps().Stat(ctx, h.node) 102 if err != nil { 103 if _, ok := err.(idutil.NoSuchNameError); ok { 104 // The node is not found, so just return ENOTSUP 105 return fuse.ENOTSUP 106 } 107 return err 108 } 109 110 resp.Xattr = makeQuarantine(time.Unix(0, de.Mtime)) 111 return nil 112 default: 113 return err 114 } 115 } 116 117 // Setxattr implements the fs.NodeSetxattrer interface. 118 func (h *QuarantineXattrHandler) Setxattr(ctx context.Context, 119 req *fuse.SetxattrRequest) (err error) { 120 ctx = h.folder.fs.config.MaybeStartTrace(ctx, "QuarantineXattrHandler.Setxattr", 121 fmt.Sprintf("%s %s", h.node.GetBasename(), req.Name)) 122 defer func() { h.folder.fs.config.MaybeFinishTrace(ctx, err) }() 123 124 h.folder.fs.log.CDebugf(ctx, "QuarantineXattrHandler Setxattr %s", req.Name) 125 defer func() { err = h.folder.processError(ctx, libkbfs.ReadMode, err) }() 126 127 if req.Name != quarantineXattrName { 128 // The request is not about quarantine. Let the OS fallback to ._ file 129 // based method. 130 return fuse.ENOTSUP 131 } 132 133 return h.folder.fs.config.XattrStore().SetXattr(ctx, 134 h.node.GetBlockID(), libkbfs.XattrAppleQuarantine, req.Xattr) 135 } 136 137 // Removexattr implements the fs.NodeRemovexattrer interface. 138 func (h *QuarantineXattrHandler) Removexattr( 139 ctx context.Context, req *fuse.RemovexattrRequest) error { 140 if req.Name != quarantineXattrName { 141 // The request is not about quarantine. Let the OS fallback to ._ file 142 // based method. 143 return fuse.ENOTSUP 144 } 145 146 return h.folder.fs.config.XattrStore().SetXattr(ctx, 147 h.node.GetBlockID(), libkbfs.XattrAppleQuarantine, nil) 148 }