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 5package nodefs 6 7import ( 8 "fmt" 9 "log" 10 "sync" 11 12 "github.com/hanwen/go-fuse/v2/fuse" 13) 14 15type parentData struct { 16 parent *Inode 17 name string 18} 19 20// An Inode reflects the kernel's idea of the inode. Inodes have IDs 21// that are communicated to the kernel, and they have a tree 22// structure: a directory Inode may contain named children. Each 23// Inode object is paired with a Node object, which file system 24// implementers should supply. 25type Inode struct { 26 handled handled 27 28 // Number of open files and its protection. 29 openFilesMutex sync.Mutex 30 openFiles []*openedFile 31 32 fsInode Node 33 34 // Each inode belongs to exactly one fileSystemMount. This 35 // pointer is constant during the lifetime, except upon 36 // Unmount() when it is set to nil. 37 mount *fileSystemMount 38 39 // All data below is protected by treeLock. 40 children map[string]*Inode 41 // Due to hard links, an Inode can have many parents. 42 parents map[parentData]struct{} 43 44 // Non-nil if this inode is a mountpoint, ie. the Root of a 45 // NodeFileSystem. 46 mountPoint *fileSystemMount 47} 48 49func newInode(isDir bool, fsNode Node) *Inode { 50 me := new(Inode) 51 me.parents = map[parentData]struct{}{} 52 if isDir { 53 me.children = make(map[string]*Inode, initDirSize) 54 } 55 me.fsInode = fsNode 56 me.fsInode.SetInode(me) 57 return me 58} 59 60// public methods. 61 62// Print the inode. The default print method may not be used for 63// debugging, as dumping the map requires synchronization. 64func (n *Inode) String() string { 65 66 return fmt.Sprintf("node{%d}", n.handled.handle) 67} 68 69// Returns any open file, preferably a r/w one. 70func (n *Inode) AnyFile() (file File) { 71 n.openFilesMutex.Lock() 72 for _, f := range n.openFiles { 73 if file == nil || f.WithFlags.OpenFlags&fuse.O_ANYWRITE != 0 { 74 file = f.WithFlags.File 75 } 76 } 77 n.openFilesMutex.Unlock() 78 79 return file 80} 81 82// Children returns all children of this inode. 83func (n *Inode) Children() (out map[string]*Inode) { 84 n.mount.treeLock.RLock() 85 out = make(map[string]*Inode, len(n.children)) 86 for k, v := range n.children { 87 out[k] = v 88 } 89 n.mount.treeLock.RUnlock() 90 91 return out 92} 93 94// Parent returns a random parent and the name this inode has under this parent. 95// This function can be used to walk up the directory tree. It will not cross 96// sub-mounts. 97func (n *Inode) Parent() (parent *Inode, name string) { 98 if n.mountPoint != nil { 99 return nil, "" 100 } 101 n.mount.treeLock.RLock() 102 defer n.mount.treeLock.RUnlock() 103 for k := range n.parents { 104 return k.parent, k.name 105 } 106 return nil, "" 107} 108 109// FsChildren returns all the children from the same filesystem. It 110// will skip mountpoints. 111func (n *Inode) FsChildren() (out map[string]*Inode) { 112 n.mount.treeLock.RLock() 113 out = map[string]*Inode{} 114 for k, v := range n.children { 115 if v.mount == n.mount { 116 out[k] = v 117 } 118 } 119 n.mount.treeLock.RUnlock() 120 121 return out 122} 123 124// Node returns the file-system specific node. 125func (n *Inode) Node() Node { 126 return n.fsInode 127} 128 129// Files() returns an opens file that have bits in common with the 130// give mask. Use mask==0 to return all files. 131func (n *Inode) Files(mask uint32) (files []WithFlags) { 132 n.openFilesMutex.Lock() 133 for _, f := range n.openFiles { 134 if mask == 0 || f.WithFlags.OpenFlags&mask != 0 { 135 files = append(files, f.WithFlags) 136 } 137 } 138 n.openFilesMutex.Unlock() 139 return files 140} 141 142// IsDir returns true if this is a directory. 143func (n *Inode) IsDir() bool { 144 return n.children != nil 145} 146 147// NewChild adds a new child inode to this inode. 148func (n *Inode) NewChild(name string, isDir bool, fsi Node) *Inode { 149 ch := newInode(isDir, fsi) 150 ch.mount = n.mount 151 n.AddChild(name, ch) 152 return ch 153} 154 155// GetChild returns a child inode with the given name, or nil if it 156// does not exist. 157func (n *Inode) GetChild(name string) (child *Inode) { 158 n.mount.treeLock.RLock() 159 child = n.children[name] 160 n.mount.treeLock.RUnlock() 161 162 return child 163} 164 165// AddChild adds a child inode. The parent inode must be a directory 166// node. 167func (n *Inode) AddChild(name string, child *Inode) { 168 if child == nil { 169 log.Panicf("adding nil child as %q", name) 170 } 171 n.mount.treeLock.Lock() 172 n.addChild(name, child) 173 n.mount.treeLock.Unlock() 174} 175 176// TreeWatcher is an additional interface that Nodes can implement. 177// If they do, the OnAdd and OnRemove are called for operations on the 178// file system tree. These functions run under a lock, so they should 179// not do blocking operations. 180type TreeWatcher interface { 181 OnAdd(parent *Inode, name string) 182 OnRemove(parent *Inode, name string) 183} 184 185// RmChild removes an inode by name, and returns it. It returns nil if 186// child does not exist. 187func (n *Inode) RmChild(name string) (ch *Inode) { 188 n.mount.treeLock.Lock() 189 ch = n.rmChild(name) 190 n.mount.treeLock.Unlock() 191 return 192} 193 194////////////////////////////////////////////////////////////// 195// private 196 197// addChild adds "child" to our children under name "name". 198// Must be called with treeLock for the mount held. 199func (n *Inode) addChild(name string, child *Inode) { 200 if paranoia { 201 ch := n.children[name] 202 if ch != nil { 203 log.Panicf("Already have an Inode with same name: %v: %v", name, ch) 204 } 205 } 206 n.children[name] = child 207 child.parents[parentData{n, name}] = struct{}{} 208 if w, ok := child.Node().(TreeWatcher); ok && child.mountPoint == nil { 209 w.OnAdd(n, name) 210 } 211} 212 213// rmChild throws out child "name". This means (1) deleting "name" from our 214// "children" map and (2) deleting ourself from the child's "parents" map. 215// Must be called with treeLock for the mount held. 216func (n *Inode) rmChild(name string) *Inode { 217 ch := n.children[name] 218 if ch != nil { 219 delete(n.children, name) 220 delete(ch.parents, parentData{n, name}) 221 if w, ok := ch.Node().(TreeWatcher); ok && ch.mountPoint == nil { 222 w.OnRemove(n, name) 223 } 224 } 225 return ch 226} 227 228// Can only be called on untouched root inodes. 229func (n *Inode) mountFs(opts *Options) { 230 n.mountPoint = &fileSystemMount{ 231 openFiles: newPortableHandleMap(), 232 mountInode: n, 233 options: opts, 234 } 235 n.mount = n.mountPoint 236} 237 238// Must be called with treeLock held. 239func (n *Inode) canUnmount() bool { 240 for _, v := range n.children { 241 if v.mountPoint != nil { 242 // This access may be out of date, but it is no 243 // problem to err on the safe side. 244 return false 245 } 246 if !v.canUnmount() { 247 return false 248 } 249 } 250 251 n.openFilesMutex.Lock() 252 ok := len(n.openFiles) == 0 253 n.openFilesMutex.Unlock() 254 return ok 255} 256 257func (n *Inode) getMountDirEntries() (out []fuse.DirEntry) { 258 n.mount.treeLock.RLock() 259 for k, v := range n.children { 260 if v.mountPoint != nil { 261 out = append(out, fuse.DirEntry{ 262 Name: k, 263 Mode: fuse.S_IFDIR, 264 }) 265 } 266 } 267 n.mount.treeLock.RUnlock() 268 269 return out 270} 271 272const initDirSize = 20 273 274func (n *Inode) verify(cur *fileSystemMount) { 275 n.handled.verify() 276 if n.mountPoint != nil { 277 if n != n.mountPoint.mountInode { 278 log.Panicf("mountpoint mismatch %v %v", n, n.mountPoint.mountInode) 279 } 280 cur = n.mountPoint 281 282 cur.treeLock.Lock() 283 defer cur.treeLock.Unlock() 284 } 285 if n.mount != cur { 286 log.Panicf("n.mount not set correctly %v %v", n.mount, cur) 287 } 288 289 for nm, ch := range n.children { 290 if ch == nil { 291 log.Panicf("Found nil child: %q", nm) 292 } 293 ch.verify(cur) 294 } 295} 296