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.
@@ -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., 59 Temple Place, Suite 330, Boston, MA
19
- # 02111-1307 USA.
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,v 1.26 2005/11/01 11:40:26 dido Exp $
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 Qid.decode(msgbytes)
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
- return(sprintf("qid(%s, %s, %s)", @qtype, @version, @path))
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 DirEntry
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 DirEntry. This also calculates the size of the
151
- # DirEntry in bytes.
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 DirEntry
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 << Util.strpack(@name)
162
- str << Util.strpack(@uid)
163
- str << Util.strpack(@gid)
164
- str << Util.strpack(@muid)
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 DirEntry
149
+ # Unserialize a Stat
171
150
  #
172
- # +bytes+:: [String] serialized string representation of a DirEntry
173
- # return value:: [DirEntry] the DirEntry corresponding to the
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 DirEntry
155
+ # a Stat
177
156
  #
178
- def DirEntry.decode(bytes)
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 = DirEntry.new
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 DirEntry")
178
+ raise RStyx::StyxException.new("failed to decode Stat")
200
179
  end
201
- de.qid = Qid.decode(bytes[8..20])
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 DirEntry")
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 = Util.strextract(bytes, 41)
212
- de.uid, offset = Util.strextract(bytes, offset)
213
- de.gid, offset = Util.strextract(bytes, offset)
214
- de.muid, offset = Util.strextract(bytes, 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 DirEntry to a human-readable string
198
+ # convert a Stat to a human-readable string
220
199
  #
221
- # return value:: a string representation of the DirEntry
200
+ # return value:: a string representation of the Stat
222
201
  #
223
202
  def to_s
224
- s = sprintf("DirEntry(%s, %s, %s, ", @size, @dtype, @dev)
225
- s << sprintf("%s, %s, %s, %s, ", @qid.to_s, @mode, @atime, @mtime)
226
- s << sprintf("%s, %s, %s, ", @length, @name, @uid)
227
- s << sprintf("%s, %s)", @gid, @muid)
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
- # Abstract class representing a message in the Styx protocol.
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 :length, :mtype, :tag, :messagebody, :body
216
+ attr_accessor :fieldvals
217
+ MESSAGE_IDS = {}
239
218
 
240
219
  ##
241
- # Convert the message to a string of bytes for transmission.
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
- # +tag+:: [Fixnum] The tag value to use for the message. If
244
- # not specified, leave the tag value as is.
245
- # return value:: [String] The marshalled byte string of the message
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 to_bytes(tag=nil)
248
- unless tag.nil?
249
- @tag = tag
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
- # Dump the Styx message to a string. The StyxMessage subclass
257
- # must define the @body variable to something meaningful for
258
- # this to work.
263
+ # The fields of the Styx message.
259
264
  #
260
- # return value:: human-readable string representing the Styx message
261
- #
262
- def to_s
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
- # Decode an arbitrary Styx message, returning the appropriate
312
- # subclass instance.
313
- #
314
- # +message+:: [String] The Styx message in serialized form
315
- # return value:: [StyxMessage] instance of StyxMessage subclass
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
- # Construct a new Tversion message
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 initialize(msize=8216, version="9P2000")
377
- @msize = msize
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
- # Decode a Tversion message. Do not call this method directly.
287
+ # Return the tag of the message
387
288
  #
388
- # +message+:: [String] raw message string
389
- # return value:: [Tversion] Tversion instance
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
- # Construct a new Rversion message
412
- #
413
- # +msize+:: [Fixnum] maximum message size
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
- # Decode an Rversion message. Do not call this method directly.
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 Rversion.decode(message)
433
- msize = message[7..10].unpack("V")[0]
434
- if msize.nil?
435
- raise StyxException.new("Invalid Rversion message")
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
- version, offset = Util.strextract(message, 11)
438
- return(Rversion.new(msize, version))
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
- # Construct a new Tauth message.
364
+ # Serialize a Styx message subclass into a byte string.
453
365
  #
454
- # +uname+:: [String] user name to attach as
455
- # +afid+:: [Fixnum] authenticated fid from auth protocol
456
- # +aname+:: [String] file tree to access
457
- #
458
- def initialize(uname, afid, aname)
459
- @afid = afid
460
- @uname = uname
461
- @aname = aname
462
- @length = 15 + @uname.length + @aname.length
463
- @mtype = TAUTH
464
- @messagebody = [@afid].pack("V")
465
- @messagebody << Util.strpack(uname)
466
- @messagebody << Util.strpack(aname)
467
- @body = sprintf("%s, %s, %s", @afid, @uname, @aname)
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
- # Decode a Tauth message.
472
- #
473
- # +message+:: [String] the message to decode
474
- # return:: [Tauth] the Tauth object
475
- #
476
- def Tauth.decode(message)
477
- afid = message[7..10].unpack("V")[0]
478
- if afid.nil?
479
- raise RStyx::StyxException.new("invalid Tauth message")
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
- uname, offset = Util.strextract(message, 11)
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
- # Server response to Rauth message, giving qid of authenticator qid.
437
+ # Class representing a Tversion message sent by a Styx client.
490
438
  #
491
- class Rauth < StyxMessage
492
- attr_accessor :aqid
493
-
494
- ##
495
- # Construct a new Rauth message
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
- # Message returned to indicate an error condition
447
+ # Class representing an Rversion message sent by a Styx server.
520
448
  #
521
- class Rerror < StyxMessage
522
- attr_accessor :ename
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
- # Message sent to abort a previous message.
456
+ # Class representing a Tauth message sent by a Styx client.
553
457
  #
554
- class Tflush < StyxMessage
555
- attr_accessor :oldtag
556
-
557
- ##
558
- # Construct a new Tflush message given the tag to flush
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
- # Server response to a Tflush message
466
+ # Class representing an Rauth message sent by a Styx server.
589
467
  #
590
- class Rflush < StyxMessage
591
- ##
592
- # Make an Rflush message
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
- # Message sent to attach to the server as a certain user
474
+ # Class representing a Tattach message sent by a Styx client.
616
475
  #
617
476
  class Tattach < StyxMessage
618
- attr_accessor :fid, :afid, :uname, :aname
619
-
620
- NOFID = 0xffffffff
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
- # Server response to Tattach message, giving qid of root
485
+ # Class representing an Rattach message sent by a Styx server.
663
486
  #
664
487
  class Rattach < StyxMessage
665
- attr_accessor :qid
488
+ StyxMessage::MESSAGE_IDS[Rattach] = 105
489
+ add_field(:qid, 'Qid')
490
+ end
666
491
 
667
- ##
668
- # Construct a new Rattach message
669
- #
670
- # +qid+:: [Qid] the qid of the root
671
- #
672
- def initialize(qid)
673
- @qid = qid
674
- @length = 20 # exact length
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
- # Decode an Rattach message.
682
- #
683
- # +message+:: [String] the message to decode
684
- # return:: [Rattach] the Rattach object
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
- # Message sent to get a handle to a file on the server
512
+ # Class representing a Tflush message sent by a Styx client.
694
513
  #
695
- class Twalk < StyxMessage
696
- attr_accessor :fid, :newfid, :path, :path_elements
697
- MAXWELEM = 16 # maximum of sixteen name elements
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
- # Decode a Twalk message.
731
- #
732
- # +message+:: [String] the message to decode
733
- # return:: [Twalk] the Twalk object
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
- # Server reply to Twalk message
527
+ # Class representing a Twalk message sent by a Styx client.
752
528
  #
753
- class Rwalk < StyxMessage
754
- attr_accessor :qids
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
- # Construct a new Rwalk message, given a list of qid's
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
- # Decode an Rwalk message.
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
- # Message sent to prepare an existing file for reading or writing.
553
+ # Class representing a Topen message sent by a Styx client.
808
554
  #
809
555
  class Topen < StyxMessage
810
- attr_accessor :fid, :mode
811
-
812
- # File access modes in Styx
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
- # Server reply to Topen message
562
+ # Class representing an Ropen message sent by a Styx server.
867
563
  #
868
564
  class Ropen < StyxMessage
869
- attr_accessor :qid, :iounit
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
- # Message sent to create a new file on the server.
571
+ # Class representing a Tcreate message sent by a Styx client.
907
572
  #
908
- class Tcreate < Topen
909
- attr_accessor :name, :perm
910
-
911
- DMDIR = 0x80000000 # logical-OR with perm to make directory
912
- DMAPPEND = 0x40000000 # logical-OR with perm to make appendonly
913
- DMEXCL = 0x20000000 # logical-OR with perm to make exclusive use
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
- # Server reply to Tcreate message. Except for the message tag, it
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 < Ropen
962
- ##
963
- # Construct a new Rcreate message
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
- # Message sent to read data from a file given fid, offset, and length
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
- attr_accessor :fid, :offset, :count
1002
-
1003
- ##
1004
- # Construct a new Tread message.
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
- # Server response to Tread message giving the data from the read.
601
+ # Class representing an Rread message sent by a Styx server.
1041
602
  #
1042
603
  class Rread < StyxMessage
1043
- attr_accessor :count, :data
604
+ StyxMessage::MESSAGE_IDS[Rread] = 117
605
+ add_field(:data, "Bstr")
1044
606
 
1045
- ##
1046
- # Construct a new Rread message.
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
- # Message sent to write data to a file given fid, offset, and
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
- attr_accessor :fid, :offset, :data, :count
1086
-
1087
- ##
1088
- # Construct a new Twrite message.
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
- # Server response to Twrite message.
623
+ # Class representing an Rwrite message sent by a Styx server.
1131
624
  #
1132
625
  class Rwrite < StyxMessage
1133
- attr_accessor :count
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
- # Message sent to forget about a fid, closing a file.
631
+ # Class representing a Tclunk message sent by a Styx client.
1166
632
  #
1167
633
  class Tclunk < StyxMessage
1168
- attr_accessor :fid
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
- # Server response to a Tclunk message
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
- # Message sent to delete a file, given its fid.
646
+ # Class representing a Tremove message sent by a Styx client.
1230
647
  #
1231
648
  class Tremove < StyxMessage
1232
- attr_accessor :fid
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
- # Server response to a Tremove message
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
- # Message sent to obtain information about a file given its fid.
661
+ # Class representing a Tstat message sent by a Styx client.
1294
662
  #
1295
663
  class Tstat < StyxMessage
1296
- attr_accessor :fid
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
- # Server response to a Tstat message.
669
+ # Class representing an Rstat message sent by a Styx server.
1330
670
  #
1331
671
  class Rstat < StyxMessage
1332
- attr_accessor :stat
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
- # Message sent to update the status of a file given a fid and a DirEntry
677
+ # Class representing a Twstat message sent by a Styx client.
1365
678
  #
1366
679
  class Twstat < StyxMessage
1367
- attr_accessor :fid, :stat
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
- # Server response to a Twstat message
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
+