Skip to content

Commit 4dd5a5e

Browse files
vgoughValient Gough
authored and
Valient Gough
committed
add in-memory filesystem example
1 parent 41938c1 commit 4dd5a5e

File tree

3 files changed

+398
-10
lines changed

3 files changed

+398
-10
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99

1010
/clockfs
1111
/hellofs
12+
/memfs

examples/memfs/memfs.go

+385
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
// Memfs implements an in-memory file system.
2+
package main
3+
4+
import (
5+
"flag"
6+
"fmt"
7+
"log"
8+
"os"
9+
"sync"
10+
"sync/atomic"
11+
"time"
12+
13+
"bazil.org/fuse"
14+
"bazil.org/fuse/fs"
15+
"golang.org/x/net/context"
16+
)
17+
18+
// Compile-time interface checks.
19+
var _ fs.FS = &MemFs{}
20+
var _ fs.FSStatfser = &MemFs{}
21+
22+
var _ fs.Node = &Dir{}
23+
var _ fs.NodeCreater = &Dir{}
24+
var _ fs.NodeMkdirer = &Dir{}
25+
var _ fs.NodeRemover = &Dir{}
26+
var _ fs.NodeRenamer = &Dir{}
27+
var _ fs.NodeStringLookuper = &Dir{}
28+
29+
var _ fs.HandleReadAller = &File{}
30+
var _ fs.HandleWriter = &File{}
31+
var _ fs.Node = &File{}
32+
var _ fs.NodeOpener = &File{}
33+
var _ fs.NodeSetattrer = &File{}
34+
35+
// debug flag enables logging of debug messages to stderr.
36+
var debug = flag.Bool("debug", false, "enable debug log messages to stderr")
37+
38+
var Usage = func() {
39+
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
40+
fmt.Fprintf(os.Stderr, " %s MOUNTPOINT\n", os.Args[0])
41+
flag.PrintDefaults()
42+
}
43+
44+
func debugLog(msg interface{}) {
45+
fmt.Fprintf(os.Stderr, "%v\n", msg)
46+
}
47+
48+
func main() {
49+
flag.Usage = Usage
50+
flag.Parse()
51+
52+
if flag.NArg() != 1 {
53+
Usage()
54+
os.Exit(2)
55+
}
56+
57+
mountpoint := flag.Arg(0)
58+
c, err := fuse.Mount(
59+
mountpoint,
60+
fuse.FSName("memfs"),
61+
fuse.Subtype("memfs"),
62+
fuse.LocalVolume(),
63+
fuse.VolumeName("Memory FS"),
64+
)
65+
if err != nil {
66+
log.Fatal(err)
67+
}
68+
defer c.Close()
69+
70+
f := &MemFs{
71+
nodeCount: 1,
72+
}
73+
f.root = f.newDir(os.ModeDir | 0777)
74+
if f.root.attr.Inode != 1 {
75+
panic("Root node should have been assigned id 1")
76+
}
77+
78+
server := fs.Server{
79+
FS: f,
80+
}
81+
if *debug {
82+
server.Debug = debugLog
83+
}
84+
err = server.Serve(c)
85+
if err != nil {
86+
log.Fatal(err)
87+
}
88+
89+
<-c.Ready
90+
if err := c.MountError; err != nil {
91+
log.Fatal(err)
92+
}
93+
}
94+
95+
type MemFs struct {
96+
root *Dir
97+
nodeId uint64
98+
99+
nodeCount uint64
100+
size int64
101+
}
102+
103+
func (m *MemFs) nextId() uint64 {
104+
return atomic.AddUint64(&m.nodeId, 1)
105+
}
106+
107+
func (m *MemFs) newDir(mode os.FileMode) *Dir {
108+
n := time.Now()
109+
return &Dir{
110+
attr: fuse.Attr{
111+
Inode: m.nextId(),
112+
Atime: n,
113+
Mtime: n,
114+
Ctime: n,
115+
Crtime: n,
116+
Mode: os.ModeDir | mode,
117+
},
118+
fs: m,
119+
nodes: make(map[string]fs.Node),
120+
}
121+
}
122+
123+
func (m *MemFs) newFile(mode os.FileMode) *File {
124+
n := time.Now()
125+
return &File{
126+
attr: fuse.Attr{
127+
Inode: m.nextId(),
128+
Atime: n,
129+
Mtime: n,
130+
Ctime: n,
131+
Crtime: n,
132+
Mode: mode,
133+
},
134+
data: make([]byte, 0),
135+
}
136+
}
137+
138+
type Dir struct {
139+
sync.RWMutex
140+
attr fuse.Attr
141+
142+
fs *MemFs
143+
parent *Dir
144+
nodes map[string]fs.Node
145+
}
146+
147+
type File struct {
148+
sync.RWMutex
149+
attr fuse.Attr
150+
151+
fs *MemFs
152+
data []byte
153+
}
154+
155+
func (f *MemFs) Root() (fs.Node, error) {
156+
return f.root, nil
157+
}
158+
159+
func (f *MemFs) Statfs(ctx context.Context, req *fuse.StatfsRequest,
160+
resp *fuse.StatfsResponse) error {
161+
resp.Blocks = uint64((atomic.LoadInt64(&f.size) + 511) / 512)
162+
resp.Bsize = 512
163+
resp.Files = atomic.LoadUint64(&f.nodeCount)
164+
return nil
165+
}
166+
167+
func (f *File) Attr(o *fuse.Attr) {
168+
f.RLock()
169+
*o = f.attr
170+
f.RUnlock()
171+
}
172+
173+
func (d *Dir) Attr(o *fuse.Attr) {
174+
d.RLock()
175+
*o = d.attr
176+
d.RUnlock()
177+
}
178+
179+
func (d *Dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
180+
d.RLock()
181+
n, exist := d.nodes[name]
182+
d.RUnlock()
183+
184+
if !exist {
185+
return nil, fuse.ENOENT
186+
}
187+
return n, nil
188+
}
189+
190+
func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
191+
d.RLock()
192+
dirs := make([]fuse.Dirent, len(d.nodes)+2)
193+
194+
// Add special references.
195+
dirs[0] = fuse.Dirent{
196+
Name: ".",
197+
Inode: d.attr.Inode,
198+
Type: fuse.DT_Dir,
199+
}
200+
dirs[1] = fuse.Dirent{
201+
Name: "..",
202+
Type: fuse.DT_Dir,
203+
}
204+
if d.parent != nil {
205+
dirs[1].Inode = d.parent.attr.Inode
206+
} else {
207+
dirs[1].Inode = d.attr.Inode
208+
}
209+
210+
// Add remaining files.
211+
idx := 2
212+
for name, node := range d.nodes {
213+
ent := fuse.Dirent{
214+
Name: name,
215+
}
216+
switch n := node.(type) {
217+
case *File:
218+
ent.Inode = n.attr.Inode
219+
ent.Type = fuse.DT_File
220+
case *Dir:
221+
ent.Inode = n.attr.Inode
222+
ent.Type = fuse.DT_Dir
223+
}
224+
dirs[idx] = ent
225+
idx++
226+
}
227+
d.RUnlock()
228+
return dirs, nil
229+
}
230+
231+
func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
232+
d.Lock()
233+
defer d.Unlock()
234+
235+
if _, exists := d.nodes[req.Name]; exists {
236+
return nil, fuse.EEXIST
237+
}
238+
239+
n := d.fs.newDir(req.Mode)
240+
d.nodes[req.Name] = n
241+
atomic.AddUint64(&d.fs.nodeCount, 1)
242+
243+
return n, nil
244+
}
245+
246+
func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
247+
resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
248+
d.Lock()
249+
defer d.Unlock()
250+
251+
if _, exists := d.nodes[req.Name]; exists {
252+
return nil, nil, fuse.EEXIST
253+
}
254+
255+
n := d.fs.newFile(req.Mode)
256+
n.fs = d.fs
257+
d.nodes[req.Name] = n
258+
atomic.AddUint64(&d.fs.nodeCount, 1)
259+
260+
resp.Attr = n.attr
261+
262+
return n, n, nil
263+
}
264+
265+
func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
266+
nd := newDir.(*Dir)
267+
if d.attr.Inode == nd.attr.Inode {
268+
d.Lock()
269+
defer d.Unlock()
270+
} else if d.attr.Inode < nd.attr.Inode {
271+
d.Lock()
272+
defer d.Unlock()
273+
nd.Lock()
274+
defer nd.Unlock()
275+
} else {
276+
nd.Lock()
277+
defer nd.Unlock()
278+
d.Lock()
279+
defer d.Unlock()
280+
}
281+
282+
if _, exists := d.nodes[req.OldName]; !exists {
283+
return fuse.ENOENT
284+
}
285+
286+
// Rename can be used as an atomic replace, override an existing file.
287+
if old, exists := nd.nodes[req.NewName]; exists {
288+
atomic.AddUint64(&d.fs.nodeCount, ^uint64(0)) // decrement by one
289+
if oldFile, ok := old.(*File); !ok {
290+
atomic.AddInt64(&d.fs.size, -int64(oldFile.attr.Size))
291+
}
292+
}
293+
294+
nd.nodes[req.NewName] = d.nodes[req.OldName]
295+
delete(d.nodes, req.OldName)
296+
return nil
297+
}
298+
299+
func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
300+
d.Lock()
301+
defer d.Unlock()
302+
303+
if n, exists := d.nodes[req.Name]; !exists {
304+
return fuse.ENOENT
305+
} else if req.Dir && len(n.(*Dir).nodes) > 0 {
306+
return fuse.ENOTEMPTY
307+
}
308+
309+
delete(d.nodes, req.Name)
310+
atomic.AddUint64(&d.fs.nodeCount, ^uint64(0)) // decrement by one
311+
return nil
312+
}
313+
314+
func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle,
315+
error) {
316+
return f, nil
317+
}
318+
319+
func (f *File) ReadAll(ctx context.Context) ([]byte, error) {
320+
f.RLock()
321+
out := make([]byte, len(f.data))
322+
copy(out, f.data)
323+
f.RUnlock()
324+
325+
return out, nil
326+
}
327+
328+
func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
329+
f.Lock()
330+
331+
l := len(req.Data)
332+
end := int(req.Offset) + l
333+
if end > len(f.data) {
334+
delta := end - len(f.data)
335+
f.data = append(f.data, make([]byte, delta)...)
336+
f.attr.Size = uint64(len(f.data))
337+
atomic.AddInt64(&f.fs.size, int64(delta))
338+
}
339+
copy(f.data[req.Offset:end], req.Data)
340+
resp.Size = l
341+
342+
f.Unlock()
343+
return nil
344+
}
345+
346+
func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest,
347+
resp *fuse.SetattrResponse) error {
348+
f.Lock()
349+
350+
if req.Valid.Size() {
351+
delta := int(req.Size) - len(f.data)
352+
if delta > 0 {
353+
f.data = append(f.data, make([]byte, delta)...)
354+
} else {
355+
f.data = f.data[0:req.Size]
356+
}
357+
f.attr.Size = req.Size
358+
atomic.AddInt64(&f.fs.size, int64(delta))
359+
}
360+
361+
if req.Valid.Mode() {
362+
f.attr.Mode = req.Mode
363+
}
364+
365+
if req.Valid.Atime() {
366+
f.attr.Atime = req.Atime
367+
}
368+
369+
if req.Valid.AtimeNow() {
370+
f.attr.Atime = time.Now()
371+
}
372+
373+
if req.Valid.Mtime() {
374+
f.attr.Mtime = req.Mtime
375+
}
376+
377+
if req.Valid.MtimeNow() {
378+
f.attr.Mtime = time.Now()
379+
}
380+
381+
resp.Attr = f.attr
382+
383+
f.Unlock()
384+
return nil
385+
}

0 commit comments

Comments
 (0)