rstyx 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +482 -0
- data/ChangeLog +4 -0
- data/Manifest.txt +17 -0
- data/NEWS +2 -0
- data/README +41 -0
- data/Rakefile +55 -0
- data/examples/readstyxfile.rb +48 -0
- data/lib/rstyx/authmodules.rb +90 -0
- data/lib/rstyx/client.rb +709 -693
- data/lib/rstyx/common.rb +71 -0
- data/lib/rstyx/errors.rb +35 -2
- data/lib/rstyx/messages.rb +377 -1113
- data/lib/rstyx/server.rb +1305 -0
- data/lib/rstyx/version.rb +2 -2
- data/lib/rstyx.rb +11 -4
- data/tests/tc_client.rb +260 -45
- data/tests/tc_message.rb +245 -388
- data/tests/tc_styxservproto.rb +596 -0
- metadata +51 -26
data/lib/rstyx/messages.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
#
|
3
|
-
# Copyright (C) 2005 Rafael Sevilla
|
3
|
+
# Copyright (C) 2005,2006 Rafael Sevilla
|
4
4
|
# This file is part of RStyx
|
5
5
|
#
|
6
6
|
# RStyx is free software; you can redistribute it and/or modify
|
@@ -15,16 +15,16 @@
|
|
15
15
|
#
|
16
16
|
# You should have received a copy of the GNU Lesser General Public
|
17
17
|
# License along with RStyx; if not, write to the Free Software
|
18
|
-
# Foundation, Inc.,
|
19
|
-
#
|
18
|
+
# Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA.
|
20
20
|
#
|
21
21
|
# Styx Message classes and utility functions
|
22
22
|
#
|
23
23
|
# Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
|
24
|
-
# Copyright:: Copyright (c) 2005 Rafael R. Sevilla
|
24
|
+
# Copyright:: Copyright (c) 2005,2006 Rafael R. Sevilla
|
25
25
|
# License:: GNU Lesser General Public License
|
26
26
|
#
|
27
|
-
# $Id: messages.rb
|
27
|
+
# $Id: messages.rb 229 2007-08-10 08:03:58Z dido $
|
28
28
|
#
|
29
29
|
|
30
30
|
require 'rstyx/errors'
|
@@ -32,52 +32,6 @@ require 'rstyx/errors'
|
|
32
32
|
module RStyx
|
33
33
|
|
34
34
|
module Message
|
35
|
-
##
|
36
|
-
# Utility class
|
37
|
-
#
|
38
|
-
class Util
|
39
|
-
##
|
40
|
-
# Extract a Ruby string given a byte string, with an offset pointing
|
41
|
-
# to the start of the Styx string to extract. Returns the Styx string
|
42
|
-
# plus the offset of the first character just after the end of the
|
43
|
-
# string thus extracted.
|
44
|
-
#
|
45
|
-
# +str+:: [String] the byte string to extract from
|
46
|
-
# +offset+:: [Fixnum] an offset into str for the Styx string to be
|
47
|
-
# extracted
|
48
|
-
# return value:: [Array]
|
49
|
-
# 0:: [String] the Ruby string
|
50
|
-
# 1:: [Fixnum] the offset of the first character after
|
51
|
-
#
|
52
|
-
# raises:: StyxException, if no length was found or if the length
|
53
|
-
# of the string is different from its declared length.
|
54
|
-
#
|
55
|
-
def Util.strextract(str, offset)
|
56
|
-
length = (str[offset..(offset + 1)].unpack("v"))[0]
|
57
|
-
if length.nil?
|
58
|
-
raise StyxException.new("invalid string, no length found")
|
59
|
-
end
|
60
|
-
|
61
|
-
offset += 2
|
62
|
-
nstr = (str[offset..(offset + length - 1)])
|
63
|
-
if (nstr.length != length)
|
64
|
-
raise StyxException.new("invalid string")
|
65
|
-
end
|
66
|
-
return([nstr, offset + length])
|
67
|
-
end
|
68
|
-
|
69
|
-
##
|
70
|
-
# Pack a string into the canonical format of number of bytes as short
|
71
|
-
# unsigned int followed by the string itself
|
72
|
-
#
|
73
|
-
# +str+:: [String] the Ruby string to pack
|
74
|
-
# return value:: [String] the corresponding Styx string
|
75
|
-
#
|
76
|
-
def Util.strpack(str)
|
77
|
-
return([str.length, str].pack("vA*"))
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
35
|
##
|
82
36
|
# Class representing the server's view of a file.
|
83
37
|
#
|
@@ -100,6 +54,13 @@ module RStyx
|
|
100
54
|
@path = path
|
101
55
|
end
|
102
56
|
|
57
|
+
##
|
58
|
+
# Equality
|
59
|
+
#
|
60
|
+
def ==(x)
|
61
|
+
return(self.to_bytes == x.to_bytes)
|
62
|
+
end
|
63
|
+
|
103
64
|
##
|
104
65
|
# Get the byte string representation of the Qid
|
105
66
|
#
|
@@ -118,7 +79,7 @@ module RStyx
|
|
118
79
|
# return value:: [Qid] the Qid represented by the byte string.
|
119
80
|
# raises:: StyxException if the string cannot be decoded as a Qid
|
120
81
|
#
|
121
|
-
def
|
82
|
+
def self.from_bytes(msgbytes)
|
122
83
|
qtype, version, pathlo, pathhi = msgbytes.unpack("CVVV")
|
123
84
|
if qtype.nil? || version.nil? || pathlo.nil? || pathhi.nil?
|
124
85
|
raise StyxException.new("QID failed decode")
|
@@ -134,23 +95,38 @@ module RStyx
|
|
134
95
|
# return value:: [String] a textual representation of the Qid
|
135
96
|
#
|
136
97
|
def to_s
|
137
|
-
|
98
|
+
val = sprintf("(Qid 0x%02x %d 0x%02x)", @qtype, @version, @path)
|
99
|
+
return(val)
|
138
100
|
end
|
139
101
|
end
|
140
102
|
|
141
103
|
##
|
142
104
|
# Class representing an entry in a directory (e.g. the result of a
|
143
|
-
# stat message)
|
105
|
+
# stat message). See Inferno man page stat(5) for more details.
|
144
106
|
#
|
145
|
-
class
|
107
|
+
class Stat
|
146
108
|
attr_accessor :size, :dtype, :dev, :qid, :mode, :atime, :mtime
|
147
109
|
attr_accessor :length, :name, :uid, :gid, :muid
|
148
110
|
|
111
|
+
def self.strextract(str, offset)
|
112
|
+
length = (str[offset..(offset + 1)].unpack("v"))[0]
|
113
|
+
if length.nil?
|
114
|
+
raise StyxException.new("invalid string, no length found")
|
115
|
+
end
|
116
|
+
|
117
|
+
offset += 2
|
118
|
+
nstr = (str[offset..(offset + length - 1)])
|
119
|
+
if (nstr.length != length)
|
120
|
+
raise StyxException.new("invalid string")
|
121
|
+
end
|
122
|
+
return([nstr, offset + length])
|
123
|
+
end
|
124
|
+
|
149
125
|
##
|
150
|
-
# Serialize a
|
151
|
-
#
|
126
|
+
# Serialize a Stat. This also calculates the size of the
|
127
|
+
# Stat in bytes.
|
152
128
|
#
|
153
|
-
# return value:: [String] the serialized version of the
|
129
|
+
# return value:: [String] the serialized version of the Stat
|
154
130
|
#
|
155
131
|
def to_bytes
|
156
132
|
str = [@dtype, @dev].pack("vV")
|
@@ -158,24 +134,27 @@ module RStyx
|
|
158
134
|
lengthlo = @length & 0xffffffff
|
159
135
|
lengthhi = (@length >> 32) & 0xffffffff
|
160
136
|
str << [@mode, @atime, @mtime, lengthlo, lengthhi].pack("VVVVV")
|
161
|
-
str <<
|
162
|
-
|
163
|
-
|
164
|
-
|
137
|
+
str << [@name.length, @name].pack("va*")
|
138
|
+
@uid ||= ""
|
139
|
+
@gid ||= ""
|
140
|
+
@muid ||= ""
|
141
|
+
str << [@uid.length, @uid].pack("va*")
|
142
|
+
str << [@gid.length, @gid].pack("va*")
|
143
|
+
str << [@muid.length, @muid].pack("va*")
|
165
144
|
@size = str.length
|
166
145
|
return([@size].pack("v") + str)
|
167
146
|
end
|
168
147
|
|
169
148
|
##
|
170
|
-
# Unserialize a
|
149
|
+
# Unserialize a Stat
|
171
150
|
#
|
172
|
-
# +bytes+:: [String] serialized string representation of a
|
173
|
-
# return value:: [
|
151
|
+
# +bytes+:: [String] serialized string representation of a Stat
|
152
|
+
# return value:: [Stat] the Stat corresponding to the
|
174
153
|
# passed string
|
175
154
|
# raises:: StyxException if +bytes+ cannot be properly decoded as
|
176
|
-
# a
|
155
|
+
# a Stat
|
177
156
|
#
|
178
|
-
def
|
157
|
+
def self.from_bytes(bytes)
|
179
158
|
# From Inferno stat(5)
|
180
159
|
#
|
181
160
|
# 0-1 = size
|
@@ -193,1240 +172,525 @@ module RStyx
|
|
193
172
|
# 43 + namelen to 44 + namelen = uid length
|
194
173
|
# 45 + namelen to 44 + namelen + uidlen = uid
|
195
174
|
# 45 + namelen + uidlen
|
196
|
-
de =
|
175
|
+
de = Stat.new
|
197
176
|
de.size, de.dtype, de.dev = (bytes[0..7]).unpack("vvV")
|
198
177
|
if de.size.nil? || de.dtype.nil? || de.dev.nil?
|
199
|
-
raise RStyx::StyxException.new("failed to decode
|
178
|
+
raise RStyx::StyxException.new("failed to decode Stat")
|
200
179
|
end
|
201
|
-
de.qid = Qid.
|
180
|
+
de.qid = Qid.from_bytes(bytes[8..20])
|
202
181
|
de.mode, de.atime, de.mtime, lengthlo, lengthhi =
|
203
182
|
(bytes[21..40]).unpack("VVVVV")
|
204
183
|
|
205
184
|
if de.mode.nil? || de.atime.nil? || de.mtime.nil? ||
|
206
185
|
lengthlo.nil? || lengthhi.nil?
|
207
|
-
raise RStyx::StyxException.new("failed to decode
|
186
|
+
raise RStyx::StyxException.new("failed to decode Stat")
|
208
187
|
end
|
209
188
|
# combine in little-endian
|
210
189
|
de.length = lengthlo | (lengthhi << 32)
|
211
|
-
de.name, offset =
|
212
|
-
de.uid, offset =
|
213
|
-
de.gid, offset =
|
214
|
-
de.muid, offset =
|
190
|
+
de.name, offset = self.strextract(bytes, 41)
|
191
|
+
de.uid, offset = self.strextract(bytes, offset)
|
192
|
+
de.gid, offset = self.strextract(bytes, offset)
|
193
|
+
de.muid, offset = self.strextract(bytes, offset)
|
215
194
|
return(de)
|
216
195
|
end
|
217
196
|
|
218
197
|
##
|
219
|
-
# convert a
|
198
|
+
# convert a Stat to a human-readable string
|
220
199
|
#
|
221
|
-
# return value:: a string representation of the
|
200
|
+
# return value:: a string representation of the Stat
|
222
201
|
#
|
223
202
|
def to_s
|
224
|
-
s = sprintf("
|
225
|
-
s << sprintf("%s, %
|
226
|
-
|
227
|
-
s << sprintf("%s
|
203
|
+
s = sprintf("(Stat %d 0x%02x, 0x%04x ", @size, @dtype, @dev)
|
204
|
+
s << sprintf("%s, 0x%04x 0x%04x 0x%04x ",
|
205
|
+
@qid.to_s, @mode, @atime, @mtime)
|
206
|
+
s << sprintf("%d %s %s ", @length, @name, @uid)
|
207
|
+
s << sprintf("%s %s)", @gid, @muid)
|
228
208
|
return(s)
|
229
209
|
end
|
230
210
|
end
|
231
211
|
|
232
212
|
##
|
233
|
-
#
|
234
|
-
# The basic attributes are the length, type, tag, messagebody,
|
235
|
-
# and (printable) body.
|
213
|
+
# Base class of a Styx message.
|
236
214
|
#
|
237
215
|
class StyxMessage
|
238
|
-
attr_accessor :
|
216
|
+
attr_accessor :fieldvals
|
217
|
+
MESSAGE_IDS = {}
|
239
218
|
|
240
219
|
##
|
241
|
-
#
|
220
|
+
# Add a field to the StyxMessage. Used by subclasses to define
|
221
|
+
# the message field. The +name+ should be a Symbol that gives
|
222
|
+
# the name of the field (preferably the canonical name given in the
|
223
|
+
# Inferno manual page intro(5)), and the type may be:
|
242
224
|
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
#
|
225
|
+
# 1. Any valid format string used by String#unpack or Array#pack.
|
226
|
+
# 2. Cstr, which is a UTF-8 string, which will be serialized as
|
227
|
+
# a two-byte unsigned length (in bytes) followed by the string's
|
228
|
+
# data itself, and deserialized from this representation into
|
229
|
+
# a standard Ruby string.
|
230
|
+
# 3. CstrList, which deserializes into an array of Ruby strings,
|
231
|
+
# and is serialized into a two-byte unsigned count of strings
|
232
|
+
# followed by each of the strings itself, as in Cstr.
|
233
|
+
# 3. Bstr, which is a binary string. It will be serialized to a
|
234
|
+
# four-byte unsigned length (in bytes) followed by the string's
|
235
|
+
# data itself, and deserialized from this representation into
|
236
|
+
# a standard Ruby string.
|
237
|
+
# 4. Qid, which deserializes into a Qid object instance and is
|
238
|
+
# serialized into a 13-byte binary representation.
|
239
|
+
# 5. QidList, which deserializes into an array of Qids and is
|
240
|
+
# serialized into a two-byte unsigned count of Qids followed
|
241
|
+
# by the serialized representations of each of the Qids.
|
242
|
+
# 6. ULongLong, which deserializes into a Ruby Fixnum and is
|
243
|
+
# serialized into a 64-bit little-endian value.
|
244
|
+
# 7. Stat, which deserializes into a Stat object instance and is
|
245
|
+
# serialized into the stat format described in the Inferno
|
246
|
+
# man page stat(5). See the Stat class for more details.
|
246
247
|
#
|
247
|
-
def
|
248
|
-
|
249
|
-
|
248
|
+
def self.add_field(name, type)
|
249
|
+
self.fields << [name, type]
|
250
|
+
|
251
|
+
# Create accessor methods for the field
|
252
|
+
define_method(name) do
|
253
|
+
instance_variable_get("@fieldvals")[name]
|
254
|
+
end
|
255
|
+
|
256
|
+
define_method(name.to_s + "=") do |val|
|
257
|
+
fname = instance_variable_get("@fieldvals")
|
258
|
+
fname[name] = val
|
250
259
|
end
|
251
|
-
val = [@length, @mtype, @tag].pack("VCv") + @messagebody
|
252
|
-
return(val)
|
253
260
|
end
|
254
261
|
|
255
262
|
##
|
256
|
-
#
|
257
|
-
# must define the @body variable to something meaningful for
|
258
|
-
# this to work.
|
263
|
+
# The fields of the Styx message.
|
259
264
|
#
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
return(sprintf("%s: %s, %s, %s, %s",
|
264
|
-
self.class.name, @length, @mtype, @tag, @body))
|
265
|
+
def self.fields
|
266
|
+
# Default fields (excluding the size[4] field)
|
267
|
+
@fields ||= [[:ident, 'C'], [:tag, 'v']]
|
265
268
|
end
|
266
269
|
|
267
|
-
TVERSION = 100
|
268
|
-
RVERSION = 101
|
269
|
-
|
270
|
-
TAUTH = 102
|
271
|
-
RAUTH = 103
|
272
|
-
|
273
|
-
TATTACH = 104
|
274
|
-
RATTACH = 105
|
275
|
-
|
276
|
-
# illegal
|
277
|
-
TERROR = 106
|
278
|
-
RERROR = 107
|
279
|
-
|
280
|
-
TFLUSH = 108
|
281
|
-
RFLUSH = 109
|
282
|
-
|
283
|
-
TWALK = 110
|
284
|
-
RWALK = 111
|
285
|
-
|
286
|
-
TOPEN = 112
|
287
|
-
ROPEN = 113
|
288
|
-
|
289
|
-
TCREATE = 114
|
290
|
-
RCREATE = 115
|
291
|
-
|
292
|
-
TREAD = 116
|
293
|
-
RREAD = 117
|
294
|
-
|
295
|
-
TWRITE = 118
|
296
|
-
RWRITE = 119
|
297
|
-
|
298
|
-
TCLUNK = 120
|
299
|
-
RCLUNK = 121
|
300
|
-
|
301
|
-
TREMOVE = 122
|
302
|
-
RREMOVE = 123
|
303
|
-
|
304
|
-
TSTAT = 124
|
305
|
-
RSTAT = 125
|
306
|
-
|
307
|
-
TWSTAT = 126
|
308
|
-
RWSTAT = 127
|
309
|
-
|
310
270
|
##
|
311
|
-
#
|
312
|
-
#
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
# corresponding to the message.
|
317
|
-
# raises:: StyxException if a Styx message could not be decoded
|
318
|
-
#
|
319
|
-
def StyxMessage.decode(message)
|
320
|
-
length, type, tag = message[0..6].unpack("VCv")
|
321
|
-
if length.nil? || type.nil? || tag.nil?
|
322
|
-
raise RStyx::StyxException.new("invalid Styx message")
|
323
|
-
end
|
324
|
-
msgclass = case type
|
325
|
-
when TVERSION then Tversion
|
326
|
-
when RVERSION then Rversion
|
327
|
-
when TAUTH then Tauth
|
328
|
-
when RAUTH then Rauth
|
329
|
-
when TATTACH then Tattach
|
330
|
-
when RATTACH then Rattach
|
331
|
-
when RERROR then Rerror
|
332
|
-
when TFLUSH then Tflush
|
333
|
-
when RFLUSH then Rflush
|
334
|
-
when TWALK then Twalk
|
335
|
-
when RWALK then Rwalk
|
336
|
-
when TOPEN then Topen
|
337
|
-
when ROPEN then Ropen
|
338
|
-
when TCREATE then Tcreate
|
339
|
-
when RCREATE then Rcreate
|
340
|
-
when TREAD then Tread
|
341
|
-
when RREAD then Rread
|
342
|
-
when TWRITE then Twrite
|
343
|
-
when RWRITE then Rwrite
|
344
|
-
when TCLUNK then Tclunk
|
345
|
-
when RCLUNK then Rclunk
|
346
|
-
when TREMOVE then Tremove
|
347
|
-
when RREMOVE then Rremove
|
348
|
-
when TSTAT then Tstat
|
349
|
-
when RSTAT then Rstat
|
350
|
-
when TWSTAT then Twstat
|
351
|
-
when RWSTAT then Rwstat
|
352
|
-
else raise StyxException.new("invalid message type #{type}")
|
353
|
-
end
|
354
|
-
msgobj = msgclass.decode(message)
|
355
|
-
msgobj.length = length
|
356
|
-
msgobj.mtype = type
|
357
|
-
msgobj.tag = tag
|
358
|
-
return(msgobj)
|
271
|
+
# Create a new StyxMessage class. This takes a hash of field
|
272
|
+
# names and values, and this is put into a hash.
|
273
|
+
def initialize(fieldvals={})
|
274
|
+
ident = MESSAGE_IDS[self.class]
|
275
|
+
@fieldvals = {:ident=>ident}.merge(fieldvals)
|
359
276
|
end
|
360
277
|
|
361
|
-
end
|
362
|
-
|
363
|
-
##
|
364
|
-
# Message sent to negotiate the protocol version and message size.
|
365
|
-
# This should be the first message sent on the Styx connection.
|
366
|
-
#
|
367
|
-
class Tversion < StyxMessage
|
368
|
-
attr_accessor :msize, :version
|
369
|
-
|
370
278
|
##
|
371
|
-
#
|
372
|
-
#
|
373
|
-
# +msize+:: [Fixnum] maximum message size
|
374
|
-
# +version+:: [String] the version number we use
|
279
|
+
# Return the identifier of the message (code). This cannot
|
280
|
+
# be changed.
|
375
281
|
#
|
376
|
-
def
|
377
|
-
@
|
378
|
-
@version = version
|
379
|
-
@length = 13 + @version.length
|
380
|
-
@mtype = TVERSION
|
381
|
-
@messagebody = [@msize].pack("V") + Util.strpack(@version)
|
382
|
-
@body = sprintf("%s, %s", @msize, @version)
|
282
|
+
def ident
|
283
|
+
return(@fieldvals[:ident])
|
383
284
|
end
|
384
285
|
|
385
286
|
##
|
386
|
-
#
|
287
|
+
# Return the tag of the message
|
387
288
|
#
|
388
|
-
|
389
|
-
|
390
|
-
# raises:: StyxException if the message could not be decoded
|
391
|
-
#
|
392
|
-
def Tversion.decode(message)
|
393
|
-
msize = message[7..10].unpack("V")[0]
|
394
|
-
if msize.nil?
|
395
|
-
raise StyxException.new("Invalid Tversion message")
|
396
|
-
end
|
397
|
-
version, offset = Util.strextract(message, 11)
|
398
|
-
return(Tversion.new(msize, version))
|
289
|
+
def tag
|
290
|
+
return(@fieldvals[:tag])
|
399
291
|
end
|
400
292
|
|
401
|
-
end
|
402
|
-
|
403
|
-
##
|
404
|
-
# Response by Styx server to Tversion message sent by client when
|
405
|
-
# a connection is initiated.
|
406
|
-
#
|
407
|
-
class Rversion < StyxMessage
|
408
|
-
attr_accessor :msize, :version
|
409
|
-
|
410
293
|
##
|
411
|
-
#
|
412
|
-
|
413
|
-
|
414
|
-
# +version+:: [String] the version number we use
|
415
|
-
#
|
416
|
-
def initialize(msize=8216, version="9P2000")
|
417
|
-
@msize = msize
|
418
|
-
@version = version
|
419
|
-
@length = 13 + @version.length
|
420
|
-
@mtype = RVERSION
|
421
|
-
@messagebody = [@msize].pack("V") + Util.strpack(@version)
|
422
|
-
@body = sprintf("%s, %s", @msize, @version)
|
294
|
+
# Set the tag of the message
|
295
|
+
def tag=(t)
|
296
|
+
return(@fieldvals[:tag] = t)
|
423
297
|
end
|
424
298
|
|
425
299
|
##
|
426
|
-
#
|
427
|
-
#
|
428
|
-
# +message+:: [String] raw message string
|
429
|
-
# return value:: [Rversion] Rversion instance
|
430
|
-
# raises:: StyxException if the message could not be decoded.
|
300
|
+
# Deserialize a byte string into a StyxMessage subclass of some
|
301
|
+
# kind.
|
431
302
|
#
|
432
|
-
def
|
433
|
-
|
434
|
-
|
435
|
-
|
303
|
+
def self.from_bytes(str)
|
304
|
+
origlength = str.length
|
305
|
+
# get the length, identifier, and the rest of the string
|
306
|
+
len, ident, str = str.unpack("VCa*")
|
307
|
+
if len.nil? || len > origlength
|
308
|
+
raise StyxException.new("message string too short: #{len} bytes expected, only #{origlength} available")
|
309
|
+
end
|
310
|
+
c = MESSAGE_IDS.index(ident)
|
311
|
+
if c.nil?
|
312
|
+
raise StyxException.new("Unknown message type identifier #{ident.inspect}")
|
436
313
|
end
|
437
|
-
|
438
|
-
|
314
|
+
obj = c.new
|
315
|
+
c.fields.each do |name,type|
|
316
|
+
if name == :ident
|
317
|
+
next
|
318
|
+
end
|
319
|
+
val = nil
|
320
|
+
case type
|
321
|
+
when "Cstr"
|
322
|
+
len, str = str.unpack("va*")
|
323
|
+
val, str = str.unpack("a#{len}a*")
|
324
|
+
when "CstrList"
|
325
|
+
nstr, str = str.unpack("va*")
|
326
|
+
val = []
|
327
|
+
1.upto(nstr) do
|
328
|
+
len, str = str.unpack("va*")
|
329
|
+
xstr, str = str.unpack("a#{len}a*")
|
330
|
+
val << xstr
|
331
|
+
end
|
332
|
+
when "Bstr"
|
333
|
+
len, str = str.unpack("Va*")
|
334
|
+
val, str = str.unpack("a#{len}a*")
|
335
|
+
when "Qid"
|
336
|
+
qid, str = str.unpack("a#{Qid::QID_LENGTH}a*")
|
337
|
+
val = Qid.from_bytes(qid)
|
338
|
+
when "QidList"
|
339
|
+
nqid, str = str.unpack("va*")
|
340
|
+
val = []
|
341
|
+
1.upto(nqid) do
|
342
|
+
qid,str = str.unpack("a#{Qid::QID_LENGTH}a*")
|
343
|
+
val << Qid.from_bytes(qid)
|
344
|
+
end
|
345
|
+
when "Stat"
|
346
|
+
# See the corresponding comments under to_bytes
|
347
|
+
# (from when "Stat") for why we do it this way.
|
348
|
+
slen1 = str.unpack("v")
|
349
|
+
slen1, stat, str = str.unpack("va#{slen1}a*")
|
350
|
+
val = Stat.from_bytes(stat)
|
351
|
+
when "ULongLong"
|
352
|
+
v1, v2, str = str.unpack("VVa*")
|
353
|
+
# v1 - low word, v2 = high word
|
354
|
+
val = v2 << 32 | v1
|
355
|
+
else
|
356
|
+
val, str = str.unpack(type + "a*")
|
357
|
+
end
|
358
|
+
obj.fieldvals[name] = val
|
359
|
+
end
|
360
|
+
return(obj)
|
439
361
|
end
|
440
362
|
|
441
|
-
end
|
442
|
-
|
443
|
-
##
|
444
|
-
# Message sent to authenticate a user to the server
|
445
|
-
#
|
446
|
-
class Tauth < StyxMessage
|
447
|
-
attr_accessor :afid, :uname, :aname
|
448
|
-
|
449
|
-
NOFID = 0xffffffff
|
450
|
-
|
451
363
|
##
|
452
|
-
#
|
364
|
+
# Serialize a Styx message subclass into a byte string.
|
453
365
|
#
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
366
|
+
def to_bytes
|
367
|
+
str = ""
|
368
|
+
self.class.fields.each do |name,type|
|
369
|
+
case type
|
370
|
+
when "Cstr"
|
371
|
+
str << [@fieldvals[name].length, @fieldvals[name]].pack("va*")
|
372
|
+
when "CstrList"
|
373
|
+
strlist = @fieldvals[name]
|
374
|
+
str << [strlist.length].pack("v")
|
375
|
+
strlist.each do |s|
|
376
|
+
str << [s.length, s].pack("va*")
|
377
|
+
end
|
378
|
+
when "Bstr"
|
379
|
+
str << [@fieldvals[name].length, @fieldvals[name]].pack("Va*")
|
380
|
+
when "Qid"
|
381
|
+
str << @fieldvals[name].to_bytes
|
382
|
+
when "QidList"
|
383
|
+
qlist = @fieldvals[name]
|
384
|
+
str << [qlist.length].pack("v")
|
385
|
+
qlist.each do |q|
|
386
|
+
str << q.to_bytes
|
387
|
+
end
|
388
|
+
when "ULongLong"
|
389
|
+
# low dword
|
390
|
+
str << [@fieldvals[name] & 0xffffffff].pack("V")
|
391
|
+
# high dword
|
392
|
+
str << [(@fieldvals[name] >> 32) & 0xffffffff].pack("V")
|
393
|
+
when "Stat"
|
394
|
+
# From the Inferno stat(5) man page:
|
395
|
+
#
|
396
|
+
# To make the contents of a directory, such as returned
|
397
|
+
# by read(5), easy to parse, each directory entry
|
398
|
+
# begins with a size field. For consistency, the entries
|
399
|
+
# in Twstat and Rstat messages also contain their
|
400
|
+
# size, which means the size appears twice.
|
401
|
+
#
|
402
|
+
# And so this is why we prefix the serialized version of
|
403
|
+
# the stat message with the size here, and when deserializing
|
404
|
+
# we do the same thing.
|
405
|
+
#
|
406
|
+
statstr = @fieldvals[name].to_bytes
|
407
|
+
str << [statstr.length, statstr].pack("va*")
|
408
|
+
else
|
409
|
+
# format string for Array#pack
|
410
|
+
str << [@fieldvals[name]].pack(type)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
# add length
|
414
|
+
str = [str.length + 4].pack("V") + str
|
415
|
+
return(str)
|
468
416
|
end
|
469
417
|
|
470
418
|
##
|
471
|
-
#
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
419
|
+
# Convert a Styx message into a human-readable string.
|
420
|
+
def to_s
|
421
|
+
# First, start with the Styx message class name
|
422
|
+
str = "(" + self.class.to_s.split("::")[-1]
|
423
|
+
self.class.fields.each do |name, type|
|
424
|
+
# Ignore ident (redundant, as it is already expressed in
|
425
|
+
# the class name)
|
426
|
+
if name == :ident
|
427
|
+
next
|
428
|
+
end
|
429
|
+
str << " " + name.inspect + "=>" + @fieldvals[name].to_s.inspect
|
480
430
|
end
|
481
|
-
|
482
|
-
aname, offset = Util.strextract(message, offset)
|
483
|
-
return(Tauth.new(uname, afid, aname))
|
431
|
+
str << ")"
|
484
432
|
end
|
485
433
|
|
486
434
|
end
|
487
435
|
|
488
436
|
##
|
489
|
-
#
|
437
|
+
# Class representing a Tversion message sent by a Styx client.
|
490
438
|
#
|
491
|
-
class
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
#
|
497
|
-
# +qid+:: [Qid] The qid of the authenticator file
|
498
|
-
#
|
499
|
-
def initialize(qid)
|
500
|
-
@aqid = qid
|
501
|
-
@length = 20 # exact length
|
502
|
-
@mtype = RAUTH
|
503
|
-
@messagebody = @aqid.to_bytes
|
504
|
-
end
|
505
|
-
|
506
|
-
##
|
507
|
-
# Decode an Rauth message.
|
508
|
-
#
|
509
|
-
# +message+:: [String] the message to decode
|
510
|
-
# return:: [Rauth] the Rauth object
|
511
|
-
#
|
512
|
-
def Rauth.decode(message)
|
513
|
-
qid = Qid.decode(message[7..19])
|
514
|
-
return(Rauth.new(qid))
|
515
|
-
end
|
439
|
+
class Tversion < StyxMessage
|
440
|
+
StyxMessage::MESSAGE_IDS[Tversion] = 100
|
441
|
+
#
|
442
|
+
add_field(:msize, 'V')
|
443
|
+
add_field(:version, 'Cstr')
|
516
444
|
end
|
517
445
|
|
518
446
|
##
|
519
|
-
#
|
447
|
+
# Class representing an Rversion message sent by a Styx server.
|
520
448
|
#
|
521
|
-
class
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
# Construct a new Rerror message given the error name
|
526
|
-
#
|
527
|
-
# +ename+:: [String] error name for error object
|
528
|
-
#
|
529
|
-
def initialize(ename)
|
530
|
-
@ename = ename
|
531
|
-
@length = 9 + @ename.length
|
532
|
-
@mtype = RERROR
|
533
|
-
@body = ename
|
534
|
-
@messagebody = Util.strpack(@ename)
|
535
|
-
end
|
536
|
-
|
537
|
-
##
|
538
|
-
# Decode an Rerror message. Do not use this method directly.
|
539
|
-
# Use only from within StyxMessage.decode.
|
540
|
-
#
|
541
|
-
# +message+:: [String] raw message string
|
542
|
-
# return:: [Rerror] Rerror instance
|
543
|
-
#
|
544
|
-
def Rerror.decode(message)
|
545
|
-
ename, offset = Util.strextract(message, 7)
|
546
|
-
return(Rerror.new(ename))
|
547
|
-
end
|
548
|
-
|
449
|
+
class Rversion < StyxMessage
|
450
|
+
StyxMessage::MESSAGE_IDS[Rversion] = 101
|
451
|
+
add_field(:msize, 'V')
|
452
|
+
add_field(:version, 'Cstr')
|
549
453
|
end
|
550
454
|
|
551
455
|
##
|
552
|
-
#
|
456
|
+
# Class representing a Tauth message sent by a Styx client.
|
553
457
|
#
|
554
|
-
class
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
#
|
560
|
-
# +oldtag+:: the tag to flush
|
561
|
-
#
|
562
|
-
def initialize(oldtag)
|
563
|
-
@length = 9
|
564
|
-
@mtype = TFLUSH
|
565
|
-
@oldtag = oldtag
|
566
|
-
@messagebody = [@oldtag].pack("v")
|
567
|
-
@body = sprintf("%s", oldtag)
|
568
|
-
end
|
569
|
-
|
570
|
-
##
|
571
|
-
# Decode a Tflush message. Do not use this method directly.
|
572
|
-
# Use only from within StyxMessage.decode.
|
573
|
-
#
|
574
|
-
# +message+:: [String] raw message string
|
575
|
-
# return:: [Tflush] Tflush instance represented by string
|
576
|
-
#
|
577
|
-
def Tflush.decode(message)
|
578
|
-
oldtag = message[7..8].unpack("v")[0]
|
579
|
-
if (oldtag.nil?)
|
580
|
-
raise RStyx::StyxException.new("invalid Tflush message")
|
581
|
-
end
|
582
|
-
return(Tflush.new(oldtag))
|
583
|
-
end
|
584
|
-
|
458
|
+
class Tauth < StyxMessage
|
459
|
+
StyxMessage::MESSAGE_IDS[Tauth] = 102
|
460
|
+
add_field(:afid, 'V')
|
461
|
+
add_field(:uname, 'Cstr')
|
462
|
+
add_field(:aname, 'Cstr')
|
585
463
|
end
|
586
464
|
|
587
465
|
##
|
588
|
-
#
|
466
|
+
# Class representing an Rauth message sent by a Styx server.
|
589
467
|
#
|
590
|
-
class
|
591
|
-
|
592
|
-
|
593
|
-
#
|
594
|
-
def initialize
|
595
|
-
@mtype = RFLUSH
|
596
|
-
@length = 7
|
597
|
-
@messagebody = ""
|
598
|
-
@body = ""
|
599
|
-
end
|
600
|
-
|
601
|
-
##
|
602
|
-
# Decode an Rflush message
|
603
|
-
#
|
604
|
-
# +message+:: [String] raw message string
|
605
|
-
# return:: [Rflush] Rflush instance represented by string
|
606
|
-
#
|
607
|
-
def Rflush.decode(message)
|
608
|
-
# This does absolutely nothing! An RFlush is nothing but a
|
609
|
-
# bare StyxMessage.
|
610
|
-
return(Rflush.new)
|
611
|
-
end
|
468
|
+
class Rauth < StyxMessage
|
469
|
+
StyxMessage::MESSAGE_IDS[Rauth] = 103
|
470
|
+
add_field(:aqid, 'Qid')
|
612
471
|
end
|
613
472
|
|
614
473
|
##
|
615
|
-
#
|
474
|
+
# Class representing a Tattach message sent by a Styx client.
|
616
475
|
#
|
617
476
|
class Tattach < StyxMessage
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
# Construct a new Tattach message.
|
624
|
-
#
|
625
|
-
# +fid+:: [Fixnum] fid for the attach message
|
626
|
-
# +uname+:: [String] user name to attach as
|
627
|
-
# +afid+:: [Fixnum] authenticated fid from auth protocol
|
628
|
-
# +aname+:: [String] file tree to access
|
629
|
-
#
|
630
|
-
def initialize(fid, uname, afid=NOFID, aname="")
|
631
|
-
@fid = fid
|
632
|
-
@afid = afid
|
633
|
-
@uname = uname
|
634
|
-
@aname = aname
|
635
|
-
@length = 19 + @uname.length + @aname.length
|
636
|
-
@mtype = TATTACH
|
637
|
-
@messagebody = [@fid, @afid].pack("VV")
|
638
|
-
@messagebody << Util.strpack(uname)
|
639
|
-
@messagebody << Util.strpack(aname)
|
640
|
-
@body = sprintf("%s, %s, %s, %s", @fid, @afid, @uname, @aname)
|
641
|
-
end
|
642
|
-
|
643
|
-
##
|
644
|
-
# Decode a Tattach message.
|
645
|
-
#
|
646
|
-
# +message+:: [String] the message to decode
|
647
|
-
# return:: [Tattach] the Tattach object
|
648
|
-
#
|
649
|
-
def Tattach.decode(message)
|
650
|
-
fid, afid = message[7..14].unpack("VV")
|
651
|
-
if fid.nil? || afid.nil?
|
652
|
-
raise RStyx::StyxException.new("invalid Tattach message")
|
653
|
-
end
|
654
|
-
uname, offset = Util.strextract(message, 15)
|
655
|
-
aname, offset = Util.strextract(message, offset)
|
656
|
-
return(Tattach.new(fid, uname, afid, aname))
|
657
|
-
end
|
658
|
-
|
477
|
+
StyxMessage::MESSAGE_IDS[Tattach] = 104
|
478
|
+
add_field(:fid, 'V')
|
479
|
+
add_field(:afid, 'V')
|
480
|
+
add_field(:uname, 'Cstr')
|
481
|
+
add_field(:aname, 'Cstr')
|
659
482
|
end
|
660
483
|
|
661
484
|
##
|
662
|
-
#
|
485
|
+
# Class representing an Rattach message sent by a Styx server.
|
663
486
|
#
|
664
487
|
class Rattach < StyxMessage
|
665
|
-
|
488
|
+
StyxMessage::MESSAGE_IDS[Rattach] = 105
|
489
|
+
add_field(:qid, 'Qid')
|
490
|
+
end
|
666
491
|
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
@mtype = RATTACH
|
676
|
-
@messagebody = @qid.to_bytes
|
677
|
-
@body = @qid.to_s
|
492
|
+
##
|
493
|
+
# Class representing a Terror message. This is not actually valid
|
494
|
+
# and should never be used.
|
495
|
+
#
|
496
|
+
class Terror < StyxMessage
|
497
|
+
StyxMessage::MESSAGE_IDS[Terror] = 106
|
498
|
+
def initialize
|
499
|
+
raise StyxException.new("Terror class instantiated")
|
678
500
|
end
|
501
|
+
end
|
679
502
|
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
def Rattach.decode(message)
|
687
|
-
qid = Qid.decode(message[7..19])
|
688
|
-
return(Rattach.new(qid))
|
689
|
-
end
|
503
|
+
##
|
504
|
+
# Class representing an Rerror message sent by a Styx server.
|
505
|
+
#
|
506
|
+
class Rerror < StyxMessage
|
507
|
+
StyxMessage::MESSAGE_IDS[Rerror] = 107
|
508
|
+
add_field(:ename, "Cstr")
|
690
509
|
end
|
691
510
|
|
692
511
|
##
|
693
|
-
#
|
512
|
+
# Class representing a Tflush message sent by a Styx client.
|
694
513
|
#
|
695
|
-
class
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
# Construct a new Twalk message given a fid, the newfid to use,
|
700
|
-
# and the path on the server, separated by forward slashes ('/').
|
701
|
-
#
|
702
|
-
# +fid+:: [Fixnum] initial existing fid
|
703
|
-
# +newfid+:: [Fixnum] proposed new fid
|
704
|
-
# +path+:: [String] new path desired
|
705
|
-
#
|
706
|
-
def initialize(fid, newfid, path)
|
707
|
-
@fid = fid
|
708
|
-
@newfid = newfid
|
709
|
-
@mtype = TWALK
|
710
|
-
@path_elements = []
|
711
|
-
@length = 17 # updated by length of path elements
|
712
|
-
path_elem_str = ""
|
713
|
-
# Cycle through the path elements, adding them to a message string
|
714
|
-
path.split("/").each do |elem|
|
715
|
-
if elem.length <= 0
|
716
|
-
next
|
717
|
-
end
|
718
|
-
@length += elem.length + 2
|
719
|
-
path_elem_str << Util.strpack(elem)
|
720
|
-
@path_elements << elem
|
721
|
-
end
|
722
|
-
if @path_elements.length > MAXWELEM
|
723
|
-
raise StyxException.new("path exceeded MAXWELEM (#{MAXWELEM}) elements; break the walk into several messages")
|
724
|
-
end
|
725
|
-
@messagebody = [@fid, @newfid, @path_elements.length].pack("VVv") + path_elem_str
|
726
|
-
@body = sprintf("%s, %s, %s", @fid, @newfid, path)
|
727
|
-
end
|
514
|
+
class Tflush < StyxMessage
|
515
|
+
StyxMessage::MESSAGE_IDS[Tflush] = 108
|
516
|
+
add_field(:oldtag, "v")
|
517
|
+
end
|
728
518
|
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
#
|
735
|
-
def Twalk.decode(message)
|
736
|
-
fid, newfid, nwname = message[7..16].unpack("VVv")
|
737
|
-
if fid.nil? || newfid.nil? || nwname.nil?
|
738
|
-
raise RStyx::StyxException.new("invalid Twalk message")
|
739
|
-
end
|
740
|
-
offset = 17
|
741
|
-
path_elements = []
|
742
|
-
1.upto(nwname) do
|
743
|
-
wname, offset = Util.strextract(message, offset)
|
744
|
-
path_elements << wname
|
745
|
-
end
|
746
|
-
return(Twalk.new(fid, newfid, path_elements.join("/")))
|
747
|
-
end
|
519
|
+
##
|
520
|
+
# Class representing an Rflush message sent by a Styx server.
|
521
|
+
#
|
522
|
+
class Rflush < StyxMessage
|
523
|
+
StyxMessage::MESSAGE_IDS[Rflush] = 109
|
748
524
|
end
|
749
525
|
|
750
526
|
##
|
751
|
-
#
|
527
|
+
# Class representing a Twalk message sent by a Styx client.
|
752
528
|
#
|
753
|
-
class
|
754
|
-
|
529
|
+
class Twalk < StyxMessage
|
530
|
+
StyxMessage::MESSAGE_IDS[Twalk] = 110
|
531
|
+
add_field(:fid, "V")
|
532
|
+
add_field(:newfid, "V")
|
533
|
+
add_field(:wnames, "CstrList")
|
755
534
|
|
756
|
-
|
757
|
-
|
758
|
-
#
|
759
|
-
# +qids+:: [Array of Qid] array of Qid's in Rwalk
|
760
|
-
#
|
761
|
-
def initialize(qids)
|
762
|
-
@qids = qids
|
763
|
-
@mtype = RWALK
|
764
|
-
nwqid = @qids.length
|
765
|
-
@length = 9 # updated by qids that follow
|
766
|
-
@messagebody = [nwqid].pack("v")
|
767
|
-
@body = ""
|
768
|
-
i = 1
|
769
|
-
|
770
|
-
# add each qid into the messagebody
|
771
|
-
qids.each do |qid|
|
772
|
-
qidbytes = qid.to_bytes
|
773
|
-
@messagebody << qidbytes
|
774
|
-
@length += Qid::QID_LENGTH
|
775
|
-
@body << qid.to_s
|
776
|
-
if i < nwqid
|
777
|
-
@body << ", "
|
778
|
-
end
|
779
|
-
i += 1
|
780
|
-
end
|
781
|
-
@body << sprintf("%s, %s", @qids.length, @body)
|
535
|
+
def path=(str)
|
536
|
+
@fieldvals[:wnames] = str.split(File::SEPARATOR)
|
782
537
|
end
|
783
538
|
|
784
|
-
|
785
|
-
|
786
|
-
#
|
787
|
-
# +message+:: [String] the message to decode
|
788
|
-
# return:: [Rwalk] the Rwalk object
|
789
|
-
#
|
790
|
-
def Rwalk.decode(message)
|
791
|
-
nwqid = message[7..8].unpack("v")[0]
|
792
|
-
if nwqid.nil?
|
793
|
-
raise RStyx::StyxException.new("invalid Rwalk message")
|
794
|
-
end
|
795
|
-
offset = 9
|
796
|
-
qids = []
|
797
|
-
1.upto(nwqid) do |i|
|
798
|
-
qids << Qid.decode(message[offset..(offset + Qid::QID_LENGTH-1)])
|
799
|
-
offset += Qid::QID_LENGTH
|
800
|
-
end
|
801
|
-
return(Rwalk.new(qids))
|
539
|
+
def path
|
540
|
+
return(@fieldvals[:wnames].join(File::SEPARATOR))
|
802
541
|
end
|
542
|
+
end
|
803
543
|
|
544
|
+
##
|
545
|
+
# Class representing an Rwalk message sent by a Styx server.
|
546
|
+
#
|
547
|
+
class Rwalk < StyxMessage
|
548
|
+
StyxMessage::MESSAGE_IDS[Rwalk] = 111
|
549
|
+
add_field(:qids, "QidList")
|
804
550
|
end
|
805
551
|
|
806
552
|
##
|
807
|
-
#
|
553
|
+
# Class representing a Topen message sent by a Styx client.
|
808
554
|
#
|
809
555
|
class Topen < StyxMessage
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
OREAD = 0
|
814
|
-
OWRITE = 1
|
815
|
-
ORDWR = 2
|
816
|
-
OEXEC = 3
|
817
|
-
OTRUNC = 0x10
|
818
|
-
ORCLOSE = 0x40
|
819
|
-
|
820
|
-
##
|
821
|
-
# Construct a new Topen message, given the fid and the mode
|
822
|
-
#
|
823
|
-
# +fid+:: [Fixnum] the fid to open
|
824
|
-
# +mode+:: [Fixnum] the mode
|
825
|
-
#
|
826
|
-
def initialize(fid, mode)
|
827
|
-
@length = 12
|
828
|
-
@mtype = TOPEN
|
829
|
-
@fid = fid
|
830
|
-
@mode = mode
|
831
|
-
# test the modes
|
832
|
-
if (@mode & 0xf > 3)
|
833
|
-
raise RStyx::StyxException.new("invalid file mode #{sprintf("%02x", @mode)}")
|
834
|
-
end
|
835
|
-
|
836
|
-
if (@mode & ~0x5f != 0)
|
837
|
-
raise RStyx::StyxException.new("invalid file mode #{sprintf("%02x", @mode)}")
|
838
|
-
end
|
839
|
-
|
840
|
-
if ((@mode & OTRUNC) != 0) && !((@mode & 0xf == OWRITE) ||
|
841
|
-
(@mode & 0xf == ORDWR))
|
842
|
-
raise RStyx::StyxException.new("Can only truncate file when opening in write mode")
|
843
|
-
end
|
844
|
-
|
845
|
-
@messagebody = [@fid, @mode].pack("VC")
|
846
|
-
@body = sprintf("%s, %s", @fid, @mode)
|
847
|
-
end
|
848
|
-
|
849
|
-
##
|
850
|
-
# Decode a Topen message.
|
851
|
-
#
|
852
|
-
# +message+:: [String] the message to decode
|
853
|
-
# return:: [Topen] the Topen object
|
854
|
-
#
|
855
|
-
def Topen.decode(message)
|
856
|
-
fid, mode = message[7..11].unpack("VC")
|
857
|
-
if fid.nil? || mode.nil?
|
858
|
-
raise RStyx::StyxException.new("invalid Topen message")
|
859
|
-
end
|
860
|
-
return(Topen.new(fid, mode))
|
861
|
-
end
|
862
|
-
|
556
|
+
StyxMessage::MESSAGE_IDS[Topen] = 112
|
557
|
+
add_field(:fid, "V")
|
558
|
+
add_field(:mode, "C")
|
863
559
|
end
|
864
560
|
|
865
561
|
##
|
866
|
-
#
|
562
|
+
# Class representing an Ropen message sent by a Styx server.
|
867
563
|
#
|
868
564
|
class Ropen < StyxMessage
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
# Construct a new Ropen message
|
873
|
-
#
|
874
|
-
# +qid+:: [Qid] Qid for the opened file
|
875
|
-
# +iounit+:: [Fixnum] the maximum number of bytes guaranteed to be
|
876
|
-
# read or written to the file without breaking the
|
877
|
-
# I/O transfer into multiple messages (0=no limit).
|
878
|
-
#
|
879
|
-
def initialize(qid, iounit=0)
|
880
|
-
@length = 24
|
881
|
-
@mtype = ROPEN
|
882
|
-
@qid = qid
|
883
|
-
@iounit = iounit
|
884
|
-
@messagebody = @qid.to_bytes + [@iounit].pack("V")
|
885
|
-
@body = @qid.to_s + ", " + sprintf("%s", @iounit)
|
886
|
-
end
|
887
|
-
|
888
|
-
##
|
889
|
-
# Decode an Ropen message.
|
890
|
-
#
|
891
|
-
# +message+:: [String] the message to decode
|
892
|
-
# return:: [Ropen] the Ropen object
|
893
|
-
#
|
894
|
-
def Ropen.decode(message)
|
895
|
-
qid = Qid.decode(message[7..19])
|
896
|
-
iounit = message[20..23].unpack("V")[0]
|
897
|
-
if iounit.nil?
|
898
|
-
raise RStyx::StyxException.new("invalid Ropen message")
|
899
|
-
end
|
900
|
-
return(Ropen.new(qid, iounit))
|
901
|
-
end
|
902
|
-
|
565
|
+
StyxMessage::MESSAGE_IDS[Ropen] = 113
|
566
|
+
add_field(:qid, "Qid")
|
567
|
+
add_field(:iounit, "V")
|
903
568
|
end
|
904
569
|
|
905
570
|
##
|
906
|
-
#
|
571
|
+
# Class representing a Tcreate message sent by a Styx client.
|
907
572
|
#
|
908
|
-
class Tcreate <
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
##
|
916
|
-
# Construct a new Tcreate message, given the fid, name, permissions,
|
917
|
-
# and mode
|
918
|
-
#
|
919
|
-
# +fid+:: [Fixnum] the fid to open
|
920
|
-
# +name+:: [String] the name of the file to create
|
921
|
-
# +permissions+:: [Fixnum] the permissions for the new file
|
922
|
-
# +mode+:: [Fixnum] the mode (see Topen)
|
923
|
-
#
|
924
|
-
def initialize(fid, name, mode, perm=nil)
|
925
|
-
super(fid, mode)
|
926
|
-
@mtype = TCREATE
|
927
|
-
@name = name
|
928
|
-
@length = 18 + @name.length
|
929
|
-
# use 0644 as default permissions if no permissions mask specified
|
930
|
-
@perm = (perm.nil?) ? 0644 : perm
|
931
|
-
@messagebody = [@fid].pack("V") + Util.strpack(@name) +
|
932
|
-
[@perm, @mode].pack("VC")
|
933
|
-
@body = sprintf("%s, %s, %s, %s", @fid, @name, @perm, @mode)
|
934
|
-
end
|
935
|
-
|
936
|
-
##
|
937
|
-
# Decode a Tcreate message.
|
938
|
-
#
|
939
|
-
# +message+:: [String] the message to decode
|
940
|
-
# return:: [Tcreate] the Tcreate object
|
941
|
-
#
|
942
|
-
def Tcreate.decode(message)
|
943
|
-
fid = message[7..10].unpack("V")[0]
|
944
|
-
if (fid.nil?)
|
945
|
-
raise RStyx::StyxException.new("invalid Tcreate message")
|
946
|
-
end
|
947
|
-
name, offset = Util.strextract(message, 11)
|
948
|
-
perm, mode = message[offset..(offset+5)].unpack("VC")
|
949
|
-
if (perm.nil? || mode.nil?)
|
950
|
-
raise RStyx::StyxException.new("invalid Tcreate message")
|
951
|
-
end
|
952
|
-
return(Tcreate.new(fid, name, mode, perm))
|
953
|
-
end
|
954
|
-
|
573
|
+
class Tcreate < StyxMessage
|
574
|
+
StyxMessage::MESSAGE_IDS[Tcreate] = 114
|
575
|
+
add_field(:fid, "V")
|
576
|
+
add_field(:name, "Cstr")
|
577
|
+
add_field(:perm, "V")
|
578
|
+
add_field(:mode, "C")
|
955
579
|
end
|
956
580
|
|
957
581
|
##
|
958
|
-
#
|
959
|
-
# is identical to the Ropen message, and is decoded the same way.
|
582
|
+
# Class representing an Rcreate message sent by a Styx server.
|
960
583
|
#
|
961
|
-
class Rcreate <
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
# +qid+:: [Qid] Qid for the opened file
|
966
|
-
# +iounit+:: [Fixnum] the maximum number of bytes guaranteed to be
|
967
|
-
# read or written to the file without breaking the
|
968
|
-
# I/O transfer into multiple messages (0=no limit).
|
969
|
-
#
|
970
|
-
def initialize(qid, iounit=0)
|
971
|
-
super(qid, iounit)
|
972
|
-
@mtype = RCREATE
|
973
|
-
end
|
974
|
-
|
975
|
-
##
|
976
|
-
# Decode an Rcreate message.
|
977
|
-
#
|
978
|
-
# +message+:: [String] the message to decode
|
979
|
-
# return:: [Ropen] the Rcreate object
|
980
|
-
#
|
981
|
-
# FIXME: this is identical to Ropen.decode with the only difference
|
982
|
-
# being the Rcreate.new call at return. How do you obtain
|
983
|
-
# the defining class for a static method?
|
984
|
-
#
|
985
|
-
def Rcreate.decode(message)
|
986
|
-
qid = Qid.decode(message[7..19])
|
987
|
-
iounit = message[20..23].unpack("V")[0]
|
988
|
-
if iounit.nil?
|
989
|
-
raise RStyx::StyxException.new("invalid Ropen message")
|
990
|
-
end
|
991
|
-
return(Rcreate.new(qid, iounit))
|
992
|
-
end
|
993
|
-
|
584
|
+
class Rcreate < StyxMessage
|
585
|
+
StyxMessage::MESSAGE_IDS[Rcreate] = 115
|
586
|
+
add_field(:qid, "Qid")
|
587
|
+
add_field(:iounit, "V")
|
994
588
|
end
|
995
589
|
|
996
590
|
##
|
997
|
-
#
|
998
|
-
# of data to be read.
|
591
|
+
# Class representing a Tread message sent by a Styx client.
|
999
592
|
#
|
1000
593
|
class Tread < StyxMessage
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
#
|
1006
|
-
# +fid+:: [Fixnum] fid of file to read from
|
1007
|
-
# +offset+:: [Fixnum] offset to read from
|
1008
|
-
# +count+:: [Fixnum] number of bytes to read
|
1009
|
-
#
|
1010
|
-
def initialize(fid, offset, count)
|
1011
|
-
@length = 23
|
1012
|
-
@mtype = TREAD
|
1013
|
-
@fid = fid
|
1014
|
-
@offset = offset
|
1015
|
-
offsetlo = @offset & 0xffffffff
|
1016
|
-
offsethi = (@offset >> 32) & 0xffffffff
|
1017
|
-
@count = count
|
1018
|
-
@messagebody = [@fid, offsetlo, offsethi, @count].pack("VVVV")
|
1019
|
-
@body = sprintf("%s, %s, %s", @fid, @offset, @count)
|
1020
|
-
end
|
1021
|
-
|
1022
|
-
##
|
1023
|
-
# Decode a Tread message.
|
1024
|
-
#
|
1025
|
-
# +message+:: [String] the message to decode
|
1026
|
-
# return:: [Tread] the Tread object
|
1027
|
-
#
|
1028
|
-
def Tread.decode(message)
|
1029
|
-
fid, offsetlo, offsethi, count = message[7..22].unpack("VVVV")
|
1030
|
-
if fid.nil? || offsetlo.nil? || offsethi.nil? || count.nil?
|
1031
|
-
raise StyxException.new("invalid Tread message")
|
1032
|
-
end
|
1033
|
-
offset = offsetlo | (offsethi << 32)
|
1034
|
-
return(Tread.new(fid, offset, count))
|
1035
|
-
end
|
1036
|
-
|
594
|
+
StyxMessage::MESSAGE_IDS[Tread] = 116
|
595
|
+
add_field(:fid, "V")
|
596
|
+
add_field(:offset, "ULongLong")
|
597
|
+
add_field(:count, "V")
|
1037
598
|
end
|
1038
599
|
|
1039
600
|
##
|
1040
|
-
#
|
601
|
+
# Class representing an Rread message sent by a Styx server.
|
1041
602
|
#
|
1042
603
|
class Rread < StyxMessage
|
1043
|
-
|
604
|
+
StyxMessage::MESSAGE_IDS[Rread] = 117
|
605
|
+
add_field(:data, "Bstr")
|
1044
606
|
|
1045
|
-
|
1046
|
-
|
1047
|
-
#
|
1048
|
-
# +data+:: [String] the data read from the file
|
1049
|
-
#
|
1050
|
-
def initialize(data)
|
1051
|
-
@count = data.length
|
1052
|
-
@data = data
|
1053
|
-
@length = 11 + @count
|
1054
|
-
@mtype = RREAD
|
1055
|
-
@messagebody = [data.length].pack("V") + data
|
1056
|
-
# put only the first twenty bytes in the description
|
1057
|
-
@body = sprintf("%s, %s", @count, @data[0..19])
|
1058
|
-
end
|
1059
|
-
|
1060
|
-
##
|
1061
|
-
# Decode an Rread message.
|
1062
|
-
#
|
1063
|
-
# +message+:: [String] the message to decode
|
1064
|
-
# return:: [Rread] the Rread object
|
1065
|
-
#
|
1066
|
-
def Rread.decode(message)
|
1067
|
-
count = message[7..10].unpack("V")[0]
|
1068
|
-
if (count.nil?)
|
1069
|
-
raise StyxException.new("invalid Rread message")
|
1070
|
-
end
|
1071
|
-
data = message.slice(11, count)
|
1072
|
-
if (data.length != count)
|
1073
|
-
raise StyxException.new("invalid Rread message, count and data length do not coincide")
|
1074
|
-
end
|
1075
|
-
return(Rread.new(data))
|
607
|
+
def count
|
608
|
+
return(@fieldvals[:data].length)
|
1076
609
|
end
|
1077
|
-
|
1078
610
|
end
|
1079
611
|
|
1080
612
|
##
|
1081
|
-
#
|
1082
|
-
# the data to be written.
|
613
|
+
# Class representing a Twrite message sent by a Styx client.
|
1083
614
|
#
|
1084
615
|
class Twrite < StyxMessage
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
#
|
1090
|
-
# +fid+:: [Fixnum] fid of file to read from
|
1091
|
-
# +offset+:: [Fixnum] offset to read from
|
1092
|
-
# +data+:: [String] data to be written
|
1093
|
-
#
|
1094
|
-
def initialize(fid, offset, data)
|
1095
|
-
@count = data.length
|
1096
|
-
@data = data
|
1097
|
-
@length = @count + 23
|
1098
|
-
@mtype = TWRITE
|
1099
|
-
@fid = fid
|
1100
|
-
@offset = offset
|
1101
|
-
offsetlo = @offset & 0xffffffff
|
1102
|
-
offsethi = (@offset >> 32) & 0xffffffff
|
1103
|
-
@messagebody = [@fid, offsetlo, offsethi, @count].pack("VVVV") +
|
1104
|
-
@data
|
1105
|
-
@body = sprintf("%s, %s, %s, %s", @fid, @offset, @count, @data[0..19])
|
1106
|
-
end
|
1107
|
-
|
1108
|
-
##
|
1109
|
-
# Decode a Twrite message.
|
1110
|
-
#
|
1111
|
-
# +message+:: [String] the message to decode
|
1112
|
-
# return:: [Rwrite] the Twrite object
|
1113
|
-
#
|
1114
|
-
def Twrite.decode(message)
|
1115
|
-
fid, offsetlo, offsethi, count = message[7..22].unpack("VVVV")
|
1116
|
-
if (fid.nil? || offsetlo.nil? || offsethi.nil? || count.nil?)
|
1117
|
-
raise StyxException.new("invalid Twrite message")
|
1118
|
-
end
|
1119
|
-
offset = offsetlo | (offsethi << 32)
|
1120
|
-
data = message.slice(23, count)
|
1121
|
-
if (data.length != count)
|
1122
|
-
raise StyxException.new("invalid Twrite message, count and data length do not coincide")
|
1123
|
-
end
|
1124
|
-
return(Twrite.new(fid, offset, data))
|
1125
|
-
end
|
1126
|
-
|
616
|
+
StyxMessage::MESSAGE_IDS[Twrite] = 118
|
617
|
+
add_field(:fid, "V")
|
618
|
+
add_field(:offset, "ULongLong")
|
619
|
+
add_field(:data, "Bstr")
|
1127
620
|
end
|
1128
621
|
|
1129
622
|
##
|
1130
|
-
#
|
623
|
+
# Class representing an Rwrite message sent by a Styx server.
|
1131
624
|
#
|
1132
625
|
class Rwrite < StyxMessage
|
1133
|
-
|
1134
|
-
|
1135
|
-
##
|
1136
|
-
# Construct a new Rwrite message.
|
1137
|
-
#
|
1138
|
-
# +count+:: [Fixnum] the number of bytes actually written.
|
1139
|
-
#
|
1140
|
-
def initialize(count)
|
1141
|
-
@count = count
|
1142
|
-
@length = 11
|
1143
|
-
@mtype = RWRITE
|
1144
|
-
@messagebody = [@count].pack("V")
|
1145
|
-
@body = sprintf("%s", @count)
|
1146
|
-
end
|
1147
|
-
|
1148
|
-
##
|
1149
|
-
# Decode an Rwrite message.
|
1150
|
-
#
|
1151
|
-
# +message+:: [String] the message to decode
|
1152
|
-
# return:: [Rwrite] the Rwrite object
|
1153
|
-
#
|
1154
|
-
def Rwrite.decode(message)
|
1155
|
-
count = message[7..10].unpack("V")[0]
|
1156
|
-
if count.nil?
|
1157
|
-
raise StyxException.new("invalid Rwrite message")
|
1158
|
-
end
|
1159
|
-
return(Rwrite.new(count))
|
1160
|
-
end
|
1161
|
-
|
626
|
+
StyxMessage::MESSAGE_IDS[Rwrite] = 119
|
627
|
+
add_field(:count, "V")
|
1162
628
|
end
|
1163
629
|
|
1164
630
|
##
|
1165
|
-
#
|
631
|
+
# Class representing a Tclunk message sent by a Styx client.
|
1166
632
|
#
|
1167
633
|
class Tclunk < StyxMessage
|
1168
|
-
|
1169
|
-
|
1170
|
-
##
|
1171
|
-
# Construct a new Tclunk message given the fid to clunk.
|
1172
|
-
#
|
1173
|
-
# +fid+:: The fid to clunk
|
1174
|
-
#
|
1175
|
-
def initialize(fid)
|
1176
|
-
@length = 11
|
1177
|
-
@mtype = TCLUNK
|
1178
|
-
@fid = fid
|
1179
|
-
@messagebody = [@fid].pack("V")
|
1180
|
-
@body = sprintf("%s", fid)
|
1181
|
-
end
|
1182
|
-
|
1183
|
-
##
|
1184
|
-
# Decode a Tclunk message. Do not use this method directly.
|
1185
|
-
# Use only from within StyxMessage.decode.
|
1186
|
-
#
|
1187
|
-
# +message+:: [String] raw message string
|
1188
|
-
# return:: [Tclunk] Tclunk instance represented by string
|
1189
|
-
#
|
1190
|
-
def Tclunk.decode(message)
|
1191
|
-
fid = message[7..10].unpack("V")[0]
|
1192
|
-
if (fid.nil?)
|
1193
|
-
raise RStyx::StyxException.new("invalid Tclunk message")
|
1194
|
-
end
|
1195
|
-
return(Tclunk.new(fid))
|
1196
|
-
end
|
1197
|
-
|
634
|
+
StyxMessage::MESSAGE_IDS[Tclunk] = 120
|
635
|
+
add_field(:fid, "V")
|
1198
636
|
end
|
1199
637
|
|
1200
638
|
##
|
1201
|
-
#
|
639
|
+
# Class representing an Rclunk message sent by a Styx server.
|
1202
640
|
#
|
1203
641
|
class Rclunk < StyxMessage
|
1204
|
-
|
1205
|
-
# Make an Rclunk message
|
1206
|
-
#
|
1207
|
-
def initialize
|
1208
|
-
@mtype = RCLUNK
|
1209
|
-
@length = 7
|
1210
|
-
@messagebody = ""
|
1211
|
-
@body = ""
|
1212
|
-
end
|
1213
|
-
|
1214
|
-
##
|
1215
|
-
# Decode an Rclunk message
|
1216
|
-
#
|
1217
|
-
# +message+:: [String] raw message string
|
1218
|
-
# return:: [Rclunk] Rclunk instance represented by string
|
1219
|
-
#
|
1220
|
-
def Rclunk.decode(message)
|
1221
|
-
# This does absolutely nothing! An Rclunk is nothing but a
|
1222
|
-
# bare StyxMessage.
|
1223
|
-
return(Rclunk.new)
|
1224
|
-
end
|
1225
|
-
|
642
|
+
StyxMessage::MESSAGE_IDS[Rclunk] = 121
|
1226
643
|
end
|
1227
644
|
|
1228
645
|
##
|
1229
|
-
#
|
646
|
+
# Class representing a Tremove message sent by a Styx client.
|
1230
647
|
#
|
1231
648
|
class Tremove < StyxMessage
|
1232
|
-
|
1233
|
-
|
1234
|
-
##
|
1235
|
-
# Construct a new Tremove message given the fid to clunk.
|
1236
|
-
#
|
1237
|
-
# +fid+:: The fid to remove
|
1238
|
-
#
|
1239
|
-
def initialize(fid)
|
1240
|
-
@length = 11
|
1241
|
-
@mtype = TREMOVE
|
1242
|
-
@fid = fid
|
1243
|
-
@messagebody = [@fid].pack("V")
|
1244
|
-
@body = sprintf("%s", fid)
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
##
|
1248
|
-
# Decode a Tremove message. Do not use this method directly.
|
1249
|
-
# Use only from within StyxMessage.decode.
|
1250
|
-
#
|
1251
|
-
# +message+:: [String] raw message string
|
1252
|
-
# return:: [Tremove] Tremove instance represented by string
|
1253
|
-
#
|
1254
|
-
def Tremove.decode(message)
|
1255
|
-
fid = message[7..10].unpack("V")[0]
|
1256
|
-
if (fid.nil?)
|
1257
|
-
raise RStyx::StyxException.new("invalid Tremove message")
|
1258
|
-
end
|
1259
|
-
return(Tremove.new(fid))
|
1260
|
-
end
|
1261
|
-
|
649
|
+
StyxMessage::MESSAGE_IDS[Tremove] = 122
|
650
|
+
add_field(:fid, "V")
|
1262
651
|
end
|
1263
652
|
|
1264
653
|
##
|
1265
|
-
#
|
654
|
+
# Class representing an Rremove message sent by a Styx server.
|
1266
655
|
#
|
1267
656
|
class Rremove < StyxMessage
|
1268
|
-
|
1269
|
-
# Make an Rremove message
|
1270
|
-
#
|
1271
|
-
def initialize
|
1272
|
-
@mtype = RREMOVE
|
1273
|
-
@length = 7
|
1274
|
-
@messagebody = ""
|
1275
|
-
@body = ""
|
1276
|
-
end
|
1277
|
-
|
1278
|
-
##
|
1279
|
-
# Decode an Rremove message
|
1280
|
-
#
|
1281
|
-
# +message+:: [String] raw message string
|
1282
|
-
# return:: [Rremove] Rremove instance represented by string
|
1283
|
-
#
|
1284
|
-
def Rremove.decode(message)
|
1285
|
-
# This does absolutely nothing! An Rremove is nothing but a
|
1286
|
-
# bare StyxMessage.
|
1287
|
-
return(Rremove.new)
|
1288
|
-
end
|
1289
|
-
|
657
|
+
StyxMessage::MESSAGE_IDS[Rremove] = 123
|
1290
658
|
end
|
1291
659
|
|
1292
660
|
##
|
1293
|
-
#
|
661
|
+
# Class representing a Tstat message sent by a Styx client.
|
1294
662
|
#
|
1295
663
|
class Tstat < StyxMessage
|
1296
|
-
|
1297
|
-
|
1298
|
-
##
|
1299
|
-
# Construct a new Tstat message given the fid to stat.
|
1300
|
-
#
|
1301
|
-
# +fid+:: The fid to stat
|
1302
|
-
#
|
1303
|
-
def initialize(fid)
|
1304
|
-
@length = 11
|
1305
|
-
@mtype = TSTAT
|
1306
|
-
@fid = fid
|
1307
|
-
@messagebody = [@fid].pack("V")
|
1308
|
-
@body = sprintf("%s", fid)
|
1309
|
-
end
|
1310
|
-
|
1311
|
-
##
|
1312
|
-
# Decode a Tstat message. Do not use this method directly.
|
1313
|
-
# Use only from within StyxMessage.decode.
|
1314
|
-
#
|
1315
|
-
# message:: [String] raw message string
|
1316
|
-
# return:: [Tstat] Tstat instance represented by string
|
1317
|
-
#
|
1318
|
-
def Tstat.decode(message)
|
1319
|
-
fid = message[7..10].unpack("V")[0]
|
1320
|
-
if (fid.nil?)
|
1321
|
-
raise RStyx::StyxException.new("invalid Tclunk message")
|
1322
|
-
end
|
1323
|
-
return(Tstat.new(fid))
|
1324
|
-
end
|
1325
|
-
|
664
|
+
StyxMessage::MESSAGE_IDS[Tstat] = 124
|
665
|
+
add_field(:fid, "V")
|
1326
666
|
end
|
1327
667
|
|
1328
668
|
##
|
1329
|
-
#
|
669
|
+
# Class representing an Rstat message sent by a Styx server.
|
1330
670
|
#
|
1331
671
|
class Rstat < StyxMessage
|
1332
|
-
|
1333
|
-
|
1334
|
-
##
|
1335
|
-
# Make an Rstat message given a DirEntry.
|
1336
|
-
#
|
1337
|
-
# +stat+:: [DirEntry] the file status information
|
1338
|
-
#
|
1339
|
-
def initialize(stat)
|
1340
|
-
@stat = stat
|
1341
|
-
@mtype = RSTAT
|
1342
|
-
@messagebody = @stat.to_bytes
|
1343
|
-
@messagebody = [@stat.size + 2].pack("v") + @messagebody
|
1344
|
-
@length = 7 + @messagebody.length
|
1345
|
-
@body = sprintf("%s", @stat)
|
1346
|
-
end
|
1347
|
-
|
1348
|
-
##
|
1349
|
-
# Decode an Rstat message. Do not use this method directly.
|
1350
|
-
# Use only from within StyxMessage.decode.
|
1351
|
-
#
|
1352
|
-
# +message+:: [String] raw message string
|
1353
|
-
# return:: [Tclunk] Tclunk instance represented by string
|
1354
|
-
#
|
1355
|
-
def Rstat.decode(message)
|
1356
|
-
len = message[0..3].unpack("V")[0]
|
1357
|
-
stat = DirEntry.decode(message[9..len])
|
1358
|
-
return(Rstat.new(stat))
|
1359
|
-
end
|
1360
|
-
|
672
|
+
StyxMessage::MESSAGE_IDS[Rstat] = 125
|
673
|
+
add_field(:stat, "Stat")
|
1361
674
|
end
|
1362
675
|
|
1363
676
|
##
|
1364
|
-
#
|
677
|
+
# Class representing a Twstat message sent by a Styx client.
|
1365
678
|
#
|
1366
679
|
class Twstat < StyxMessage
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
# Make a Twstat message given a fid and a DirEntry.
|
1371
|
-
#
|
1372
|
-
# +fid+:: [Fixnum] the fid of the file to change
|
1373
|
-
# +stat+:: [DirEntry] the new file status information
|
1374
|
-
#
|
1375
|
-
def initialize(fid, stat)
|
1376
|
-
@fid = fid
|
1377
|
-
@stat = stat
|
1378
|
-
@mtype = TWSTAT
|
1379
|
-
@messagebody = @stat.to_bytes
|
1380
|
-
@messagebody = [@fid].pack("V") +
|
1381
|
-
[@stat.size + 2].pack("v") + @messagebody
|
1382
|
-
@length = 7 + @messagebody.length
|
1383
|
-
@body = sprintf("%s, %s", @fid, @stat)
|
1384
|
-
end
|
1385
|
-
|
1386
|
-
##
|
1387
|
-
# Decode a Twstat message. Do not use this method directly.
|
1388
|
-
# Use only from within StyxMessage.decode.
|
1389
|
-
#
|
1390
|
-
# +message+:: [String] raw message string
|
1391
|
-
# return:: [Twstat] Twstat instance represented by string
|
1392
|
-
#
|
1393
|
-
def Twstat.decode(message)
|
1394
|
-
len = message[0..3].unpack("V")[0]
|
1395
|
-
fid = message[7..10].unpack("V")[0]
|
1396
|
-
stat = DirEntry.decode(message[13..len])
|
1397
|
-
return(Twstat.new(fid, stat))
|
1398
|
-
end
|
1399
|
-
|
680
|
+
StyxMessage::MESSAGE_IDS[Twstat] = 126
|
681
|
+
add_field(:fid, "V")
|
682
|
+
add_field(:stat, "Stat")
|
1400
683
|
end
|
1401
684
|
|
1402
685
|
##
|
1403
|
-
#
|
686
|
+
# Class representing an Rwstat message sent by a Styx server.
|
1404
687
|
#
|
1405
688
|
class Rwstat < StyxMessage
|
1406
|
-
|
1407
|
-
# Make an Rwstat message
|
1408
|
-
#
|
1409
|
-
def initialize
|
1410
|
-
@mtype = RWSTAT
|
1411
|
-
@length = 7
|
1412
|
-
@messagebody = ""
|
1413
|
-
@body = ""
|
1414
|
-
end
|
1415
|
-
|
1416
|
-
##
|
1417
|
-
# Decode an Rwstat message
|
1418
|
-
#
|
1419
|
-
# +message+:: [String] raw message string
|
1420
|
-
# return:: [Rwstat] Rwstat instance represented by string
|
1421
|
-
#
|
1422
|
-
def Rwstat.decode(message)
|
1423
|
-
# This does absolutely nothing! An Rwstat is nothing but a
|
1424
|
-
# bare StyxMessage.
|
1425
|
-
return(Rwstat.new)
|
1426
|
-
end
|
1427
|
-
|
689
|
+
StyxMessage::MESSAGE_IDS[Rwstat] = 127
|
1428
690
|
end
|
1429
691
|
|
1430
692
|
end # module Message
|
1431
693
|
|
1432
694
|
end # module RStyx
|
695
|
+
|
696
|
+
|