Skip to content

Commit c4fc668

Browse files
committed
mounting, directory listing work
1 parent 16bace8 commit c4fc668

16 files changed

+577
-123
lines changed

errors.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,28 @@ func (s *NFSStatusError) Code() ResponseCode {
170170
// MarshalBinary - The binary form of the code.
171171
func (s *NFSStatusError) MarshalBinary() (data []byte, err error) {
172172
var resp [4]byte
173-
binary.LittleEndian.PutUint32(resp[0:4], uint32(s.NFSStatus))
173+
binary.BigEndian.PutUint32(resp[0:4], uint32(s.NFSStatus))
174+
return resp[:], nil
175+
}
176+
177+
// NFSStatusErrorWithOpAttr is an NFS error where a 'post_op_attr' is expected
178+
type NFSStatusErrorWithOpAttr struct {
179+
NFSStatus
180+
}
181+
182+
// Error is The wrapped error
183+
func (s *NFSStatusErrorWithOpAttr) Error() string {
184+
return s.NFSStatus.String()
185+
}
186+
187+
// Code for NFS issues are successful RPC responses
188+
func (s *NFSStatusErrorWithOpAttr) Code() ResponseCode {
189+
return ResponseCodeSuccess
190+
}
191+
192+
// MarshalBinary - The binary form of the code.
193+
func (s *NFSStatusErrorWithOpAttr) MarshalBinary() (data []byte, err error) {
194+
var resp [8]byte
195+
binary.BigEndian.PutUint32(resp[0:4], uint32(s.NFSStatus))
174196
return resp[:], nil
175197
}

example/helloworld/main.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,19 @@ func (h *HelloWorldHandler) Mount(ctx context.Context, conn net.Conn, req nfs.Mo
2525
return
2626
}
2727

28+
// FSStat provides information about a filesystem.
29+
func (h *HelloWorldHandler) FSStat(ctx context.Context, f billy.Filesystem, s *nfs.FSStat) error {
30+
return nil
31+
}
32+
2833
// ToHandle handled by CachingHandler
29-
func (h *HelloWorldHandler) ToHandle(f billy.Filesystem, s string) []byte {
34+
func (h *HelloWorldHandler) ToHandle(f billy.Filesystem, s []string) []byte {
3035
return []byte{}
3136
}
3237

3338
// FromHandle handled by CachingHandler
34-
func (h *HelloWorldHandler) FromHandle([]byte) (billy.Filesystem, string, error) {
35-
return nil, "", nil
39+
func (h *HelloWorldHandler) FromHandle([]byte) (billy.Filesystem, []string, error) {
40+
return nil, []string{}, nil
3641
}
3742

3843
func main() {

file.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ func ToFileAttribute(info os.FileInfo) FileAttribute {
9292
}
9393

9494
// WritePostOpAttrs writes the `post_op_attr` representation of a files attributes
95-
func WritePostOpAttrs(writer io.Writer, fs billy.Filesystem, path string) {
96-
attrs, err := fs.Stat(path)
95+
func WritePostOpAttrs(writer io.Writer, fs billy.Filesystem, path []string) {
96+
attrs, err := fs.Stat(fs.Join(path...))
9797
if err != nil {
9898
_ = xdr.Write(writer, uint32(0))
9999
}

filesystem.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package nfs
2+
3+
import "time"
4+
5+
// FSStat returns metadata about a file system
6+
type FSStat struct {
7+
TotalSize uint64
8+
FreeSize uint64
9+
AvailableSize uint64
10+
TotalFiles uint64
11+
FreeFiles uint64
12+
AvailableFiles uint64
13+
// CacheHint is called "invarsec" in the nfs standard
14+
CacheHint time.Duration
15+
}

handler.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@ import (
99

1010
// Handler represents the interface of the file system / vfs being exposed over NFS
1111
type Handler interface {
12+
// Required methods
13+
1214
Mount(context.Context, net.Conn, MountRequest) (MountStatus, billy.Filesystem, []AuthFlavor)
1315

16+
// Optional methods - generic helpers or trivial implementations can be sufficient depending on use case.
17+
18+
// Fill in information about a file system's free space.
19+
FSStat(context.Context, billy.Filesystem, *FSStat) error
20+
1421
// represent file objects as opaque references
15-
ToHandle(fs billy.Filesystem, path string) []byte
16-
FromHandle(fh []byte) (billy.Filesystem, string, error)
22+
// Can be safely implemented via helpers/cachinghandler.
23+
ToHandle(fs billy.Filesystem, path []string) []byte
24+
FromHandle(fh []byte) (billy.Filesystem, []string, error)
1725
}

helpers/cachinghandler.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,24 @@ type CachingHandler struct {
2525

2626
type entry struct {
2727
f billy.Filesystem
28-
p string
28+
p []string
2929
}
3030

3131
// ToHandle takes a file and represents it with an opaque handle to reference it.
3232
// In stateless nfs (when it's serving a unix fs) this can be the device + inode
3333
// but we can generalize with a stateful local cache of handed out IDs.
34-
func (c *CachingHandler) ToHandle(f billy.Filesystem, path string) []byte {
34+
func (c *CachingHandler) ToHandle(f billy.Filesystem, path []string) []byte {
3535
id := uuid.New()
3636
c.activeHandles.Add(id, entry{f, path})
3737
b, _ := id.MarshalBinary()
3838
return b
3939
}
4040

4141
// FromHandle converts from an opaque handle to the file it represents
42-
func (c *CachingHandler) FromHandle(fh []byte) (billy.Filesystem, string, error) {
42+
func (c *CachingHandler) FromHandle(fh []byte) (billy.Filesystem, []string, error) {
4343
id, err := uuid.FromBytes(fh)
4444
if err != nil {
45-
return nil, "", err
45+
return nil, []string{}, err
4646
}
4747

4848
if cache, ok := c.activeHandles.Get(id); ok {
@@ -51,5 +51,5 @@ func (c *CachingHandler) FromHandle(fh []byte) (billy.Filesystem, string, error)
5151
return f.f, f.p, nil
5252
}
5353
}
54-
return nil, "", &nfs.NFSStatusError{NFSStatus: nfs.NFSStatusStale}
54+
return nil, []string{}, &nfs.NFSStatusError{NFSStatus: nfs.NFSStatusStale}
5555
}

mount.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313

1414
func init() {
1515
RegisterMessageHandler(mountServiceID, uint32(MountProcMount), onMount)
16+
RegisterMessageHandler(mountServiceID, uint32(MountProcUmnt), onUMount)
1617
}
1718

1819
func onMount(ctx context.Context, w *response, userHandle Handler) error {
@@ -31,11 +32,21 @@ func onMount(ctx context.Context, w *response, userHandle Handler) error {
3132
return err
3233
}
3334

34-
rootHndl := userHandle.ToHandle(handle, "/")
35+
rootHndl := userHandle.ToHandle(handle, []string{})
3536

3637
if status == MountStatusOk {
3738
xdr.Write(writer, rootHndl)
3839
xdr.Write(writer, flavors)
3940
}
4041
return w.Write(writer.Bytes())
4142
}
43+
44+
func onUMount(ctx context.Context, w *response, userHandle Handler) error {
45+
_, err := xdr.ReadOpaque(w.req.Body)
46+
if err != nil {
47+
return err
48+
}
49+
50+
w.writeHeader(ResponseCodeSuccess)
51+
return nil
52+
}

nfs.go

+5-109
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package nfs
22

33
import (
4-
"bytes"
54
"context"
6-
7-
"github.com/go-git/go-billy/v5"
8-
"github.com/vmware/go-nfs-client/nfs/xdr"
95
)
106

117
const (
@@ -14,115 +10,15 @@ const (
1410

1511
func init() {
1612
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureNull), onNull)
13+
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureLookup), onLookup)
1714
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureGetAttr), onGetAttr)
15+
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureAccess), onAccess)
16+
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureReadDirPlus), onReadDirPlus)
17+
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureFSStat), onFSStat)
1818
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedureFSInfo), onFSInfo)
19+
RegisterMessageHandler(nfsServiceID, uint32(NFSProcedurePathConf), onPathConf)
1920
}
2021

