small refactor*
This commit is contained in:
parent
b6b541e050
commit
24a4d30275
232 changed files with 2164 additions and 1906 deletions
server/pkg/go-nfs
379
server/pkg/go-nfs/file.go
Normal file
379
server/pkg/go-nfs/file.go
Normal file
|
@ -0,0 +1,379 @@
|
|||
package nfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/server/pkg/go-nfs/file"
|
||||
"github.com/willscott/go-nfs-client/nfs/xdr"
|
||||
)
|
||||
|
||||
// FileAttribute holds metadata about a filesystem object
|
||||
type FileAttribute struct {
|
||||
Type FileType
|
||||
FileMode uint32
|
||||
Nlink uint32
|
||||
UID uint32
|
||||
GID uint32
|
||||
Filesize uint64
|
||||
Used uint64
|
||||
SpecData [2]uint32
|
||||
FSID uint64
|
||||
Fileid uint64
|
||||
Atime, Mtime, Ctime FileTime
|
||||
}
|
||||
|
||||
// FileType represents a NFS File Type
|
||||
type FileType uint32
|
||||
|
||||
// Enumeration of NFS FileTypes
|
||||
const (
|
||||
FileTypeRegular FileType = iota + 1
|
||||
FileTypeDirectory
|
||||
FileTypeBlock
|
||||
FileTypeCharacter
|
||||
FileTypeLink
|
||||
FileTypeSocket
|
||||
FileTypeFIFO
|
||||
)
|
||||
|
||||
func (f FileType) String() string {
|
||||
switch f {
|
||||
case FileTypeRegular:
|
||||
return "Regular"
|
||||
case FileTypeDirectory:
|
||||
return "Directory"
|
||||
case FileTypeBlock:
|
||||
return "Block Device"
|
||||
case FileTypeCharacter:
|
||||
return "Character Device"
|
||||
case FileTypeLink:
|
||||
return "Symbolic Link"
|
||||
case FileTypeSocket:
|
||||
return "Socket"
|
||||
case FileTypeFIFO:
|
||||
return "FIFO"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Mode provides the OS interpreted mode of the file attributes
|
||||
func (f *FileAttribute) Mode() os.FileMode {
|
||||
return os.FileMode(f.FileMode)
|
||||
}
|
||||
|
||||
// FileCacheAttribute is the subset of FileAttribute used by
|
||||
// wcc_attr
|
||||
type FileCacheAttribute struct {
|
||||
Filesize uint64
|
||||
Mtime, Ctime FileTime
|
||||
}
|
||||
|
||||
// AsCache provides the wcc view of the file attributes
|
||||
func (f FileAttribute) AsCache() *FileCacheAttribute {
|
||||
wcc := FileCacheAttribute{
|
||||
Filesize: f.Filesize,
|
||||
Mtime: f.Mtime,
|
||||
Ctime: f.Ctime,
|
||||
}
|
||||
return &wcc
|
||||
}
|
||||
|
||||
// ToFileAttribute creates an NFS fattr3 struct from an OS.FileInfo
|
||||
func ToFileAttribute(info os.FileInfo, filePath string) *FileAttribute {
|
||||
f := FileAttribute{}
|
||||
|
||||
m := info.Mode()
|
||||
f.FileMode = uint32(m)
|
||||
if info.IsDir() {
|
||||
f.Type = FileTypeDirectory
|
||||
} else if m&os.ModeSymlink != 0 {
|
||||
f.Type = FileTypeLink
|
||||
} else if m&os.ModeCharDevice != 0 {
|
||||
f.Type = FileTypeCharacter
|
||||
} else if m&os.ModeDevice != 0 {
|
||||
f.Type = FileTypeBlock
|
||||
} else if m&os.ModeSocket != 0 {
|
||||
f.Type = FileTypeSocket
|
||||
} else if m&os.ModeNamedPipe != 0 {
|
||||
f.Type = FileTypeFIFO
|
||||
} else {
|
||||
f.Type = FileTypeRegular
|
||||
}
|
||||
// The number of hard links to the file.
|
||||
f.Nlink = 1
|
||||
|
||||
if a := file.GetInfo(info); a != nil {
|
||||
f.Nlink = a.Nlink
|
||||
f.UID = a.UID
|
||||
f.GID = a.GID
|
||||
f.SpecData = [2]uint32{a.Major, a.Minor}
|
||||
f.Fileid = a.Fileid
|
||||
} else {
|
||||
hasher := fnv.New64()
|
||||
_, _ = hasher.Write([]byte(filePath))
|
||||
f.Fileid = hasher.Sum64()
|
||||
}
|
||||
|
||||
f.Filesize = uint64(info.Size())
|
||||
f.Used = uint64(info.Size())
|
||||
f.Atime = ToNFSTime(info.ModTime())
|
||||
f.Mtime = f.Atime
|
||||
f.Ctime = f.Atime
|
||||
return &f
|
||||
}
|
||||
|
||||
// tryStat attempts to create a FileAttribute from a path.
|
||||
func tryStat(ctx context.Context, fs Filesystem, path []string) *FileAttribute {
|
||||
fullPath := fs.Join(path...)
|
||||
attrs, err := fs.Lstat(ctx, fullPath)
|
||||
if err != nil || attrs == nil {
|
||||
Log.Errorf("err loading attrs for %s: %v", fs.Join(path...), err)
|
||||
return nil
|
||||
}
|
||||
return ToFileAttribute(attrs, fullPath)
|
||||
}
|
||||
|
||||
// WriteWcc writes the `wcc_data` representation of an object.
|
||||
func WriteWcc(writer io.Writer, pre *FileCacheAttribute, post *FileAttribute) error {
|
||||
if pre == nil {
|
||||
if err := xdr.Write(writer, uint32(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := xdr.Write(writer, uint32(1)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xdr.Write(writer, *pre); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if post == nil {
|
||||
if err := xdr.Write(writer, uint32(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := xdr.Write(writer, uint32(1)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xdr.Write(writer, *post); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePostOpAttrs writes the `post_op_attr` representation of a files attributes
|
||||
func WritePostOpAttrs(writer io.Writer, post *FileAttribute) error {
|
||||
if post == nil {
|
||||
if err := xdr.Write(writer, uint32(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := xdr.Write(writer, uint32(1)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xdr.Write(writer, *post); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFileAttributes represents a command to update some metadata
|
||||
// about a file.
|
||||
type SetFileAttributes struct {
|
||||
SetMode *uint32
|
||||
SetUID *uint32
|
||||
SetGID *uint32
|
||||
SetSize *uint64
|
||||
SetAtime *time.Time
|
||||
SetMtime *time.Time
|
||||
}
|
||||
|
||||
// Apply uses a `Change` implementation to set defined attributes on a
|
||||
// provided file.
|
||||
func (s *SetFileAttributes) Apply(ctx context.Context, changer Change, fs Filesystem, file string) error {
|
||||
curOS, err := fs.Lstat(ctx, file)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return &NFSStatusError{NFSStatusNoEnt, os.ErrNotExist}
|
||||
} else if errors.Is(err, os.ErrPermission) {
|
||||
return &NFSStatusError{NFSStatusAccess, os.ErrPermission}
|
||||
} else if err != nil {
|
||||
return nil
|
||||
}
|
||||
curr := ToFileAttribute(curOS, file)
|
||||
|
||||
if s.SetMode != nil {
|
||||
mode := os.FileMode(*s.SetMode) & os.ModePerm
|
||||
if mode != curr.Mode().Perm() {
|
||||
if changer == nil {
|
||||
return &NFSStatusError{NFSStatusNotSupp, os.ErrPermission}
|
||||
}
|
||||
if err := changer.Chmod(ctx, file, mode); err != nil {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
return &NFSStatusError{NFSStatusAccess, os.ErrPermission}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.SetUID != nil || s.SetGID != nil {
|
||||
euid := curr.UID
|
||||
if s.SetUID != nil {
|
||||
euid = *s.SetUID
|
||||
}
|
||||
egid := curr.GID
|
||||
if s.SetGID != nil {
|
||||
egid = *s.SetGID
|
||||
}
|
||||
if euid != curr.UID || egid != curr.GID {
|
||||
if changer == nil {
|
||||
return &NFSStatusError{NFSStatusNotSupp, os.ErrPermission}
|
||||
}
|
||||
if err := changer.Lchown(ctx, file, int(euid), int(egid)); err != nil {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
return &NFSStatusError{NFSStatusAccess, os.ErrPermission}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.SetSize != nil {
|
||||
if curr.Mode()&os.ModeSymlink != 0 {
|
||||
return &NFSStatusError{NFSStatusNotSupp, os.ErrInvalid}
|
||||
}
|
||||
fp, err := fs.OpenFile(ctx, file, os.O_WRONLY|os.O_EXCL, 0)
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
return &NFSStatusError{NFSStatusAccess, err}
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close(ctx)
|
||||
|
||||
if *s.SetSize > math.MaxInt64 {
|
||||
return &NFSStatusError{NFSStatusInval, os.ErrInvalid}
|
||||
}
|
||||
if err := fp.Truncate(ctx, int64(*s.SetSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fp.Close(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if s.SetAtime != nil || s.SetMtime != nil {
|
||||
atime := curr.Atime.Native()
|
||||
if s.SetAtime != nil {
|
||||
atime = s.SetAtime
|
||||
}
|
||||
mtime := curr.Mtime.Native()
|
||||
if s.SetMtime != nil {
|
||||
mtime = s.SetMtime
|
||||
}
|
||||
if atime != curr.Atime.Native() || mtime != curr.Mtime.Native() {
|
||||
if changer == nil {
|
||||
return &NFSStatusError{NFSStatusNotSupp, os.ErrPermission}
|
||||
}
|
||||
if err := changer.Chtimes(ctx, file, *atime, *mtime); err != nil {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
return &NFSStatusError{NFSStatusAccess, err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mode returns a mode if specified or the provided default mode.
|
||||
func (s *SetFileAttributes) Mode(def os.FileMode) os.FileMode {
|
||||
if s.SetMode != nil {
|
||||
return os.FileMode(*s.SetMode) & os.ModePerm
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// ReadSetFileAttributes reads an sattr3 xdr stream into a go struct.
|
||||
func ReadSetFileAttributes(r io.Reader) (*SetFileAttributes, error) {
|
||||
attrs := SetFileAttributes{}
|
||||
hasMode, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasMode != 0 {
|
||||
mode, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrs.SetMode = &mode
|
||||
}
|
||||
hasUID, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasUID != 0 {
|
||||
uid, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrs.SetUID = &uid
|
||||
}
|
||||
hasGID, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasGID != 0 {
|
||||
gid, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrs.SetGID = &gid
|
||||
}
|
||||
hasSize, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasSize != 0 {
|
||||
var size uint64
|
||||
attrs.SetSize = &size
|
||||
if err := xdr.Read(r, &size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
aTime, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if aTime == 1 {
|
||||
now := time.Now()
|
||||
attrs.SetAtime = &now
|
||||
} else if aTime == 2 {
|
||||
t := FileTime{}
|
||||
if err := xdr.Read(r, &t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrs.SetAtime = t.Native()
|
||||
}
|
||||
mTime, err := xdr.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mTime == 1 {
|
||||
now := time.Now()
|
||||
attrs.SetMtime = &now
|
||||
} else if mTime == 2 {
|
||||
t := FileTime{}
|
||||
if err := xdr.Read(r, &t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrs.SetMtime = t.Native()
|
||||
}
|
||||
return &attrs, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue