mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
feature (plg_backend_nfs4): poc for nfsv4
This commit is contained in:
parent
86175220cd
commit
2853898f75
12 changed files with 19164 additions and 0 deletions
164
server/plugin/plg_backend_nfs4/index.go
Normal file
164
server/plugin/plg_backend_nfs4/index.go
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
package plg_backend_nfs4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
"github.com/mickael-kerjean/filestash/server/plugin/plg_backend_nfs4/repo/nfs4"
|
||||
)
|
||||
|
||||
const DEFAULT_PORT = ":2049"
|
||||
|
||||
type Nfs4Share struct {
|
||||
client *nfs4.NfsClient
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func init() {
|
||||
Backend.Register("nfs4", Nfs4Share{})
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Init(params map[string]string, app *App) (IBackend, error) {
|
||||
if params["hostname"] == "" {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
var (
|
||||
uid uint32 = 1000
|
||||
gid uint32 = 1000
|
||||
)
|
||||
if params["uid"] != "" {
|
||||
if _uid, err := strconv.Atoi(params["uid"]); err == nil {
|
||||
uid = uint32(_uid)
|
||||
}
|
||||
}
|
||||
if params["gid"] != "" {
|
||||
if _gid, err := strconv.Atoi(params["gid"]); err == nil {
|
||||
gid = uint32(_gid)
|
||||
}
|
||||
}
|
||||
if params["machine_name"] == "" {
|
||||
params["machine_name"] = "filestash"
|
||||
}
|
||||
if strings.Contains(params["hostname"], ":") == false {
|
||||
params["hostname"] = params["hostname"] + DEFAULT_PORT
|
||||
}
|
||||
client, err := nfs4.NewNfsClient(app.Context, params["hostname"], nfs4.AuthParams{
|
||||
MachineName: params["machine_name"],
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Nfs4Share{
|
||||
client,
|
||||
app.Context,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this Nfs4Share) LoginForm() Form {
|
||||
return Form{
|
||||
Elmnts: []FormElement{
|
||||
FormElement{
|
||||
Name: "type",
|
||||
Type: "hidden",
|
||||
Value: "nfs4",
|
||||
},
|
||||
FormElement{
|
||||
Name: "hostname",
|
||||
Type: "text",
|
||||
Placeholder: "Hostname",
|
||||
},
|
||||
FormElement{
|
||||
Name: "advanced",
|
||||
Type: "enable",
|
||||
Placeholder: "Advanced",
|
||||
Target: []string{"nfs_uid", "nfs_gid", "nfs_machinename"},
|
||||
},
|
||||
FormElement{
|
||||
Id: "nfs_uid",
|
||||
Name: "uid",
|
||||
Type: "number",
|
||||
Placeholder: "uid",
|
||||
},
|
||||
FormElement{
|
||||
Id: "nfs_gid",
|
||||
Name: "gid",
|
||||
Type: "number",
|
||||
Placeholder: "gid",
|
||||
},
|
||||
FormElement{
|
||||
Id: "nfs_machinename",
|
||||
Name: "machine_name",
|
||||
Type: "text",
|
||||
Placeholder: "machine name",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Ls(path string) ([]os.FileInfo, error) {
|
||||
list, err := this.client.GetFileList(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files := make([]os.FileInfo, 0)
|
||||
for _, info := range list {
|
||||
files = append(files, File{
|
||||
FName: info.Name,
|
||||
FType: func() string {
|
||||
if info.IsDir {
|
||||
return "directory"
|
||||
}
|
||||
return "file"
|
||||
}(),
|
||||
FSize: int64(info.Size),
|
||||
FTime: int64(info.Mtime.Nanosecond()),
|
||||
})
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Cat(path string) (io.ReadCloser, error) {
|
||||
_, err := this.client.GetFileInfo(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go this.client.ReadFileAll(path, pw)
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Mkdir(path string) error {
|
||||
return this.client.MakePath(path)
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Rm(path string) error {
|
||||
if strings.HasSuffix(path, "/") {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
return nfs4.RemoveRecursive(this.client, path)
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Mv(from string, to string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Touch(path string) error {
|
||||
_, err := this.client.WriteFile(path, false, 0, strings.NewReader(""))
|
||||
return err
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Save(path string, file io.Reader) error {
|
||||
_, err := this.client.ReWriteFile(path, file)
|
||||
return err
|
||||
}
|
||||
|
||||
func (this Nfs4Share) Close() {
|
||||
this.client.Close()
|
||||
return
|
||||
}
|
||||
1
server/plugin/plg_backend_nfs4/repo/README.md
Normal file
1
server/plugin/plg_backend_nfs4/repo/README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
TODO: we can't go get github.com/kha7iq/go-nfs-client => fork over and fix it instead
|
||||
30
server/plugin/plg_backend_nfs4/repo/internal/cleanuper.go
Normal file
30
server/plugin/plg_backend_nfs4/repo/internal/cleanuper.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package internal
|
||||
|
||||
type cleanuper struct {
|
||||
cleanupErr func() error
|
||||
cleanup func()
|
||||
}
|
||||
|
||||
func NewCleanupErr(cl func() error) *cleanuper {
|
||||
return &cleanuper{cleanupErr: cl}
|
||||
}
|
||||
|
||||
func NewCleanup(cl func()) *cleanuper {
|
||||
return &cleanuper{cleanup: cl}
|
||||
}
|
||||
|
||||
func (c *cleanuper) Disarm() {
|
||||
c.cleanupErr = nil
|
||||
c.cleanup = nil
|
||||
}
|
||||
|
||||
func (c *cleanuper) Cleanup() {
|
||||
if c.cleanupErr != nil {
|
||||
_ = c.cleanupErr()
|
||||
}
|
||||
|
||||
if c.cleanup != nil {
|
||||
c.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
14486
server/plugin/plg_backend_nfs4/repo/internal/nfs4.go
Normal file
14486
server/plugin/plg_backend_nfs4/repo/internal/nfs4.go
Normal file
File diff suppressed because it is too large
Load diff
2191
server/plugin/plg_backend_nfs4/repo/internal/nfs4.x
Normal file
2191
server/plugin/plg_backend_nfs4/repo/internal/nfs4.x
Normal file
File diff suppressed because it is too large
Load diff
17
server/plugin/plg_backend_nfs4/repo/internal/nfsconst.go
Normal file
17
server/plugin/plg_backend_nfs4/repo/internal/nfsconst.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package internal
|
||||
|
||||
/*
|
||||
const OPEN4_SHARE_ACCESS_READ = 0x00000001
|
||||
|
||||
const OPEN4_SHARE_ACCESS_WRITE = 0x00000002
|
||||
|
||||
const OPEN4_SHARE_ACCESS_BOTH = 0x00000003
|
||||
|
||||
const OPEN4_SHARE_DENY_NONE = 0x00000000
|
||||
|
||||
const OPEN4_SHARE_DENY_READ = 0x00000001
|
||||
|
||||
const OPEN4_SHARE_DENY_WRITE = 0x00000002
|
||||
|
||||
const OPEN4_SHARE_DENY_BOTH = 0x00000003
|
||||
*/
|
||||
917
server/plugin/plg_backend_nfs4/repo/internal/rpc.go
Normal file
917
server/plugin/plg_backend_nfs4/repo/internal/rpc.go
Normal file
|
|
@ -0,0 +1,917 @@
|
|||
// Code generated by goxdr -B -p nfs4 nfs4/rpc.x; DO NOT EDIT.
|
||||
|
||||
package internal
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var _ = fmt.Sprintf
|
||||
var _ context.Context
|
||||
|
||||
//
|
||||
// Data types defined in XDR file
|
||||
//
|
||||
|
||||
type Auth_flavor int32
|
||||
const (
|
||||
AUTH_NONE Auth_flavor = 0
|
||||
AUTH_SYS Auth_flavor = 1
|
||||
AUTH_SHORT Auth_flavor = 2
|
||||
AUTH_DH Auth_flavor = 3
|
||||
)
|
||||
|
||||
type Opaque_auth struct {
|
||||
Flavor Auth_flavor
|
||||
Body []byte // bound 400
|
||||
}
|
||||
|
||||
type Msg_type int32
|
||||
const (
|
||||
CALL Msg_type = 0
|
||||
REPLY Msg_type = 1
|
||||
)
|
||||
|
||||
/* A reply to a call message can take on two forms: the message was
|
||||
either accepted or rejected. */
|
||||
type Reply_stat int32
|
||||
const (
|
||||
MSG_ACCEPTED Reply_stat = 0
|
||||
MSG_DENIED Reply_stat = 1
|
||||
)
|
||||
|
||||
/* Given that a call message was accepted, the following is the status
|
||||
of an attempt to call a remote procedure. */
|
||||
type Accept_stat int32
|
||||
const (
|
||||
/* RPC executed successfully */
|
||||
SUCCESS Accept_stat = 0
|
||||
/* remote hasn't exported program */
|
||||
PROG_UNAVAIL Accept_stat = 1
|
||||
/* remote can't support version # */
|
||||
PROG_MISMATCH Accept_stat = 2
|
||||
/* program can't support procedure */
|
||||
PROC_UNAVAIL Accept_stat = 3
|
||||
/* procedure can't decode params */
|
||||
GARBAGE_ARGS Accept_stat = 4
|
||||
/* e.g. memory allocation failure */
|
||||
SYSTEM_ERR Accept_stat = 5
|
||||
)
|
||||
|
||||
/* Reasons why a call message was rejected: */
|
||||
type Reject_stat int32
|
||||
const (
|
||||
/* RPC version number != 2 */
|
||||
RPC_MISMATCH Reject_stat = 0
|
||||
/* remote can't authenticate caller */
|
||||
AUTH_ERROR Reject_stat = 1
|
||||
)
|
||||
|
||||
/* Why authentication failed: */
|
||||
type Auth_stat int32
|
||||
const (
|
||||
/* success */
|
||||
AUTH_OK Auth_stat = 0
|
||||
/* bad credential (seal broken) */
|
||||
AUTH_BADCRED Auth_stat = 1
|
||||
/* client must begin new session */
|
||||
AUTH_REJECTEDCRED Auth_stat = 2
|
||||
/* bad verifier (seal broken) */
|
||||
AUTH_BADVERF Auth_stat = 3
|
||||
/* verifier expired or replayed */
|
||||
AUTH_REJECTEDVERF Auth_stat = 4
|
||||
/* rejected for security reasons */
|
||||
AUTH_TOOWEAK Auth_stat = 5
|
||||
/* bogus response verifier */
|
||||
AUTH_INVALIDRESP Auth_stat = 6
|
||||
/* reason unknown */
|
||||
AUTH_FAILED Auth_stat = 7
|
||||
/* kerberos generic error */
|
||||
AUTH_KERB_GENERIC Auth_stat = 8
|
||||
/* time of credential expired */
|
||||
AUTH_TIMEEXPIRE Auth_stat = 9
|
||||
/* problem with ticket file */
|
||||
AUTH_TKT_FILE Auth_stat = 10
|
||||
/* can't decode authenticator */
|
||||
AUTH_DECODE Auth_stat = 11
|
||||
/* wrong net address in ticket */
|
||||
AUTH_NET_ADDR Auth_stat = 12
|
||||
/* no credentials for user */
|
||||
RPCSEC_GSS_CREDPROBLEM Auth_stat = 13
|
||||
/* problem with context */
|
||||
RPCSEC_GSS_CTXPROBLEM Auth_stat = 14
|
||||
)
|
||||
|
||||
/* Body of an RPC call: */
|
||||
type Call_body struct {
|
||||
/* must be equal to two (2) */
|
||||
Rpcvers uint32
|
||||
Prog uint32
|
||||
Vers uint32
|
||||
Proc uint32
|
||||
Cred Opaque_auth
|
||||
Verf Opaque_auth
|
||||
}
|
||||
|
||||
/* Reply to an RPC call that was accepted by the server: */
|
||||
type Accepted_reply struct {
|
||||
Verf Opaque_auth
|
||||
Reply_data XdrAnon_Accepted_reply_Reply_data
|
||||
}
|
||||
type XdrAnon_Accepted_reply_Reply_data struct {
|
||||
// The union discriminant Stat selects among the following arms:
|
||||
// SUCCESS:
|
||||
// Results() *[0]byte
|
||||
// PROG_MISMATCH:
|
||||
// Mismatch_info() *XdrAnon_Accepted_reply_Reply_data_Mismatch_info
|
||||
// default:
|
||||
// void
|
||||
Stat Accept_stat
|
||||
U interface{}
|
||||
}
|
||||
type XdrAnon_Accepted_reply_Reply_data_Mismatch_info struct {
|
||||
Low uint32
|
||||
High uint32
|
||||
}
|
||||
|
||||
/* Reply to an RPC call that was rejected by the server: */
|
||||
type Rejected_reply struct {
|
||||
// The union discriminant Stat selects among the following arms:
|
||||
// RPC_MISMATCH:
|
||||
// Mismatch_info() *XdrAnon_Rejected_reply_Mismatch_info
|
||||
// AUTH_ERROR:
|
||||
// Rj_why() *Auth_stat
|
||||
Stat Reject_stat
|
||||
U interface{}
|
||||
}
|
||||
type XdrAnon_Rejected_reply_Mismatch_info struct {
|
||||
Low uint32
|
||||
High uint32
|
||||
}
|
||||
|
||||
/* Body of a reply to an RPC call: */
|
||||
type Reply_body struct {
|
||||
// The union discriminant Stat selects among the following arms:
|
||||
// MSG_ACCEPTED:
|
||||
// Areply() *Accepted_reply
|
||||
// MSG_DENIED:
|
||||
// Rreply() *Rejected_reply
|
||||
Stat Reply_stat
|
||||
U interface{}
|
||||
}
|
||||
|
||||
/* The RPC message: */
|
||||
type Rpc_msg struct {
|
||||
Xid uint32
|
||||
Body XdrAnon_Rpc_msg_Body
|
||||
}
|
||||
type XdrAnon_Rpc_msg_Body struct {
|
||||
// The union discriminant Mtype selects among the following arms:
|
||||
// CALL:
|
||||
// Cbody() *Call_body
|
||||
// REPLY:
|
||||
// Rbody() *Reply_body
|
||||
Mtype Msg_type
|
||||
U interface{}
|
||||
}
|
||||
|
||||
//
|
||||
// Helper types and generated marshaling functions
|
||||
//
|
||||
|
||||
var _XdrNames_Auth_flavor = map[int32]string{
|
||||
int32(AUTH_NONE): "AUTH_NONE",
|
||||
int32(AUTH_SYS): "AUTH_SYS",
|
||||
int32(AUTH_SHORT): "AUTH_SHORT",
|
||||
int32(AUTH_DH): "AUTH_DH",
|
||||
int32(RPCSEC_GSS): "RPCSEC_GSS",
|
||||
}
|
||||
var _XdrValues_Auth_flavor = map[string]int32{
|
||||
"AUTH_NONE": int32(AUTH_NONE),
|
||||
"AUTH_SYS": int32(AUTH_SYS),
|
||||
"AUTH_SHORT": int32(AUTH_SHORT),
|
||||
"AUTH_DH": int32(AUTH_DH),
|
||||
"RPCSEC_GSS": int32(RPCSEC_GSS),
|
||||
}
|
||||
func (Auth_flavor) XdrEnumNames() map[int32]string {
|
||||
return _XdrNames_Auth_flavor
|
||||
}
|
||||
func (v Auth_flavor) String() string {
|
||||
if s, ok := _XdrNames_Auth_flavor[int32(v)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Auth_flavor#%d", v)
|
||||
}
|
||||
func (v *Auth_flavor) Scan(ss fmt.ScanState, _ rune) error {
|
||||
if tok, err := ss.Token(true, XdrSymChar); err != nil {
|
||||
return err
|
||||
} else {
|
||||
stok := string(tok)
|
||||
if val, ok := _XdrValues_Auth_flavor[stok]; ok {
|
||||
*v = Auth_flavor(val)
|
||||
return nil
|
||||
} else if stok == "Auth_flavor" {
|
||||
if n, err := fmt.Fscanf(ss, "#%d", (*int32)(v));
|
||||
n == 1 && err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return XdrError(fmt.Sprintf("%s is not a valid Auth_flavor.", stok))
|
||||
}
|
||||
}
|
||||
func (v Auth_flavor) GetU32() uint32 { return uint32(v) }
|
||||
func (v *Auth_flavor) SetU32(n uint32) { *v = Auth_flavor(n) }
|
||||
func (v *Auth_flavor) XdrPointer() interface{} { return v }
|
||||
func (Auth_flavor) XdrTypeName() string { return "Auth_flavor" }
|
||||
func (v Auth_flavor) XdrValue() interface{} { return v }
|
||||
func (v *Auth_flavor) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
type XdrType_Auth_flavor = *Auth_flavor
|
||||
func XDR_Auth_flavor(v *Auth_flavor) *Auth_flavor { return v }
|
||||
type XdrType_Opaque_auth = *Opaque_auth
|
||||
func (v *Opaque_auth) XdrPointer() interface{} { return v }
|
||||
func (Opaque_auth) XdrTypeName() string { return "Opaque_auth" }
|
||||
func (v Opaque_auth) XdrValue() interface{} { return v }
|
||||
func (v *Opaque_auth) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *Opaque_auth) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
x.Marshal(x.Sprintf("%sflavor", name), XDR_Auth_flavor(&v.Flavor))
|
||||
x.Marshal(x.Sprintf("%sbody", name), XdrVecOpaque{&v.Body, 400})
|
||||
}
|
||||
func XDR_Opaque_auth(v *Opaque_auth) *Opaque_auth { return v }
|
||||
var _XdrNames_Msg_type = map[int32]string{
|
||||
int32(CALL): "CALL",
|
||||
int32(REPLY): "REPLY",
|
||||
}
|
||||
var _XdrValues_Msg_type = map[string]int32{
|
||||
"CALL": int32(CALL),
|
||||
"REPLY": int32(REPLY),
|
||||
}
|
||||
func (Msg_type) XdrEnumNames() map[int32]string {
|
||||
return _XdrNames_Msg_type
|
||||
}
|
||||
func (v Msg_type) String() string {
|
||||
if s, ok := _XdrNames_Msg_type[int32(v)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Msg_type#%d", v)
|
||||
}
|
||||
func (v *Msg_type) Scan(ss fmt.ScanState, _ rune) error {
|
||||
if tok, err := ss.Token(true, XdrSymChar); err != nil {
|
||||
return err
|
||||
} else {
|
||||
stok := string(tok)
|
||||
if val, ok := _XdrValues_Msg_type[stok]; ok {
|
||||
*v = Msg_type(val)
|
||||
return nil
|
||||
} else if stok == "Msg_type" {
|
||||
if n, err := fmt.Fscanf(ss, "#%d", (*int32)(v));
|
||||
n == 1 && err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return XdrError(fmt.Sprintf("%s is not a valid Msg_type.", stok))
|
||||
}
|
||||
}
|
||||
func (v Msg_type) GetU32() uint32 { return uint32(v) }
|
||||
func (v *Msg_type) SetU32(n uint32) { *v = Msg_type(n) }
|
||||
func (v *Msg_type) XdrPointer() interface{} { return v }
|
||||
func (Msg_type) XdrTypeName() string { return "Msg_type" }
|
||||
func (v Msg_type) XdrValue() interface{} { return v }
|
||||
func (v *Msg_type) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
type XdrType_Msg_type = *Msg_type
|
||||
func XDR_Msg_type(v *Msg_type) *Msg_type { return v }
|
||||
var _XdrNames_Reply_stat = map[int32]string{
|
||||
int32(MSG_ACCEPTED): "MSG_ACCEPTED",
|
||||
int32(MSG_DENIED): "MSG_DENIED",
|
||||
}
|
||||
var _XdrValues_Reply_stat = map[string]int32{
|
||||
"MSG_ACCEPTED": int32(MSG_ACCEPTED),
|
||||
"MSG_DENIED": int32(MSG_DENIED),
|
||||
}
|
||||
func (Reply_stat) XdrEnumNames() map[int32]string {
|
||||
return _XdrNames_Reply_stat
|
||||
}
|
||||
func (v Reply_stat) String() string {
|
||||
if s, ok := _XdrNames_Reply_stat[int32(v)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Reply_stat#%d", v)
|
||||
}
|
||||
func (v *Reply_stat) Scan(ss fmt.ScanState, _ rune) error {
|
||||
if tok, err := ss.Token(true, XdrSymChar); err != nil {
|
||||
return err
|
||||
} else {
|
||||
stok := string(tok)
|
||||
if val, ok := _XdrValues_Reply_stat[stok]; ok {
|
||||
*v = Reply_stat(val)
|
||||
return nil
|
||||
} else if stok == "Reply_stat" {
|
||||
if n, err := fmt.Fscanf(ss, "#%d", (*int32)(v));
|
||||
n == 1 && err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return XdrError(fmt.Sprintf("%s is not a valid Reply_stat.", stok))
|
||||
}
|
||||
}
|
||||
func (v Reply_stat) GetU32() uint32 { return uint32(v) }
|
||||
func (v *Reply_stat) SetU32(n uint32) { *v = Reply_stat(n) }
|
||||
func (v *Reply_stat) XdrPointer() interface{} { return v }
|
||||
func (Reply_stat) XdrTypeName() string { return "Reply_stat" }
|
||||
func (v Reply_stat) XdrValue() interface{} { return v }
|
||||
func (v *Reply_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
type XdrType_Reply_stat = *Reply_stat
|
||||
func XDR_Reply_stat(v *Reply_stat) *Reply_stat { return v }
|
||||
var _XdrNames_Accept_stat = map[int32]string{
|
||||
int32(SUCCESS): "SUCCESS",
|
||||
int32(PROG_UNAVAIL): "PROG_UNAVAIL",
|
||||
int32(PROG_MISMATCH): "PROG_MISMATCH",
|
||||
int32(PROC_UNAVAIL): "PROC_UNAVAIL",
|
||||
int32(GARBAGE_ARGS): "GARBAGE_ARGS",
|
||||
int32(SYSTEM_ERR): "SYSTEM_ERR",
|
||||
}
|
||||
var _XdrValues_Accept_stat = map[string]int32{
|
||||
"SUCCESS": int32(SUCCESS),
|
||||
"PROG_UNAVAIL": int32(PROG_UNAVAIL),
|
||||
"PROG_MISMATCH": int32(PROG_MISMATCH),
|
||||
"PROC_UNAVAIL": int32(PROC_UNAVAIL),
|
||||
"GARBAGE_ARGS": int32(GARBAGE_ARGS),
|
||||
"SYSTEM_ERR": int32(SYSTEM_ERR),
|
||||
}
|
||||
func (Accept_stat) XdrEnumNames() map[int32]string {
|
||||
return _XdrNames_Accept_stat
|
||||
}
|
||||
func (v Accept_stat) String() string {
|
||||
if s, ok := _XdrNames_Accept_stat[int32(v)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Accept_stat#%d", v)
|
||||
}
|
||||
func (v *Accept_stat) Scan(ss fmt.ScanState, _ rune) error {
|
||||
if tok, err := ss.Token(true, XdrSymChar); err != nil {
|
||||
return err
|
||||
} else {
|
||||
stok := string(tok)
|
||||
if val, ok := _XdrValues_Accept_stat[stok]; ok {
|
||||
*v = Accept_stat(val)
|
||||
return nil
|
||||
} else if stok == "Accept_stat" {
|
||||
if n, err := fmt.Fscanf(ss, "#%d", (*int32)(v));
|
||||
n == 1 && err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return XdrError(fmt.Sprintf("%s is not a valid Accept_stat.", stok))
|
||||
}
|
||||
}
|
||||
func (v Accept_stat) GetU32() uint32 { return uint32(v) }
|
||||
func (v *Accept_stat) SetU32(n uint32) { *v = Accept_stat(n) }
|
||||
func (v *Accept_stat) XdrPointer() interface{} { return v }
|
||||
func (Accept_stat) XdrTypeName() string { return "Accept_stat" }
|
||||
func (v Accept_stat) XdrValue() interface{} { return v }
|
||||
func (v *Accept_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
type XdrType_Accept_stat = *Accept_stat
|
||||
func XDR_Accept_stat(v *Accept_stat) *Accept_stat { return v }
|
||||
var _XdrNames_Reject_stat = map[int32]string{
|
||||
int32(RPC_MISMATCH): "RPC_MISMATCH",
|
||||
int32(AUTH_ERROR): "AUTH_ERROR",
|
||||
}
|
||||
var _XdrValues_Reject_stat = map[string]int32{
|
||||
"RPC_MISMATCH": int32(RPC_MISMATCH),
|
||||
"AUTH_ERROR": int32(AUTH_ERROR),
|
||||
}
|
||||
func (Reject_stat) XdrEnumNames() map[int32]string {
|
||||
return _XdrNames_Reject_stat
|
||||
}
|
||||
func (v Reject_stat) String() string {
|
||||
if s, ok := _XdrNames_Reject_stat[int32(v)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Reject_stat#%d", v)
|
||||
}
|
||||
func (v *Reject_stat) Scan(ss fmt.ScanState, _ rune) error {
|
||||
if tok, err := ss.Token(true, XdrSymChar); err != nil {
|
||||
return err
|
||||
} else {
|
||||
stok := string(tok)
|
||||
if val, ok := _XdrValues_Reject_stat[stok]; ok {
|
||||
*v = Reject_stat(val)
|
||||
return nil
|
||||
} else if stok == "Reject_stat" {
|
||||
if n, err := fmt.Fscanf(ss, "#%d", (*int32)(v));
|
||||
n == 1 && err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return XdrError(fmt.Sprintf("%s is not a valid Reject_stat.", stok))
|
||||
}
|
||||
}
|
||||
func (v Reject_stat) GetU32() uint32 { return uint32(v) }
|
||||
func (v *Reject_stat) SetU32(n uint32) { *v = Reject_stat(n) }
|
||||
func (v *Reject_stat) XdrPointer() interface{} { return v }
|
||||
func (Reject_stat) XdrTypeName() string { return "Reject_stat" }
|
||||
func (v Reject_stat) XdrValue() interface{} { return v }
|
||||
func (v *Reject_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
type XdrType_Reject_stat = *Reject_stat
|
||||
func XDR_Reject_stat(v *Reject_stat) *Reject_stat { return v }
|
||||
var _XdrNames_Auth_stat = map[int32]string{
|
||||
int32(AUTH_OK): "AUTH_OK",
|
||||
int32(AUTH_BADCRED): "AUTH_BADCRED",
|
||||
int32(AUTH_REJECTEDCRED): "AUTH_REJECTEDCRED",
|
||||
int32(AUTH_BADVERF): "AUTH_BADVERF",
|
||||
int32(AUTH_REJECTEDVERF): "AUTH_REJECTEDVERF",
|
||||
int32(AUTH_TOOWEAK): "AUTH_TOOWEAK",
|
||||
int32(AUTH_INVALIDRESP): "AUTH_INVALIDRESP",
|
||||
int32(AUTH_FAILED): "AUTH_FAILED",
|
||||
int32(AUTH_KERB_GENERIC): "AUTH_KERB_GENERIC",
|
||||
int32(AUTH_TIMEEXPIRE): "AUTH_TIMEEXPIRE",
|
||||
int32(AUTH_TKT_FILE): "AUTH_TKT_FILE",
|
||||
int32(AUTH_DECODE): "AUTH_DECODE",
|
||||
int32(AUTH_NET_ADDR): "AUTH_NET_ADDR",
|
||||
int32(RPCSEC_GSS_CREDPROBLEM): "RPCSEC_GSS_CREDPROBLEM",
|
||||
int32(RPCSEC_GSS_CTXPROBLEM): "RPCSEC_GSS_CTXPROBLEM",
|
||||
}
|
||||
var _XdrValues_Auth_stat = map[string]int32{
|
||||
"AUTH_OK": int32(AUTH_OK),
|
||||
"AUTH_BADCRED": int32(AUTH_BADCRED),
|
||||
"AUTH_REJECTEDCRED": int32(AUTH_REJECTEDCRED),
|
||||
"AUTH_BADVERF": int32(AUTH_BADVERF),
|
||||
"AUTH_REJECTEDVERF": int32(AUTH_REJECTEDVERF),
|
||||
"AUTH_TOOWEAK": int32(AUTH_TOOWEAK),
|
||||
"AUTH_INVALIDRESP": int32(AUTH_INVALIDRESP),
|
||||
"AUTH_FAILED": int32(AUTH_FAILED),
|
||||
"AUTH_KERB_GENERIC": int32(AUTH_KERB_GENERIC),
|
||||
"AUTH_TIMEEXPIRE": int32(AUTH_TIMEEXPIRE),
|
||||
"AUTH_TKT_FILE": int32(AUTH_TKT_FILE),
|
||||
"AUTH_DECODE": int32(AUTH_DECODE),
|
||||
"AUTH_NET_ADDR": int32(AUTH_NET_ADDR),
|
||||
"RPCSEC_GSS_CREDPROBLEM": int32(RPCSEC_GSS_CREDPROBLEM),
|
||||
"RPCSEC_GSS_CTXPROBLEM": int32(RPCSEC_GSS_CTXPROBLEM),
|
||||
}
|
||||
func (Auth_stat) XdrEnumNames() map[int32]string {
|
||||
return _XdrNames_Auth_stat
|
||||
}
|
||||
func (v Auth_stat) String() string {
|
||||
if s, ok := _XdrNames_Auth_stat[int32(v)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Auth_stat#%d", v)
|
||||
}
|
||||
func (v *Auth_stat) Scan(ss fmt.ScanState, _ rune) error {
|
||||
if tok, err := ss.Token(true, XdrSymChar); err != nil {
|
||||
return err
|
||||
} else {
|
||||
stok := string(tok)
|
||||
if val, ok := _XdrValues_Auth_stat[stok]; ok {
|
||||
*v = Auth_stat(val)
|
||||
return nil
|
||||
} else if stok == "Auth_stat" {
|
||||
if n, err := fmt.Fscanf(ss, "#%d", (*int32)(v));
|
||||
n == 1 && err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return XdrError(fmt.Sprintf("%s is not a valid Auth_stat.", stok))
|
||||
}
|
||||
}
|
||||
func (v Auth_stat) GetU32() uint32 { return uint32(v) }
|
||||
func (v *Auth_stat) SetU32(n uint32) { *v = Auth_stat(n) }
|
||||
func (v *Auth_stat) XdrPointer() interface{} { return v }
|
||||
func (Auth_stat) XdrTypeName() string { return "Auth_stat" }
|
||||
func (v Auth_stat) XdrValue() interface{} { return v }
|
||||
func (v *Auth_stat) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
type XdrType_Auth_stat = *Auth_stat
|
||||
func XDR_Auth_stat(v *Auth_stat) *Auth_stat { return v }
|
||||
type XdrType_Call_body = *Call_body
|
||||
func (v *Call_body) XdrPointer() interface{} { return v }
|
||||
func (Call_body) XdrTypeName() string { return "Call_body" }
|
||||
func (v Call_body) XdrValue() interface{} { return v }
|
||||
func (v *Call_body) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *Call_body) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
x.Marshal(x.Sprintf("%srpcvers", name), XDR_uint32(&v.Rpcvers))
|
||||
x.Marshal(x.Sprintf("%sprog", name), XDR_uint32(&v.Prog))
|
||||
x.Marshal(x.Sprintf("%svers", name), XDR_uint32(&v.Vers))
|
||||
x.Marshal(x.Sprintf("%sproc", name), XDR_uint32(&v.Proc))
|
||||
x.Marshal(x.Sprintf("%scred", name), XDR_Opaque_auth(&v.Cred))
|
||||
x.Marshal(x.Sprintf("%sverf", name), XDR_Opaque_auth(&v.Verf))
|
||||
}
|
||||
func XDR_Call_body(v *Call_body) *Call_body { return v }
|
||||
type XdrType_XdrAnon_Accepted_reply_Reply_data_Mismatch_info = *XdrAnon_Accepted_reply_Reply_data_Mismatch_info
|
||||
func (v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrPointer() interface{} { return v }
|
||||
func (XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrTypeName() string { return "XdrAnon_Accepted_reply_Reply_data_Mismatch_info" }
|
||||
func (v XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrValue() interface{} { return v }
|
||||
func (v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
x.Marshal(x.Sprintf("%slow", name), XDR_uint32(&v.Low))
|
||||
x.Marshal(x.Sprintf("%shigh", name), XDR_uint32(&v.High))
|
||||
}
|
||||
func XDR_XdrAnon_Accepted_reply_Reply_data_Mismatch_info(v *XdrAnon_Accepted_reply_Reply_data_Mismatch_info) *XdrAnon_Accepted_reply_Reply_data_Mismatch_info { return v }
|
||||
type _XdrArray_0_opaque [0]byte
|
||||
func (v *_XdrArray_0_opaque) GetByteSlice() []byte { return v[:] }
|
||||
func (v *_XdrArray_0_opaque) XdrTypeName() string { return "opaque[]" }
|
||||
func (v *_XdrArray_0_opaque) XdrValue() interface{} { return v[:] }
|
||||
func (v *_XdrArray_0_opaque) XdrPointer() interface{} { return (*[0]byte)(v) }
|
||||
func (v *_XdrArray_0_opaque) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *_XdrArray_0_opaque) String() string { return fmt.Sprintf("%x", v[:]) }
|
||||
func (v *_XdrArray_0_opaque) Scan(ss fmt.ScanState, c rune) error {
|
||||
return XdrArrayOpaqueScan(v[:], ss, c)
|
||||
}
|
||||
func (_XdrArray_0_opaque) XdrArraySize() uint32 {
|
||||
const bound uint32 = 0 // Force error if not const or doesn't fit
|
||||
return bound
|
||||
}
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) Results() *[0]byte {
|
||||
switch u.Stat {
|
||||
case SUCCESS:
|
||||
if v, ok := u.U.(*[0]byte); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero [0]byte
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("XdrAnon_Accepted_reply_Reply_data.Results accessed when Stat == %v", u.Stat)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) Mismatch_info() *XdrAnon_Accepted_reply_Reply_data_Mismatch_info {
|
||||
switch u.Stat {
|
||||
case PROG_MISMATCH:
|
||||
if v, ok := u.U.(*XdrAnon_Accepted_reply_Reply_data_Mismatch_info); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero XdrAnon_Accepted_reply_Reply_data_Mismatch_info
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("XdrAnon_Accepted_reply_Reply_data.Mismatch_info accessed when Stat == %v", u.Stat)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u XdrAnon_Accepted_reply_Reply_data) XdrValid() bool {
|
||||
return true
|
||||
}
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionTag() XdrNum32 {
|
||||
return XDR_Accept_stat(&u.Stat)
|
||||
}
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionTagName() string {
|
||||
return "Stat"
|
||||
}
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionBody() XdrType {
|
||||
switch u.Stat {
|
||||
case SUCCESS:
|
||||
return (*_XdrArray_0_opaque)(u.Results())
|
||||
case PROG_MISMATCH:
|
||||
return XDR_XdrAnon_Accepted_reply_Reply_data_Mismatch_info(u.Mismatch_info())
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) XdrUnionBodyName() string {
|
||||
switch u.Stat {
|
||||
case SUCCESS:
|
||||
return "Results"
|
||||
case PROG_MISMATCH:
|
||||
return "Mismatch_info"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
type XdrType_XdrAnon_Accepted_reply_Reply_data = *XdrAnon_Accepted_reply_Reply_data
|
||||
func (v *XdrAnon_Accepted_reply_Reply_data) XdrPointer() interface{} { return v }
|
||||
func (XdrAnon_Accepted_reply_Reply_data) XdrTypeName() string { return "XdrAnon_Accepted_reply_Reply_data" }
|
||||
func (v XdrAnon_Accepted_reply_Reply_data) XdrValue() interface{} { return v }
|
||||
func (v *XdrAnon_Accepted_reply_Reply_data) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (u *XdrAnon_Accepted_reply_Reply_data) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
XDR_Accept_stat(&u.Stat).XdrMarshal(x, x.Sprintf("%sstat", name))
|
||||
switch u.Stat {
|
||||
case SUCCESS:
|
||||
x.Marshal(x.Sprintf("%sresults", name), (*_XdrArray_0_opaque)(u.Results()))
|
||||
return
|
||||
case PROG_MISMATCH:
|
||||
x.Marshal(x.Sprintf("%smismatch_info", name), XDR_XdrAnon_Accepted_reply_Reply_data_Mismatch_info(u.Mismatch_info()))
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
func XDR_XdrAnon_Accepted_reply_Reply_data(v *XdrAnon_Accepted_reply_Reply_data) *XdrAnon_Accepted_reply_Reply_data { return v}
|
||||
type XdrType_Accepted_reply = *Accepted_reply
|
||||
func (v *Accepted_reply) XdrPointer() interface{} { return v }
|
||||
func (Accepted_reply) XdrTypeName() string { return "Accepted_reply" }
|
||||
func (v Accepted_reply) XdrValue() interface{} { return v }
|
||||
func (v *Accepted_reply) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *Accepted_reply) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
x.Marshal(x.Sprintf("%sverf", name), XDR_Opaque_auth(&v.Verf))
|
||||
x.Marshal(x.Sprintf("%sreply_data", name), XDR_XdrAnon_Accepted_reply_Reply_data(&v.Reply_data))
|
||||
}
|
||||
func XDR_Accepted_reply(v *Accepted_reply) *Accepted_reply { return v }
|
||||
type XdrType_XdrAnon_Rejected_reply_Mismatch_info = *XdrAnon_Rejected_reply_Mismatch_info
|
||||
func (v *XdrAnon_Rejected_reply_Mismatch_info) XdrPointer() interface{} { return v }
|
||||
func (XdrAnon_Rejected_reply_Mismatch_info) XdrTypeName() string { return "XdrAnon_Rejected_reply_Mismatch_info" }
|
||||
func (v XdrAnon_Rejected_reply_Mismatch_info) XdrValue() interface{} { return v }
|
||||
func (v *XdrAnon_Rejected_reply_Mismatch_info) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *XdrAnon_Rejected_reply_Mismatch_info) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
x.Marshal(x.Sprintf("%slow", name), XDR_uint32(&v.Low))
|
||||
x.Marshal(x.Sprintf("%shigh", name), XDR_uint32(&v.High))
|
||||
}
|
||||
func XDR_XdrAnon_Rejected_reply_Mismatch_info(v *XdrAnon_Rejected_reply_Mismatch_info) *XdrAnon_Rejected_reply_Mismatch_info { return v }
|
||||
var _XdrTags_Rejected_reply = map[int32]bool{
|
||||
XdrToI32(RPC_MISMATCH): true,
|
||||
XdrToI32(AUTH_ERROR): true,
|
||||
}
|
||||
func (_ Rejected_reply) XdrValidTags() map[int32]bool {
|
||||
return _XdrTags_Rejected_reply
|
||||
}
|
||||
func (u *Rejected_reply) Mismatch_info() *XdrAnon_Rejected_reply_Mismatch_info {
|
||||
switch u.Stat {
|
||||
case RPC_MISMATCH:
|
||||
if v, ok := u.U.(*XdrAnon_Rejected_reply_Mismatch_info); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero XdrAnon_Rejected_reply_Mismatch_info
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("Rejected_reply.Mismatch_info accessed when Stat == %v", u.Stat)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u *Rejected_reply) Rj_why() *Auth_stat {
|
||||
switch u.Stat {
|
||||
case AUTH_ERROR:
|
||||
if v, ok := u.U.(*Auth_stat); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero Auth_stat
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("Rejected_reply.Rj_why accessed when Stat == %v", u.Stat)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u Rejected_reply) XdrValid() bool {
|
||||
switch u.Stat {
|
||||
case RPC_MISMATCH,AUTH_ERROR:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (u *Rejected_reply) XdrUnionTag() XdrNum32 {
|
||||
return XDR_Reject_stat(&u.Stat)
|
||||
}
|
||||
func (u *Rejected_reply) XdrUnionTagName() string {
|
||||
return "Stat"
|
||||
}
|
||||
func (u *Rejected_reply) XdrUnionBody() XdrType {
|
||||
switch u.Stat {
|
||||
case RPC_MISMATCH:
|
||||
return XDR_XdrAnon_Rejected_reply_Mismatch_info(u.Mismatch_info())
|
||||
case AUTH_ERROR:
|
||||
return XDR_Auth_stat(u.Rj_why())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *Rejected_reply) XdrUnionBodyName() string {
|
||||
switch u.Stat {
|
||||
case RPC_MISMATCH:
|
||||
return "Mismatch_info"
|
||||
case AUTH_ERROR:
|
||||
return "Rj_why"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
type XdrType_Rejected_reply = *Rejected_reply
|
||||
func (v *Rejected_reply) XdrPointer() interface{} { return v }
|
||||
func (Rejected_reply) XdrTypeName() string { return "Rejected_reply" }
|
||||
func (v Rejected_reply) XdrValue() interface{} { return v }
|
||||
func (v *Rejected_reply) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (u *Rejected_reply) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
XDR_Reject_stat(&u.Stat).XdrMarshal(x, x.Sprintf("%sstat", name))
|
||||
switch u.Stat {
|
||||
case RPC_MISMATCH:
|
||||
x.Marshal(x.Sprintf("%smismatch_info", name), XDR_XdrAnon_Rejected_reply_Mismatch_info(u.Mismatch_info()))
|
||||
return
|
||||
case AUTH_ERROR:
|
||||
x.Marshal(x.Sprintf("%srj_why", name), XDR_Auth_stat(u.Rj_why()))
|
||||
return
|
||||
}
|
||||
XdrPanic("invalid Stat (%v) in Rejected_reply", u.Stat)
|
||||
}
|
||||
func XDR_Rejected_reply(v *Rejected_reply) *Rejected_reply { return v}
|
||||
var _XdrTags_Reply_body = map[int32]bool{
|
||||
XdrToI32(MSG_ACCEPTED): true,
|
||||
XdrToI32(MSG_DENIED): true,
|
||||
}
|
||||
func (_ Reply_body) XdrValidTags() map[int32]bool {
|
||||
return _XdrTags_Reply_body
|
||||
}
|
||||
func (u *Reply_body) Areply() *Accepted_reply {
|
||||
switch u.Stat {
|
||||
case MSG_ACCEPTED:
|
||||
if v, ok := u.U.(*Accepted_reply); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero Accepted_reply
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("Reply_body.Areply accessed when Stat == %v", u.Stat)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u *Reply_body) Rreply() *Rejected_reply {
|
||||
switch u.Stat {
|
||||
case MSG_DENIED:
|
||||
if v, ok := u.U.(*Rejected_reply); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero Rejected_reply
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("Reply_body.Rreply accessed when Stat == %v", u.Stat)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u Reply_body) XdrValid() bool {
|
||||
switch u.Stat {
|
||||
case MSG_ACCEPTED,MSG_DENIED:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (u *Reply_body) XdrUnionTag() XdrNum32 {
|
||||
return XDR_Reply_stat(&u.Stat)
|
||||
}
|
||||
func (u *Reply_body) XdrUnionTagName() string {
|
||||
return "Stat"
|
||||
}
|
||||
func (u *Reply_body) XdrUnionBody() XdrType {
|
||||
switch u.Stat {
|
||||
case MSG_ACCEPTED:
|
||||
return XDR_Accepted_reply(u.Areply())
|
||||
case MSG_DENIED:
|
||||
return XDR_Rejected_reply(u.Rreply())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *Reply_body) XdrUnionBodyName() string {
|
||||
switch u.Stat {
|
||||
case MSG_ACCEPTED:
|
||||
return "Areply"
|
||||
case MSG_DENIED:
|
||||
return "Rreply"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
type XdrType_Reply_body = *Reply_body
|
||||
func (v *Reply_body) XdrPointer() interface{} { return v }
|
||||
func (Reply_body) XdrTypeName() string { return "Reply_body" }
|
||||
func (v Reply_body) XdrValue() interface{} { return v }
|
||||
func (v *Reply_body) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (u *Reply_body) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
XDR_Reply_stat(&u.Stat).XdrMarshal(x, x.Sprintf("%sstat", name))
|
||||
switch u.Stat {
|
||||
case MSG_ACCEPTED:
|
||||
x.Marshal(x.Sprintf("%sareply", name), XDR_Accepted_reply(u.Areply()))
|
||||
return
|
||||
case MSG_DENIED:
|
||||
x.Marshal(x.Sprintf("%srreply", name), XDR_Rejected_reply(u.Rreply()))
|
||||
return
|
||||
}
|
||||
XdrPanic("invalid Stat (%v) in Reply_body", u.Stat)
|
||||
}
|
||||
func XDR_Reply_body(v *Reply_body) *Reply_body { return v}
|
||||
var _XdrTags_XdrAnon_Rpc_msg_Body = map[int32]bool{
|
||||
XdrToI32(CALL): true,
|
||||
XdrToI32(REPLY): true,
|
||||
}
|
||||
func (_ XdrAnon_Rpc_msg_Body) XdrValidTags() map[int32]bool {
|
||||
return _XdrTags_XdrAnon_Rpc_msg_Body
|
||||
}
|
||||
func (u *XdrAnon_Rpc_msg_Body) Cbody() *Call_body {
|
||||
switch u.Mtype {
|
||||
case CALL:
|
||||
if v, ok := u.U.(*Call_body); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero Call_body
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("XdrAnon_Rpc_msg_Body.Cbody accessed when Mtype == %v", u.Mtype)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u *XdrAnon_Rpc_msg_Body) Rbody() *Reply_body {
|
||||
switch u.Mtype {
|
||||
case REPLY:
|
||||
if v, ok := u.U.(*Reply_body); ok {
|
||||
return v
|
||||
} else {
|
||||
var zero Reply_body
|
||||
u.U = &zero
|
||||
return &zero
|
||||
}
|
||||
default:
|
||||
XdrPanic("XdrAnon_Rpc_msg_Body.Rbody accessed when Mtype == %v", u.Mtype)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func (u XdrAnon_Rpc_msg_Body) XdrValid() bool {
|
||||
switch u.Mtype {
|
||||
case CALL,REPLY:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (u *XdrAnon_Rpc_msg_Body) XdrUnionTag() XdrNum32 {
|
||||
return XDR_Msg_type(&u.Mtype)
|
||||
}
|
||||
func (u *XdrAnon_Rpc_msg_Body) XdrUnionTagName() string {
|
||||
return "Mtype"
|
||||
}
|
||||
func (u *XdrAnon_Rpc_msg_Body) XdrUnionBody() XdrType {
|
||||
switch u.Mtype {
|
||||
case CALL:
|
||||
return XDR_Call_body(u.Cbody())
|
||||
case REPLY:
|
||||
return XDR_Reply_body(u.Rbody())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *XdrAnon_Rpc_msg_Body) XdrUnionBodyName() string {
|
||||
switch u.Mtype {
|
||||
case CALL:
|
||||
return "Cbody"
|
||||
case REPLY:
|
||||
return "Rbody"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
type XdrType_XdrAnon_Rpc_msg_Body = *XdrAnon_Rpc_msg_Body
|
||||
func (v *XdrAnon_Rpc_msg_Body) XdrPointer() interface{} { return v }
|
||||
func (XdrAnon_Rpc_msg_Body) XdrTypeName() string { return "XdrAnon_Rpc_msg_Body" }
|
||||
func (v XdrAnon_Rpc_msg_Body) XdrValue() interface{} { return v }
|
||||
func (v *XdrAnon_Rpc_msg_Body) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (u *XdrAnon_Rpc_msg_Body) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
XDR_Msg_type(&u.Mtype).XdrMarshal(x, x.Sprintf("%smtype", name))
|
||||
switch u.Mtype {
|
||||
case CALL:
|
||||
x.Marshal(x.Sprintf("%scbody", name), XDR_Call_body(u.Cbody()))
|
||||
return
|
||||
case REPLY:
|
||||
x.Marshal(x.Sprintf("%srbody", name), XDR_Reply_body(u.Rbody()))
|
||||
return
|
||||
}
|
||||
XdrPanic("invalid Mtype (%v) in XdrAnon_Rpc_msg_Body", u.Mtype)
|
||||
}
|
||||
func XDR_XdrAnon_Rpc_msg_Body(v *XdrAnon_Rpc_msg_Body) *XdrAnon_Rpc_msg_Body { return v}
|
||||
type XdrType_Rpc_msg = *Rpc_msg
|
||||
func (v *Rpc_msg) XdrPointer() interface{} { return v }
|
||||
func (Rpc_msg) XdrTypeName() string { return "Rpc_msg" }
|
||||
func (v Rpc_msg) XdrValue() interface{} { return v }
|
||||
func (v *Rpc_msg) XdrMarshal(x XDR, name string) { x.Marshal(name, v) }
|
||||
func (v *Rpc_msg) XdrRecurse(x XDR, name string) {
|
||||
if name != "" {
|
||||
name = x.Sprintf("%s.", name)
|
||||
}
|
||||
x.Marshal(x.Sprintf("%sxid", name), XDR_uint32(&v.Xid))
|
||||
x.Marshal(x.Sprintf("%sbody", name), XDR_XdrAnon_Rpc_msg_Body(&v.Body))
|
||||
}
|
||||
func XDR_Rpc_msg(v *Rpc_msg) *Rpc_msg { return v }
|
||||
138
server/plugin/plg_backend_nfs4/repo/internal/rpc.x
Normal file
138
server/plugin/plg_backend_nfs4/repo/internal/rpc.x
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/* RPC message format as defined by RFC 5531 */
|
||||
|
||||
enum auth_flavor {
|
||||
AUTH_NONE = 0,
|
||||
AUTH_SYS = 1,
|
||||
AUTH_SHORT = 2,
|
||||
AUTH_DH = 3,
|
||||
RPCSEC_GSS = 6
|
||||
};
|
||||
|
||||
struct opaque_auth {
|
||||
auth_flavor flavor;
|
||||
opaque body<400>;
|
||||
};
|
||||
|
||||
enum msg_type {
|
||||
CALL = 0,
|
||||
REPLY = 1
|
||||
};
|
||||
|
||||
/* A reply to a call message can take on two forms: the message was
|
||||
either accepted or rejected. */
|
||||
enum reply_stat {
|
||||
MSG_ACCEPTED = 0,
|
||||
MSG_DENIED = 1
|
||||
};
|
||||
|
||||
/* Given that a call message was accepted, the following is the status
|
||||
of an attempt to call a remote procedure. */
|
||||
enum accept_stat {
|
||||
SUCCESS = 0, /* RPC executed successfully */
|
||||
PROG_UNAVAIL = 1, /* remote hasn't exported program */
|
||||
PROG_MISMATCH = 2, /* remote can't support version # */
|
||||
PROC_UNAVAIL = 3, /* program can't support procedure */
|
||||
GARBAGE_ARGS = 4, /* procedure can't decode params */
|
||||
SYSTEM_ERR = 5 /* e.g. memory allocation failure */
|
||||
};
|
||||
|
||||
/* Reasons why a call message was rejected: */
|
||||
enum reject_stat {
|
||||
RPC_MISMATCH = 0, /* RPC version number != 2 */
|
||||
AUTH_ERROR = 1 /* remote can't authenticate caller */
|
||||
};
|
||||
|
||||
/* Why authentication failed: */
|
||||
enum auth_stat {
|
||||
AUTH_OK = 0, /* success */
|
||||
/*
|
||||
* failed at remote end
|
||||
*/
|
||||
AUTH_BADCRED = 1, /* bad credential (seal broken) */
|
||||
AUTH_REJECTEDCRED = 2, /* client must begin new session */
|
||||
AUTH_BADVERF = 3, /* bad verifier (seal broken) */
|
||||
AUTH_REJECTEDVERF = 4, /* verifier expired or replayed */
|
||||
AUTH_TOOWEAK = 5, /* rejected for security reasons */
|
||||
/*
|
||||
* failed locally
|
||||
*/
|
||||
AUTH_INVALIDRESP = 6, /* bogus response verifier */
|
||||
AUTH_FAILED = 7, /* reason unknown */
|
||||
/*
|
||||
* AUTH_KERB errors; deprecated. See [RFC2695]
|
||||
*/
|
||||
AUTH_KERB_GENERIC = 8, /* kerberos generic error */
|
||||
AUTH_TIMEEXPIRE = 9, /* time of credential expired */
|
||||
AUTH_TKT_FILE = 10, /* problem with ticket file */
|
||||
AUTH_DECODE = 11, /* can't decode authenticator */
|
||||
AUTH_NET_ADDR = 12, /* wrong net address in ticket */
|
||||
/*
|
||||
* RPCSEC_GSS GSS related errors
|
||||
*/
|
||||
RPCSEC_GSS_CREDPROBLEM = 13, /* no credentials for user */
|
||||
RPCSEC_GSS_CTXPROBLEM = 14 /* problem with context */
|
||||
};
|
||||
|
||||
/* Body of an RPC call: */
|
||||
struct call_body {
|
||||
unsigned int rpcvers; /* must be equal to two (2) */
|
||||
unsigned int prog;
|
||||
unsigned int vers;
|
||||
unsigned int proc;
|
||||
opaque_auth cred;
|
||||
opaque_auth verf;
|
||||
/* procedure-specific parameters start here */
|
||||
};
|
||||
|
||||
/* Reply to an RPC call that was accepted by the server: */
|
||||
struct accepted_reply {
|
||||
opaque_auth verf;
|
||||
union switch (accept_stat stat) {
|
||||
case SUCCESS:
|
||||
opaque results[0];
|
||||
/*
|
||||
* procedure-specific results start here
|
||||
*/
|
||||
case PROG_MISMATCH:
|
||||
struct {
|
||||
unsigned int low;
|
||||
unsigned int high;
|
||||
} mismatch_info;
|
||||
default:
|
||||
/*
|
||||
* Void. Cases include PROG_UNAVAIL, PROC_UNAVAIL,
|
||||
* GARBAGE_ARGS, and SYSTEM_ERR.
|
||||
*/
|
||||
void;
|
||||
} reply_data;
|
||||
};
|
||||
|
||||
/* Reply to an RPC call that was rejected by the server: */
|
||||
union rejected_reply switch (reject_stat stat) {
|
||||
case RPC_MISMATCH:
|
||||
struct {
|
||||
unsigned int low;
|
||||
unsigned int high;
|
||||
} mismatch_info;
|
||||
case AUTH_ERROR:
|
||||
auth_stat rj_why;
|
||||
};
|
||||
|
||||
/* Body of a reply to an RPC call: */
|
||||
union reply_body switch (reply_stat stat) {
|
||||
case MSG_ACCEPTED:
|
||||
accepted_reply areply;
|
||||
case MSG_DENIED:
|
||||
rejected_reply rreply;
|
||||
};
|
||||
|
||||
/* The RPC message: */
|
||||
struct rpc_msg {
|
||||
unsigned int xid;
|
||||
union switch (msg_type mtype) {
|
||||
case CALL:
|
||||
call_body cbody;
|
||||
case REPLY:
|
||||
reply_body rbody;
|
||||
} body;
|
||||
};
|
||||
28
server/plugin/plg_backend_nfs4/repo/internal/types.go
Normal file
28
server/plugin/plg_backend_nfs4/repo/internal/types.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package internal
|
||||
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type Uint32_t = uint32
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type Uint64_t = uint64
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type Int64_t = int64
|
||||
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type XDR_Uint32_t = *XdrUint32
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type XdrType_Uint32_t = XdrType_uint32
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type XDR_Uint64_t = *XdrUint64
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type XdrType_Uint64_t = XdrType_uint64
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type XdrType_Int64_t = XdrType_int64
|
||||
//goland:noinspection GoSnakeCaseUsage
|
||||
type XDR_Int64_t = *XdrInt64
|
||||
|
||||
func MinUint64(i1, i2 uint64) uint64 {
|
||||
if i1 < i2 {
|
||||
return i1
|
||||
}
|
||||
return i2
|
||||
}
|
||||
903
server/plugin/plg_backend_nfs4/repo/nfs4/client.go
Normal file
903
server/plugin/plg_backend_nfs4/repo/nfs4/client.go
Normal file
|
|
@ -0,0 +1,903 @@
|
|||
package nfs4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/mickael-kerjean/filestash/server/plugin/plg_backend_nfs4/repo/internal"
|
||||
)
|
||||
|
||||
const NfsReadBlockLen = 512 * 1024
|
||||
|
||||
var standardNfsAttrs = Bitmap4{
|
||||
1<<FATTR4_TYPE | 1<<FATTR4_SIZE,
|
||||
1 << (FATTR4_TIME_MODIFY - 32),
|
||||
}
|
||||
|
||||
type NfsInterface interface {
|
||||
Ping() error
|
||||
Close()
|
||||
|
||||
GetFileList(path string) ([]FileInfo, error)
|
||||
GetFileInfo(path string) (FileInfo, error)
|
||||
ReadFileAll(path string, writer io.Writer) (uint64, error)
|
||||
ReadFile(path string, offset, count uint64, writer io.Writer) (uint64, error)
|
||||
|
||||
ReWriteFile(path string, reader io.Reader) (written uint64, err error)
|
||||
WriteFile(path string, truncate bool, offset uint64, reader io.Reader) (written uint64, err error)
|
||||
|
||||
DeleteFile(path string) error
|
||||
MakePath(path string) error
|
||||
}
|
||||
type NfsClient struct {
|
||||
conn net.Conn
|
||||
xid uint32
|
||||
|
||||
nfsSeqId uint32
|
||||
|
||||
authType Auth_flavor
|
||||
authData []byte
|
||||
|
||||
clientId string
|
||||
clientIdShort uint64
|
||||
openConfirmed bool
|
||||
|
||||
rootFh Nfs_fh4
|
||||
}
|
||||
|
||||
var _ NfsInterface = &NfsClient{}
|
||||
|
||||
type AuthParams struct {
|
||||
Uid, Gid uint32
|
||||
MachineName string
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
IsDir bool
|
||||
Size uint64
|
||||
Mtime time.Time
|
||||
}
|
||||
|
||||
// Create the NFS client with the specified parameters. The `server` string must include port.
|
||||
// The default NFS port is 2049. E.g.: "127.0.0.1:2049".
|
||||
// `auth` should contain a fairly unique MachineId to make sure clients can be distinguished.
|
||||
func NewNfsClient(ctx context.Context, server string, auth AuthParams) (*NfsClient, error) {
|
||||
d := net.Dialer{}
|
||||
conn, err := d.DialContext(ctx, "tcp", server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNfsClientWithConn(conn, auth)
|
||||
}
|
||||
|
||||
func NewNfsClientWithConn(conn net.Conn, auth AuthParams) (*NfsClient, error) {
|
||||
clientId, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
randId := make([]byte, 8)
|
||||
_, _ = rand.Read(randId)
|
||||
clientId += "-" + hex.EncodeToString(randId)
|
||||
|
||||
cli := &NfsClient{
|
||||
conn: conn,
|
||||
authType: AUTH_SYS,
|
||||
authData: makeAuthData(auth),
|
||||
nfsSeqId: 0,
|
||||
clientId: clientId,
|
||||
}
|
||||
cl := NewCleanup(cli.Close)
|
||||
defer cl.Cleanup()
|
||||
|
||||
err = cli.Ping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cli.setClientId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cli.retrieveRootFh()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cl.Disarm()
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) Close() {
|
||||
_ = c.conn.Close()
|
||||
}
|
||||
|
||||
func makeAuthData(auth AuthParams) []byte {
|
||||
machName := auth.MachineName
|
||||
if len(machName) > 255 {
|
||||
machName = machName[0:255]
|
||||
}
|
||||
|
||||
ap := Authsys_parms{
|
||||
Machinename: machName,
|
||||
Uid: auth.Uid,
|
||||
Gid: auth.Gid,
|
||||
Gids: nil,
|
||||
}
|
||||
|
||||
// Fill the machine ID (needs not to be super-unique but nice to have them distinct)
|
||||
_ = binary.Read(rand.Reader, binary.LittleEndian, &ap.Stamp)
|
||||
|
||||
apBuf := bytes.NewBuffer([]byte{})
|
||||
XdrOut{Out: apBuf}.Marshal("", &ap)
|
||||
|
||||
return apBuf.Bytes()
|
||||
}
|
||||
|
||||
func (c *NfsClient) sendMessage(proc XdrProc) (xid uint32, err error) {
|
||||
xid = c.xid
|
||||
c.xid++
|
||||
|
||||
msg := Rpc_msg{
|
||||
Xid: xid,
|
||||
Body: XdrAnon_Rpc_msg_Body{
|
||||
Mtype: CALL,
|
||||
U: &Call_body{
|
||||
Rpcvers: 2,
|
||||
Prog: proc.Prog(),
|
||||
Vers: proc.Vers(),
|
||||
Proc: proc.Proc(),
|
||||
Cred: Opaque_auth{
|
||||
Flavor: c.authType,
|
||||
Body: c.authData,
|
||||
},
|
||||
Verf: Opaque_auth{
|
||||
Flavor: AUTH_NONE,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// The marshaller for some reason loves to panic.
|
||||
defer func() {
|
||||
if i := recover(); i != nil {
|
||||
if e, ok := i.(XdrError); ok {
|
||||
err = e
|
||||
} else {
|
||||
panic(i)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
out := XdrOut{Out: buffer}
|
||||
out.Marshal("", &msg)
|
||||
if _, ok := proc.GetArg().(XdrType_void); !ok {
|
||||
out.Marshal("", proc.GetArg())
|
||||
}
|
||||
|
||||
// Yep, the RPC protocol requires this strange OR
|
||||
err = binary.Write(c.conn, binary.BigEndian, 0x80000000|uint32(buffer.Len()))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = c.conn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *NfsClient) readNfsMessage(result XdrType) (xid uint32, err error) {
|
||||
lenBuf := make([]byte, 4)
|
||||
_, err = io.ReadFull(c.conn, lenBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// The RPC protocol sets the MSB to 1 for length fields. Don't ask me why.
|
||||
msgLen := binary.BigEndian.Uint32(lenBuf) & 0x7fffffff
|
||||
|
||||
msgBuf := make([]byte, msgLen)
|
||||
_, err = io.ReadFull(c.conn, msgBuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// The unmarshaller for some reason loves to panic. Sigh.
|
||||
defer func() {
|
||||
if i := recover(); i != nil {
|
||||
if e, ok := i.(XdrError); ok {
|
||||
err = e
|
||||
} else {
|
||||
panic(i)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
reply := Rpc_msg{}
|
||||
in := XdrIn{In: bytes.NewReader(msgBuf)}
|
||||
in.Marshal("", &reply)
|
||||
if _, ok := result.(XdrType_void); !ok {
|
||||
in.Marshal("", result)
|
||||
}
|
||||
|
||||
if !c.isRpcSuccess(&reply) {
|
||||
err = fmt.Errorf("RPC error: %s", c.getRpcError(&reply))
|
||||
return
|
||||
}
|
||||
xid = reply.Xid
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns true iff msg is an accepted REPLY with status SUCCESS.
|
||||
func (c *NfsClient) isRpcSuccess(msg *Rpc_msg) bool {
|
||||
return msg != nil &&
|
||||
msg.Body.Mtype == REPLY &&
|
||||
msg.Body.Rbody().Stat == MSG_ACCEPTED &&
|
||||
msg.Body.Rbody().Areply().Reply_data.Stat == SUCCESS
|
||||
}
|
||||
|
||||
// An *Rpc_msg can represent an error. Call IsSuccess to see if there
|
||||
// was actually an error.
|
||||
func (c *NfsClient) getRpcError(m *Rpc_msg) string {
|
||||
if m.Body.Mtype != REPLY {
|
||||
return "RPC message not a REPLY"
|
||||
} else if m.Body.Rbody().Stat == MSG_ACCEPTED {
|
||||
stat := m.Body.Rbody().Areply().Reply_data.Stat
|
||||
c := stat.String()
|
||||
if stat == PROG_MISMATCH {
|
||||
mmi := m.Body.Rbody().Areply().Reply_data.Mismatch_info()
|
||||
c = fmt.Sprintf("%s (low %d, high %d)", c, mmi.Low, mmi.High)
|
||||
}
|
||||
return c
|
||||
} else if m.Body.Rbody().Stat == MSG_DENIED {
|
||||
stat := m.Body.Rbody().Rreply().Stat
|
||||
c := stat.String()
|
||||
return c
|
||||
}
|
||||
return "Invalid reply_stat"
|
||||
}
|
||||
|
||||
func (c *NfsClient) Ping() error {
|
||||
nullProc := XdrProc_NFSPROC4_NULL{}
|
||||
|
||||
xid, err := c.sendMessage(&nullProc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xidRes, err := c.readNfsMessage(nullProc.GetRes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if xidRes != xid {
|
||||
return fmt.Errorf("mismathced xids: %d and %d", xid, xidRes)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) runNfsTransaction(ops []Nfs_argop4, pathHint string) ([]Nfs_resop4, error) {
|
||||
compound := XdrProc_NFSPROC4_COMPOUND{}
|
||||
|
||||
args := compound.GetArg().(*COMPOUND4args)
|
||||
args.Argarray = ops
|
||||
|
||||
xid, err := c.sendMessage(&compound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xid2, err := c.readNfsMessage(compound.GetRes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xid != xid2 {
|
||||
return nil, fmt.Errorf("xids don't match: %d and %d", xid, xid2)
|
||||
}
|
||||
|
||||
res := compound.GetRes().(*COMPOUND4res)
|
||||
// TODO: translate the error better
|
||||
if res.Status != NFS4_OK {
|
||||
return nil, &NfsError{
|
||||
Path: pathHint,
|
||||
ErrorCode: NfsErrorCode(res.Status),
|
||||
ErrorString: fmt.Sprintf("NFS error: %s (%d), path='%s'",
|
||||
res.Status.String(), int32(res.Status), pathHint),
|
||||
}
|
||||
}
|
||||
|
||||
return res.Resarray, nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) setClientId() error {
|
||||
res, err := c.runNfsTransaction([]Nfs_argop4{{
|
||||
Argop: OP_SETCLIENTID,
|
||||
U: &SETCLIENTID4args{
|
||||
Client: Nfs_client_id4{
|
||||
Verifier: Verifier4{},
|
||||
Id: []byte(c.clientId),
|
||||
},
|
||||
Callback: Cb_client4{},
|
||||
Callback_ident: 0,
|
||||
},
|
||||
}}, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resOk := res[0].Opsetclientid().Resok4()
|
||||
c.clientIdShort = resOk.Clientid
|
||||
|
||||
_, err = c.runNfsTransaction([]Nfs_argop4{{
|
||||
Argop: OP_SETCLIENTID_CONFIRM,
|
||||
U: &SETCLIENTID_CONFIRM4args{
|
||||
Clientid: resOk.Clientid,
|
||||
Setclientid_confirm: resOk.Setclientid_confirm,
|
||||
},
|
||||
}}, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) retrieveRootFh() error {
|
||||
res, err := c.runNfsTransaction([]Nfs_argop4{
|
||||
{
|
||||
Argop: OP_PUTROOTFH,
|
||||
},
|
||||
{
|
||||
Argop: OP_GETFH,
|
||||
},
|
||||
}, "/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.rootFh = res[1].Opgetfh().Resok4().Object
|
||||
return nil
|
||||
}
|
||||
|
||||
func splitPath(path string) []string {
|
||||
splits := strings.Split(path, "/")
|
||||
curPos := 0
|
||||
for _, s := range splits {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
splits[curPos] = s
|
||||
curPos++
|
||||
}
|
||||
return splits[0:curPos]
|
||||
}
|
||||
|
||||
func (c *NfsClient) GetFileList(path string) ([]FileInfo, error) {
|
||||
var args = c.makePathLookupArgs(splitPath(path))
|
||||
|
||||
args = append(args,
|
||||
Nfs_argop4{Argop: OP_GETFH},
|
||||
Nfs_argop4{Argop: OP_READDIR,
|
||||
U: &READDIR4args{
|
||||
Cookie: 0,
|
||||
Cookieverf: Verifier4{},
|
||||
Dircount: 1024 * 128,
|
||||
Maxcount: 1024 * 128,
|
||||
Attr_request: standardNfsAttrs,
|
||||
}},
|
||||
)
|
||||
|
||||
res, err := c.runNfsTransaction(args, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fileList []FileInfo
|
||||
|
||||
dirFh := res[len(res)-2].Opgetfh().Resok4().Object
|
||||
|
||||
curDirList := res[len(res)-1].Opreaddir().Resok4()
|
||||
for {
|
||||
ent := curDirList.Reply.Entries
|
||||
if ent == nil {
|
||||
break
|
||||
}
|
||||
for {
|
||||
fileList = append(fileList, c.translateFileMeta(string(ent.Name), ent.Attrs))
|
||||
if ent.Nextentry == nil {
|
||||
break
|
||||
}
|
||||
ent = ent.Nextentry
|
||||
}
|
||||
|
||||
if curDirList.Reply.Eof {
|
||||
break
|
||||
}
|
||||
res, err := c.runNfsTransaction([]Nfs_argop4{
|
||||
{
|
||||
Argop: OP_PUTFH,
|
||||
U: &PUTFH4args{Object: dirFh},
|
||||
},
|
||||
{
|
||||
Argop: OP_READDIR,
|
||||
U: &READDIR4args{
|
||||
Cookie: ent.Cookie,
|
||||
Cookieverf: curDirList.Cookieverf,
|
||||
Dircount: 1024 * 128,
|
||||
Maxcount: 1024 * 128,
|
||||
Attr_request: standardNfsAttrs,
|
||||
},
|
||||
},
|
||||
}, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curDirList = res[1].Opreaddir().Resok4()
|
||||
}
|
||||
|
||||
return fileList, nil
|
||||
}
|
||||
|
||||
// Make the commands to navigate the path to its leaf
|
||||
func (c *NfsClient) makePathLookupArgs(path []string) []Nfs_argop4 {
|
||||
var args []Nfs_argop4
|
||||
args = append(args, Nfs_argop4{Argop: OP_PUTROOTFH})
|
||||
|
||||
// Add lookups for the path components
|
||||
for _, p := range path {
|
||||
args = append(args, Nfs_argop4{
|
||||
Argop: OP_LOOKUP,
|
||||
U: &LOOKUP4args{Objname: Component4(p)},
|
||||
})
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (c *NfsClient) translateFileMeta(name string, attrs Fattr4) FileInfo {
|
||||
res := FileInfo{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
curOff := 0
|
||||
|
||||
atm := attrs.Attrmask
|
||||
if len(atm) > 0 && atm[0]&(1<<FATTR4_TYPE) != 0 {
|
||||
fileType := binary.BigEndian.Uint32(attrs.Attr_vals[curOff : curOff+4])
|
||||
curOff += 4
|
||||
|
||||
res.IsDir = Nfs_ftype4(fileType) == NF4DIR
|
||||
}
|
||||
|
||||
if len(atm) > 0 && atm[0]&(1<<FATTR4_SIZE) != 0 {
|
||||
res.Size = binary.BigEndian.Uint64(attrs.Attr_vals[curOff : curOff+8])
|
||||
curOff += 8
|
||||
}
|
||||
|
||||
if len(atm) > 1 && atm[1]&(1<<(FATTR4_TIME_MODIFY-32)) != 0 {
|
||||
mtimeSec := binary.BigEndian.Uint64(attrs.Attr_vals[curOff : curOff+8])
|
||||
curOff += 8
|
||||
|
||||
mtimeNsec := binary.BigEndian.Uint32(attrs.Attr_vals[curOff : curOff+4])
|
||||
curOff += 4
|
||||
|
||||
// I hope this works for times before 1970-01-01...
|
||||
res.Mtime = time.Unix(int64(mtimeSec), int64(mtimeNsec))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (c *NfsClient) GetFileInfo(path string) (FileInfo, error) {
|
||||
args := c.makePathLookupArgs(splitPath(path))
|
||||
args = append(args,
|
||||
Nfs_argop4{
|
||||
Argop: OP_GETATTR,
|
||||
U: &GETATTR4args{
|
||||
Attr_request: standardNfsAttrs,
|
||||
},
|
||||
})
|
||||
|
||||
res, err := c.runNfsTransaction(args, path)
|
||||
if err != nil {
|
||||
return FileInfo{}, err
|
||||
}
|
||||
|
||||
splits := splitPath(path)
|
||||
var name string
|
||||
if len(splits) == 1 {
|
||||
name = path
|
||||
} else {
|
||||
name = splits[len(splits)-1]
|
||||
}
|
||||
|
||||
resInfo := c.translateFileMeta(name,
|
||||
res[len(res)-1].Opgetattr().Resok4().Obj_attributes)
|
||||
return resInfo, nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) ReadFileAll(path string, writer io.Writer) (uint64, error) {
|
||||
return c.ReadFile(path, 0, math.MaxUint64, writer)
|
||||
}
|
||||
|
||||
func (c *NfsClient) ReadFile(path string, offset, count uint64, writer io.Writer) (uint64, error) {
|
||||
anonymousStateId := Stateid4{}
|
||||
args := c.makePathLookupArgs(splitPath(path))
|
||||
|
||||
args = append(args,
|
||||
Nfs_argop4{Argop: OP_GETFH},
|
||||
Nfs_argop4{
|
||||
Argop: OP_READ,
|
||||
U: &READ4args{
|
||||
Stateid: anonymousStateId,
|
||||
Offset: offset,
|
||||
Count: Count4(MinUint64(NfsReadBlockLen, count)),
|
||||
},
|
||||
})
|
||||
|
||||
res, err := c.runNfsTransaction(args, path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
flDataBlock := res[len(res)-1].Opread().Resok4()
|
||||
fileFh := res[len(res)-2].Opgetfh().Resok4().Object
|
||||
|
||||
var dataRead uint64
|
||||
for {
|
||||
_, err := writer.Write(flDataBlock.Data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ln := len(flDataBlock.Data)
|
||||
offset += uint64(ln)
|
||||
count -= uint64(ln)
|
||||
dataRead += uint64(ln)
|
||||
if flDataBlock.Eof || count == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Get the next file block
|
||||
res, err := c.runNfsTransaction([]Nfs_argop4{
|
||||
{
|
||||
Argop: OP_PUTFH,
|
||||
U: &PUTFH4args{Object: fileFh},
|
||||
},
|
||||
{
|
||||
Argop: OP_READ,
|
||||
U: &READ4args{
|
||||
Stateid: anonymousStateId,
|
||||
Offset: offset,
|
||||
Count: Count4(MinUint64(NfsReadBlockLen, count)),
|
||||
},
|
||||
},
|
||||
}, path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
flDataBlock = res[1].Opread().Resok4()
|
||||
}
|
||||
|
||||
return dataRead, nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) openFileForWrite(path string, truncate bool) (Stateid4, Nfs_fh4, error) {
|
||||
splits := splitPath(path)
|
||||
|
||||
// Put the directory FH as the current one
|
||||
args := c.makePathLookupArgs(splits[0 : len(splits)-1])
|
||||
// Make the file claim (i.e. the file name on top of the directory FH)
|
||||
flClaim := Component4(splits[len(splits)-1])
|
||||
|
||||
var fileAttrs Fattr4
|
||||
if truncate {
|
||||
// Set the file size and mode (Unix access mask)
|
||||
fileAttrs.Attr_vals = make([]byte, 12)
|
||||
// This file size is set to 0
|
||||
binary.BigEndian.PutUint32(fileAttrs.Attr_vals[8:], MODE4_WUSR|MODE4_RUSR|MODE4_WGRP|MODE4_RGRP)
|
||||
fileAttrs.Attrmask = Bitmap4{1 << FATTR4_SIZE, 1 << (FATTR4_MODE - 32)}
|
||||
} else {
|
||||
// Set the file mode (Unix access mask)
|
||||
fileAttrs.Attr_vals = make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(fileAttrs.Attr_vals, MODE4_WUSR|MODE4_RUSR|MODE4_WGRP|MODE4_RGRP)
|
||||
fileAttrs.Attrmask = Bitmap4{0, 1 << (FATTR4_MODE - 32)}
|
||||
}
|
||||
|
||||
args = append(args,
|
||||
Nfs_argop4{
|
||||
Argop: OP_OPEN,
|
||||
U: &OPEN4args{
|
||||
Seqid: c.nfsSeqId,
|
||||
Share_access: OPEN4_SHARE_ACCESS_WRITE,
|
||||
Share_deny: OPEN4_SHARE_DENY_NONE,
|
||||
Owner: Open_owner4{
|
||||
Clientid: c.clientIdShort,
|
||||
Owner: []byte(c.clientId),
|
||||
},
|
||||
Openhow: Openflag4{
|
||||
Opentype: OPEN4_CREATE,
|
||||
U: &Createhow4{
|
||||
Mode: UNCHECKED4,
|
||||
U: &fileAttrs,
|
||||
},
|
||||
},
|
||||
Claim: Open_claim4{
|
||||
Claim: CLAIM_NULL,
|
||||
U: &flClaim,
|
||||
},
|
||||
},
|
||||
},
|
||||
Nfs_argop4{
|
||||
Argop: OP_GETFH,
|
||||
})
|
||||
|
||||
res, err := c.runNfsTransaction(args, path)
|
||||
c.incrementNfsSeq(err)
|
||||
if err != nil {
|
||||
return Stateid4{}, Nfs_fh4{}, err
|
||||
}
|
||||
|
||||
openRes := res[len(res)-2].Opopen().Resok4()
|
||||
openFh := res[len(res)-1].Opgetfh().Resok4().Object
|
||||
|
||||
// We need to confirm the opened file receipt the first time we run the operation
|
||||
if !c.openConfirmed {
|
||||
res, err = c.runNfsTransaction([]Nfs_argop4{
|
||||
{
|
||||
Argop: OP_PUTFH,
|
||||
U: &PUTFH4args{Object: openFh},
|
||||
},
|
||||
{
|
||||
Argop: OP_OPEN_CONFIRM,
|
||||
U: &OPEN_CONFIRM4args{
|
||||
Open_stateid: openRes.Stateid,
|
||||
Seqid: c.nfsSeqId,
|
||||
},
|
||||
},
|
||||
}, path)
|
||||
c.incrementNfsSeq(err)
|
||||
if err != nil {
|
||||
return Stateid4{}, Nfs_fh4{}, err
|
||||
}
|
||||
c.openConfirmed = true
|
||||
return res[len(res)-1].Opopen_confirm().Resok4().Open_stateid, openFh, nil
|
||||
}
|
||||
|
||||
return openRes.Stateid, openFh, nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) closeFile(stateId Stateid4, fh Nfs_fh4, path string) error {
|
||||
_, err := c.runNfsTransaction([]Nfs_argop4{
|
||||
{
|
||||
Argop: OP_PUTFH,
|
||||
U: &PUTFH4args{Object: fh},
|
||||
},
|
||||
{
|
||||
Argop: OP_CLOSE,
|
||||
U: &CLOSE4args{
|
||||
Open_stateid: stateId,
|
||||
Seqid: c.nfsSeqId,
|
||||
},
|
||||
},
|
||||
}, path)
|
||||
c.incrementNfsSeq(err)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) ReWriteFile(path string, reader io.Reader) (written uint64, err error) {
|
||||
return c.WriteFile(path, true, 0, reader)
|
||||
}
|
||||
|
||||
func (c *NfsClient) WriteFile(path string, truncate bool, offset uint64,
|
||||
reader io.Reader) (written uint64, err error) {
|
||||
|
||||
stateId, fh, err := c.openFileForWrite(path, truncate)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err = c.closeFile(stateId, fh, path)
|
||||
}()
|
||||
|
||||
block := make([]byte, NfsReadBlockLen)
|
||||
for {
|
||||
var curRead int
|
||||
curRead, err = reader.Read(block)
|
||||
if curRead == 0 || err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Write the block!
|
||||
err = c.writeBlock(stateId, fh, written+offset, block[0:curRead], path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
written += uint64(curRead)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *NfsClient) writeBlock(id Stateid4, fh Nfs_fh4, offset uint64, data []byte, path string) error {
|
||||
for len(data) > 0 {
|
||||
res, err := c.runNfsTransaction([]Nfs_argop4{
|
||||
{
|
||||
Argop: OP_PUTFH,
|
||||
U: &PUTFH4args{Object: fh},
|
||||
},
|
||||
{
|
||||
Argop: OP_WRITE,
|
||||
U: &WRITE4args{
|
||||
Stateid: id,
|
||||
Offset: offset,
|
||||
Stable: UNSTABLE4,
|
||||
Data: data,
|
||||
},
|
||||
},
|
||||
}, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
written := res[1].Opwrite().Resok4().Count
|
||||
data = data[written:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) DeleteFile(path string) error {
|
||||
splits := splitPath(path)
|
||||
// Put the directory FH as the current one
|
||||
args := c.makePathLookupArgs(splits[0 : len(splits)-1])
|
||||
// Make the file claim (i.e. the file name on top of the directory FH)
|
||||
flClaim := Component4(splits[len(splits)-1])
|
||||
|
||||
args = append(args,
|
||||
Nfs_argop4{
|
||||
Argop: OP_REMOVE,
|
||||
U: &REMOVE4args{
|
||||
Target: flClaim,
|
||||
},
|
||||
})
|
||||
|
||||
_, err := c.runNfsTransaction(args, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NfsClient) MakePath(path string) error {
|
||||
curPath := ""
|
||||
var curPathElems []string
|
||||
|
||||
for _, curElem := range splitPath(path) {
|
||||
if curPath != "" {
|
||||
curPath += "/"
|
||||
}
|
||||
curPath += curElem
|
||||
curPathElems = append(curPathElems, curElem)
|
||||
|
||||
fi, err := c.GetFileInfo(curPath)
|
||||
if err == nil && !fi.IsDir {
|
||||
return &NfsError{
|
||||
ErrorCode: ERROR_NOTDIR,
|
||||
ErrorString: fmt.Sprintf("NFS error: should be a directory (%d), path='%s'",
|
||||
ERROR_NOTDIR, curPath),
|
||||
Path: curPath,
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if IsNfsError(err, ERROR_NOENT) {
|
||||
args := c.makePathLookupArgs(curPathElems[0 : len(curPathElems)-1])
|
||||
// Make the file claim (i.e. the file name on top of the directory FH)
|
||||
flClaim := Component4(curElem)
|
||||
|
||||
// Set the file mode (Unix access mask)
|
||||
var dirAttrs Fattr4
|
||||
dirAttrs.Attr_vals = make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(dirAttrs.Attr_vals,
|
||||
MODE4_WUSR|MODE4_RUSR|MODE4_XUSR|MODE4_WGRP|MODE4_RGRP|MODE4_XGRP)
|
||||
dirAttrs.Attrmask = Bitmap4{0, 1 << (FATTR4_MODE - 32)}
|
||||
|
||||
args = append(args,
|
||||
Nfs_argop4{
|
||||
Argop: OP_CREATE,
|
||||
U: &CREATE4args{
|
||||
Objtype: Createtype4{
|
||||
Type: NF4DIR,
|
||||
},
|
||||
Objname: flClaim,
|
||||
Createattrs: dirAttrs,
|
||||
},
|
||||
})
|
||||
_, err := c.runNfsTransaction(args, curPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Increment NFS sequence ID for all operations, even the ones that return
|
||||
// errors, except for a pre-defined list in https://tools.ietf.org/html/rfc3530#section-8.1.5
|
||||
func (c *NfsClient) incrementNfsSeq(err error) {
|
||||
if err == nil {
|
||||
c.nfsSeqId++
|
||||
return
|
||||
}
|
||||
|
||||
nfsErr, ok := err.(*NfsError)
|
||||
if !ok {
|
||||
c.nfsSeqId++
|
||||
return
|
||||
}
|
||||
|
||||
switch Nfsstat4(nfsErr.ErrorCode) {
|
||||
case
|
||||
NFS4ERR_STALE_CLIENTID, NFS4ERR_STALE_STATEID,
|
||||
NFS4ERR_BAD_STATEID, NFS4ERR_BAD_SEQID, NFS4ERR_BADXDR,
|
||||
NFS4ERR_RESOURCE, NFS4ERR_NOFILEHANDLE:
|
||||
return
|
||||
default:
|
||||
c.nfsSeqId++
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveRecursive(nfs NfsInterface, path string) error {
|
||||
list, err := nfs.GetFileList(path)
|
||||
if IsNfsError(err, ERROR_NOENT) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fl := range list {
|
||||
curPath := path + "/" + fl.Name
|
||||
if fl.IsDir {
|
||||
err = RemoveRecursive(nfs, curPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = nfs.DeleteFile(curPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = nfs.DeleteFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
153
server/plugin/plg_backend_nfs4/repo/nfs4/nfs_err.go
Normal file
153
server/plugin/plg_backend_nfs4/repo/nfs4/nfs_err.go
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
package nfs4
|
||||
|
||||
type NfsError struct {
|
||||
Path string
|
||||
ErrorCode NfsErrorCode
|
||||
ErrorString string
|
||||
}
|
||||
|
||||
func (n *NfsError) Error() string {
|
||||
return n.ErrorString
|
||||
}
|
||||
|
||||
func IsNfsError(err error, code NfsErrorCode) bool {
|
||||
ne, ok := err.(*NfsError)
|
||||
return ok && ne.ErrorCode == code
|
||||
}
|
||||
|
||||
/*
|
||||
* Error status. See https://www.rfc-editor.org/rfc/rfc7530
|
||||
*/
|
||||
type NfsErrorCode int32
|
||||
const (
|
||||
/* everything is okay */
|
||||
OK NfsErrorCode = 0
|
||||
/* caller not privileged */
|
||||
ERROR_PERM NfsErrorCode = 1
|
||||
/* no such file/directory */
|
||||
ERROR_NOENT NfsErrorCode = 2
|
||||
/* hard I/O error */
|
||||
ERROR_IO NfsErrorCode = 5
|
||||
/* no such device */
|
||||
ERROR_NXIO NfsErrorCode = 6
|
||||
/* access denied */
|
||||
ERROR_ACCESS NfsErrorCode = 13
|
||||
/* file already exists */
|
||||
ERROR_EXIST NfsErrorCode = 17
|
||||
/* different filesystems */
|
||||
ERROR_XDEV NfsErrorCode = 18
|
||||
/* should be a directory */
|
||||
ERROR_NOTDIR NfsErrorCode = 20
|
||||
/* should not be directory */
|
||||
ERROR_ISDIR NfsErrorCode = 21
|
||||
/* invalid argument */
|
||||
ERROR_INVAL NfsErrorCode = 22
|
||||
/* file exceeds server max */
|
||||
ERROR_FBIG NfsErrorCode = 27
|
||||
/* no space on filesystem */
|
||||
ERROR_NOSPC NfsErrorCode = 28
|
||||
/* read-only filesystem */
|
||||
ERROR_ROFS NfsErrorCode = 30
|
||||
/* too many hard links */
|
||||
ERROR_MLINK NfsErrorCode = 31
|
||||
/* name exceeds server max */
|
||||
ERROR_NAMETOOLONG NfsErrorCode = 63
|
||||
/* directory not empty */
|
||||
ERROR_NOTEMPTY NfsErrorCode = 66
|
||||
/* hard quota limit reached*/
|
||||
ERROR_DQUOT NfsErrorCode = 69
|
||||
/* file no longer exists */
|
||||
ERROR_STALE NfsErrorCode = 70
|
||||
/* Illegal filehandle */
|
||||
ERROR_BADHANDLE NfsErrorCode = 10001
|
||||
/* READDIR cookie is stale */
|
||||
ERROR_BAD_COOKIE NfsErrorCode = 10003
|
||||
/* operation not supported */
|
||||
ERROR_NOTSUPP NfsErrorCode = 10004
|
||||
/* response limit exceeded */
|
||||
ERROR_TOOSMALL NfsErrorCode = 10005
|
||||
/* undefined server error */
|
||||
ERROR_SERVERFAULT NfsErrorCode = 10006
|
||||
/* type invalid for CREATE */
|
||||
ERROR_BADTYPE NfsErrorCode = 10007
|
||||
/* file "busy" - retry */
|
||||
ERROR_DELAY NfsErrorCode = 10008
|
||||
/* nverify says attrs same */
|
||||
ERROR_SAME NfsErrorCode = 10009
|
||||
/* lock unavailable */
|
||||
ERROR_DENIED NfsErrorCode = 10010
|
||||
/* lock lease expired */
|
||||
ERROR_EXPIRED NfsErrorCode = 10011
|
||||
/* I/O failed due to lock */
|
||||
ERROR_LOCKED NfsErrorCode = 10012
|
||||
/* in grace period */
|
||||
ERROR_GRACE NfsErrorCode = 10013
|
||||
/* filehandle expired */
|
||||
ERROR_FHEXPIRED NfsErrorCode = 10014
|
||||
/* share reserve denied */
|
||||
ERROR_SHARE_DENIED NfsErrorCode = 10015
|
||||
/* wrong security flavor */
|
||||
ERROR_WRONGSEC NfsErrorCode = 10016
|
||||
/* clientid in use */
|
||||
ERROR_CLID_INUSE NfsErrorCode = 10017
|
||||
/* resource exhaustion */
|
||||
ERROR_RESOURCE NfsErrorCode = 10018
|
||||
/* filesystem relocated */
|
||||
ERROR_MOVED NfsErrorCode = 10019
|
||||
/* current FH is not set */
|
||||
ERROR_NOFILEHANDLE NfsErrorCode = 10020
|
||||
/* minor vers not supp */
|
||||
ERROR_MINOR_VERS_MISMATCH NfsErrorCode = 10021
|
||||
/* server has rebooted */
|
||||
ERROR_STALE_CLIENTID NfsErrorCode = 10022
|
||||
/* server has rebooted */
|
||||
ERROR_STALE_STATEID NfsErrorCode = 10023
|
||||
/* state is out of sync */
|
||||
ERROR_OLD_STATEID NfsErrorCode = 10024
|
||||
/* incorrect stateid */
|
||||
ERROR_BAD_STATEID NfsErrorCode = 10025
|
||||
/* request is out of seq. */
|
||||
ERROR_BAD_SEQID NfsErrorCode = 10026
|
||||
/* verify - attrs not same */
|
||||
ERROR_NOT_SAME NfsErrorCode = 10027
|
||||
/* lock range not supported*/
|
||||
ERROR_LOCK_RANGE NfsErrorCode = 10028
|
||||
/* should be file/directory*/
|
||||
ERROR_SYMLINK NfsErrorCode = 10029
|
||||
/* no saved filehandle */
|
||||
ERROR_RESTOREFH NfsErrorCode = 10030
|
||||
/* some filesystem moved */
|
||||
ERROR_LEASE_MOVED NfsErrorCode = 10031
|
||||
/* recommended attr not sup*/
|
||||
ERROR_ATTRNOTSUPP NfsErrorCode = 10032
|
||||
/* reclaim outside of grace*/
|
||||
ERROR_NO_GRACE NfsErrorCode = 10033
|
||||
/* reclaim error at server */
|
||||
ERROR_RECLAIM_BAD NfsErrorCode = 10034
|
||||
/* conflict on reclaim */
|
||||
ERROR_RECLAIM_CONFLICT NfsErrorCode = 10035
|
||||
/* ZDR decode failed */
|
||||
ERROR_BADZDR NfsErrorCode = 10036
|
||||
/* file locks held at CLOSE*/
|
||||
ERROR_LOCKS_HELD NfsErrorCode = 10037
|
||||
/* conflict in OPEN and I/O*/
|
||||
ERROR_OPENMODE NfsErrorCode = 10038
|
||||
/* owner translation bad */
|
||||
ERROR_BADOWNER NfsErrorCode = 10039
|
||||
/* utf-8 char not supported*/
|
||||
ERROR_BADCHAR NfsErrorCode = 10040
|
||||
/* name not supported */
|
||||
ERROR_BADNAME NfsErrorCode = 10041
|
||||
/* lock range not supported*/
|
||||
ERROR_BAD_RANGE NfsErrorCode = 10042
|
||||
/* no atomic up/downgrade */
|
||||
ERROR_LOCK_NOTSUPP NfsErrorCode = 10043
|
||||
/* undefined operation */
|
||||
ERROR_OP_ILLEGAL NfsErrorCode = 10044
|
||||
/* file locking deadlock */
|
||||
ERROR_DEADLOCK NfsErrorCode = 10045
|
||||
/* open file blocks op. */
|
||||
ERROR_FILE_OPEN NfsErrorCode = 10046
|
||||
/* lockowner state revoked */
|
||||
ERROR_ADMIN_REVOKED NfsErrorCode = 10047
|
||||
)
|
||||
136
server/plugin/plg_backend_nfs4/repo/nfs4/supervised_conn.go
Normal file
136
server/plugin/plg_backend_nfs4/repo/nfs4/supervised_conn.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package nfs4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A wrapper for net.Conn that adds ability to cancel operations via a context.Context and
|
||||
// also supports deadlines.
|
||||
type SupervisedConnection struct {
|
||||
delegate net.Conn
|
||||
ctx context.Context
|
||||
deadline time.Time
|
||||
|
||||
mtx sync.Mutex
|
||||
closeSignal chan bool
|
||||
closeErr error
|
||||
isClosed int32
|
||||
}
|
||||
|
||||
func NewSupervisedConnection(delegate net.Conn,
|
||||
ctx context.Context) (*SupervisedConnection, error) {
|
||||
|
||||
res := &SupervisedConnection{
|
||||
delegate: delegate,
|
||||
ctx: ctx,
|
||||
closeSignal: make(chan bool),
|
||||
}
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
res.signalDone()
|
||||
case <-res.closeSignal:
|
||||
}
|
||||
}()
|
||||
|
||||
deadline, ok := ctx.Deadline()
|
||||
if ok {
|
||||
res.deadline = deadline
|
||||
err := delegate.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) signalDone() {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
if s.isClosed != 0 {
|
||||
return
|
||||
}
|
||||
// Cancel pending operations
|
||||
s.closeErr = s.delegate.Close()
|
||||
s.isClosed = 1
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) SetReadDeadline(t time.Time) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
// Don't allow deadline extensions if we're getting interrupted
|
||||
if !s.deadline.IsZero() && s.deadline.Before(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.delegate.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) SetWriteDeadline(t time.Time) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
// Don't allow deadline extensions if we're getting interrupted
|
||||
if !s.deadline.IsZero() && s.deadline.Before(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.delegate.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) SetDeadline(t time.Time) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
// Don't allow deadline extensions if we're getting interrupted
|
||||
if !s.deadline.IsZero() && s.deadline.Before(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.delegate.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) Close() error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
if s.isClosed != 0 {
|
||||
return s.closeErr
|
||||
}
|
||||
|
||||
close(s.closeSignal)
|
||||
s.isClosed = 1
|
||||
|
||||
s.closeErr = s.delegate.Close()
|
||||
return s.closeErr
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) Read(b []byte) (n int, err error) {
|
||||
// s.isClosed can only go from 0 to 1, so there's no need to do
|
||||
// synchronization here.
|
||||
if s.isClosed != 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return s.delegate.Read(b)
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) Write(b []byte) (n int, err error) {
|
||||
// s.isClosed can only go from 0 to 1, so there's no need to do
|
||||
// synchronization here.
|
||||
if s.isClosed != 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return s.delegate.Write(b)
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) LocalAddr() net.Addr {
|
||||
return s.delegate.LocalAddr()
|
||||
}
|
||||
|
||||
func (s *SupervisedConnection) RemoteAddr() net.Addr {
|
||||
return s.delegate.RemoteAddr()
|
||||
}
|
||||
Loading…
Reference in a new issue