1package openssh
2
3import (
4	sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
5)
6
7const extensionStatVFS = "statvfs@openssh.com"
8
9// RegisterExtensionStatVFS registers the "statvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
10func RegisterExtensionStatVFS() {
11	sshfx.RegisterExtendedPacketType(extensionStatVFS, func() sshfx.ExtendedData {
12		return new(StatVFSExtendedPacket)
13	})
14}
15
16// ExtensionStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
17func ExtensionStatVFS() *sshfx.ExtensionPair {
18	return &sshfx.ExtensionPair{
19		Name: extensionStatVFS,
20		Data: "2",
21	}
22}
23
24// StatVFSExtendedPacket defines the statvfs@openssh.com extend packet.
25type StatVFSExtendedPacket struct {
26	Path string
27}
28
29// Type returns the SSH_FXP_EXTENDED packet type.
30func (ep *StatVFSExtendedPacket) Type() sshfx.PacketType {
31	return sshfx.PacketTypeExtended
32}
33
34// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
35func (ep *StatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
36	p := &sshfx.ExtendedPacket{
37		ExtendedRequest: extensionStatVFS,
38
39		Data: ep,
40	}
41	return p.MarshalPacket(reqid, b)
42}
43
44// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
45func (ep *StatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
46	buf.AppendString(ep.Path)
47}
48
49// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
50//
51// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
52func (ep *StatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
53	size := 4 + len(ep.Path) // string(path)
54
55	buf := sshfx.NewBuffer(make([]byte, 0, size))
56
57	ep.MarshalInto(buf)
58
59	return buf.Bytes(), nil
60}
61
62// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
63func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
64	if ep.Path, err = buf.ConsumeString(); err != nil {
65		return err
66	}
67
68	return nil
69}
70
71// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
72func (ep *StatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
73	return ep.UnmarshalFrom(sshfx.NewBuffer(data))
74}
75
76const extensionFStatVFS = "fstatvfs@openssh.com"
77
78// RegisterExtensionFStatVFS registers the "fstatvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
79func RegisterExtensionFStatVFS() {
80	sshfx.RegisterExtendedPacketType(extensionFStatVFS, func() sshfx.ExtendedData {
81		return new(FStatVFSExtendedPacket)
82	})
83}
84
85// ExtensionFStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
86func ExtensionFStatVFS() *sshfx.ExtensionPair {
87	return &sshfx.ExtensionPair{
88		Name: extensionFStatVFS,
89		Data: "2",
90	}
91}
92
93// FStatVFSExtendedPacket defines the fstatvfs@openssh.com extend packet.
94type FStatVFSExtendedPacket struct {
95	Path string
96}
97
98// Type returns the SSH_FXP_EXTENDED packet type.
99func (ep *FStatVFSExtendedPacket) Type() sshfx.PacketType {
100	return sshfx.PacketTypeExtended
101}
102
103// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
104func (ep *FStatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
105	p := &sshfx.ExtendedPacket{
106		ExtendedRequest: extensionFStatVFS,
107
108		Data: ep,
109	}
110	return p.MarshalPacket(reqid, b)
111}
112
113// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
114func (ep *FStatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
115	buf.AppendString(ep.Path)
116}
117
118// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
119//
120// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
121func (ep *FStatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
122	size := 4 + len(ep.Path) // string(path)
123
124	buf := sshfx.NewBuffer(make([]byte, 0, size))
125
126	ep.MarshalInto(buf)
127
128	return buf.Bytes(), nil
129}
130
131// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
132func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
133	if ep.Path, err = buf.ConsumeString(); err != nil {
134		return err
135	}
136
137	return nil
138}
139
140// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
141func (ep *FStatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
142	return ep.UnmarshalFrom(sshfx.NewBuffer(data))
143}
144
145// The values for the MountFlags field.
146// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
147const (
148	MountFlagsReadOnly = 0x1 // SSH_FXE_STATVFS_ST_RDONLY
149	MountFlagsNoSUID   = 0x2 // SSH_FXE_STATVFS_ST_NOSUID
150)
151
152// StatVFSExtendedReplyPacket defines the extended reply packet for statvfs@openssh.com and fstatvfs@openssh.com requests.
153type StatVFSExtendedReplyPacket struct {
154	BlockSize     uint64 /* f_bsize:   file system block size */
155	FragmentSize  uint64 /* f_frsize:  fundamental fs block size / fagment size */
156	Blocks        uint64 /* f_blocks:  number of blocks (unit f_frsize) */
157	BlocksFree    uint64 /* f_bfree:   free blocks in filesystem */
158	BlocksAvail   uint64 /* f_bavail:  free blocks for non-root */
159	Files         uint64 /* f_files:   total file inodes */
160	FilesFree     uint64 /* f_ffree:   free file inodes */
161	FilesAvail    uint64 /* f_favail:  free file inodes for to non-root */
162	FilesystemID  uint64 /* f_fsid:    file system id */
163	MountFlags    uint64 /* f_flag:    bit mask of mount flag values */
164	MaxNameLength uint64 /* f_namemax: maximum filename length */
165}
166
167// Type returns the SSH_FXP_EXTENDED_REPLY packet type.
168func (ep *StatVFSExtendedReplyPacket) Type() sshfx.PacketType {
169	return sshfx.PacketTypeExtendedReply
170}
171
172// MarshalPacket returns ep as a two-part binary encoding of the full extended reply packet.
173func (ep *StatVFSExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
174	p := &sshfx.ExtendedReplyPacket{
175		Data: ep,
176	}
177	return p.MarshalPacket(reqid, b)
178}
179
180// UnmarshalPacketBody returns ep as a two-part binary encoding of the full extended reply packet.
181func (ep *StatVFSExtendedReplyPacket) UnmarshalPacketBody(buf *sshfx.Buffer) (err error) {
182	p := &sshfx.ExtendedReplyPacket{
183		Data: ep,
184	}
185	return p.UnmarshalPacketBody(buf)
186}
187
188// MarshalInto encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
189func (ep *StatVFSExtendedReplyPacket) MarshalInto(buf *sshfx.Buffer) {
190	buf.AppendUint64(ep.BlockSize)
191	buf.AppendUint64(ep.FragmentSize)
192	buf.AppendUint64(ep.Blocks)
193	buf.AppendUint64(ep.BlocksFree)
194	buf.AppendUint64(ep.BlocksAvail)
195	buf.AppendUint64(ep.Files)
196	buf.AppendUint64(ep.FilesFree)
197	buf.AppendUint64(ep.FilesAvail)
198	buf.AppendUint64(ep.FilesystemID)
199	buf.AppendUint64(ep.MountFlags)
200	buf.AppendUint64(ep.MaxNameLength)
201}
202
203// MarshalBinary encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
204//
205// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended reply packet.
206func (ep *StatVFSExtendedReplyPacket) MarshalBinary() ([]byte, error) {
207	size := 11 * 8 // 11 × uint64(various)
208
209	b := sshfx.NewBuffer(make([]byte, 0, size))
210	ep.MarshalInto(b)
211	return b.Bytes(), nil
212}
213
214// UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
215func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
216	if ep.BlockSize, err = buf.ConsumeUint64(); err != nil {
217		return err
218	}
219	if ep.FragmentSize, err = buf.ConsumeUint64(); err != nil {
220		return err
221	}
222	if ep.Blocks, err = buf.ConsumeUint64(); err != nil {
223		return err
224	}
225	if ep.BlocksFree, err = buf.ConsumeUint64(); err != nil {
226		return err
227	}
228	if ep.BlocksAvail, err = buf.ConsumeUint64(); err != nil {
229		return err
230	}
231	if ep.Files, err = buf.ConsumeUint64(); err != nil {
232		return err
233	}
234	if ep.FilesFree, err = buf.ConsumeUint64(); err != nil {
235		return err
236	}
237	if ep.FilesAvail, err = buf.ConsumeUint64(); err != nil {
238		return err
239	}
240	if ep.FilesystemID, err = buf.ConsumeUint64(); err != nil {
241		return err
242	}
243	if ep.MountFlags, err = buf.ConsumeUint64(); err != nil {
244		return err
245	}
246	if ep.MaxNameLength, err = buf.ConsumeUint64(); err != nil {
247		return err
248	}
249
250	return nil
251}
252
253// UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
254func (ep *StatVFSExtendedReplyPacket) UnmarshalBinary(data []byte) (err error) {
255	return ep.UnmarshalFrom(sshfx.NewBuffer(data))
256}
257