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  }