github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/p9/version.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package p9 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 ) 22 23 const ( 24 // highestSupportedVersion is the highest supported version X in a 25 // version string of the format 9P2000.L.Google.X. 26 // 27 // Clients are expected to start requesting this version number and 28 // to continuously decrement it until a Tversion request succeeds. 29 highestSupportedVersion uint32 = 13 30 31 // lowestSupportedVersion is the lowest supported version X in a 32 // version string of the format 9P2000.L.Google.X. 33 // 34 // Clients are free to send a Tversion request at a version below this 35 // value but are expected to encounter an Rlerror in response. 36 lowestSupportedVersion uint32 = 0 37 38 // baseVersion is the base version of 9P that this package must always 39 // support. It is equivalent to 9P2000.L.Google.0. 40 baseVersion = "9P2000.L" 41 ) 42 43 // HighestVersionString returns the highest possible version string that a client 44 // may request or a server may support. 45 func HighestVersionString() string { 46 return versionString(highestSupportedVersion) 47 } 48 49 // parseVersion parses a Tversion version string into a numeric version number 50 // if the version string is supported by p9. Otherwise returns (0, false). 51 // 52 // From Tversion(9P): "Version strings are defined such that, if the client string 53 // contains one or more period characters, the initial substring up to but not 54 // including any single period in the version string defines a version of the protocol." 55 // 56 // p9 intentionally diverges from this and always requires that the version string 57 // start with 9P2000.L to express that it is always compatible with 9P2000.L. The 58 // only supported versions extensions are of the format 9p2000.L.Google.X where X 59 // is an ever increasing version counter. 60 // 61 // Version 9P2000.L.Google.0 implies 9P2000.L. 62 // 63 // New versions must always be a strict superset of 9P2000.L. A version increase must 64 // define a predicate representing the feature extension introduced by that version. The 65 // predicate must be commented and should take the format: 66 // 67 // // VersionSupportsX returns true if version v supports X and must be checked when ... 68 // func VersionSupportsX(v int32) bool { 69 // ... 70 // ) 71 func parseVersion(str string) (uint32, bool) { 72 // Special case the base version which lacks the ".Google.X" suffix. This 73 // version always means version 0. 74 if str == baseVersion { 75 return 0, true 76 } 77 substr := strings.Split(str, ".") 78 if len(substr) != 4 { 79 return 0, false 80 } 81 if substr[0] != "9P2000" || substr[1] != "L" || substr[2] != "Google" || len(substr[3]) == 0 { 82 return 0, false 83 } 84 version, err := strconv.ParseUint(substr[3], 10, 32) 85 if err != nil { 86 return 0, false 87 } 88 return uint32(version), true 89 } 90 91 // versionString formats a p9 version number into a Tversion version string. 92 func versionString(version uint32) string { 93 // Special case the base version so that clients expecting this string 94 // instead of the 9P2000.L.Google.0 equivalent get it. This is important 95 // for backwards compatibility with legacy servers that check for exactly 96 // the baseVersion and allow nothing else. 97 if version == 0 { 98 return baseVersion 99 } 100 return fmt.Sprintf("9P2000.L.Google.%d", version) 101 } 102 103 // VersionSupportsTflushf returns true if version v supports the Tflushf message. 104 // This predicate must be checked by clients before attempting to make a Tflushf 105 // request. If this predicate returns false, then clients may safely no-op. 106 func VersionSupportsTflushf(v uint32) bool { 107 return v >= 1 108 } 109 110 // versionSupportsTwalkgetattr returns true if version v supports the 111 // Twalkgetattr message. This predicate must be checked by clients before 112 // attempting to make a Twalkgetattr request. 113 func versionSupportsTwalkgetattr(v uint32) bool { 114 return v >= 2 115 } 116 117 // versionSupportsTucreation returns true if version v supports the Tucreation 118 // messages (Tucreate, Tusymlink, Tumkdir, Tumknod). This predicate must be 119 // checked by clients before attempting to make a Tucreation request. 120 // If Tucreation messages are not supported, their non-UID supporting 121 // counterparts (Tlcreate, Tsymlink, Tmkdir, Tmknod) should be used. 122 func versionSupportsTucreation(v uint32) bool { 123 return v >= 3 124 } 125 126 // VersionSupportsConnect returns true if version v supports the Tlconnect 127 // message. This predicate must be checked by clients 128 // before attempting to make a Tlconnect request. If Tlconnect messages are not 129 // supported, Tlopen should be used. 130 func VersionSupportsConnect(v uint32) bool { 131 return v >= 4 132 } 133 134 // VersionSupportsAnonymous returns true if version v supports Tlconnect 135 // with the AnonymousSocket mode. This predicate must be checked by clients 136 // before attempting to use the AnonymousSocket Tlconnect mode. 137 func VersionSupportsAnonymous(v uint32) bool { 138 return v >= 5 139 } 140 141 // VersionSupportsMultiUser returns true if version v supports multi-user fake 142 // directory permissions and ID values. 143 func VersionSupportsMultiUser(v uint32) bool { 144 return v >= 6 145 } 146 147 // versionSupportsTallocate returns true if version v supports Allocate(). 148 func versionSupportsTallocate(v uint32) bool { 149 return v >= 7 150 } 151 152 // versionSupportsFlipcall returns true if version v supports IPC channels from 153 // the flipcall package. Note that these must be negotiated, but this version 154 // string indicates that such a facility exists. 155 func versionSupportsFlipcall(v uint32) bool { 156 return v >= 8 157 } 158 159 // VersionSupportsOpenTruncateFlag returns true if version v supports 160 // passing the OpenTruncate flag to Tlopen. 161 func VersionSupportsOpenTruncateFlag(v uint32) bool { 162 return v >= 9 163 } 164 165 // versionSupportsGetSetXattr returns true if version v supports 166 // the Tgetxattr and Tsetxattr messages. 167 func versionSupportsGetSetXattr(v uint32) bool { 168 return v >= 10 169 } 170 171 // versionSupportsListRemoveXattr returns true if version v supports 172 // the Tlistxattr and Tremovexattr messages. 173 func versionSupportsListRemoveXattr(v uint32) bool { 174 return v >= 11 175 } 176 177 // versionSupportsTsetattrclunk returns true if version v supports 178 // the Tsetattrclunk message. 179 func versionSupportsTsetattrclunk(v uint32) bool { 180 return v >= 12 181 } 182 183 // versionSupportsTmultiGetAttr returns true if version v supports 184 // the TmultiGetAttr message. 185 func versionSupportsTmultiGetAttr(v uint32) bool { 186 return v >= 13 187 }