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 "log" 9 "sync" 10 "unsafe" 11 12 "github.com/hanwen/go-fuse/v2/fuse" 13) 14 15// openedFile stores either an open dir or an open file. 16type openedFile struct { 17 handled 18 19 WithFlags 20 21 dir *connectorDir 22} 23 24type fileSystemMount struct { 25 // Node that we were mounted on. 26 mountInode *Inode 27 28 // Parent to the mountInode. 29 parentInode *Inode 30 31 // Options for the mount. 32 options *Options 33 34 // Protects the "children" and "parents" hashmaps of the inodes 35 // within the mount. 36 // treeLock should be acquired before openFilesLock. 37 // 38 // If multiple treeLocks must be acquired, the treeLocks 39 // closer to the root must be acquired first. 40 treeLock sync.RWMutex 41 42 // Manage filehandles of open files. 43 openFiles handleMap 44 45 Debug bool 46 47 connector *FileSystemConnector 48} 49 50// Must called with lock for parent held. 51func (m *fileSystemMount) mountName() string { 52 for k, v := range m.parentInode.children { 53 if m.mountInode == v { 54 return k 55 } 56 } 57 panic("not found") 58} 59 60func (m *fileSystemMount) setOwner(attr *fuse.Attr) { 61 if m.options.Owner != nil { 62 attr.Owner = *m.options.Owner 63 } 64} 65 66func (m *fileSystemMount) fillEntry(out *fuse.EntryOut) { 67 out.SetEntryTimeout(m.options.EntryTimeout) 68 out.SetAttrTimeout(m.options.AttrTimeout) 69 m.setOwner(&out.Attr) 70 if out.Mode&fuse.S_IFDIR == 0 && out.Nlink == 0 { 71 out.Nlink = 1 72 } 73} 74 75func (m *fileSystemMount) fillAttr(out *fuse.AttrOut, nodeId uint64) { 76 out.SetTimeout(m.options.AttrTimeout) 77 m.setOwner(&out.Attr) 78 if out.Ino == 0 { 79 out.Ino = nodeId 80 } 81} 82 83func (m *fileSystemMount) getOpenedFile(h uint64) *openedFile { 84 var b *openedFile 85 if h != 0 { 86 b = (*openedFile)(unsafe.Pointer(m.openFiles.Decode(h))) 87 } 88 89 if b != nil && m.connector.debug && b.WithFlags.Description != "" { 90 log.Printf("File %d = %q", h, b.WithFlags.Description) 91 } 92 return b 93} 94 95func (m *fileSystemMount) unregisterFileHandle(handle uint64, node *Inode) *openedFile { 96 _, obj := m.openFiles.Forget(handle, 1) 97 opened := (*openedFile)(unsafe.Pointer(obj)) 98 node.openFilesMutex.Lock() 99 idx := -1 100 for i, v := range node.openFiles { 101 if v == opened { 102 idx = i 103 break 104 } 105 } 106 107 l := len(node.openFiles) 108 if idx == l-1 { 109 node.openFiles[idx] = nil 110 } else { 111 node.openFiles[idx] = node.openFiles[l-1] 112 } 113 node.openFiles = node.openFiles[:l-1] 114 node.openFilesMutex.Unlock() 115 116 return opened 117} 118 119// registerFileHandle registers f or dir to have a handle. 120// 121// The handle is then used as file-handle in communications with kernel. 122// 123// If dir != nil the handle is registered for OpenDir and the inner file (see 124// below) must be nil. If dir = nil the handle is registered for regular open & 125// friends. 126// 127// f can be nil, or a WithFlags that leads to File=nil. For !OpenDir, if that 128// is the case, returned handle will be 0 to indicate a handleless open, and 129// the filesystem operations on the opened file will be routed to be served by 130// the node. 131// 132// other arguments: 133// 134// node - Inode for which f or dir were opened, 135// flags - file open flags, like O_RDWR. 136func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f File, flags uint32) (handle uint64, opened *openedFile) { 137 b := &openedFile{ 138 dir: dir, 139 WithFlags: WithFlags{ 140 File: f, 141 OpenFlags: flags, 142 }, 143 } 144 145 for { 146 withFlags, ok := f.(*WithFlags) 147 if !ok { 148 break 149 } 150 151 b.WithFlags.File = withFlags.File 152 b.WithFlags.FuseFlags |= withFlags.FuseFlags 153 b.WithFlags.Description += withFlags.Description 154 f = withFlags.File 155 } 156 157 // don't allow both dir and file 158 if dir != nil && b.WithFlags.File != nil { 159 panic("registerFileHandle: both dir and file are set.") 160 } 161 162 if b.WithFlags.File == nil && dir == nil { 163 // it was just WithFlags{...}, but the file itself is nil 164 return 0, b 165 } 166 167 if b.WithFlags.File != nil { 168 b.WithFlags.File.SetInode(node) 169 } 170 171 node.openFilesMutex.Lock() 172 node.openFiles = append(node.openFiles, b) 173 handle, _ = m.openFiles.Register(&b.handled) 174 node.openFilesMutex.Unlock() 175 return handle, b 176} 177 178// Creates a return entry for a non-existent path. 179func (m *fileSystemMount) negativeEntry(out *fuse.EntryOut) bool { 180 if m.options.NegativeTimeout > 0.0 { 181 out.NodeId = 0 182 out.SetEntryTimeout(m.options.NegativeTimeout) 183 return true 184 } 185 return false 186} 187