github.com/hanwen/go-fuse@v1.0.0/fuse/nodefs/inode.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 import ( 8 "fmt" 9 "log" 10 "sync" 11 12 "github.com/hanwen/go-fuse/fuse" 13 ) 14 15 type 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. 25 type 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 49 func 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. 64 func (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. 70 func (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. 83 func (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. 97 func (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. 111 func (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. 125 func (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. 131 func (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. 143 func (n *Inode) IsDir() bool { 144 return n.children != nil 145 } 146 147 // NewChild adds a new child inode to this inode. 148 func (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. 157 func (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. 167 func (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. 180 type 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. 187 func (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. 199 func (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. 216 func (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. 229 func (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. 239 func (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 257 func (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 272 const initDirSize = 20 273 274 func (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 }