small refactor*
This commit is contained in:
parent
b6b541e050
commit
24a4d30275
232 changed files with 2164 additions and 1906 deletions
server/pkg/go-nfs
195
server/pkg/go-nfs/nfs_onreaddir.go
Normal file
195
server/pkg/go-nfs/nfs_onreaddir.go
Normal file
|
@ -0,0 +1,195 @@
|
|||
package nfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"github.com/willscott/go-nfs-client/nfs/xdr"
|
||||
)
|
||||
|
||||
type readDirArgs struct {
|
||||
Handle []byte
|
||||
Cookie uint64
|
||||
CookieVerif uint64
|
||||
Count uint32
|
||||
}
|
||||
|
||||
type readDirEntity struct {
|
||||
FileID uint64
|
||||
Name []byte
|
||||
Cookie uint64
|
||||
Next bool
|
||||
}
|
||||
|
||||
func onReadDir(ctx context.Context, w *response, userHandle Handler) error {
|
||||
w.errorFmt = opAttrErrorFormatter
|
||||
obj := readDirArgs{}
|
||||
err := xdr.Read(w.req.Body, &obj)
|
||||
if err != nil {
|
||||
return &NFSStatusError{NFSStatusInval, err}
|
||||
}
|
||||
|
||||
if obj.Count < 1024 {
|
||||
return &NFSStatusError{NFSStatusTooSmall, io.ErrShortBuffer}
|
||||
}
|
||||
|
||||
fs, p, err := userHandle.FromHandle(ctx, obj.Handle)
|
||||
if err != nil {
|
||||
return &NFSStatusError{NFSStatusStale, err}
|
||||
}
|
||||
|
||||
contents, verifier, err := getDirListingWithVerifier(ctx, userHandle, obj.Handle, obj.CookieVerif)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if obj.Cookie > 0 && obj.CookieVerif > 0 && verifier != obj.CookieVerif {
|
||||
return &NFSStatusError{NFSStatusBadCookie, nil}
|
||||
}
|
||||
|
||||
entities := make([]readDirEntity, 0)
|
||||
maxBytes := uint32(100) // conservative overhead measure
|
||||
|
||||
started := obj.Cookie == 0
|
||||
if started {
|
||||
// add '.' and '..' to entities
|
||||
dotdotFileID := uint64(0)
|
||||
if len(p) > 0 {
|
||||
dda := tryStat(ctx, fs, p[0:len(p)-1])
|
||||
if dda != nil {
|
||||
dotdotFileID = dda.Fileid
|
||||
}
|
||||
}
|
||||
dotFileID := uint64(0)
|
||||
da := tryStat(ctx, fs, p)
|
||||
if da != nil {
|
||||
dotFileID = da.Fileid
|
||||
}
|
||||
entities = append(entities,
|
||||
readDirEntity{Name: []byte("."), Cookie: 0, Next: true, FileID: dotFileID},
|
||||
readDirEntity{Name: []byte(".."), Cookie: 1, Next: true, FileID: dotdotFileID},
|
||||
)
|
||||
}
|
||||
|
||||
eof := true
|
||||
maxEntities := userHandle.HandleLimit() / 2
|
||||
for i, c := range contents {
|
||||
// cookie equates to index within contents + 2 (for '.' and '..')
|
||||
cookie := uint64(i + 2)
|
||||
if started {
|
||||
maxBytes += 512 // TODO: better estimation.
|
||||
if maxBytes > obj.Count || len(entities) > maxEntities {
|
||||
eof = false
|
||||
break
|
||||
}
|
||||
|
||||
attrs := ToFileAttribute(c, path.Join(append(p, c.Name())...))
|
||||
entities = append(entities, readDirEntity{
|
||||
FileID: attrs.Fileid,
|
||||
Name: []byte(c.Name()),
|
||||
Cookie: cookie,
|
||||
Next: true,
|
||||
})
|
||||
} else if cookie == obj.Cookie {
|
||||
started = true
|
||||
}
|
||||
}
|
||||
|
||||
writer := bytes.NewBuffer([]byte{})
|
||||
if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil {
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
if err := WritePostOpAttrs(writer, tryStat(ctx, fs, p)); err != nil {
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
|
||||
if err := xdr.Write(writer, verifier); err != nil {
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
|
||||
if err := xdr.Write(writer, len(entities) > 0); err != nil { // next
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
if len(entities) > 0 {
|
||||
entities[len(entities)-1].Next = false
|
||||
// no next for last entity
|
||||
|
||||
for _, e := range entities {
|
||||
if err := xdr.Write(writer, e); err != nil {
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := xdr.Write(writer, eof); err != nil {
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
// TODO: track writer size at this point to validate maxcount estimation and stop early if needed.
|
||||
|
||||
if err := w.Write(writer.Bytes()); err != nil {
|
||||
return &NFSStatusError{NFSStatusServerFault, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDirListingWithVerifier(ctx context.Context, userHandle Handler, fsHandle []byte, verifier uint64) ([]fs.FileInfo, uint64, error) {
|
||||
// figure out what directory it is.
|
||||
fs, p, err := userHandle.FromHandle(ctx, fsHandle)
|
||||
if err != nil {
|
||||
return nil, 0, &NFSStatusError{NFSStatusStale, err}
|
||||
}
|
||||
|
||||
path := fs.Join(p...)
|
||||
// see if the verifier has this dir cached:
|
||||
if vh, ok := userHandle.(CachingHandler); verifier != 0 && ok {
|
||||
entries := vh.DataForVerifier(path, verifier)
|
||||
if entries != nil {
|
||||
return entries, verifier, nil
|
||||
}
|
||||
}
|
||||
// load the entries.
|
||||
contents, err := fs.ReadDir(ctx, path)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return nil, 0, &NFSStatusError{NFSStatusAccess, err}
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, 0, &NFSStatusError{timeoutStatus, err}
|
||||
}
|
||||
return nil, 0, &NFSStatusError{NFSStatusIO, err}
|
||||
}
|
||||
|
||||
sort.Slice(contents, func(i, j int) bool {
|
||||
return contents[i].Name() < contents[j].Name()
|
||||
})
|
||||
|
||||
if vh, ok := userHandle.(CachingHandler); ok {
|
||||
// let the user handler make a verifier if it can.
|
||||
v := vh.VerifierFor(path, contents)
|
||||
return contents, v, nil
|
||||
}
|
||||
|
||||
id := hashPathAndContents(path, contents)
|
||||
return contents, id, nil
|
||||
}
|
||||
|
||||
func hashPathAndContents(path string, contents []fs.FileInfo) uint64 {
|
||||
//calculate a cookie-verifier.
|
||||
vHash := sha256.New()
|
||||
|
||||
// Add the path to avoid collisions of directories with the same content
|
||||
vHash.Write([]byte(path))
|
||||
|
||||
for _, c := range contents {
|
||||
vHash.Write([]byte(c.Name())) // Never fails according to the docs
|
||||
}
|
||||
|
||||
verify := vHash.Sum(nil)[0:8]
|
||||
return binary.BigEndian.Uint64(verify)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue