aboutsummaryrefslogtreecommitdiff
path: root/internal/encoding/ssh/filexfer/openssh/statvfs.go
blob: 3e9015f7cdb9c98f303202051433cd9a1eb76263 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package openssh

import (
	sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
)

const extensionStatVFS = "statvfs@openssh.com"

// RegisterExtensionStatVFS registers the "statvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
func RegisterExtensionStatVFS() {
	sshfx.RegisterExtendedPacketType(extensionStatVFS, func() sshfx.ExtendedData {
		return new(StatVFSExtendedPacket)
	})
}

// ExtensionStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
func ExtensionStatVFS() *sshfx.ExtensionPair {
	return &sshfx.ExtensionPair{
		Name: extensionStatVFS,
		Data: "2",
	}
}

// StatVFSExtendedPacket defines the statvfs@openssh.com extend packet.
type StatVFSExtendedPacket struct {
	Path string
}

// Type returns the SSH_FXP_EXTENDED packet type.
func (ep *StatVFSExtendedPacket) Type() sshfx.PacketType {
	return sshfx.PacketTypeExtended
}

// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
func (ep *StatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
	p := &sshfx.ExtendedPacket{
		ExtendedRequest: extensionStatVFS,

		Data: ep,
	}
	return p.MarshalPacket(reqid, b)
}

// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
func (ep *StatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
	buf.AppendString(ep.Path)
}

// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
//
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
func (ep *StatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
	size := 4 + len(ep.Path) // string(path)

	buf := sshfx.NewBuffer(make([]byte, 0, size))

	ep.MarshalInto(buf)

	return buf.Bytes(), nil
}

// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
	if ep.Path, err = buf.ConsumeString(); err != nil {
		return err
	}

	return nil
}

// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
func (ep *StatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
	return ep.UnmarshalFrom(sshfx.NewBuffer(data))
}

const extensionFStatVFS = "fstatvfs@openssh.com"

// RegisterExtensionFStatVFS registers the "fstatvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
func RegisterExtensionFStatVFS() {
	sshfx.RegisterExtendedPacketType(extensionFStatVFS, func() sshfx.ExtendedData {
		return new(FStatVFSExtendedPacket)
	})
}

// ExtensionFStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
func ExtensionFStatVFS() *sshfx.ExtensionPair {
	return &sshfx.ExtensionPair{
		Name: extensionFStatVFS,
		Data: "2",
	}
}

// FStatVFSExtendedPacket defines the fstatvfs@openssh.com extend packet.
type FStatVFSExtendedPacket struct {
	Path string
}

// Type returns the SSH_FXP_EXTENDED packet type.
func (ep *FStatVFSExtendedPacket) Type() sshfx.PacketType {
	return sshfx.PacketTypeExtended
}

// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
func (ep *FStatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
	p := &sshfx.ExtendedPacket{
		ExtendedRequest: extensionFStatVFS,

		Data: ep,
	}
	return p.MarshalPacket(reqid, b)
}

// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
func (ep *FStatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
	buf.AppendString(ep.Path)
}

// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
//
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
func (ep *FStatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
	size := 4 + len(ep.Path) // string(path)

	buf := sshfx.NewBuffer(make([]byte, 0, size))

	ep.MarshalInto(buf)

	return buf.Bytes(), nil
}

// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
	if ep.Path, err = buf.ConsumeString(); err != nil {
		return err
	}

	return nil
}

// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
func (ep *FStatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
	return ep.UnmarshalFrom(sshfx.NewBuffer(data))
}

// The values for the MountFlags field.
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
const (
	MountFlagsReadOnly = 0x1 // SSH_FXE_STATVFS_ST_RDONLY
	MountFlagsNoSUID   = 0x2 // SSH_FXE_STATVFS_ST_NOSUID
)

// StatVFSExtendedReplyPacket defines the extended reply packet for statvfs@openssh.com and fstatvfs@openssh.com requests.
type StatVFSExtendedReplyPacket struct {
	BlockSize     uint64 /* f_bsize:   file system block size */
	FragmentSize  uint64 /* f_frsize:  fundamental fs block size / fagment size */
	Blocks        uint64 /* f_blocks:  number of blocks (unit f_frsize) */
	BlocksFree    uint64 /* f_bfree:   free blocks in filesystem */
	BlocksAvail   uint64 /* f_bavail:  free blocks for non-root */
	Files         uint64 /* f_files:   total file inodes */
	FilesFree     uint64 /* f_ffree:   free file inodes */
	FilesAvail    uint64 /* f_favail:  free file inodes for to non-root */
	FilesystemID  uint64 /* f_fsid:    file system id */
	MountFlags    uint64 /* f_flag:    bit mask of mount flag values */
	MaxNameLength uint64 /* f_namemax: maximum filename length */
}

// Type returns the SSH_FXP_EXTENDED_REPLY packet type.
func (ep *StatVFSExtendedReplyPacket) Type() sshfx.PacketType {
	return sshfx.PacketTypeExtendedReply
}

// MarshalPacket returns ep as a two-part binary encoding of the full extended reply packet.
func (ep *StatVFSExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
	p := &sshfx.ExtendedReplyPacket{
		Data: ep,
	}
	return p.MarshalPacket(reqid, b)
}

// UnmarshalPacketBody returns ep as a two-part binary encoding of the full extended reply packet.
func (ep *StatVFSExtendedReplyPacket) UnmarshalPacketBody(buf *sshfx.Buffer) (err error) {
	p := &sshfx.ExtendedReplyPacket{
		Data: ep,
	}
	return p.UnmarshalPacketBody(buf)
}

// MarshalInto encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
func (ep *StatVFSExtendedReplyPacket) MarshalInto(buf *sshfx.Buffer) {
	buf.AppendUint64(ep.BlockSize)
	buf.AppendUint64(ep.FragmentSize)
	buf.AppendUint64(ep.Blocks)
	buf.AppendUint64(ep.BlocksFree)
	buf.AppendUint64(ep.BlocksAvail)
	buf.AppendUint64(ep.Files)
	buf.AppendUint64(ep.FilesFree)
	buf.AppendUint64(ep.FilesAvail)
	buf.AppendUint64(ep.FilesystemID)
	buf.AppendUint64(ep.MountFlags)
	buf.AppendUint64(ep.MaxNameLength)
}

// MarshalBinary encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
//
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended reply packet.
func (ep *StatVFSExtendedReplyPacket) MarshalBinary() ([]byte, error) {
	size := 11 * 8 // 11 × uint64(various)

	b := sshfx.NewBuffer(make([]byte, 0, size))
	ep.MarshalInto(b)
	return b.Bytes(), nil
}

// UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
	if ep.BlockSize, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.FragmentSize, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.Blocks, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.BlocksFree, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.BlocksAvail, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.Files, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.FilesFree, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.FilesAvail, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.FilesystemID, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.MountFlags, err = buf.ConsumeUint64(); err != nil {
		return err
	}
	if ep.MaxNameLength, err = buf.ConsumeUint64(); err != nil {
		return err
	}

	return nil
}

// UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
func (ep *StatVFSExtendedReplyPacket) UnmarshalBinary(data []byte) (err error) {
	return ep.UnmarshalFrom(sshfx.NewBuffer(data))
}