github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfuse/file.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 "fmt" 12 "os" 13 "sync" 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/libcontext" 19 "github.com/keybase/client/go/kbfs/libkbfs" 20 "github.com/keybase/client/go/libkb" 21 "golang.org/x/net/context" 22 ) 23 24 type eiCache struct { 25 ei data.EntryInfo 26 reqID string 27 } 28 29 // eiCacheHolder caches the EntryInfo for a particular reqID. It's used for the 30 // Attr call after Create. This should only be used for operations with same 31 // reqID. 32 type eiCacheHolder struct { 33 mu sync.Mutex 34 cache *eiCache 35 } 36 37 func (c *eiCacheHolder) destroy() { 38 c.mu.Lock() 39 defer c.mu.Unlock() 40 c.cache = nil 41 } 42 43 func (c *eiCacheHolder) getAndDestroyIfMatches(reqID string) (ei *data.EntryInfo) { 44 c.mu.Lock() 45 defer c.mu.Unlock() 46 if c.cache != nil && c.cache.reqID == reqID { 47 ei = &c.cache.ei 48 c.cache = nil 49 } 50 return ei 51 } 52 53 func (c *eiCacheHolder) set(reqID string, ei data.EntryInfo) { 54 c.mu.Lock() 55 defer c.mu.Unlock() 56 c.cache = &eiCache{ 57 ei: ei, 58 reqID: reqID, 59 } 60 } 61 62 // File represents KBFS files. 63 type File struct { 64 folder *Folder 65 node libkbfs.Node 66 inode uint64 67 XattrHandler 68 69 eiCache eiCacheHolder 70 } 71 72 var _ fs.Node = (*File)(nil) 73 74 func (f *File) fillAttrWithMode( 75 ctx context.Context, ei *data.EntryInfo, a *fuse.Attr) (err error) { 76 if err = f.folder.fillAttrWithUIDAndWritePerm( 77 ctx, f.node, ei, a); err != nil { 78 return err 79 } 80 a.Mode |= 0400 81 if ei.Type == data.Exec { 82 a.Mode |= 0100 83 } 84 85 a.Inode = f.inode 86 return nil 87 } 88 89 // Attr implements the fs.Node interface for File. 90 func (f *File) Attr(ctx context.Context, a *fuse.Attr) (err error) { 91 ctx = f.folder.fs.config.MaybeStartTrace( 92 ctx, "File.Attr", f.node.GetBasename().String()) 93 defer func() { f.folder.fs.config.MaybeFinishTrace(ctx, err) }() 94 95 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "File Attr") 96 defer func() { err = f.folder.processError(ctx, libkbfs.ReadMode, err) }() 97 98 if reqID, ok := ctx.Value(CtxIDKey).(string); ok { 99 if ei := f.eiCache.getAndDestroyIfMatches(reqID); ei != nil { 100 return f.fillAttrWithMode(ctx, ei, a) 101 } 102 } 103 104 // This fits in situation 1 as described in libkbfs/delayed_cancellation.go 105 err = libcontext.EnableDelayedCancellationWithGracePeriod( 106 ctx, f.folder.fs.config.DelayedCancellationGracePeriod()) 107 if err != nil { 108 return err 109 } 110 111 return f.attr(ctx, a) 112 } 113 114 func (f *File) attr(ctx context.Context, a *fuse.Attr) (err error) { 115 de, err := f.folder.fs.config.KBFSOps().Stat(ctx, f.node) 116 if err != nil { 117 if isNoSuchNameError(err) { 118 return fuse.ESTALE 119 } 120 return err 121 } 122 123 f.node.FillCacheDuration(&a.Valid) 124 125 return f.fillAttrWithMode(ctx, &de, a) 126 } 127 128 var _ fs.NodeAccesser = (*File)(nil) 129 130 // Access implements the fs.NodeAccesser interface for File. This is necessary 131 // for macOS to correctly identify plaintext files as plaintext. If not 132 // implemented, bazil-fuse returns a nil error for every call, so when macOS 133 // checks for executable bit using Access (instead of Attr!), it gets a 134 // success, which makes it think the file is executable, yielding a "Unix 135 // executable" UTI. 136 func (f *File) Access(ctx context.Context, r *fuse.AccessRequest) (err error) { 137 ctx = f.folder.fs.config.MaybeStartTrace( 138 ctx, "File.Access", f.node.GetBasename().String()) 139 defer func() { f.folder.fs.config.MaybeFinishTrace(ctx, err) }() 140 141 if int(r.Uid) != os.Getuid() && 142 // Finder likes to use UID 0 for some operations. osxfuse already allows 143 // ACCESS and GETXATTR requests from root to go through. This allows root 144 // in ACCESS handler. See KBFS-1733 for more details. 145 int(r.Uid) != 0 { 146 // short path: not accessible by anybody other than root or the user who 147 // executed the kbfsfuse process. 148 return fuse.EPERM 149 } 150 151 if r.Mask&03 == 0 { 152 // Since we only check for w and x bits, we can return nil early here. 153 return nil 154 } 155 156 if r.Mask&01 != 0 { 157 ei, err := f.folder.fs.config.KBFSOps().Stat(ctx, f.node) 158 if err != nil { 159 if isNoSuchNameError(err) { 160 return fuse.ESTALE 161 } 162 return err 163 } 164 if ei.Type != data.Exec { 165 return fuse.EPERM 166 } 167 } 168 169 if r.Mask&02 != 0 { 170 iw, err := f.folder.isWriter(ctx) 171 if err != nil { 172 return err 173 } 174 if !iw { 175 return fuse.EPERM 176 } 177 } 178 179 return nil 180 } 181 182 var _ fs.NodeFsyncer = (*File)(nil) 183 184 func (f *File) sync(ctx context.Context) error { 185 f.eiCache.destroy() 186 err := f.folder.fs.config.KBFSOps().SyncAll(ctx, f.node.GetFolderBranch()) 187 if err != nil { 188 return err 189 } 190 191 return nil 192 } 193 194 // Fsync implements the fs.NodeFsyncer interface for File. 195 func (f *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) (err error) { 196 ctx, maybeUnmounting, cancel := wrapCtxWithShorterTimeoutForUnmount(ctx, f.folder.fs.log, int(req.Pid)) 197 defer cancel() 198 if maybeUnmounting { 199 f.folder.fs.log.CInfof(ctx, "Fsync: maybeUnmounting=true") 200 } 201 202 ctx = f.folder.fs.config.MaybeStartTrace( 203 ctx, "File.Fsync", f.node.GetBasename().String()) 204 defer func() { f.folder.fs.config.MaybeFinishTrace(ctx, err) }() 205 206 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "File Fsync") 207 defer func() { err = f.folder.processError(ctx, libkbfs.WriteMode, err) }() 208 209 if !maybeUnmounting { 210 // This fits in situation 1 as described in 211 // libkbfs/delayed_cancellation.go 212 err = libcontext.EnableDelayedCancellationWithGracePeriod( 213 ctx, f.folder.fs.config.DelayedCancellationGracePeriod()) 214 if err != nil { 215 return err 216 } 217 } 218 219 return f.sync(ctx) 220 } 221 222 var _ fs.Handle = (*File)(nil) 223 224 var _ fs.HandleReader = (*File)(nil) 225 226 // Read implements the fs.HandleReader interface for File. 227 func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, 228 resp *fuse.ReadResponse) (err error) { 229 off := req.Offset 230 sz := cap(resp.Data) 231 ctx = f.folder.fs.config.MaybeStartTrace(ctx, "File.Read", 232 fmt.Sprintf("%s off=%d sz=%d", f.node.GetBasename(), off, sz)) 233 defer func() { f.folder.fs.config.MaybeFinishTrace(ctx, err) }() 234 235 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "File Read off=%d sz=%d", off, sz) 236 defer func() { err = f.folder.processError(ctx, libkbfs.ReadMode, err) }() 237 238 n, err := f.folder.fs.config.KBFSOps().Read( 239 ctx, f.node, resp.Data[:sz], off) 240 if err != nil { 241 return err 242 } 243 resp.Data = resp.Data[:n] 244 return nil 245 } 246 247 var _ fs.HandleWriter = (*File)(nil) 248 249 // Write implements the fs.HandleWriter interface for File. 250 func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, 251 resp *fuse.WriteResponse) (err error) { 252 sz := len(req.Data) 253 ctx = f.folder.fs.config.MaybeStartTrace(ctx, "File.Write", 254 fmt.Sprintf("%s sz=%d", f.node.GetBasename(), sz)) 255 defer func() { f.folder.fs.config.MaybeFinishTrace(ctx, err) }() 256 257 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "File Write sz=%d ", sz) 258 defer func() { err = f.folder.processError(ctx, libkbfs.WriteMode, err) }() 259 260 f.eiCache.destroy() 261 if err := f.folder.fs.config.KBFSOps().Write( 262 ctx, f.node, req.Data, req.Offset); err != nil { 263 return err 264 } 265 resp.Size = len(req.Data) 266 return nil 267 } 268 269 var _ fs.NodeSetattrer = (*File)(nil) 270 271 // Setattr implements the fs.NodeSetattrer interface for File. 272 func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, 273 resp *fuse.SetattrResponse) (err error) { 274 valid := req.Valid 275 ctx = f.folder.fs.config.MaybeStartTrace(ctx, "File.SetAttr", 276 fmt.Sprintf("%s %s", f.node.GetBasename(), valid)) 277 defer func() { f.folder.fs.config.MaybeFinishTrace(ctx, err) }() 278 279 f.folder.fs.vlog.CLogf(ctx, libkb.VLog1, "File SetAttr %s", valid) 280 defer func() { err = f.folder.processError(ctx, libkbfs.WriteMode, err) }() 281 282 f.eiCache.destroy() 283 284 if valid.Size() { 285 if err := f.folder.fs.config.KBFSOps().Truncate( 286 ctx, f.node, req.Size); err != nil { 287 return err 288 } 289 valid &^= fuse.SetattrSize 290 } 291 292 if valid.Mode() { 293 // Unix has 3 exec bits, KBFS has one; we follow the user-exec bit. 294 exec := req.Mode&0100 != 0 295 err := f.folder.fs.config.KBFSOps().SetEx( 296 ctx, f.node, exec) 297 if err != nil { 298 return err 299 } 300 valid &^= fuse.SetattrMode 301 } 302 303 if valid.Mtime() { 304 err := f.folder.fs.config.KBFSOps().SetMtime( 305 ctx, f.node, &req.Mtime) 306 if err != nil { 307 return err 308 } 309 valid &^= fuse.SetattrMtime | fuse.SetattrMtimeNow 310 } 311 312 if valid.Uid() || valid.Gid() { 313 // You can't set the UID/GID on KBFS files, but we don't want 314 // to return ENOSYS because that causes scary warnings on some 315 // programs like mv. Instead ignore it, print a debug 316 // message, and advertise this behavior on the 317 // "understand_kbfs" doc online. 318 f.folder.fs.vlog.CLogf( 319 ctx, libkb.VLog1, "Ignoring unsupported attempt to set "+ 320 "the UID/GID on a file") 321 valid &^= fuse.SetattrUid | fuse.SetattrGid 322 } 323 324 // KBFS has no concept of persistent atime; explicitly don't handle it 325 valid &^= fuse.SetattrAtime | fuse.SetattrAtimeNow 326 327 // things we don't need to explicitly handle 328 valid &^= fuse.SetattrLockOwner | fuse.SetattrHandle 329 330 // KBFS has no concept of chflags(2); explicitly ignore those 331 valid &^= fuse.SetattrFlags 332 333 if valid != 0 { 334 // don't let an unhandled operation slip by without error 335 f.folder.fs.log.CInfof(ctx, "Setattr did not handle %v", valid) 336 return fuse.ENOSYS 337 } 338 339 return f.attr(ctx, &resp.Attr) 340 } 341 342 var _ fs.NodeForgetter = (*File)(nil) 343 344 // Forget kernel reference to this node. 345 func (f *File) Forget() { 346 f.eiCache.destroy() 347 f.folder.forgetNode(f.node) 348 }