/
fs_common.go
194 lines (167 loc) · 4.49 KB
/
fs_common.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package core
import (
"encoding/binary"
"fmt"
"os"
"sync"
"time"
"unsafe"
"go.uber.org/zap"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
"github.com/oneconcern/datamon/pkg/model"
)
func statFS() (err error) {
// TODO: Find the free space on the device and set the attributes accordingly.
// TODO: Find optimal block size (Default to the one used by underlying FS)
return
}
func formLookupKey(id fuseops.InodeID, childName string) []byte {
i := formKey(id)
c := model.UnsafeStringToBytes(childName)
return append(i, c...)
}
func formKey(id fuseops.InodeID) []byte {
b := make([]byte, unsafe.Sizeof(uint64(0)))
binary.BigEndian.PutUint64(b, uint64(id))
return b
}
// Add the root of the FS into the FS.
func (fs *fsMutable) initRoot() (err error) {
_, found := fs.lookupTree.Get(formKey(fuseops.RootInodeID))
if found {
return
}
err = fs.createNode(
formLookupKey(fuseops.RootInodeID, rootPath),
fuseops.RootInodeID,
rootPath,
nil,
fuseutil.DT_Directory,
true)
return
}
// Run validations before creating a node. Need to take locks before calling.
func (fs *fsMutable) preCreateCheck(parentInode fuseops.InodeID, lk []byte) error {
// check parent exists
key := formKey(parentInode)
e, found := fs.iNodeStore.Get(key)
if !found {
return fuse.ENOENT
}
// parent is a directory
n := e.(*nodeEntry)
if !n.attr.Mode.IsDir() {
return fuse.ENOTDIR
}
// check child name not taken
_, found = fs.lookupTree.Get(lk)
if found {
return fuse.EEXIST
}
return nil
}
func (fs *fsMutable) insertReadDirEntry(id fuseops.InodeID, dirEnt *fuseutil.Dirent) {
if fs.readDirMap[id] == nil {
fs.readDirMap[id] = make(map[fuseops.InodeID]*fuseutil.Dirent)
}
fs.readDirMap[id][dirEnt.Inode] = dirEnt
}
func (fs *fsMutable) insertLookupEntry(id fuseops.InodeID, child string, entry lookupEntry) {
fs.lookupTree, _, _ = fs.lookupTree.Insert(formLookupKey(id, child), entry)
}
// Create a node. Need to hold the locks before calling.
func (fs *fsMutable) createNode(lk []byte, parentINode fuseops.InodeID, childName string,
entry *fuseops.ChildInodeEntry, nodeType fuseutil.DirentType, isRoot bool) error {
// Create lookup key if not already created.
if lk == nil {
lk = formLookupKey(parentINode, childName)
}
var iNodeID fuseops.InodeID
if !isRoot {
iNodeID = fs.iNodeGenerator.allocINode()
} else {
iNodeID = parentINode
}
// lookup
fs.lookupTree, _, _ = fs.lookupTree.Insert(lk, lookupEntry{iNode: iNodeID})
// Default to common case of create file
var linkCount = fileLinkCount
var defaultMode os.FileMode = fileDefaultMode
var defaultSize uint64
if nodeType == fuseutil.DT_Directory {
linkCount = dirLinkCount
defaultMode = dirDefaultMode
defaultSize = dirInitialSize
fs.readDirMap[iNodeID] = make(map[fuseops.InodeID]*fuseutil.Dirent)
} else {
// dont return error as open file will retry this.
file, err := fs.localCache.Create(fmt.Sprint(iNodeID))
if err != nil {
fs.backingFiles[iNodeID] = &file
} else {
fs.l.Error("failed to create backing file",
zap.Error(err),
zap.String("child", childName),
zap.Uint64("parent", uint64(parentINode)))
}
}
d := &fuseutil.Dirent{
Inode: iNodeID,
Name: childName,
Type: nodeType,
}
if !isRoot {
fs.insertReadDirEntry(parentINode, d)
}
ts := time.Now()
attr := fuseops.InodeAttributes{
Size: defaultSize,
Nlink: linkCount,
Mode: defaultMode,
Atime: ts,
Mtime: ts,
Ctime: ts,
Crtime: ts,
Uid: defaultGID,
Gid: defaultUID,
}
//iNode Store
fs.iNodeStore, _, _ = fs.iNodeStore.Insert(formKey(iNodeID), &nodeEntry{
lock: sync.Mutex{},
refCount: 1, // As per spec CreateFileOp
pathToBackingFile: getPathToBackingFile(iNodeID),
attr: attr,
})
if nodeType == fuseutil.DT_Directory {
// Increment parent ref count.
p, _ := fs.iNodeStore.Get(formKey(parentINode))
parentNodeEntry := p.(*nodeEntry)
parentNodeEntry.attr.Nlink++
}
// If return is expected
if entry != nil {
entry.Attributes = attr
entry.EntryExpiration = time.Now().Add(cacheYearLong)
entry.AttributesExpiration = time.Now().Add(cacheYearLong)
entry.Child = iNodeID
}
return nil
}
func getPathToBackingFile(iNode fuseops.InodeID) string {
return fmt.Sprint(uint64(iNode))
}
func shouldDelete(n *nodeEntry) bool {
// LookupCount should be zero.
if n.attr.Mode.IsDir() {
if n.refCount == 0 {
return true
}
} else {
if n.refCount == 0 && n.attr.Nlink == 0 {
return true
}
}
return false
}