rstyx 0.1.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/lib/rstyx/client.rb +499 -0
- data/lib/rstyx/errors.rb +40 -0
- data/lib/rstyx/messages.rb +1429 -0
- data/lib/rstyx/version.rb +37 -0
- data/lib/rstyx.rb +23 -0
- data/tests/tc_message.rb +1288 -0
- metadata +52 -0
@@ -0,0 +1,1429 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Copyright (C) 2005 Rafael Sevilla
|
4
|
+
# This file is part of RStyx
|
5
|
+
#
|
6
|
+
# RStyx is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1
|
9
|
+
# of the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# RStyx is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
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.
|
20
|
+
#
|
21
|
+
# Styx Message classes and utility functions
|
22
|
+
#
|
23
|
+
# Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
|
24
|
+
# Copyright:: Copyright (c) 2005 Rafael R. Sevilla
|
25
|
+
# License:: GNU Lesser General Public License
|
26
|
+
#
|
27
|
+
# $Id: messages.rb,v 1.24 2005/09/30 05:35:42 dido Exp $
|
28
|
+
#
|
29
|
+
|
30
|
+
require 'rstyx/errors'
|
31
|
+
|
32
|
+
module RStyx
|
33
|
+
|
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
|
+
##
|
82
|
+
# Class representing the server's view of a file.
|
83
|
+
#
|
84
|
+
class Qid
|
85
|
+
attr_accessor :qtype, :version, :path
|
86
|
+
|
87
|
+
QID_LENGTH = 13 # size of a Qid
|
88
|
+
|
89
|
+
##
|
90
|
+
# Create a new Qid object.
|
91
|
+
#
|
92
|
+
# +type+:: [Fixnum] the type of file (directory, append only file, etc.)
|
93
|
+
# +version+:: [Fixnum] the version number of the file
|
94
|
+
# +path+:: [Fixnum] a 64-bit integer that should be unique among all
|
95
|
+
# files being served
|
96
|
+
#
|
97
|
+
def initialize(type, version, path)
|
98
|
+
@qtype = type
|
99
|
+
@version = version
|
100
|
+
@path = path
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Get the byte string representation of the Qid
|
105
|
+
#
|
106
|
+
# return value:: [String] the byte string representation of the Qid
|
107
|
+
#
|
108
|
+
def to_bytes
|
109
|
+
pathlo = @path & 0xffffffff
|
110
|
+
pathhi = (@path >> 32) & 0xffffffff
|
111
|
+
return([@qtype, @version, pathlo, pathhi].pack("CVVV"))
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Decode a serialized Qid from its byte string representation
|
116
|
+
#
|
117
|
+
# +msgbytes+:: [String] the byte string representation of the qid
|
118
|
+
# return value:: [Qid] the Qid represented by the byte string.
|
119
|
+
# raises:: StyxException if the string cannot be decoded as a Qid
|
120
|
+
#
|
121
|
+
def Qid.decode(msgbytes)
|
122
|
+
qtype, version, pathlo, pathhi = msgbytes.unpack("CVVV")
|
123
|
+
if qtype.nil? || version.nil? || pathlo.nil? || pathhi.nil?
|
124
|
+
raise StyxException.new("QID failed decode")
|
125
|
+
end
|
126
|
+
# recombine in little-endian mode
|
127
|
+
path = pathlo | (pathhi << 32)
|
128
|
+
return(Qid.new(qtype, version, path))
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Dump a Qid
|
133
|
+
#
|
134
|
+
# return value:: [String] a textual representation of the Qid
|
135
|
+
#
|
136
|
+
def to_s
|
137
|
+
return(sprintf("qid(%s, %s, %s)", @qtype, @version, @path))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Class representing an entry in a directory (e.g. the result of a
|
143
|
+
# stat message)
|
144
|
+
#
|
145
|
+
class DirEntry
|
146
|
+
attr_accessor :size, :dtype, :dev, :qid, :mode, :atime, :mtime
|
147
|
+
attr_accessor :length, :name, :uid, :gid, :muid
|
148
|
+
|
149
|
+
##
|
150
|
+
# Serialize a DirEntry. This also calculates the size of the
|
151
|
+
# DirEntry in bytes.
|
152
|
+
#
|
153
|
+
# return value:: [String] the serialized version of the DirEntry
|
154
|
+
#
|
155
|
+
def to_bytes
|
156
|
+
str = [@dtype, @dev].pack("vV")
|
157
|
+
str << @qid.to_bytes
|
158
|
+
lengthlo = @length & 0xffffffff
|
159
|
+
lengthhi = (@length >> 32) & 0xffffffff
|
160
|
+
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)
|
165
|
+
@size = str.length
|
166
|
+
return([size].pack("v") + str)
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Unserialize a DirEntry
|
171
|
+
#
|
172
|
+
# +bytes+:: [String] serialized string representation of a DirEntry
|
173
|
+
# return value:: [DirEntry] the DirEntry corresponding to the
|
174
|
+
# passed string
|
175
|
+
# raises:: StyxException if +bytes+ cannot be properly decoded as
|
176
|
+
# a DirEntry
|
177
|
+
#
|
178
|
+
def DirEntry.decode(bytes)
|
179
|
+
# From Inferno stat(5)
|
180
|
+
#
|
181
|
+
# 0-1 = size
|
182
|
+
# 2-3 = type
|
183
|
+
# 4-7 = dev
|
184
|
+
# 8 = Qid.type
|
185
|
+
# 9-12 = Qid.vers
|
186
|
+
# 13-20 = Qid.path
|
187
|
+
# 21-24 = mode
|
188
|
+
# 25-28 = atime
|
189
|
+
# 29-32 = mtime
|
190
|
+
# 33-40 = length
|
191
|
+
# 41-42 = name length
|
192
|
+
# 43 to 42 + namelen = name
|
193
|
+
# 43 + namelen to 44 + namelen = uid length
|
194
|
+
# 45 + namelen to 44 + namelen + uidlen = uid
|
195
|
+
# 45 + namelen + uidlen
|
196
|
+
de = DirEntry.new
|
197
|
+
de.size, de.dtype, de.dev = (bytes[0..7]).unpack("vvV")
|
198
|
+
if de.size.nil? || de.dtype.nil? || de.dev.nil?
|
199
|
+
raise RStyx::StyxException.new("failed to decode DirEntry")
|
200
|
+
end
|
201
|
+
de.qid = Qid.decode(bytes[8..20])
|
202
|
+
de.mode, de.atime, de.mtime, lengthlo, lengthhi =
|
203
|
+
(bytes[21..40]).unpack("VVVVV")
|
204
|
+
|
205
|
+
if de.mode.nil? || de.atime.nil? || de.mtime.nil? ||
|
206
|
+
lengthlo.nil? || lengthhi.nil?
|
207
|
+
raise RStyx::StyxException.new("failed to decode DirEntry")
|
208
|
+
end
|
209
|
+
# combine in little-endian
|
210
|
+
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)
|
215
|
+
return(de)
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# convert a DirEntry to a human-readable string
|
220
|
+
#
|
221
|
+
# return value:: a string representation of the DirEntry
|
222
|
+
#
|
223
|
+
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)
|
228
|
+
return(s)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
##
|
233
|
+
# Abstract class representing a message in the Styx protocol.
|
234
|
+
# The basic attributes are the length, type, tag, messagebody,
|
235
|
+
# and (printable) body.
|
236
|
+
#
|
237
|
+
class StyxMessage
|
238
|
+
attr_accessor :length, :mtype, :tag, :messagebody, :body
|
239
|
+
|
240
|
+
##
|
241
|
+
# Convert the message to a string of bytes for transmission.
|
242
|
+
#
|
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
|
246
|
+
#
|
247
|
+
def to_bytes(tag=nil)
|
248
|
+
unless tag.nil?
|
249
|
+
@tag = tag
|
250
|
+
end
|
251
|
+
val = [@length, @mtype, @tag].pack("VCv") + @messagebody
|
252
|
+
return(val)
|
253
|
+
end
|
254
|
+
|
255
|
+
##
|
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.
|
259
|
+
#
|
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
|
+
end
|
266
|
+
|
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
|
+
##
|
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)
|
359
|
+
end
|
360
|
+
|
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
|
+
##
|
371
|
+
# Construct a new Tversion message
|
372
|
+
#
|
373
|
+
# +msize+:: [Fixnum] maximum message size
|
374
|
+
# +version+:: [String] the version number we use
|
375
|
+
#
|
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)
|
383
|
+
end
|
384
|
+
|
385
|
+
##
|
386
|
+
# Decode a Tversion message. Do not call this method directly.
|
387
|
+
#
|
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))
|
399
|
+
end
|
400
|
+
|
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
|
+
##
|
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)
|
423
|
+
end
|
424
|
+
|
425
|
+
##
|
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.
|
431
|
+
#
|
432
|
+
def Rversion.decode(message)
|
433
|
+
msize = message[7..10].unpack("V")[0]
|
434
|
+
if msize.nil?
|
435
|
+
raise StyxException.new("Invalid Rversion message")
|
436
|
+
end
|
437
|
+
version, offset = Util.strextract(message, 11)
|
438
|
+
return(Rversion.new(msize, version))
|
439
|
+
end
|
440
|
+
|
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
|
+
##
|
452
|
+
# Construct a new Tauth message.
|
453
|
+
#
|
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)
|
468
|
+
end
|
469
|
+
|
470
|
+
##
|
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")
|
480
|
+
end
|
481
|
+
uname, offset = Util.strextract(message, 11)
|
482
|
+
aname, offset = Util.strextract(message, offset)
|
483
|
+
return(Tauth.new(uname, afid, aname))
|
484
|
+
end
|
485
|
+
|
486
|
+
end
|
487
|
+
|
488
|
+
##
|
489
|
+
# Server response to Rauth message, giving qid of authenticator qid.
|
490
|
+
#
|
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
|
516
|
+
end
|
517
|
+
|
518
|
+
##
|
519
|
+
# Message returned to indicate an error condition
|
520
|
+
#
|
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
|
+
|
549
|
+
end
|
550
|
+
|
551
|
+
##
|
552
|
+
# Message sent to abort a previous message.
|
553
|
+
#
|
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
|
+
|
585
|
+
end
|
586
|
+
|
587
|
+
##
|
588
|
+
# Server response to a Tflush message
|
589
|
+
#
|
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
|
612
|
+
end
|
613
|
+
|
614
|
+
##
|
615
|
+
# Message sent to attach to the server as a certain user
|
616
|
+
#
|
617
|
+
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
|
+
|
659
|
+
end
|
660
|
+
|
661
|
+
##
|
662
|
+
# Server response to Tattach message, giving qid of root
|
663
|
+
#
|
664
|
+
class Rattach < StyxMessage
|
665
|
+
attr_accessor :qid
|
666
|
+
|
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
|
678
|
+
end
|
679
|
+
|
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
|
690
|
+
end
|
691
|
+
|
692
|
+
##
|
693
|
+
# Message sent to get a handle to a file on the server
|
694
|
+
#
|
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
|
728
|
+
|
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
|
748
|
+
end
|
749
|
+
|
750
|
+
##
|
751
|
+
# Server reply to Twalk message
|
752
|
+
#
|
753
|
+
class Rwalk < StyxMessage
|
754
|
+
attr_accessor :qids
|
755
|
+
|
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)
|
782
|
+
end
|
783
|
+
|
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))
|
802
|
+
end
|
803
|
+
|
804
|
+
end
|
805
|
+
|
806
|
+
##
|
807
|
+
# Message sent to prepare an existing file for reading or writing.
|
808
|
+
#
|
809
|
+
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
|
+
|
863
|
+
end
|
864
|
+
|
865
|
+
##
|
866
|
+
# Server reply to Topen message
|
867
|
+
#
|
868
|
+
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
|
+
|
903
|
+
end
|
904
|
+
|
905
|
+
##
|
906
|
+
# Message sent to create a new file on the server.
|
907
|
+
#
|
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
|
+
|
955
|
+
end
|
956
|
+
|
957
|
+
##
|
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.
|
960
|
+
#
|
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
|
+
|
994
|
+
end
|
995
|
+
|
996
|
+
##
|
997
|
+
# Message sent to read data from a file given fid, offset, and length
|
998
|
+
# of data to be read.
|
999
|
+
#
|
1000
|
+
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
|
+
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
##
|
1040
|
+
# Server response to Tread message giving the data from the read.
|
1041
|
+
#
|
1042
|
+
class Rread < StyxMessage
|
1043
|
+
attr_accessor :count, :data
|
1044
|
+
|
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))
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
##
|
1081
|
+
# Message sent to write data to a file given fid, offset, and
|
1082
|
+
# the data to be written.
|
1083
|
+
#
|
1084
|
+
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
|
+
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
##
|
1130
|
+
# Server response to Twrite message.
|
1131
|
+
#
|
1132
|
+
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
|
+
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
##
|
1165
|
+
# Message sent to forget about a fid, closing a file.
|
1166
|
+
#
|
1167
|
+
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
|
+
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
##
|
1201
|
+
# Server response to a Tclunk message
|
1202
|
+
#
|
1203
|
+
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
|
+
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
##
|
1229
|
+
# Message sent to delete a file, given its fid.
|
1230
|
+
#
|
1231
|
+
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
|
+
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
##
|
1265
|
+
# Server response to a Tremove message
|
1266
|
+
#
|
1267
|
+
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
|
+
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
##
|
1293
|
+
# Message sent to obtain information about a file given its fid.
|
1294
|
+
#
|
1295
|
+
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
|
+
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
##
|
1329
|
+
# Server response to a Tstat message.
|
1330
|
+
#
|
1331
|
+
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
|
+
@length = 7 + @messagebody.length
|
1344
|
+
@body = sprintf("%s", @stat)
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
##
|
1348
|
+
# Decode an Rstat message. Do not use this method directly.
|
1349
|
+
# Use only from within StyxMessage.decode.
|
1350
|
+
#
|
1351
|
+
# +message+:: [String] raw message string
|
1352
|
+
# return:: [Tclunk] Tclunk instance represented by string
|
1353
|
+
#
|
1354
|
+
def Rstat.decode(message)
|
1355
|
+
len = message[0..3].unpack("V")[0]
|
1356
|
+
stat = DirEntry.decode(message[7..len])
|
1357
|
+
return(Rstat.new(stat))
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
##
|
1363
|
+
# Message sent to update the status of a file given a fid and a DirEntry
|
1364
|
+
#
|
1365
|
+
class Twstat < StyxMessage
|
1366
|
+
attr_accessor :fid, :stat
|
1367
|
+
|
1368
|
+
##
|
1369
|
+
# Make a Twstat message given a fid and a DirEntry.
|
1370
|
+
#
|
1371
|
+
# +fid+:: [Fixnum] the fid of the file to change
|
1372
|
+
# +stat+:: [DirEntry] the new file status information
|
1373
|
+
#
|
1374
|
+
def initialize(fid, stat)
|
1375
|
+
@fid = fid
|
1376
|
+
@stat = stat
|
1377
|
+
@mtype = TWSTAT
|
1378
|
+
@messagebody = [@fid].pack("V") + @stat.to_bytes
|
1379
|
+
@length = 7 + @messagebody.length
|
1380
|
+
@body = sprintf("%s, %s", @fid, @stat)
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
##
|
1384
|
+
# Decode a Twstat message. Do not use this method directly.
|
1385
|
+
# Use only from within StyxMessage.decode.
|
1386
|
+
#
|
1387
|
+
# +message+:: [String] raw message string
|
1388
|
+
# return:: [Twstat] Twstat instance represented by string
|
1389
|
+
#
|
1390
|
+
def Twstat.decode(message)
|
1391
|
+
len = message[0..3].unpack("V")[0]
|
1392
|
+
fid = message[7..10].unpack("V")[0]
|
1393
|
+
stat = DirEntry.decode(message[11..len])
|
1394
|
+
return(Twstat.new(fid, stat))
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
##
|
1400
|
+
# Server response to a Twstat message
|
1401
|
+
#
|
1402
|
+
class Rwstat < StyxMessage
|
1403
|
+
##
|
1404
|
+
# Make an Rwstat message
|
1405
|
+
#
|
1406
|
+
def initialize
|
1407
|
+
@mtype = RWSTAT
|
1408
|
+
@length = 7
|
1409
|
+
@messagebody = ""
|
1410
|
+
@body = ""
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
##
|
1414
|
+
# Decode an Rwstat message
|
1415
|
+
#
|
1416
|
+
# +message+:: [String] raw message string
|
1417
|
+
# return:: [Rwstat] Rwstat instance represented by string
|
1418
|
+
#
|
1419
|
+
def Rwstat.decode(message)
|
1420
|
+
# This does absolutely nothing! An Rwstat is nothing but a
|
1421
|
+
# bare StyxMessage.
|
1422
|
+
return(Rwstat.new)
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
end
|
1426
|
+
|
1427
|
+
end # module Message
|
1428
|
+
|
1429
|
+
end # module RStyx
|