-
Notifications
You must be signed in to change notification settings - Fork 47
Support discriminated unions correctly #2
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,10 @@ const ( | |
| NFSProc3FSInfo = 19 | ||
| NFSProc3Commit = 21 | ||
|
|
||
| // The size in bytes of the opaque cookie verifier passed by | ||
| // READDIR and READDIRPLUS. | ||
| NFS3_COOKIEVERFSIZE = 8 | ||
|
|
||
| // file types | ||
| NF3Reg = 1 | ||
| NF3Dir = 2 | ||
|
|
@@ -51,19 +55,37 @@ type Sattr3 struct { | |
| Mode SetMode | ||
| UID SetUID | ||
| GID SetUID | ||
| Size uint64 | ||
| Atime NFS3Time | ||
| Mtime NFS3Time | ||
| Size SetSize | ||
| Atime SetTime | ||
| Mtime SetTime | ||
| } | ||
|
|
||
| type SetMode struct { | ||
| Set uint32 | ||
| Mode uint32 | ||
| SetIt bool `xdr:"union"` | ||
| Mode uint32 `xdr:"unioncase=1"` | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for being late to the game, but all sequences of "isset bool + value" are described in the RFC as optional data type. This type supported using
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rasky Thanks for the review at all! This was a very targeted PR done with the NFS RFC in one hand, wireshark trace in the other. Does the same @matthewavery If you know wireshark/delve can you pick this up and use it to teach those tools to @AngieCris and/or @anchal-agrawal. If not I'll run that with the three of you next time I'm in Austin. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hickeng the PR is technically correct, I'm just suggesting a better usage of the Go XDR library. If you read RFC4506, there are two different data types: discriminated unions (§4.15) and optional types (§4.19). Optional types are defined as a special case of discriminated unions, where the discriminator is a boolean type, and the FALSE case is empty; so it does make sense that you can use a discriminated union for those structures, it's just that the optional type is more easy to reason, lets you write less code, and more readable. NFS RFC has many optional types, but it's true that they're described as XDR unions in the spec. I'm not sure why, must be some historical reason, but the two are really equivalent. As for applying optional values to multi-field structs, it's sufficient to use the correct level of indirection: type WccData struct {
Before *struct {
Size uint64
MTime NFS3Time
CTime NFS3Time
} `xdr:"optional"`
After PostOpAttr
}Notice that you need to change |
||
|
|
||
| type SetUID struct { | ||
| Set uint32 | ||
| UID uint32 | ||
| SetIt bool `xdr:"union"` | ||
| UID uint32 `xdr:"unioncase=1"` | ||
| } | ||
|
|
||
| type SetSize struct { | ||
| SetIt bool `xdr:"union"` | ||
| Size uint64 `xdr:"unioncase=1"` | ||
| } | ||
|
|
||
| type TimeHow int | ||
|
|
||
| const ( | ||
| DontChange TimeHow = iota | ||
| SetToServerTime | ||
| SetToClientTime | ||
| ) | ||
|
|
||
| type SetTime struct { | ||
| SetIt TimeHow `xdr:"union"` | ||
| Time NFS3Time `xdr:"unioncase=2"` //SetToClientTime | ||
| } | ||
|
|
||
| type NFS3Time struct { | ||
|
|
@@ -109,65 +131,81 @@ func (f *Fattr) Sys() interface{} { | |
| return nil | ||
| } | ||
|
|
||
| type PostOpFH3 struct { | ||
| IsSet bool `xdr:"union"` | ||
| FH []byte `xdr:"unioncase=1"` | ||
| } | ||
|
|
||
| type PostOpAttr struct { | ||
| IsSet bool `xdr:"union"` | ||
| Attr Fattr `xdr:"unioncase=1"` | ||
| } | ||
|
|
||
| type EntryPlus struct { | ||
| FileId uint64 | ||
| FileName string | ||
| Cookie uint64 | ||
| Attr struct { | ||
| Follows uint32 | ||
| Attr Fattr | ||
| } | ||
| FHSet uint32 | ||
| FH []byte | ||
| ValueFollows uint32 | ||
| Attr PostOpAttr | ||
| Handle PostOpFH3 | ||
| // NextEntry *EntryPlus | ||
| } | ||
|
|
||
| func (e *EntryPlus) Name() string { | ||
| return e.FileName | ||
| } | ||
|
|
||
| func (e *EntryPlus) Size() int64 { | ||
| if e.Attr.Follows == 0 { | ||
| if !e.Attr.IsSet { | ||
| return 0 | ||
| } | ||
|
|
||
| return e.Attr.Attr.Size() | ||
| } | ||
|
|
||
| func (e *EntryPlus) Mode() os.FileMode { | ||
| if e.Attr.Follows == 0 { | ||
| if !e.Attr.IsSet { | ||
| return 0 | ||
| } | ||
|
|
||
| return e.Attr.Attr.Mode() | ||
| } | ||
|
|
||
| func (e *EntryPlus) ModTime() time.Time { | ||
| if e.Attr.Follows == 0 { | ||
| if !e.Attr.IsSet { | ||
| return time.Time{} | ||
| } | ||
|
|
||
| return e.Attr.Attr.ModTime() | ||
| } | ||
|
|
||
| func (e *EntryPlus) IsDir() bool { | ||
| if e.Attr.Follows == 0 { | ||
| if !e.Attr.IsSet { | ||
| return false | ||
| } | ||
|
|
||
| return e.Attr.Attr.IsDir() | ||
| } | ||
|
|
||
| func (e *EntryPlus) Sys() interface{} { | ||
| if e.Attr.Follows == 0 { | ||
| if !e.Attr.IsSet { | ||
| return 0 | ||
| } | ||
|
|
||
| return e.FileId | ||
| } | ||
|
|
||
| type WccData struct { | ||
| Before struct { | ||
| IsSet bool `xdr:"union"` | ||
| Size uint64 `xdr:"unioncase=1"` | ||
| MTime NFS3Time `xdr:"unioncase=1"` | ||
| CTime NFS3Time `xdr:"unioncase=1"` | ||
| } | ||
| After PostOpAttr | ||
| } | ||
|
|
||
| type FSInfo struct { | ||
| Follows uint32 | ||
| Attr PostOpAttr | ||
| RTMax uint32 | ||
| RTPref uint32 | ||
| RTMult uint32 | ||
|
|
@@ -184,6 +222,7 @@ type FSInfo struct { | |
| func DialService(addr string, prog rpc.Mapping) (*rpc.Client, error) { | ||
| pm, err := rpc.DialPortmapper("tcp", addr) | ||
| if err != nil { | ||
| util.Errorf("Failed to connect to portmapper: %s", err) | ||
| return nil, err | ||
| } | ||
| defer pm.Close() | ||
|
|
@@ -194,6 +233,9 @@ func DialService(addr string, prog rpc.Mapping) (*rpc.Client, error) { | |
| } | ||
|
|
||
| client, err := dialService(addr, port) | ||
| if err != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, that's was pretty big bug... :-) |
||
| return nil, err | ||
| } | ||
|
|
||
| return client, nil | ||
| } | ||
|
|
@@ -222,7 +264,10 @@ func dialService(addr string, port int) (*rpc.Client, error) { | |
| Port: p, | ||
| } | ||
|
|
||
| client, err = rpc.DialTCP("tcp", ldr, fmt.Sprintf("%s:%d", addr, port)) | ||
| raddr := fmt.Sprintf("%s:%d", addr, port) | ||
| util.Debugf("Connecting to %s", raddr) | ||
|
|
||
| client, err = rpc.DialTCP("tcp", ldr, raddr) | ||
| if err == nil { | ||
| break | ||
| } | ||
|
|
@@ -236,8 +281,10 @@ func dialService(addr string, port int) (*rpc.Client, error) { | |
|
|
||
| util.Debugf("using random port %d -> %d", p, port) | ||
| } else { | ||
| raddr := fmt.Sprintf("%s:%d", addr, port) | ||
| util.Debugf("Connecting to %s from unprivileged port", raddr) | ||
|
|
||
| client, err = rpc.DialTCP("tcp", ldr, fmt.Sprintf("%s:%d", addr, port)) | ||
| client, err = rpc.DialTCP("tcp", ldr, raddr) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious. What happens the the
Followsword? I don't see it defined inPostOpAttr.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fdawg! great to hear from you :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fdawg4l Welcome! This is sort of like SAX vs DOM parsing for XML. Use of follows determines whether the gated data is even present in the XDR stream and that broke us - seems Linux had always been supplying the data, but when NetApp didn't we had misalignment in the rest of the structure.
To answer the specific query,
Followshas been replaced with thexdr:"union"annotation which is in thexdr2package specifically to support discriminated unions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OOOHH!!! ok cool. Wow. That must've sucked. Good find.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tcpdump + wireshark 4TW. Wireshark has awesome XDR/NFS protocol analytics built in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was the only way i made any progress was with wireshark. Agreed. Invaluable tool. And it lets you go up and down the stack; tcp, rpc, nfs.