summaryrefslogtreecommitdiffhomepage
path: root/pkg/p9/version.go
blob: 8783eaa7e6f477107bbc8cf9963fc3f8dcff7953 (plain)
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
// Copyright 2018 Google Inc.
//
// 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 p9

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	// highestSupportedVersion is the highest supported version X in a
	// version string of the format 9P2000.L.Google.X.
	//
	// Clients are expected to start requesting this version number and
	// to continuously decrement it until a Tversion request succeeds.
	highestSupportedVersion uint32 = 6

	// lowestSupportedVersion is the lowest supported version X in a
	// version string of the format 9P2000.L.Google.X.
	//
	// Clients are free to send a Tversion request at a version below this
	// value but are expected to encounter an Rlerror in response.
	lowestSupportedVersion uint32 = 0

	// baseVersion is the base version of 9P that this package must always
	// support.  It is equivalent to 9P2000.L.Google.0.
	baseVersion = "9P2000.L"
)

// HighestVersionString returns the highest possible version string that a client
// may request or a server may support.
func HighestVersionString() string {
	return versionString(highestSupportedVersion)
}

// parseVersion parses a Tversion version string into a numeric version number
// if the version string is supported by p9.  Otherwise returns (0, false).
//
// From Tversion(9P): "Version strings are defined such that, if the client string
// contains one or more period characters, the initial substring up to but not
// including any single period in the version string defines a version of the protocol."
//
// p9 intentionally diverges from this and always requires that the version string
// start with 9P2000.L to express that it is always compatible with 9P2000.L.  The
// only supported versions extensions are of the format 9p2000.L.Google.X where X
// is an ever increasing version counter.
//
// Version 9P2000.L.Google.0 implies 9P2000.L.
//
// New versions must always be a strict superset of 9P2000.L. A version increase must
// define a predicate representing the feature extension introduced by that version. The
// predicate must be commented and should take the format:
//
// // VersionSupportsX returns true if version v supports X and must be checked when ...
// func VersionSupportsX(v int32) bool {
//	...
// )
func parseVersion(str string) (uint32, bool) {
	// Special case the base version which lacks the ".Google.X" suffix.  This
	// version always means version 0.
	if str == baseVersion {
		return 0, true
	}
	substr := strings.Split(str, ".")
	if len(substr) != 4 {
		return 0, false
	}
	if substr[0] != "9P2000" || substr[1] != "L" || substr[2] != "Google" || len(substr[3]) == 0 {
		return 0, false
	}
	version, err := strconv.ParseUint(substr[3], 10, 32)
	if err != nil {
		return 0, false
	}
	return uint32(version), true
}

// versionString formats a p9 version number into a Tversion version string.
func versionString(version uint32) string {
	// Special case the base version so that clients expecting this string
	// instead of the 9P2000.L.Google.0 equivalent get it.  This is important
	// for backwards compatibility with legacy servers that check for exactly
	// the baseVersion and allow nothing else.
	if version == 0 {
		return baseVersion
	}
	return fmt.Sprintf("9P2000.L.Google.%d", version)
}

// VersionSupportsTflushf returns true if version v supports the Tflushf message.
// This predicate must be checked by clients before attempting to make a Tflushf
// request.  If this predicate returns false, then clients may safely no-op.
func VersionSupportsTflushf(v uint32) bool {
	return v >= 1
}

// versionSupportsTwalkgetattr returns true if version v supports the
// Twalkgetattr message. This predicate must be checked by clients before
// attempting to make a Twalkgetattr request.
func versionSupportsTwalkgetattr(v uint32) bool {
	return v >= 2
}

// versionSupportsTucreation returns true if version v supports the Tucreation
// messages (Tucreate, Tusymlink, Tumkdir, Tumknod). This predicate must be
// checked by clients before attempting to make a Tucreation request.
// If Tucreation messages are not supported, their non-UID supporting
// counterparts (Tlcreate, Tsymlink, Tmkdir, Tmknod) should be used.
func versionSupportsTucreation(v uint32) bool {
	return v >= 3
}

// VersionSupportsConnect returns true if version v supports the Tlconnect
// message. This predicate must be checked by clients
// before attempting to make a Tlconnect request. If Tlconnect messages are not
// supported, Tlopen should be used.
func VersionSupportsConnect(v uint32) bool {
	return v >= 4
}

// VersionSupportsAnonymous returns true if version v supports Tlconnect
// with the AnonymousSocket mode. This predicate must be checked by clients
// before attempting to use the AnonymousSocket Tlconnect mode.
func VersionSupportsAnonymous(v uint32) bool {
	return v >= 5
}

// VersionSupportsMultiUser returns true if version v supports multi-user fake
// directory permissions and ID values.
func VersionSupportsMultiUser(v uint32) bool {
	return v >= 6
}