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
|
import java.io.IOException
import java.math.BigInteger
/**
* This class represents a 9P connection. It provides a practical implementation with networking to the 9P methods
* described in [ProtocolTranslator]. Details about methods related to 9P can be found in [ProtocolTranslator]. Remember
* to disconnect using [disconnect] after use.
*
* Details about network-related topics can be found in [TransportLayer] and the implementation of choice.
*
* Details about 9P messages and methods can be found in [ProtocolTranslator].
*
* @param transLay The networking API backend of choice.
*
* @throws UnresolvableHostException if the host resolution made by [transLay] failed.
*/
class Connection(transLay: TransportLayer) : ProtocolTranslator {
/**
* Networking API.
*/
private val tl: TransportLayer = transLay
/**
* Tag generator.
*/
private val tagGen = TagGenerator(TagGenerator.TagGenerationMethod.INCREMENTAL, 1u)
/**
* Maximum size for messages negotiated between the client and the server.
*/
private var maxSize: UInt = 0u
/**
* Each FID associated with each file.
*/
val fids: Map<UInt, String> = emptyMap()
// 9P constants.
val DEFAULT_VERSION = "9P2000"
val NOTAG = 0.toUShort().inv()
val NOFID = 0.toUInt().inv()
/**
* Disconnect from the remote host,
*
* @throws IOException if an I/O error occurred while closing the socket.
*/
fun disconnect() {
this.tl.close()
}
/**
* Handy function to create an [InMessage] instance and check for errors. It uses [tl] and [maxSize] for instancing
* the [InMessage] class.
*
* @return A pair of: (1) a nullable string (which can be: `null` if no error occurred, empty if an error occurred
* with no message, or non-empty with the error message) and (2) the optional [InMessage] instance (null if an error
* occurred).
*/
private fun checkedInMessage(reqTag: UShort): Pair<String?, InMessage?> {
val imsg: InMessage
try {
imsg = InMessage(this.tl, this.maxSize, reqTag)
} catch (ime: InvalidMessageException) {
return Pair(ime.message!!, null)
}
if (imsg.type == NinePMessageType.RERROR) {
imsg.applyField(InMessage.Field("ename", InMessage.Field.Type.STRING, 0u))
return Pair(imsg.fieldsStr["ename"]!!, null)
}
return Pair(null, imsg)
}
override fun version(msize: UInt, version: String): String? {
val omsg = OutMessage(NinePMessageType.TVERSION, this.NOTAG, listOf("msize", "version"),
mapOf(
"msize" to BigInteger(msize.toString())
),
mapOf(
"version" to version
),
emptyMap(),
this.maxSize
)
val imsg: InMessage
try {
imsg = InMessage(this.tl, msize, omsg.tag)
} catch (ime: InvalidMessageException) {
return ime.message
}
imsg.applySchema(listOf(
InMessage.Field("msize", InMessage.Field.Type.INTEGER, 4u),
InMessage.Field("version", InMessage.Field.Type.STRING, 0u)
))
val remoteMaxSize = imsg.fieldsInt["msize"]!!.toInt().toUInt()
if (remoteMaxSize > this.maxSize) {
return "Invalid remote msize value (too big)."
}
this.maxSize = remoteMaxSize
if (imsg.fieldsStr["version"] == "unknown") {
return "Unknown version."
}
return null
}
override fun auth(afid: UInt, uname: String, aname: String) {
// TODO: Leave this unimplemented until p9any and TLS are implemented
TODO("Not yet implemented")
}
override fun flush() {
TODO("Not yet implemented")
}
override fun attach() {
TODO("Not yet implemented")
}
override fun walk(path: String) {
TODO("Not yet implemented")
}
override fun open(path: String) {
TODO("Not yet implemented")
}
override fun create(path: String) {
TODO("Not yet implemented")
}
override fun read(fid: UInt, offset: ULong, count: UInt): Pair<String?, Array<UByte>> {
val omsg = OutMessage(NinePMessageType.TREAD, this.tagGen.generate(), listOf("fid", "offset", "count"),
mapOf(
"fid" to BigInteger(fid.toString()),
"offset" to BigInteger(offset.toString()),
"count" to BigInteger(count.toString())
),
emptyMap(),
emptyMap(),
this.maxSize
)
val checkErr = checkedInMessage(omsg.tag)
if (checkErr.first != null) {
return Pair(checkErr.first, emptyArray())
}
val imsg = checkErr.second!!
imsg.applyField(InMessage.Field("count", InMessage.Field.Type.INTEGER, 4u))
val count = imsg.fieldsInt["count"]!!.toInt().toUInt()
imsg.applyField(InMessage.Field("data", InMessage.Field.Type.RAW, count))
return Pair(null, imsg.fieldsRaw["data"]!!)
}
override fun write(fid: UInt, offset: ULong, count: UInt, data: Iterable<UByte>): String? {
}
override fun clunk(path: String) {
TODO("Not yet implemented")
}
override fun remove(path: String) {
TODO("Not yet implemented")
}
override fun stat(path: String) {
TODO("Not yet implemented")
}
override fun wstat(path: String) {
TODO("Not yet implemented")
}
}
|