-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathmemfs.go
238 lines (193 loc) · 6.85 KB
/
memfs.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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// Package memfs implements an in-memory replicated file system with Bazil
// FUSE and whose primary purpose is to implement all FUSE interfaces.
package memfs
import (
"fmt"
"os"
"strings"
"sync"
"golang.org/x/net/context"
"bazil.org/fuse"
"bazil.org/fuse/fs"
"github.com/bbengfort/sequence"
)
const (
programName = "memfs"
majorVersion = 0
minorVersion = 2
microVersion = 4
releaseLevel = "final"
minBlockSize = uint64(512)
)
var (
logger *Logger
)
func init() {
logger, _ = InitLogger("", "DEBUG")
}
//===========================================================================
// New MemFS File System
//===========================================================================
// New MemFS file system created from a mount path and a configuration. This
// is the entry point for creating and launching all in-memory file systems.
func New(mount string, config *Config) *FileSystem {
// Set the Log Level
if config.Level != "" {
logger, _ = InitLogger("", strings.ToUpper(config.Level))
}
// Create the file system
fs := new(FileSystem)
fs.MountPoint = mount
fs.Config = config
fs.Sequence, _ = sequence.New()
// Set the UID and GID of the file system
fs.uid = uint32(os.Geteuid())
fs.gid = uint32(os.Getegid())
// Set other system flags from the configuration
fs.readonly = fs.Config.ReadOnly
// Create the root directory
fs.root = new(Dir)
fs.root.Init("/", 0755, nil, fs)
// Return the file system
return fs
}
//===========================================================================
// File System Struct
//===========================================================================
// FileSystem implements the fuse.FS* interfaces as well as providing a
// lockable interaction structure to ensure concurrent accesses succeed.
type FileSystem struct {
sync.Mutex // FileSystem can be locked and unlocked
MountPoint string // Path to the mount location on disk
Config *Config // Configuration of the FileSystem
Conn *fuse.Conn // Hook to the FUSE connection object
Sequence *sequence.Sequence // Monotonically increasing counter for inodes
root *Dir // The root of the file system
uid uint32 // The user id of the process running the file system
gid uint32 // The group id of the process running the file system
nfiles uint64 // The number of files in the file system
ndirs uint64 // The number of directories in the file system
nbytes uint64 // The amount of data in the file system
readonly bool // If the file system is readonly or not
}
// Run the FileSystem, mounting the MountPoint and connecting to FUSE
func (mfs *FileSystem) Run() error {
var err error
// Unmount the FS in case it was mounted with errors.
fuse.Unmount(mfs.MountPoint)
// Create the mount options to pass to Mount.
opts := []fuse.MountOption{
fuse.VolumeName("MemFS"),
fuse.FSName("memfs"),
fuse.Subtype("memfs"),
}
// If we're in readonly mode - pass to the mount options
if mfs.readonly {
opts = append(opts, fuse.ReadOnly())
}
// Mount the FS with the specified options
if mfs.Conn, err = fuse.Mount(mfs.MountPoint, opts...); err != nil {
return err
}
// Ensure that the file system is shutdown
defer mfs.Conn.Close()
logger.Info("mounted memfs:// on %s", mfs.MountPoint)
// Serve the file system
if err = fs.Serve(mfs.Conn, mfs); err != nil {
return err
}
logger.Info("post serve")
// Check if the mount process has an error to report
<-mfs.Conn.Ready
if mfs.Conn.MountError != nil {
return mfs.Conn.MountError
}
return nil
}
// Shutdown the FileSystem unmounting the MountPoint and disconnecting FUSE.
func (mfs *FileSystem) Shutdown() error {
logger.Info("shutting the file system down gracefully")
if mfs.Conn == nil {
return nil
}
if err := fuse.Unmount(mfs.MountPoint); err != nil {
return err
}
return nil
}
//===========================================================================
// Implement fuse.FS* Methods
//===========================================================================
// Root is called to obtain the Node for the file system root. Implements the
// fuse.FS interface required of all file systems.
func (mfs *FileSystem) Root() (fs.Node, error) {
return mfs.root, nil
}
// Destroy is called when the file system is shutting down. Implements the
// fuse.FSDestroyer interface.
//
// Linux only sends this request for block device backed (fuseblk)
// filesystems, to allow them to flush writes to disk before the
// unmount completes.
func (mfs *FileSystem) Destroy() {
logger.Info("file system is being destroyed")
}
// GenerateInode is called to pick a dynamic inode number when it
// would otherwise be 0. Implements the fuse.FSInodeGenerator interface.
//
// Not all filesystems bother tracking inodes, but FUSE requires
// the inode to be set, and fewer duplicates in general makes UNIX
// tools work better.
//
// Operations where the nodes may return 0 inodes include Getattr,
// Setattr and ReadDir.
//
// If FS does not implement FSInodeGenerator, GenerateDynamicInode
// is used.
//
// Implementing this is useful to e.g. constrain the range of
// inode values used for dynamic inodes.
func (mfs *FileSystem) GenerateInode(parentInode uint64, name string) uint64 {
return fs.GenerateDynamicInode(parentInode, name)
}
// Statfs is called to obtain file system metadata. Implements fuse.FSStatfser
// by writing the metadata to the resp.
func (mfs *FileSystem) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
logger.Debug("statfs called on file system")
// Compute the total number of available blocks
resp.Blocks = mfs.Config.CacheSize / minBlockSize
// Compute the number of used blocks
numblocks := Blocks(mfs.nbytes)
// Report the number of free and available blocks for the block size
resp.Bfree = resp.Blocks - numblocks
resp.Bavail = resp.Blocks - numblocks
resp.Bsize = uint32(minBlockSize)
// Report the total number of files in the file system (and those free)
resp.Files = mfs.nfiles
resp.Ffree = 0
// Report the maximum length of a name and the minimum fragment size
resp.Namelen = 2048
resp.Frsize = uint32(minBlockSize)
return nil
}
//===========================================================================
// Version and Package Information
//===========================================================================
// PackageVersion composes version information from the constants in this file
// and returns a string that defines current information about the package.
func PackageVersion() string {
vstr := fmt.Sprintf("%d.%d", majorVersion, minorVersion)
if microVersion > 0 {
vstr += fmt.Sprintf(".%d", microVersion)
}
switch releaseLevel {
case "final":
return vstr
case "alpha":
return vstr + "a"
case "beta":
return vstr + "b"
default:
return vstr
}
}