github.com/hanwen/go-fuse@v1.0.0/fuse/nodefs/fsconnector.go (about) 1 // Copyright 2016 the Go-FUSE Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package nodefs 6 7 // This file contains the internal logic of the 8 // FileSystemConnector. The functions for satisfying the raw interface 9 // are in fsops.go 10 11 import ( 12 "log" 13 "path/filepath" 14 "strings" 15 "sync" 16 "time" 17 "unsafe" 18 19 "github.com/hanwen/go-fuse/fuse" 20 ) 21 22 // Tests should set to true. 23 var paranoia = false 24 25 // FileSystemConnector translates the raw FUSE protocol (serialized 26 // structs of uint32/uint64) to operations on Go objects representing 27 // files and directories. 28 type FileSystemConnector struct { 29 debug bool 30 31 // Callbacks for talking back to the kernel. 32 server *fuse.Server 33 34 // Translate between uint64 handles and *Inode. 35 inodeMap handleMap 36 37 // The root of the FUSE file system. 38 rootNode *Inode 39 40 // This lock prevents Lookup() and Forget() from running concurrently. 41 // Locking at this level is a big hammer, but makes sure we don't return 42 // forgotten nodes to the kernel. Problems solved by this lock: 43 // https://github.com/hanwen/go-fuse/issues/168 44 // https://github.com/rfjakob/gocryptfs/issues/322 45 lookupLock sync.Mutex 46 } 47 48 // NewOptions generates FUSE options that correspond to libfuse's 49 // defaults. 50 func NewOptions() *Options { 51 return &Options{ 52 NegativeTimeout: 0, 53 AttrTimeout: time.Second, 54 EntryTimeout: time.Second, 55 Owner: fuse.CurrentOwner(), 56 } 57 } 58 59 // NewFileSystemConnector creates a FileSystemConnector with the given 60 // options. 61 func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) { 62 c = new(FileSystemConnector) 63 if opts == nil { 64 opts = NewOptions() 65 } 66 c.inodeMap = newPortableHandleMap() 67 c.rootNode = newInode(true, root) 68 69 c.verify() 70 c.mountRoot(opts) 71 72 // FUSE does not issue a LOOKUP for 1 (obviously), but it does 73 // issue a forget. This lookupUpdate is to make the counts match. 74 c.lookupUpdate(c.rootNode) 75 c.debug = opts.Debug 76 77 return c 78 } 79 80 // Server returns the fuse.Server that talking to the kernel. 81 func (c *FileSystemConnector) Server() *fuse.Server { 82 return c.server 83 } 84 85 // SetDebug toggles printing of debug information. This function is 86 // deprecated. Set the Debug option in the Options struct instead. 87 func (c *FileSystemConnector) SetDebug(debug bool) { 88 c.debug = debug 89 } 90 91 // This verifies invariants of the data structure. This routine 92 // acquires tree locks as it walks the inode tree. 93 func (c *FileSystemConnector) verify() { 94 if !paranoia { 95 return 96 } 97 root := c.rootNode 98 root.verify(c.rootNode.mountPoint) 99 } 100 101 // childLookup fills entry information for a newly created child inode 102 func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) { 103 n.Node().GetAttr(&out.Attr, nil, context) 104 n.mount.fillEntry(out) 105 out.NodeId, out.Generation = c.fsConn().lookupUpdate(n) 106 if out.Ino == 0 { 107 out.Ino = out.NodeId 108 } 109 if out.Nlink == 0 { 110 // With Nlink == 0, newer kernels will refuse link 111 // operations. 112 out.Nlink = 1 113 } 114 } 115 116 func (c *rawBridge) toInode(nodeid uint64) *Inode { 117 if nodeid == fuse.FUSE_ROOT_ID { 118 return c.rootNode 119 } 120 i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid))) 121 return i 122 } 123 124 // Must run outside treeLock. Returns the nodeId and generation. 125 func (c *FileSystemConnector) lookupUpdate(node *Inode) (id, generation uint64) { 126 id, generation = c.inodeMap.Register(&node.handled) 127 c.verify() 128 return 129 } 130 131 // forgetUpdate decrements the reference counter for "nodeID" by "forgetCount". 132 // Must run outside treeLock. 133 func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) { 134 if nodeID == fuse.FUSE_ROOT_ID { 135 c.rootNode.Node().OnUnmount() 136 137 // We never got a lookup for root, so don't try to 138 // forget root. 139 return 140 } 141 142 // Prevent concurrent modification of the tree while we are processing 143 // the FORGET 144 node := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeID))) 145 node.mount.treeLock.Lock() 146 defer node.mount.treeLock.Unlock() 147 148 if forgotten, _ := c.inodeMap.Forget(nodeID, forgetCount); forgotten { 149 if len(node.children) > 0 || !node.Node().Deletable() || 150 node == c.rootNode || node.mountPoint != nil { 151 // We cannot forget a directory that still has children as these 152 // would become unreachable. 153 return 154 } 155 // We have to remove ourself from all parents. 156 // Create a copy of node.parents so we can safely iterate over it 157 // while modifying the original. 158 parents := make(map[parentData]struct{}, len(node.parents)) 159 for k, v := range node.parents { 160 parents[k] = v 161 } 162 163 for p := range parents { 164 // This also modifies node.parents 165 p.parent.rmChild(p.name) 166 } 167 168 node.fsInode.OnForget() 169 } 170 // TODO - try to drop children even forget was not successful. 171 c.verify() 172 } 173 174 // InodeCount returns the number of inodes registered with the kernel. 175 func (c *FileSystemConnector) InodeHandleCount() int { 176 return c.inodeMap.Count() 177 } 178 179 // Finds a node within the currently known inodes, returns the last 180 // known node and the remaining unknown path components. If parent is 181 // nil, start from FUSE mountpoint. 182 func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) { 183 if parent == nil { 184 parent = c.rootNode 185 } 186 if fullPath == "" { 187 return parent, nil 188 } 189 190 sep := string(filepath.Separator) 191 fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep) 192 comps := strings.Split(fullPath, sep) 193 194 node := parent 195 if node.mountPoint == nil { 196 node.mount.treeLock.RLock() 197 defer node.mount.treeLock.RUnlock() 198 } 199 200 for i, component := range comps { 201 if len(component) == 0 { 202 continue 203 } 204 205 if node.mountPoint != nil { 206 node.mount.treeLock.RLock() 207 defer node.mount.treeLock.RUnlock() 208 } 209 210 next := node.children[component] 211 if next == nil { 212 return node, comps[i:] 213 } 214 node = next 215 } 216 217 return node, nil 218 } 219 220 // Follows the path from the given parent, doing lookups as 221 // necessary. The path should be '/' separated without leading slash. 222 func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode { 223 if path == "" { 224 return parent 225 } 226 227 components := strings.Split(path, "/") 228 for _, r := range components { 229 var a fuse.Attr 230 // This will not affect inode ID lookup counts, which 231 // are only update in response to kernel requests. 232 var dummy fuse.InHeader 233 child, _ := c.internalLookup(&a, parent, r, &dummy) 234 if child == nil { 235 return nil 236 } 237 238 parent = child 239 } 240 241 return parent 242 } 243 244 func (c *FileSystemConnector) mountRoot(opts *Options) { 245 c.rootNode.mountFs(opts) 246 c.rootNode.mount.connector = c 247 c.verify() 248 } 249 250 // Mount() generates a synthetic directory node, and mounts the file 251 // system there. If opts is nil, the mount options of the root file 252 // system are inherited. The encompassing filesystem should pretend 253 // the mount point does not exist. 254 // 255 // It returns ENOENT if the directory containing the mount point does 256 // not exist, and EBUSY if the intended mount point already exists. 257 func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status { 258 node, code := c.lockMount(parent, name, root, opts) 259 if !code.Ok() { 260 return code 261 } 262 263 node.Node().OnMount(c) 264 return code 265 } 266 267 func (c *FileSystemConnector) lockMount(parent *Inode, name string, root Node, opts *Options) (*Inode, fuse.Status) { 268 defer c.verify() 269 parent.mount.treeLock.Lock() 270 defer parent.mount.treeLock.Unlock() 271 node := parent.children[name] 272 if node != nil { 273 return nil, fuse.EBUSY 274 } 275 276 node = newInode(true, root) 277 if opts == nil { 278 opts = c.rootNode.mountPoint.options 279 } 280 281 node.mountFs(opts) 282 node.mount.connector = c 283 parent.addChild(name, node) 284 285 node.mountPoint.parentInode = parent 286 if c.debug { 287 log.Printf("Mount %T on subdir %s, parent i%d", node, 288 name, c.inodeMap.Handle(&parent.handled)) 289 } 290 return node, fuse.OK 291 } 292 293 // Unmount() tries to unmount the given inode. It returns EINVAL if the 294 // path does not exist, or is not a mount point, and EBUSY if there 295 // are open files or submounts below this node. 296 func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status { 297 // TODO - racy. 298 if node.mountPoint == nil { 299 log.Println("not a mountpoint:", c.inodeMap.Handle(&node.handled)) 300 return fuse.EINVAL 301 } 302 303 nodeID := c.inodeMap.Handle(&node.handled) 304 305 // Must lock parent to update tree structure. 306 parentNode := node.mountPoint.parentInode 307 parentNode.mount.treeLock.Lock() 308 defer parentNode.mount.treeLock.Unlock() 309 310 mount := node.mountPoint 311 name := node.mountPoint.mountName() 312 if mount.openFiles.Count() > 0 { 313 return fuse.EBUSY 314 } 315 316 node.mount.treeLock.Lock() 317 defer node.mount.treeLock.Unlock() 318 319 if mount.mountInode != node { 320 log.Panicf("got two different mount inodes %v vs %v", 321 c.inodeMap.Handle(&mount.mountInode.handled), 322 c.inodeMap.Handle(&node.handled)) 323 } 324 325 if !node.canUnmount() { 326 return fuse.EBUSY 327 } 328 329 delete(parentNode.children, name) 330 node.Node().OnUnmount() 331 332 parentId := c.inodeMap.Handle(&parentNode.handled) 333 if parentNode == c.rootNode { 334 // TODO - test coverage. Currently covered by zipfs/multizip_test.go 335 parentId = fuse.FUSE_ROOT_ID 336 } 337 338 // We have to wait until the kernel has forgotten the 339 // mountpoint, so the write to node.mountPoint is no longer 340 // racy. 341 mount.treeLock.Unlock() 342 parentNode.mount.treeLock.Unlock() 343 code := c.server.DeleteNotify(parentId, nodeID, name) 344 345 if code.Ok() { 346 delay := 100 * time.Microsecond 347 348 for { 349 // This operation is rare, so we kludge it to avoid 350 // contention. 351 time.Sleep(delay) 352 delay = delay * 2 353 if !c.inodeMap.Has(nodeID) { 354 break 355 } 356 357 if delay >= time.Second { 358 // We limit the wait at one second. If 359 // it takes longer, something else is 360 // amiss, and we would be waiting forever. 361 log.Println("kernel did not issue FORGET for node on Unmount.") 362 break 363 } 364 } 365 366 } 367 368 parentNode.mount.treeLock.Lock() 369 mount.treeLock.Lock() 370 mount.mountInode = nil 371 node.mountPoint = nil 372 373 return fuse.OK 374 } 375 376 // FileNotify notifies the kernel that data and metadata of this inode 377 // has changed. After this call completes, the kernel will issue a 378 // new GetAttr requests for metadata and new Read calls for content. 379 // Use negative offset for metadata-only invalidation, and zero-length 380 // for invalidating all content. 381 func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) fuse.Status { 382 var nID uint64 383 if node == c.rootNode { 384 nID = fuse.FUSE_ROOT_ID 385 } else { 386 nID = c.inodeMap.Handle(&node.handled) 387 } 388 389 if nID == 0 { 390 return fuse.OK 391 } 392 return c.server.InodeNotify(nID, off, length) 393 } 394 395 // FileNotifyStoreCache notifies the kernel about changed data of the inode. 396 // 397 // This call is similar to FileNotify, but instead of only invalidating a data 398 // region, it puts updated data directly to the kernel cache: 399 // 400 // After this call completes, the kernel has put updated data into the inode's cache, 401 // and will use data from that cache for non direct-IO reads from the inode 402 // in corresponding data region. After kernel's cache data is evicted, the kernel 403 // will have to issue new Read calls on user request to get data content. 404 // 405 // ENOENT is returned if the kernel does not currently have entry for this 406 // inode in its dentry cache. 407 func (c *FileSystemConnector) FileNotifyStoreCache(node *Inode, off int64, data []byte) fuse.Status { 408 var nID uint64 409 if node == c.rootNode { 410 nID = fuse.FUSE_ROOT_ID 411 } else { 412 nID = c.inodeMap.Handle(&node.handled) 413 } 414 415 if nID == 0 { 416 // the kernel does not currently know about this inode. 417 return fuse.ENOENT 418 } 419 return c.server.InodeNotifyStoreCache(nID, off, data) 420 } 421 422 // FileRetrieveCache retrieves data from kernel's inode cache. 423 // 424 // This call retrieves data from kernel's inode cache @ offset and up to 425 // len(dest) bytes. If kernel cache has fewer consecutive data starting at 426 // offset, that fewer amount is returned. In particular if inode data at offset 427 // is not cached (0, OK) is returned. 428 // 429 // If the kernel does not currently have entry for this inode in its dentry 430 // cache (0, OK) is still returned, pretending that the inode could be known to 431 // the kernel, but kernel's inode cache is empty. 432 func (c *FileSystemConnector) FileRetrieveCache(node *Inode, off int64, dest []byte) (n int, st fuse.Status) { 433 var nID uint64 434 if node == c.rootNode { 435 nID = fuse.FUSE_ROOT_ID 436 } else { 437 nID = c.inodeMap.Handle(&node.handled) 438 } 439 440 if nID == 0 { 441 // the kernel does not currently know about this inode. 442 // -> we can pretend that its cache for the inode is empty. 443 return 0, fuse.OK 444 } 445 return c.server.InodeRetrieveCache(nID, off, dest) 446 } 447 448 // EntryNotify makes the kernel forget the entry data from the given 449 // name from a directory. After this call, the kernel will issue a 450 // new lookup request for the given name when necessary. No filesystem 451 // related locks should be held when calling this. 452 func (c *FileSystemConnector) EntryNotify(node *Inode, name string) fuse.Status { 453 var nID uint64 454 if node == c.rootNode { 455 nID = fuse.FUSE_ROOT_ID 456 } else { 457 nID = c.inodeMap.Handle(&node.handled) 458 } 459 460 if nID == 0 { 461 return fuse.OK 462 } 463 return c.server.EntryNotify(nID, name) 464 } 465 466 // DeleteNotify signals to the kernel that the named entry in dir for 467 // the child disappeared. No filesystem related locks should be held 468 // when calling this. 469 func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) fuse.Status { 470 var nID uint64 471 472 if dir == c.rootNode { 473 nID = fuse.FUSE_ROOT_ID 474 } else { 475 nID = c.inodeMap.Handle(&dir.handled) 476 } 477 478 if nID == 0 { 479 return fuse.OK 480 } 481 482 chId := c.inodeMap.Handle(&child.handled) 483 484 return c.server.DeleteNotify(nID, chId, name) 485 }