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