2122
func onNull(ctx context.Context, w *response, userHandle Handler) error {
2223
return w.Write([]byte{})
2324
}
24-
25-
func onGetAttr(ctx context.Context, w *response, userHandle Handler) error {
26-
handle, err := xdr.ReadOpaque(w.req.Body)
27-
if err != nil {
28-
// TODO: wrap
29-
return err
30-
}
31-
32-
fs, path, err := userHandle.FromHandle(handle)
33-
if err != nil {
34-
return &NFSStatusError{NFSStatusStale}
35-
}
36-
37-
info, err := fs.Stat(path)
38-
if err != nil {
39-
// TODO: wrap
40-
return err
41-
}
42-
attr := ToFileAttribute(info)
43-
44-
writer := bytes.NewBuffer([]byte{})
45-
if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil {
46-
return err
47-
}
48-
if err := xdr.Write(writer, attr); err != nil {
49-
return err
50-
}
51-
52-
return w.Write(writer.Bytes())
53-
}
54-
55-
const (
56-
// FSInfoPropertyLink does the FS support hard links?
57-
FSInfoPropertyLink = 0x0001
58-
// FSInfoPropertySymlink does the FS support soft links?
59-
FSInfoPropertySymlink = 0x0002
60-
// FSInfoPropertyHomogeneous does the FS need PATHCONF calls for each file
61-
FSInfoPropertyHomogeneous = 0x0008
62-
// FSInfoPropertyCanSetTime can the FS support setting access/mod times?
63-
FSInfoPropertyCanSetTime = 0x0010
64-
)
65-
66-
func onFSInfo(ctx context.Context, w *response, userHandle Handler) error {
67-
roothandle, err := xdr.ReadOpaque(w.req.Body)
68-
if err != nil {
69-
// TODO: wrap
70-
return err
71-
}
72-
fs, path, err := userHandle.FromHandle(roothandle)
73-
if err != nil {
74-
// TODO: wrap
75-
return err
76-
}
77-
78-
writer := bytes.NewBuffer([]byte{})
79-
if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil {
80-
return err
81-
}
82-
WritePostOpAttrs(writer, fs, path)
83-
84-
type fsinfores struct {
85-
Rtmax uint32
86-
Rtpref uint32
87-
Rtmult uint32
88-
Wtmax uint32
89-
Wtpref uint32
90-
Wtmult uint32
91-
Dtpref uint32
92-
Maxfilesize uint64
93-
TimeDelta uint64
94-
Properties uint32
95-
}
96-
97-
res := fsinfores{
98-
Rtmax: 1 << 30,
99-
Rtpref: 1 << 30,
100-
Rtmult: 4096,
101-
Wtmax: 1 << 30,
102-
Wtpref: 1 << 30,
103-
Wtmult: 4096,
104-
Dtpref: 8192,
105-
Maxfilesize: 1 << 62, // wild guess. this seems big.
106-
TimeDelta: 1, // nanosecond precision.
107-
Properties: 0,
108-
}
109-
110-
// TODO: these aren't great indications of support, really.
111-
if _, ok := fs.(billy.Symlink); ok {
112-
res.Properties |= FSInfoPropertyLink
113-
res.Properties |= FSInfoPropertySymlink
114-
}
115-
// TODO: if the nfs share spans multiple virtual mounts, may need
116-
// to support granular PATHINFO responses.
117-
res.Properties |= FSInfoPropertyHomogeneous
118-
// TODO: not a perfect indicator
119-
if billy.CapabilityCheck(fs, billy.WriteCapability) {
120-
res.Properties |= FSInfoPropertyCanSetTime
121-
}
122-
// TODO: this whole struct should be specifiable by the userhandler.
123-
124-
if err := xdr.Write(writer, res); err != nil {
125-
return err
126-
}
127-
return w.Write(writer.Bytes())
128-
}

