rumai 1.0.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/LICENSE +23 -0
- data/README +1 -0
- data/Rakefile +58 -0
- data/bin/rumai +38 -0
- data/doc/api/classes/IO.html +120 -0
- data/doc/api/classes/Integer.html +142 -0
- data/doc/api/classes/Integer.src/M000002.html +18 -0
- data/doc/api/classes/Object.html +113 -0
- data/doc/api/classes/Rumai.html +530 -0
- data/doc/api/classes/Rumai.src/M000007.html +18 -0
- data/doc/api/classes/Rumai.src/M000008.html +18 -0
- data/doc/api/classes/Rumai.src/M000009.html +18 -0
- data/doc/api/classes/Rumai.src/M000010.html +18 -0
- data/doc/api/classes/Rumai.src/M000011.html +18 -0
- data/doc/api/classes/Rumai.src/M000012.html +18 -0
- data/doc/api/classes/Rumai.src/M000013.html +18 -0
- data/doc/api/classes/Rumai.src/M000014.html +20 -0
- data/doc/api/classes/Rumai.src/M000015.html +18 -0
- data/doc/api/classes/Rumai.src/M000016.html +18 -0
- data/doc/api/classes/Rumai.src/M000017.html +18 -0
- data/doc/api/classes/Rumai.src/M000018.html +18 -0
- data/doc/api/classes/Rumai.src/M000019.html +18 -0
- data/doc/api/classes/Rumai.src/M000020.html +18 -0
- data/doc/api/classes/Rumai.src/M000021.html +18 -0
- data/doc/api/classes/Rumai.src/M000022.html +18 -0
- data/doc/api/classes/Rumai.src/M000023.html +18 -0
- data/doc/api/classes/Rumai.src/M000024.html +18 -0
- data/doc/api/classes/Rumai.src/M000025.html +18 -0
- data/doc/api/classes/Rumai.src/M000026.html +18 -0
- data/doc/api/classes/Rumai/Area.html +461 -0
- data/doc/api/classes/Rumai/Area.src/M000080.html +19 -0
- data/doc/api/classes/Rumai/Area.src/M000081.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000082.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000083.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000084.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000085.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000086.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000087.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000088.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000089.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000090.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000091.html +22 -0
- data/doc/api/classes/Rumai/Area.src/M000093.html +23 -0
- data/doc/api/classes/Rumai/Area.src/M000094.html +28 -0
- data/doc/api/classes/Rumai/Area.src/M000095.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000096.html +31 -0
- data/doc/api/classes/Rumai/Chain.html +179 -0
- data/doc/api/classes/Rumai/Chain.src/M000077.html +18 -0
- data/doc/api/classes/Rumai/Chain.src/M000078.html +18 -0
- data/doc/api/classes/Rumai/Chain.src/M000079.html +18 -0
- data/doc/api/classes/Rumai/Client.html +463 -0
- data/doc/api/classes/Rumai/Client.src/M000114.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000115.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000116.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000117.html +39 -0
- data/doc/api/classes/Rumai/Client.src/M000118.html +28 -0
- data/doc/api/classes/Rumai/Client.src/M000119.html +19 -0
- data/doc/api/classes/Rumai/Client.src/M000120.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000121.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000122.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000123.html +19 -0
- data/doc/api/classes/Rumai/Client.src/M000124.html +20 -0
- data/doc/api/classes/Rumai/Client.src/M000125.html +20 -0
- data/doc/api/classes/Rumai/Client.src/M000126.html +22 -0
- data/doc/api/classes/Rumai/Client.src/M000127.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000128.html +20 -0
- data/doc/api/classes/Rumai/Client.src/M000129.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000130.html +22 -0
- data/doc/api/classes/Rumai/ClientContainer.html +180 -0
- data/doc/api/classes/Rumai/ClientContainer.src/M000027.html +18 -0
- data/doc/api/classes/Rumai/ClientContainer.src/M000028.html +18 -0
- data/doc/api/classes/Rumai/ClientContainer.src/M000029.html +18 -0
- data/doc/api/classes/Rumai/ExportInstMethods.html +119 -0
- data/doc/api/classes/Rumai/IXP.html +149 -0
- data/doc/api/classes/Rumai/IXP/Agent.html +447 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000046.html +47 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000047.html +44 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000048.html +39 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000049.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000050.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000051.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000052.html +33 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000053.html +19 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000054.html +18 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000055.html +21 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000056.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000057.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000058.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000059.html +23 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000060.html +19 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.html +254 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000062.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000063.html +21 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000064.html +42 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000065.html +26 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000066.html +36 -0
- data/doc/api/classes/Rumai/IXP/Agent/MODES.html +130 -0
- data/doc/api/classes/Rumai/IXP/Agent/MODES.src/M000061.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.html +203 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000068.html +23 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000069.html +32 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000070.html +20 -0
- data/doc/api/classes/Rumai/IXP/Error.html +117 -0
- data/doc/api/classes/Rumai/IXP/Fcall.html +261 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000071.html +20 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000072.html +25 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000073.html +18 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000074.html +18 -0
- data/doc/api/classes/Rumai/IXP/Qid.html +184 -0
- data/doc/api/classes/Rumai/IXP/Rattach.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rauth.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rclunk.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rcreate.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rerror.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rflush.html +119 -0
- data/doc/api/classes/Rumai/IXP/Ropen.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rread.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rremove.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rstat.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rversion.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rwalk.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rwrite.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rwstat.html +119 -0
- data/doc/api/classes/Rumai/IXP/Stat.html +248 -0
- data/doc/api/classes/Rumai/IXP/Stat.src/M000076.html +18 -0
- data/doc/api/classes/Rumai/IXP/Stream.html +158 -0
- data/doc/api/classes/Rumai/IXP/Stream.src/M000045.html +19 -0
- data/doc/api/classes/Rumai/IXP/Struct.html +257 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000030.html +19 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000031.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000032.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000033.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000034.html +20 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000035.html +57 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.html +304 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000036.html +21 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000037.html +21 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000038.html +20 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000039.html +29 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000040.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000041.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.html +152 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000043.html +24 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000044.html +25 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.html +137 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.src/M000042.html +18 -0
- data/doc/api/classes/Rumai/IXP/Tattach.html +120 -0
- data/doc/api/classes/Rumai/IXP/Tauth.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tclunk.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tcreate.html +120 -0
- data/doc/api/classes/Rumai/IXP/Terror.html +145 -0
- data/doc/api/classes/Rumai/IXP/Terror.src/M000075.html +18 -0
- data/doc/api/classes/Rumai/IXP/Tflush.html +119 -0
- data/doc/api/classes/Rumai/IXP/Topen.html +193 -0
- data/doc/api/classes/Rumai/IXP/Tread.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tremove.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tstat.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tversion.html +137 -0
- data/doc/api/classes/Rumai/IXP/Twalk.html +120 -0
- data/doc/api/classes/Rumai/IXP/Twrite.html +120 -0
- data/doc/api/classes/Rumai/IXP/Twstat.html +119 -0
- data/doc/api/classes/Rumai/Node.html +461 -0
- data/doc/api/classes/Rumai/Node.src/M000097.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000098.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000099.html +22 -0
- data/doc/api/classes/Rumai/Node.src/M000100.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000101.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000102.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000103.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000104.html +22 -0
- data/doc/api/classes/Rumai/Node.src/M000105.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000106.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000107.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000108.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000109.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000110.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000111.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000112.html +20 -0
- data/doc/api/classes/Rumai/Node.src/M000113.html +27 -0
- data/doc/api/classes/Rumai/View.html +436 -0
- data/doc/api/classes/Rumai/View.src/M000131.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000132.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000133.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000134.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000135.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000136.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000137.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000138.html +28 -0
- data/doc/api/classes/Rumai/View.src/M000139.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000140.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000141.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000142.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000143.html +29 -0
- data/doc/api/classes/Rumai/View.src/M000144.html +20 -0
- data/doc/api/classes/Rumai/View.src/M000145.html +33 -0
- data/doc/api/classes/Rumai/View.src/M000146.html +50 -0
- data/doc/api/classes/String.html +169 -0
- data/doc/api/classes/String.src/M000005.html +18 -0
- data/doc/api/classes/String.src/M000006.html +19 -0
- data/doc/api/classes/StringIO.html +120 -0
- data/doc/api/classes/Time.html +163 -0
- data/doc/api/classes/Time.src/M000003.html +18 -0
- data/doc/api/classes/Time.src/M000004.html +18 -0
- data/doc/api/created.rid +1 -0
- data/doc/api/files/lib/rumai/fs_rb.html +115 -0
- data/doc/api/files/lib/rumai/ixp/message_rb.html +120 -0
- data/doc/api/files/lib/rumai/ixp/message_spec_rb.html +175 -0
- data/doc/api/files/lib/rumai/ixp/message_spec_rb.src/M000001.html +37 -0
- data/doc/api/files/lib/rumai/ixp/transport_rb.html +115 -0
- data/doc/api/files/lib/rumai/ixp_rb.html +116 -0
- data/doc/api/files/lib/rumai/nfo_rb.html +107 -0
- data/doc/api/files/lib/rumai/wm_rb.html +115 -0
- data/doc/api/files/lib/rumai_rb.html +115 -0
- data/doc/api/fr_class_index.html +82 -0
- data/doc/api/fr_file_index.html +34 -0
- data/doc/api/fr_method_index.html +172 -0
- data/doc/api/index.html +24 -0
- data/doc/api/rdoc-style.css +208 -0
- data/doc/guide.erb +390 -0
- data/doc/guide.html +2035 -0
- data/doc/index.html +2035 -0
- data/lib/rumai.rb +9 -0
- data/lib/rumai/fs.rb +170 -0
- data/lib/rumai/ixp.rb +9 -0
- data/lib/rumai/ixp/message.rb +642 -0
- data/lib/rumai/ixp/message_spec.rb +247 -0
- data/lib/rumai/ixp/transport.rb +382 -0
- data/lib/rumai/nfo.rb +24 -0
- data/lib/rumai/wm.rb +780 -0
- metadata +320 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Unit test for 9p.rb
|
|
2
|
+
#--
|
|
3
|
+
# Copyright 2007 Suraj N. Kurapati
|
|
4
|
+
# See the file named LICENSE for details.
|
|
5
|
+
|
|
6
|
+
$: << File.dirname(__FILE__)
|
|
7
|
+
require 'message'
|
|
8
|
+
include Rumai::IXP
|
|
9
|
+
|
|
10
|
+
require 'pp' if $DEBUG
|
|
11
|
+
|
|
12
|
+
# Transmits the given request and returns the received response.
|
|
13
|
+
def xmit aRequest
|
|
14
|
+
# send the request
|
|
15
|
+
req = aRequest
|
|
16
|
+
if $DEBUG
|
|
17
|
+
puts
|
|
18
|
+
pp req
|
|
19
|
+
pp req.to_9p
|
|
20
|
+
end
|
|
21
|
+
PIPE << req.to_9p
|
|
22
|
+
|
|
23
|
+
# recv the response
|
|
24
|
+
rsp = Fcall.from_9p(PIPE)
|
|
25
|
+
if $DEBUG
|
|
26
|
+
puts
|
|
27
|
+
pp rsp
|
|
28
|
+
pp rsp.to_9p
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
[Rerror.type, req.type + 1].should include(rsp.type)
|
|
32
|
+
req.tag.should == rsp.tag
|
|
33
|
+
rsp
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# set up the connection to wmii's IXP server
|
|
37
|
+
ADDR = ENV['WMII_ADDRESS'].sub(/.*!/, '') rescue
|
|
38
|
+
"/tmp/ns.#{ENV['USER']}.#{ENV['DISPLAY'] || ':0'}/wmii"
|
|
39
|
+
|
|
40
|
+
require 'socket'
|
|
41
|
+
PIPE = UNIXSocket.new(ADDR)
|
|
42
|
+
|
|
43
|
+
describe Tversion do
|
|
44
|
+
it 'should establish a connection' do
|
|
45
|
+
req = Tversion.new(
|
|
46
|
+
:tag => Fcall::NOTAG,
|
|
47
|
+
:msize => Tversion::MSIZE,
|
|
48
|
+
:version => Tversion::VERSION
|
|
49
|
+
)
|
|
50
|
+
rsp = xmit(req)
|
|
51
|
+
|
|
52
|
+
rsp.type.should == Rversion.type
|
|
53
|
+
rsp.version.should == req.version
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# read a directory
|
|
58
|
+
describe Tattach do
|
|
59
|
+
it 'should attach to FS root' do
|
|
60
|
+
req = Tattach.new(
|
|
61
|
+
:tag => 0,
|
|
62
|
+
:fid => 0,
|
|
63
|
+
:afid => Fcall::NOFID,
|
|
64
|
+
:uname => ENV['USER'],
|
|
65
|
+
:aname => ''
|
|
66
|
+
)
|
|
67
|
+
rsp = xmit(req)
|
|
68
|
+
|
|
69
|
+
rsp.type.should == Rattach.type
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe Tstat do
|
|
74
|
+
it 'should stat FS root' do
|
|
75
|
+
req = Tstat.new(
|
|
76
|
+
:tag => 0,
|
|
77
|
+
:fid => 0
|
|
78
|
+
)
|
|
79
|
+
rsp = xmit(req)
|
|
80
|
+
|
|
81
|
+
rsp.type.should == Rstat.type
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe Topen do
|
|
86
|
+
it 'should open the FS root for reading' do
|
|
87
|
+
req = Topen.new(
|
|
88
|
+
:tag => 0,
|
|
89
|
+
:fid => 0,
|
|
90
|
+
:mode => Topen::OREAD
|
|
91
|
+
)
|
|
92
|
+
rsp = xmit(req)
|
|
93
|
+
|
|
94
|
+
rsp.type.should == Ropen.type
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe Tread do
|
|
99
|
+
it 'should return a Stat for every file in FS root' do
|
|
100
|
+
req = Tread.new(
|
|
101
|
+
:tag => 0,
|
|
102
|
+
:fid => 0,
|
|
103
|
+
:offset => 0,
|
|
104
|
+
:count => Tversion::MSIZE
|
|
105
|
+
)
|
|
106
|
+
rsp = xmit(req)
|
|
107
|
+
|
|
108
|
+
rsp.type.should == Rread.type
|
|
109
|
+
|
|
110
|
+
if $DEBUG
|
|
111
|
+
s = StringIO.new(rsp.data, 'r')
|
|
112
|
+
a = []
|
|
113
|
+
until s.eof?
|
|
114
|
+
t = Stat.from_9p(s)
|
|
115
|
+
a << t
|
|
116
|
+
end
|
|
117
|
+
pp a
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe Tclunk do
|
|
123
|
+
it 'should close the fid for FS root' do
|
|
124
|
+
req = Tclunk.new(
|
|
125
|
+
:tag => 0,
|
|
126
|
+
:fid => 0
|
|
127
|
+
)
|
|
128
|
+
rsp = xmit(req)
|
|
129
|
+
|
|
130
|
+
rsp.type.should == Rclunk.type
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe 'A closed fid' do
|
|
135
|
+
it 'should not be readable' do
|
|
136
|
+
req = Tread.new(
|
|
137
|
+
:tag => 0,
|
|
138
|
+
:fid => 0,
|
|
139
|
+
:offset => 0,
|
|
140
|
+
:count => Tversion::MSIZE
|
|
141
|
+
)
|
|
142
|
+
rsp = xmit(req)
|
|
143
|
+
|
|
144
|
+
rsp.type.should == Rerror.type
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# read & write a file
|
|
149
|
+
describe Tattach do
|
|
150
|
+
it 'should attach to /' do
|
|
151
|
+
req = Tattach.new(
|
|
152
|
+
:tag => 0,
|
|
153
|
+
:fid => 0,
|
|
154
|
+
:afid => Fcall::NOFID,
|
|
155
|
+
:uname => ENV['USER'],
|
|
156
|
+
:aname => ''
|
|
157
|
+
)
|
|
158
|
+
rsp = xmit(req)
|
|
159
|
+
|
|
160
|
+
rsp.type.should == Rattach.type
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe Twalk do
|
|
165
|
+
it 'should walk to /rbar/status' do
|
|
166
|
+
req = Twalk.new(
|
|
167
|
+
:tag => 0,
|
|
168
|
+
:fid => 0,
|
|
169
|
+
:newfid => 1,
|
|
170
|
+
:wname => %w[rbar status]
|
|
171
|
+
)
|
|
172
|
+
rsp = xmit(req)
|
|
173
|
+
|
|
174
|
+
rsp.type.should == Rwalk.type
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
describe Tclunk do
|
|
179
|
+
it 'should close the fid for /' do
|
|
180
|
+
req = Tclunk.new(
|
|
181
|
+
:tag => 0,
|
|
182
|
+
:fid => 0
|
|
183
|
+
)
|
|
184
|
+
rsp = xmit(req)
|
|
185
|
+
|
|
186
|
+
rsp.type.should == Rclunk.type
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
describe Topen do
|
|
191
|
+
it 'should open /rbar/status for writing' do
|
|
192
|
+
req = Topen.new(
|
|
193
|
+
:tag => 0,
|
|
194
|
+
:fid => 1,
|
|
195
|
+
:mode => Topen::ORDWR
|
|
196
|
+
)
|
|
197
|
+
rsp = xmit(req)
|
|
198
|
+
|
|
199
|
+
rsp.type.should == Ropen.type
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
describe Twrite do
|
|
204
|
+
it 'should replace the file content' do
|
|
205
|
+
writeReq = Twrite.new(
|
|
206
|
+
:tag => 0,
|
|
207
|
+
:fid => 1,
|
|
208
|
+
:offset => 0,
|
|
209
|
+
:data => "hello world!!!"
|
|
210
|
+
)
|
|
211
|
+
writeRsp = xmit(writeReq)
|
|
212
|
+
|
|
213
|
+
writeRsp.type.should == Rwrite.type
|
|
214
|
+
writeRsp.count.should == writeReq.data.length
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
readReq = Tread.new(
|
|
218
|
+
:tag => 0,
|
|
219
|
+
:fid => 1,
|
|
220
|
+
:offset => 0,
|
|
221
|
+
:count => writeRsp.count
|
|
222
|
+
)
|
|
223
|
+
readRsp = xmit(readReq)
|
|
224
|
+
|
|
225
|
+
readRsp.type.should == Rread.type
|
|
226
|
+
readRsp.data.should == writeReq.data
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
describe Tclunk do
|
|
231
|
+
it 'should close the fid for /rbar/status' do
|
|
232
|
+
req = Tclunk.new(
|
|
233
|
+
:tag => 0,
|
|
234
|
+
:fid => 1
|
|
235
|
+
)
|
|
236
|
+
rsp = xmit(req)
|
|
237
|
+
|
|
238
|
+
rsp.type.should == Rclunk.type
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# at_exit do
|
|
243
|
+
# puts "just making sure there is no more data in the pipe"
|
|
244
|
+
# while c = PIPE.getc
|
|
245
|
+
# puts c
|
|
246
|
+
# end
|
|
247
|
+
# end
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# Transport layer for 9P2000 protocol.
|
|
2
|
+
#--
|
|
3
|
+
# Copyright 2007 Suraj N. Kurapati
|
|
4
|
+
# See the file named LICENSE for details.
|
|
5
|
+
|
|
6
|
+
require 'message'
|
|
7
|
+
require 'thread' # for Mutex
|
|
8
|
+
|
|
9
|
+
module Rumai
|
|
10
|
+
module IXP
|
|
11
|
+
# A thread-safe proxy that multiplexes many
|
|
12
|
+
# threads onto a single 9P2000 connection.
|
|
13
|
+
class Agent
|
|
14
|
+
attr_reader :msize
|
|
15
|
+
|
|
16
|
+
def initialize aStream
|
|
17
|
+
@stream = aStream
|
|
18
|
+
@sendLock = Mutex.new
|
|
19
|
+
@recvLock = Mutex.new
|
|
20
|
+
|
|
21
|
+
@responses = {} # tag => message
|
|
22
|
+
@tagPool = RangedPool.new(0...BYTE2_MASK)
|
|
23
|
+
|
|
24
|
+
@fidPool = RangedPool.new(0...BYTE4_MASK)
|
|
25
|
+
@msize = Tversion::MSIZE
|
|
26
|
+
|
|
27
|
+
# establish connection with 9P2000 server
|
|
28
|
+
req = Tversion.new(
|
|
29
|
+
:tag => Fcall::NOTAG,
|
|
30
|
+
:msize => Tversion::MSIZE,
|
|
31
|
+
:version => Tversion::VERSION
|
|
32
|
+
)
|
|
33
|
+
rsp = talk(req)
|
|
34
|
+
|
|
35
|
+
unless req.version == rsp.version
|
|
36
|
+
raise Error, "protocol mismatch: self=#{req.version.inspect} server=#{rsp.version.inspect}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@msize = rsp.msize
|
|
40
|
+
|
|
41
|
+
# authenticate the connection (not necessary for wmii)
|
|
42
|
+
@authFid = Fcall::NOFID
|
|
43
|
+
|
|
44
|
+
# attach to filesystem root
|
|
45
|
+
@rootFid = @fidPool.obtain
|
|
46
|
+
attach @rootFid, @authFid
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# A finite, thread-safe pool of range members.
|
|
50
|
+
class RangedPool
|
|
51
|
+
# how many new members should be added to the pool when the pool is empty?
|
|
52
|
+
FILL_RATE = 10
|
|
53
|
+
|
|
54
|
+
def initialize aRange
|
|
55
|
+
@pos = aRange.first
|
|
56
|
+
@lim = aRange.last
|
|
57
|
+
@lim = @lim.succ unless aRange.exclude_end?
|
|
58
|
+
|
|
59
|
+
@pool = Array.new
|
|
60
|
+
@lock = Mutex.new
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns an unoccupied range member from the pool.
|
|
64
|
+
def obtain
|
|
65
|
+
until member = @lock.synchronize { @pool.shift }
|
|
66
|
+
# the pool is empty, so fill it
|
|
67
|
+
@lock.synchronize do
|
|
68
|
+
FILL_RATE.times do
|
|
69
|
+
if @pos != @lim
|
|
70
|
+
@pool.push @pos
|
|
71
|
+
@pos = @pos.succ
|
|
72
|
+
else
|
|
73
|
+
break
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end or Thread.pass # range is exhausted, so wait for other threads to fill the pool
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
member
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Marks the given member as being unoccupied so
|
|
83
|
+
# that it may be occupied again in the future.
|
|
84
|
+
def release aMember
|
|
85
|
+
@lock.synchronize do
|
|
86
|
+
@pool.push aMember
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Sends the given message (Rumai::IXP::Fcall) and returns its response.
|
|
92
|
+
#
|
|
93
|
+
# This method allows you to perform a 9P2000 transaction without
|
|
94
|
+
# worrying about the details of tag collisions and thread safety.
|
|
95
|
+
#
|
|
96
|
+
def talk aRequest
|
|
97
|
+
# send the messsage
|
|
98
|
+
ticket = aRequest.tag = @tagPool.obtain
|
|
99
|
+
|
|
100
|
+
@sendLock.synchronize do
|
|
101
|
+
@stream << aRequest.to_9p
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# receive the response
|
|
105
|
+
while true
|
|
106
|
+
# check for *my* response in the bucket
|
|
107
|
+
if response = @recvLock.synchronize { @responses.delete ticket }
|
|
108
|
+
@tagPool.release ticket
|
|
109
|
+
|
|
110
|
+
if response.is_a? Rerror
|
|
111
|
+
raise Error, "#{response.ename.inspect} in response to #{aRequest.inspect}"
|
|
112
|
+
else
|
|
113
|
+
return response
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# put the next response into the bucket
|
|
117
|
+
else
|
|
118
|
+
@recvLock.synchronize do
|
|
119
|
+
response = Fcall.from_9p @stream
|
|
120
|
+
@responses[response.tag] = response
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
MODES = {
|
|
127
|
+
'r' => Topen::OREAD,
|
|
128
|
+
'w' => Topen::OWRITE,
|
|
129
|
+
't' => Topen::ORCLOSE,
|
|
130
|
+
'+' => Topen::ORDWR,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Converts the given mode string into an integer.
|
|
134
|
+
def MODES.parse aMode
|
|
135
|
+
if aMode.respond_to? :split
|
|
136
|
+
aMode.split(//).inject(0) { |m,c| m | self[c].to_i }
|
|
137
|
+
else
|
|
138
|
+
aMode.to_i
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Opens the given path for I/O access through a FidStream
|
|
143
|
+
# object. If a block is given, it is invoked with a
|
|
144
|
+
# FidStream object and the stream is closed afterwards.
|
|
145
|
+
#
|
|
146
|
+
# See File::open in the Ruby documentation.
|
|
147
|
+
def open aPath, aMode = 'r' # :yields: FidStream
|
|
148
|
+
mode = MODES.parse(aMode)
|
|
149
|
+
|
|
150
|
+
# open the file
|
|
151
|
+
pathFid = walk(aPath)
|
|
152
|
+
|
|
153
|
+
talk Topen.new(
|
|
154
|
+
:fid => pathFid,
|
|
155
|
+
:mode => mode
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
stream = FidStream.new(self, pathFid, @msize)
|
|
159
|
+
|
|
160
|
+
# return the file stream
|
|
161
|
+
if block_given?
|
|
162
|
+
begin
|
|
163
|
+
yield stream
|
|
164
|
+
ensure
|
|
165
|
+
stream.close
|
|
166
|
+
end
|
|
167
|
+
else
|
|
168
|
+
stream
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Encapsulates I/O access over a file handle (fid).
|
|
173
|
+
class FidStream
|
|
174
|
+
attr_reader :fid
|
|
175
|
+
|
|
176
|
+
def initialize aAgent, aPathFid, aMessageSize
|
|
177
|
+
@agent = aAgent
|
|
178
|
+
@fid = aPathFid
|
|
179
|
+
@msize = aMessageSize
|
|
180
|
+
@stat = @agent.stat_fid @fid
|
|
181
|
+
@closed = false
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Closes this stream.
|
|
185
|
+
def close
|
|
186
|
+
unless @closed
|
|
187
|
+
@agent.clunk @fid
|
|
188
|
+
@closed = true
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Returns the entire content of this stream. If this
|
|
193
|
+
# stream corresponds to a directory, then an Array of Stat
|
|
194
|
+
# (one for each file in the directory) will be returned.
|
|
195
|
+
def read
|
|
196
|
+
raise 'cannot read from a closed stream' if @closed
|
|
197
|
+
|
|
198
|
+
content = ''
|
|
199
|
+
offset = 0
|
|
200
|
+
|
|
201
|
+
begin
|
|
202
|
+
chunk = read_partial(offset)
|
|
203
|
+
content << chunk
|
|
204
|
+
|
|
205
|
+
count = chunk.length
|
|
206
|
+
offset = (offset + count) % BYTE8_LIMIT
|
|
207
|
+
end until count < @msize
|
|
208
|
+
|
|
209
|
+
# the content of a directory is a sequence
|
|
210
|
+
# of Stat for all files in that directory
|
|
211
|
+
if @stat.directory?
|
|
212
|
+
buffer = StringIO.new(content)
|
|
213
|
+
content = []
|
|
214
|
+
|
|
215
|
+
until buffer.eof?
|
|
216
|
+
content << Stat.from_9p(buffer)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
content
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Returns the maximum amount of content that can fit in
|
|
224
|
+
# one 9P2000 message, starting from the given offset.
|
|
225
|
+
#
|
|
226
|
+
# The end of file is reached when the returned
|
|
227
|
+
# content string is empty (has zero length).
|
|
228
|
+
def read_partial aOffset = 0
|
|
229
|
+
raise 'cannot read from a closed stream' if @closed
|
|
230
|
+
|
|
231
|
+
req = Tread.new(
|
|
232
|
+
:fid => @fid,
|
|
233
|
+
:offset => aOffset,
|
|
234
|
+
:count => @msize
|
|
235
|
+
)
|
|
236
|
+
rsp = @agent.talk(req)
|
|
237
|
+
rsp.data
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Writes the given content to the beginning of this stream.
|
|
241
|
+
def write aContent
|
|
242
|
+
raise 'closed streams cannot be written to' if @closed
|
|
243
|
+
raise 'directories cannot be written to' if @stat.directory?
|
|
244
|
+
|
|
245
|
+
offset = 0
|
|
246
|
+
content = aContent.to_s
|
|
247
|
+
|
|
248
|
+
while offset < content.length
|
|
249
|
+
chunk = content[offset, @msize]
|
|
250
|
+
|
|
251
|
+
req = Twrite.new(
|
|
252
|
+
:fid => @fid,
|
|
253
|
+
:offset => offset,
|
|
254
|
+
:count => chunk.length,
|
|
255
|
+
:data => chunk
|
|
256
|
+
)
|
|
257
|
+
rsp = @agent.talk(req)
|
|
258
|
+
|
|
259
|
+
offset += rsp.count
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
alias << write
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Returns the content of the file/directory at the given path.
|
|
267
|
+
def read aPath
|
|
268
|
+
open aPath do |f|
|
|
269
|
+
f.read
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Returns the names of all files inside the directory whose path is given.
|
|
274
|
+
def entries aPath
|
|
275
|
+
unless stat(aPath).directory?
|
|
276
|
+
raise ArgumentError, "#{aPath.inspect} is not a directory"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
read(aPath).map! {|t| t.name}
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Returns the content of the file/directory at the given path.
|
|
283
|
+
def write aPath, aContent
|
|
284
|
+
open aPath, 'w' do |f|
|
|
285
|
+
f << aContent
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Creates a new file at the given path that is accessible using
|
|
290
|
+
# the given modes for a user having the given permission bits.
|
|
291
|
+
def create aPath, aMode = 'rw', aPerm = 0644
|
|
292
|
+
prefix = File.dirname(aPath)
|
|
293
|
+
target = File.basename(aPath)
|
|
294
|
+
|
|
295
|
+
mode = MODES.parse(aMode)
|
|
296
|
+
|
|
297
|
+
with_fid do |prefixFid|
|
|
298
|
+
walk_fid prefixFid, prefix
|
|
299
|
+
|
|
300
|
+
# create the file
|
|
301
|
+
talk Tcreate.new(
|
|
302
|
+
:fid => prefixFid,
|
|
303
|
+
:name => target,
|
|
304
|
+
:perm => aPerm,
|
|
305
|
+
:mode => mode
|
|
306
|
+
)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Deletes the file at the given path.
|
|
311
|
+
def remove aPath
|
|
312
|
+
pathFid = walk(aPath)
|
|
313
|
+
remove_fid pathFid # remove also does clunk
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Deletes the file corresponding to the
|
|
317
|
+
# given FID and clunks the given FID.
|
|
318
|
+
def remove_fid aPathFid
|
|
319
|
+
talk Tremove.new(:fid => aPathFid)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Returns information about the file at the given path.
|
|
323
|
+
def stat aPath
|
|
324
|
+
with_fid do |pathFid|
|
|
325
|
+
walk_fid pathFid, aPath
|
|
326
|
+
stat_fid pathFid
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Returns information about the file referenced by the given FID.
|
|
331
|
+
def stat_fid aPathFid
|
|
332
|
+
req = Tstat.new(:fid => aPathFid)
|
|
333
|
+
rsp = talk(req)
|
|
334
|
+
rsp.stat
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Returns an FID corresponding to the given path.
|
|
338
|
+
def walk aPath
|
|
339
|
+
fid = @fidPool.obtain
|
|
340
|
+
walk_fid fid, aPath
|
|
341
|
+
fid
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Associates the given FID to the given path.
|
|
345
|
+
def walk_fid aPathFid, aPath
|
|
346
|
+
talk Twalk.new(
|
|
347
|
+
:fid => @rootFid,
|
|
348
|
+
:newfid => aPathFid,
|
|
349
|
+
:wname => aPath.to_s.split(%r{/+}).reject { |s| s.empty? }
|
|
350
|
+
)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Associates the given FID with the FS root.
|
|
354
|
+
def attach aRootFid, aAuthFid = Fcall::NOFID, aAuthName = ENV['USER']
|
|
355
|
+
talk Tattach.new(
|
|
356
|
+
:fid => aRootFid,
|
|
357
|
+
:afid => aAuthFid,
|
|
358
|
+
:uname => ENV['USER'],
|
|
359
|
+
:aname => aAuthName
|
|
360
|
+
)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Retires the given FID from use.
|
|
364
|
+
def clunk aFid
|
|
365
|
+
talk Tclunk.new(:fid => aFid)
|
|
366
|
+
@fidPool.release aFid
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
private
|
|
370
|
+
|
|
371
|
+
# Invokes the given block with a temporary FID.
|
|
372
|
+
def with_fid # :yields: fid
|
|
373
|
+
begin
|
|
374
|
+
fid = @fidPool.obtain
|
|
375
|
+
yield fid
|
|
376
|
+
ensure
|
|
377
|
+
clunk fid
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|