-
Notifications
You must be signed in to change notification settings - Fork 43
/
mount-index.go
138 lines (115 loc) · 3.33 KB
/
mount-index.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
// +build !windows
package desync
import (
"context"
"fmt"
"io"
"os"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
)
type MountFS interface {
fs.InodeEmbedder
Close() error
}
// IndexMountFS is used to FUSE mount an index file (as a blob, not an archive).
// It present a single file underneath the mountpoint.
type IndexMountFS struct {
fs.Inode
FName string // File name in the mountpoint
Idx Index // Index of the blob
Store Store
}
var _ fs.NodeOnAdder = &IndexMountFS{}
var _ MountFS = &IndexMountFS{}
// NewIndexMountFS initializes a FUSE filesystem mount based on an index and a chunk store.
func NewIndexMountFS(idx Index, name string, s Store) *IndexMountFS {
return &IndexMountFS{
FName: name,
Idx: idx,
Store: s,
}
}
// OnAdd is used to build the static filesystem structure at the start of the mount.
func (r *IndexMountFS) OnAdd(ctx context.Context) {
n := &indexFile{
idx: r.Idx,
store: r.Store,
mtime: time.Now(),
}
ch := r.NewPersistentInode(ctx, n, fs.StableAttr{Mode: fuse.S_IFREG})
r.AddChild(r.FName, ch, false)
}
func (r *IndexMountFS) Close() error {
return nil
}
var _ fs.NodeGetattrer = &indexFile{}
var _ fs.NodeOpener = &indexFile{}
type indexFile struct {
fs.Inode
idx Index // Index of the blob
store Store
mtime time.Time
}
func (n *indexFile) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
fh := newIndexFileHandle(n.idx, n.store)
return fh, fuse.FOPEN_KEEP_CACHE, fs.OK
}
func (n *indexFile) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
f := fh.(*indexFileHandle)
return f.read(dest, off)
}
func (n *indexFile) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Mode = fuse.S_IFREG | 0444
out.Size = uint64(n.idx.Length())
out.Mtime = uint64(n.mtime.Unix())
return fs.OK
}
// indexFileHandle represents a (read-only) file handle on a blob in a FUSE mounted filesystem
type indexFileHandle struct {
r *IndexPos
// perhaps not needed, but in case something is trying to use the same filehandle concurrently
mu sync.Mutex
}
// NewIndexMountFile initializes a blob file opened in a FUSE mount.
func newIndexFileHandle(idx Index, s Store) *indexFileHandle {
return &indexFileHandle{
r: NewIndexReadSeeker(idx, s),
}
}
// read from a blob file in a FUSE mount.
func (f *indexFileHandle) read(dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
if _, err := f.r.Seek(off, io.SeekStart); err != nil {
fmt.Fprintln(os.Stderr, err)
return nil, syscall.EIO
}
n, err := f.r.Read(dest)
if err != nil && err != io.EOF {
fmt.Fprintln(os.Stderr, err)
return nil, syscall.EIO
}
return fuse.ReadResultData(dest[:n]), fs.OK
}
// MountIndex mounts an index file under a FUSE mount point. The mount will only expose a single
// blob file as represented by the index.
func MountIndex(ctx context.Context, idx Index, ifs MountFS, path string, s Store, n int) error {
opts := &fs.Options{}
server, err := fs.Mount(path, ifs, opts)
if err != nil {
return err
}
go func() { // Unmount the server when the context expires
<-ctx.Done()
if err := ifs.Close(); err != nil {
fmt.Fprintln(os.Stderr, "error during unmount:", err)
}
server.Unmount()
}()
server.Wait()
return nil
}