nfs_onaccess.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package nfs
2+
3+
import (
4+
"bytes"
5+
"context"
6+
7+
"github.com/go-git/go-billy/v5"
8+
"github.com/vmware/go-nfs-client/nfs/xdr"
9+
)
10+
11+
func onAccess(ctx context.Context, w *response, userHandle Handler) error {
12+
roothandle, err := xdr.ReadOpaque(w.req.Body)
13+
if err != nil {
14+
// TODO: wrap
15+
return err
16+
}
17+
fs, path, err := userHandle.FromHandle(roothandle)
18+
if err != nil {
19+
// TODO: wrap
20+
return err
21+
}
22+
mask, err := xdr.ReadUint32(w.req.Body)
23+
if err != nil {
24+
// TODO: wrap
25+
return err
26+
}
27+
28+
writer := bytes.NewBuffer([]byte{})
29+
if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil {
30+
return err
31+
}
32+
WritePostOpAttrs(writer, fs, path)
33+
34+
if !billy.CapabilityCheck(fs, billy.WriteCapability) {
35+
mask = mask & (1 | 2 | 0x20)
36+
}
37+
38+
if err := xdr.Write(writer, mask); err != nil {
39+
return err
40+
}
41+
return w.Write(writer.Bytes())
42+
}

0 commit comments

Comments
 (0)