kubernetes/vendor/github.com/vmware/govmomi/toolbox/hgfs/protocol.go
Madhav Jivrajani 8b064fa4be *: Bump version of vmware/govmomi
Bumping version to include changes that
better handle TLS errors. Bump nescessary
to prepare for when the version of Go is
bumped to 1.20

Signed-off-by: Madhav Jivrajani <madhav.jiv@gmail.com>
2023-01-12 15:08:45 +05:30

848 lines
21 KiB
Go

/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hgfs
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"os"
"strings"
)
// See: https://github.com/vmware/open-vm-tools/blob/master/open-vm-tools/lib/include/hgfsProto.h
// Opcodes for server operations as defined in hgfsProto.h
const (
OpOpen = iota /* Open file */
OpRead /* Read from file */
OpWrite /* Write to file */
OpClose /* Close file */
OpSearchOpen /* Start new search */
OpSearchRead /* Get next search response */
OpSearchClose /* End a search */
OpGetattr /* Get file attributes */
OpSetattr /* Set file attributes */
OpCreateDir /* Create new directory */
OpDeleteFile /* Delete a file */
OpDeleteDir /* Delete a directory */
OpRename /* Rename a file or directory */
OpQueryVolumeInfo /* Query volume information */
OpOpenV2 /* Open file */
OpGetattrV2 /* Get file attributes */
OpSetattrV2 /* Set file attributes */
OpSearchReadV2 /* Get next search response */
OpCreateSymlink /* Create a symlink */
OpServerLockChange /* Change the oplock on a file */
OpCreateDirV2 /* Create a directory */
OpDeleteFileV2 /* Delete a file */
OpDeleteDirV2 /* Delete a directory */
OpRenameV2 /* Rename a file or directory */
OpOpenV3 /* Open file */
OpReadV3 /* Read from file */
OpWriteV3 /* Write to file */
OpCloseV3 /* Close file */
OpSearchOpenV3 /* Start new search */
OpSearchReadV3 /* Read V3 directory entries */
OpSearchCloseV3 /* End a search */
OpGetattrV3 /* Get file attributes */
OpSetattrV3 /* Set file attributes */
OpCreateDirV3 /* Create new directory */
OpDeleteFileV3 /* Delete a file */
OpDeleteDirV3 /* Delete a directory */
OpRenameV3 /* Rename a file or directory */
OpQueryVolumeInfoV3 /* Query volume information */
OpCreateSymlinkV3 /* Create a symlink */
OpServerLockChangeV3 /* Change the oplock on a file */
OpWriteWin32StreamV3 /* Write WIN32_STREAM_ID format data to file */
OpCreateSessionV4 /* Create a session and return host capabilities. */
OpDestroySessionV4 /* Destroy/close session. */
OpReadFastV4 /* Read */
OpWriteFastV4 /* Write */
OpSetWatchV4 /* Start monitoring directory changes. */
OpRemoveWatchV4 /* Stop monitoring directory changes. */
OpNotifyV4 /* Notification for a directory change event. */
OpSearchReadV4 /* Read V4 directory entries. */
OpOpenV4 /* Open file */
OpEnumerateStreamsV4 /* Enumerate alternative named streams for a file. */
OpGetattrV4 /* Get file attributes */
OpSetattrV4 /* Set file attributes */
OpDeleteV4 /* Delete a file or a directory */
OpLinkmoveV4 /* Rename/move/create hard link. */
OpFsctlV4 /* Sending FS control requests. */
OpAccessCheckV4 /* Access check. */
OpFsyncV4 /* Flush all cached data to the disk. */
OpQueryVolumeInfoV4 /* Query volume information. */
OpOplockAcquireV4 /* Acquire OPLOCK. */
OpOplockBreakV4 /* Break or downgrade OPLOCK. */
OpLockByteRangeV4 /* Acquire byte range lock. */
OpUnlockByteRangeV4 /* Release byte range lock. */
OpQueryEasV4 /* Query extended attributes. */
OpSetEasV4 /* Add or modify extended attributes. */
OpNewHeader = 0xff /* Header op, must be unique, distinguishes packet headers. */
)
// Status codes
const (
StatusSuccess = iota
StatusNoSuchFileOrDir
StatusInvalidHandle
StatusOperationNotPermitted
StatusFileExists
StatusNotDirectory
StatusDirNotEmpty
StatusProtocolError
StatusAccessDenied
StatusInvalidName
StatusGenericError
StatusSharingViolation
StatusNoSpace
StatusOperationNotSupported
StatusNameTooLong
StatusInvalidParameter
StatusNotSameDevice
StatusStaleSession
StatusTooManySessions
StatusTransportError
)
// Flags for attr mask
const (
AttrValidType = 1 << iota
AttrValidSize
AttrValidCreateTime
AttrValidAccessTime
AttrValidWriteTime
AttrValidChangeTime
AttrValidSpecialPerms
AttrValidOwnerPerms
AttrValidGroupPerms
AttrValidOtherPerms
AttrValidFlags
AttrValidAllocationSize
AttrValidUserID
AttrValidGroupID
AttrValidFileID
AttrValidVolID
AttrValidNonStaticFileID
AttrValidEffectivePerms
AttrValidExtendAttrSize
AttrValidReparsePoint
AttrValidShortName
)
// HeaderVersion for HGFS protocol version 4
const HeaderVersion = 0x1
// LargePacketMax is maximum size of an hgfs packet
const LargePacketMax = 0xf800 // HGFS_LARGE_PACKET_MAX
// Packet flags
const (
PacketFlagRequest = 1 << iota
PacketFlagReply
PacketFlagInfoExterror
PacketFlagValidFlags = 0x7
)
// Status is an error type that encapsulates an error status code and the cause
type Status struct {
Err error
Code uint32
}
func (s *Status) Error() string {
if s.Err != nil {
return s.Err.Error()
}
return fmt.Sprintf("hgfs.Status=%d", s.Code)
}
// errorStatus maps the given error type to a status code
func errorStatus(err error) uint32 {
if x, ok := err.(*Status); ok {
return x.Code
}
switch {
case os.IsNotExist(err):
return StatusNoSuchFileOrDir
case os.IsExist(err):
return StatusFileExists
case os.IsPermission(err):
return StatusOperationNotPermitted
}
return StatusGenericError
}
// ProtocolError wraps the given error as a Status type
func ProtocolError(err error) error {
return &Status{
Err: err,
Code: StatusProtocolError,
}
}
// Request as defined in hgfsProto.h:HgfsRequest
type Request struct {
Handle uint32
Op int32
}
// Reply as defined in hgfsProto.h:HgfsReply
type Reply struct {
Handle uint32
Status uint32
}
// Header as defined in hgfsProto.h:HgfsHeader
type Header struct {
Version uint8
Reserved1 [3]uint8
Dummy int32
PacketSize uint32
HeaderSize uint32
RequestID uint32
Op int32
Status uint32
Flags uint32
Information uint32
SessionID uint64
Reserved uint64
}
var (
headerSize = uint32(binary.Size(new(Header)))
packetSize = func(r *Packet) uint32 {
return headerSize + uint32(len(r.Payload))
}
)
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (h *Header) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
err := binary.Read(buf, binary.LittleEndian, h)
if err != nil {
return fmt.Errorf("reading hgfs header: %s", err)
}
if h.Dummy != OpNewHeader {
return fmt.Errorf("expected hgfs header with OpNewHeader (%#x), got: %#x", OpNewHeader, h.Dummy)
}
return nil
}
// Packet encapsulates an hgfs Header and Payload
type Packet struct {
Header
Payload []byte
}
// Reply composes a new Packet with the given payload or error
func (r *Packet) Reply(payload interface{}, err error) ([]byte, error) {
p := new(Packet)
status := uint32(StatusSuccess)
if err != nil {
status = errorStatus(err)
} else {
p.Payload, err = MarshalBinary(payload)
if err != nil {
return nil, err
}
}
p.Header = Header{
Version: HeaderVersion,
Dummy: OpNewHeader,
PacketSize: headerSize + uint32(len(p.Payload)),
HeaderSize: headerSize,
RequestID: r.RequestID,
Op: r.Op,
Status: status,
Flags: PacketFlagReply,
Information: 0,
SessionID: r.SessionID,
}
if Trace {
rc := "OK"
if err != nil {
rc = err.Error()
}
fmt.Fprintf(os.Stderr, "[hgfs] response %#v [%s]\n", p.Header, rc)
} else if err != nil {
log.Printf("[hgfs] op=%d error: %s", r.Op, err)
}
return p.MarshalBinary()
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *Packet) MarshalBinary() ([]byte, error) {
r.Header.PacketSize = packetSize(r)
buf, _ := MarshalBinary(r.Header)
return append(buf, r.Payload...), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *Packet) UnmarshalBinary(data []byte) error {
err := r.Header.UnmarshalBinary(data)
if err != nil {
return err
}
r.Payload = data[r.HeaderSize:r.PacketSize]
return nil
}
// Capability as defined in hgfsProto.h:HgfsCapability
type Capability struct {
Op int32
Flags uint32
}
// RequestCreateSessionV4 as defined in hgfsProto.h:HgfsRequestCreateSessionV4
type RequestCreateSessionV4 struct {
NumCapabilities uint32
MaxPacketSize uint32
Flags uint32
Reserved uint32
Capabilities []Capability
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *RequestCreateSessionV4) MarshalBinary() ([]byte, error) {
buf := new(bytes.Buffer)
r.NumCapabilities = uint32(len(r.Capabilities))
fields := []*uint32{
&r.NumCapabilities,
&r.MaxPacketSize,
&r.Flags,
&r.Reserved,
}
for _, p := range fields {
err := binary.Write(buf, binary.LittleEndian, p)
if err != nil {
return nil, err
}
}
for i := uint32(0); i < r.NumCapabilities; i++ {
err := binary.Write(buf, binary.LittleEndian, &r.Capabilities[i])
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *RequestCreateSessionV4) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
fields := []*uint32{
&r.NumCapabilities,
&r.MaxPacketSize,
&r.Flags,
&r.Reserved,
}
for _, p := range fields {
err := binary.Read(buf, binary.LittleEndian, p)
if err != nil {
return err
}
}
for i := uint32(0); i < r.NumCapabilities; i++ {
var cap Capability
err := binary.Read(buf, binary.LittleEndian, &cap)
if err != nil {
return err
}
r.Capabilities = append(r.Capabilities, cap)
}
return nil
}
// ReplyCreateSessionV4 as defined in hgfsProto.h:HgfsReplyCreateSessionV4
type ReplyCreateSessionV4 struct {
SessionID uint64
NumCapabilities uint32
MaxPacketSize uint32
IdentityOffset uint32
Flags uint32
Reserved uint32
Capabilities []Capability
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *ReplyCreateSessionV4) MarshalBinary() ([]byte, error) {
buf := new(bytes.Buffer)
fields := []interface{}{
&r.SessionID,
&r.NumCapabilities,
&r.MaxPacketSize,
&r.IdentityOffset,
&r.Flags,
&r.Reserved,
}
for _, p := range fields {
err := binary.Write(buf, binary.LittleEndian, p)
if err != nil {
return nil, err
}
}
for i := uint32(0); i < r.NumCapabilities; i++ {
err := binary.Write(buf, binary.LittleEndian, &r.Capabilities[i])
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *ReplyCreateSessionV4) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
fields := []interface{}{
&r.SessionID,
&r.NumCapabilities,
&r.MaxPacketSize,
&r.IdentityOffset,
&r.Flags,
&r.Reserved,
}
for _, p := range fields {
err := binary.Read(buf, binary.LittleEndian, p)
if err != nil {
return err
}
}
for i := uint32(0); i < r.NumCapabilities; i++ {
var cap Capability
err := binary.Read(buf, binary.LittleEndian, &cap)
if err != nil {
return err
}
r.Capabilities = append(r.Capabilities, cap)
}
return nil
}
// RequestDestroySessionV4 as defined in hgfsProto.h:HgfsRequestDestroySessionV4
type RequestDestroySessionV4 struct {
Reserved uint64
}
// ReplyDestroySessionV4 as defined in hgfsProto.h:HgfsReplyDestroySessionV4
type ReplyDestroySessionV4 struct {
Reserved uint64
}
// FileName as defined in hgfsProto.h:HgfsFileName
type FileName struct {
Length uint32
Name string
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (f *FileName) MarshalBinary() ([]byte, error) {
name := f.Name
f.Length = uint32(len(f.Name))
if f.Length == 0 {
// field is defined as 'char name[1];', this byte is required for min sizeof() validation
name = "\x00"
}
return MarshalBinary(&f.Length, name)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (f *FileName) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
_ = binary.Read(buf, binary.LittleEndian, &f.Length)
f.Name = string(buf.Next(int(f.Length)))
return nil
}
const serverPolicyRootShareName = "root"
// FromString converts name to a FileName
func (f *FileName) FromString(name string) {
name = strings.TrimPrefix(name, "/")
cp := strings.Split(name, "/")
cp = append([]string{serverPolicyRootShareName}, cp...)
f.Name = strings.Join(cp, "\x00")
f.Length = uint32(len(f.Name))
}
// Path converts FileName to a string
func (f *FileName) Path() string {
cp := strings.Split(f.Name, "\x00")
if len(cp) == 0 || cp[0] != serverPolicyRootShareName {
return "" // TODO: not happening until if/when we handle Windows shares
}
cp[0] = ""
return strings.Join(cp, "/")
}
// FileNameV3 as defined in hgfsProto.h:HgfsFileNameV3
type FileNameV3 struct {
Length uint32
Flags uint32
CaseType int32
ID uint32
Name string
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (f *FileNameV3) MarshalBinary() ([]byte, error) {
name := f.Name
f.Length = uint32(len(f.Name))
if f.Length == 0 {
// field is defined as 'char name[1];', this byte is required for min sizeof() validation
name = "\x00"
}
return MarshalBinary(&f.Length, &f.Flags, &f.CaseType, &f.ID, name)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (f *FileNameV3) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
fields := []interface{}{
&f.Length, &f.Flags, &f.CaseType, &f.ID,
}
for _, p := range fields {
if err := binary.Read(buf, binary.LittleEndian, p); err != nil {
return err
}
}
f.Name = string(buf.Next(int(f.Length)))
return nil
}
// FromString converts name to a FileNameV3
func (f *FileNameV3) FromString(name string) {
p := new(FileName)
p.FromString(name)
f.Name = p.Name
f.Length = p.Length
}
// Path converts FileNameV3 to a string
func (f *FileNameV3) Path() string {
return (&FileName{Name: f.Name, Length: f.Length}).Path()
}
// FileType
const (
FileTypeRegular = iota
FileTypeDirectory
FileTypeSymlink
)
// AttrV2 as defined in hgfsProto.h:HgfsAttrV2
type AttrV2 struct {
Mask uint64
Type int32
Size uint64
CreationTime uint64
AccessTime uint64
WriteTime uint64
AttrChangeTime uint64
SpecialPerms uint8
OwnerPerms uint8
GroupPerms uint8
OtherPerms uint8
AttrFlags uint64
AllocationSize uint64
UserID uint32
GroupID uint32
HostFileID uint64
VolumeID uint32
EffectivePerms uint32
Reserved2 uint64
}
// RequestGetattrV2 as defined in hgfsProto.h:HgfsRequestGetattrV2
type RequestGetattrV2 struct {
Request
AttrHint uint64
Handle uint32
FileName FileName
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *RequestGetattrV2) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.Request, &r.AttrHint, &r.Handle, &r.FileName)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *RequestGetattrV2) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.Request, &r.AttrHint, &r.Handle, &r.FileName)
}
// ReplyGetattrV2 as defined in hgfsProto.h:HgfsReplyGetattrV2
type ReplyGetattrV2 struct {
Reply
Attr AttrV2
SymlinkTarget FileName
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *ReplyGetattrV2) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.Reply, &r.Attr, &r.SymlinkTarget)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *ReplyGetattrV2) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.Reply, &r.Attr, &r.SymlinkTarget)
}
// RequestSetattrV2 as defined in hgfsProto.h:HgfsRequestSetattrV2
type RequestSetattrV2 struct {
Request
Hints uint64
Attr AttrV2
Handle uint32
FileName FileName
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *RequestSetattrV2) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.Request, &r.Hints, &r.Attr, &r.Handle, &r.FileName)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *RequestSetattrV2) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.Request, &r.Hints, &r.Attr, &r.Handle, &r.FileName)
}
// ReplySetattrV2 as defined in hgfsProto.h:HgfsReplySetattrV2
type ReplySetattrV2 struct {
Header Reply
}
// OpenMode
const (
OpenModeReadOnly = iota
OpenModeWriteOnly
OpenModeReadWrite
OpenModeAccmodes
)
// OpenFlags
const (
Open = iota
OpenEmpty
OpenCreate
OpenCreateSafe
OpenCreateEmpty
)
// Permissions
const (
PermRead = 4
PermWrite = 2
PermExec = 1
)
// RequestOpen as defined in hgfsProto.h:HgfsRequestOpen
type RequestOpen struct {
Request
OpenMode int32
OpenFlags int32
Permissions uint8
FileName FileName
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *RequestOpen) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.Request, &r.OpenMode, &r.OpenFlags, r.Permissions, &r.FileName)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *RequestOpen) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.Request, &r.OpenMode, &r.OpenFlags, &r.Permissions, &r.FileName)
}
// ReplyOpen as defined in hgfsProto.h:HgfsReplyOpen
type ReplyOpen struct {
Reply
Handle uint32
}
// RequestClose as defined in hgfsProto.h:HgfsRequestClose
type RequestClose struct {
Request
Handle uint32
}
// ReplyClose as defined in hgfsProto.h:HgfsReplyClose
type ReplyClose struct {
Reply
}
// Lock type
const (
LockNone = iota
LockOpportunistic
LockExclusive
LockShared
LockBatch
LockLease
)
// RequestOpenV3 as defined in hgfsProto.h:HgfsRequestOpenV3
type RequestOpenV3 struct {
Mask uint64
OpenMode int32
OpenFlags int32
SpecialPerms uint8
OwnerPerms uint8
GroupPerms uint8
OtherPerms uint8
AttrFlags uint64
AllocationSize uint64
DesiredAccess uint32
ShareAccess uint32
DesiredLock int32
Reserved1 uint64
Reserved2 uint64
FileName FileNameV3
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *RequestOpenV3) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.Mask, &r.OpenMode, &r.OpenFlags,
&r.SpecialPerms, &r.OwnerPerms, &r.GroupPerms, &r.OtherPerms,
&r.AttrFlags, &r.AllocationSize, &r.DesiredAccess, &r.ShareAccess,
&r.DesiredLock, &r.Reserved1, &r.Reserved2, &r.FileName)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *RequestOpenV3) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.Mask, &r.OpenMode, &r.OpenFlags,
&r.SpecialPerms, &r.OwnerPerms, &r.GroupPerms, &r.OtherPerms,
&r.AttrFlags, &r.AllocationSize, &r.DesiredAccess, &r.ShareAccess,
&r.DesiredLock, &r.Reserved1, &r.Reserved2, &r.FileName)
}
// ReplyOpenV3 as defined in hgfsProto.h:HgfsReplyOpenV3
type ReplyOpenV3 struct {
Handle uint32
AcquiredLock int32
Flags int32
Reserved uint32
}
// RequestReadV3 as defined in hgfsProto.h:HgfsRequestReadV3
type RequestReadV3 struct {
Handle uint32
Offset uint64
RequiredSize uint32
Reserved uint64
}
// ReplyReadV3 as defined in hgfsProto.h:HgfsReplyReadV3
type ReplyReadV3 struct {
ActualSize uint32
Reserved uint64
Payload []byte
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *ReplyReadV3) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.ActualSize, &r.Reserved, r.Payload)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *ReplyReadV3) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.ActualSize, &r.Reserved, &r.Payload)
}
// Write flags
const (
WriteAppend = 1
)
// RequestWriteV3 as defined in hgfsProto.h:HgfsRequestWriteV3
type RequestWriteV3 struct {
Handle uint32
WriteFlags uint8
Offset uint64
RequiredSize uint32
Reserved uint64
Payload []byte
}
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (r *RequestWriteV3) MarshalBinary() ([]byte, error) {
return MarshalBinary(&r.Handle, &r.WriteFlags, &r.Offset, &r.RequiredSize, &r.Reserved, r.Payload)
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (r *RequestWriteV3) UnmarshalBinary(data []byte) error {
return UnmarshalBinary(data, &r.Handle, &r.WriteFlags, &r.Offset, &r.RequiredSize, &r.Reserved, &r.Payload)
}
// ReplyWriteV3 as defined in hgfsProto.h:HgfsReplyWriteV3
type ReplyWriteV3 struct {
ActualSize uint32
Reserved uint64
}