rumai 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (230) hide show
  1. data/LICENSE +23 -0
  2. data/README +1 -0
  3. data/Rakefile +58 -0
  4. data/bin/rumai +38 -0
  5. data/doc/api/classes/IO.html +120 -0
  6. data/doc/api/classes/Integer.html +142 -0
  7. data/doc/api/classes/Integer.src/M000002.html +18 -0
  8. data/doc/api/classes/Object.html +113 -0
  9. data/doc/api/classes/Rumai.html +530 -0
  10. data/doc/api/classes/Rumai.src/M000007.html +18 -0
  11. data/doc/api/classes/Rumai.src/M000008.html +18 -0
  12. data/doc/api/classes/Rumai.src/M000009.html +18 -0
  13. data/doc/api/classes/Rumai.src/M000010.html +18 -0
  14. data/doc/api/classes/Rumai.src/M000011.html +18 -0
  15. data/doc/api/classes/Rumai.src/M000012.html +18 -0
  16. data/doc/api/classes/Rumai.src/M000013.html +18 -0
  17. data/doc/api/classes/Rumai.src/M000014.html +20 -0
  18. data/doc/api/classes/Rumai.src/M000015.html +18 -0
  19. data/doc/api/classes/Rumai.src/M000016.html +18 -0
  20. data/doc/api/classes/Rumai.src/M000017.html +18 -0
  21. data/doc/api/classes/Rumai.src/M000018.html +18 -0
  22. data/doc/api/classes/Rumai.src/M000019.html +18 -0
  23. data/doc/api/classes/Rumai.src/M000020.html +18 -0
  24. data/doc/api/classes/Rumai.src/M000021.html +18 -0
  25. data/doc/api/classes/Rumai.src/M000022.html +18 -0
  26. data/doc/api/classes/Rumai.src/M000023.html +18 -0
  27. data/doc/api/classes/Rumai.src/M000024.html +18 -0
  28. data/doc/api/classes/Rumai.src/M000025.html +18 -0
  29. data/doc/api/classes/Rumai.src/M000026.html +18 -0
  30. data/doc/api/classes/Rumai/Area.html +461 -0
  31. data/doc/api/classes/Rumai/Area.src/M000080.html +19 -0
  32. data/doc/api/classes/Rumai/Area.src/M000081.html +18 -0
  33. data/doc/api/classes/Rumai/Area.src/M000082.html +18 -0
  34. data/doc/api/classes/Rumai/Area.src/M000083.html +18 -0
  35. data/doc/api/classes/Rumai/Area.src/M000084.html +18 -0
  36. data/doc/api/classes/Rumai/Area.src/M000085.html +18 -0
  37. data/doc/api/classes/Rumai/Area.src/M000086.html +18 -0
  38. data/doc/api/classes/Rumai/Area.src/M000087.html +18 -0
  39. data/doc/api/classes/Rumai/Area.src/M000088.html +18 -0
  40. data/doc/api/classes/Rumai/Area.src/M000089.html +18 -0
  41. data/doc/api/classes/Rumai/Area.src/M000090.html +18 -0
  42. data/doc/api/classes/Rumai/Area.src/M000091.html +22 -0
  43. data/doc/api/classes/Rumai/Area.src/M000093.html +23 -0
  44. data/doc/api/classes/Rumai/Area.src/M000094.html +28 -0
  45. data/doc/api/classes/Rumai/Area.src/M000095.html +18 -0
  46. data/doc/api/classes/Rumai/Area.src/M000096.html +31 -0
  47. data/doc/api/classes/Rumai/Chain.html +179 -0
  48. data/doc/api/classes/Rumai/Chain.src/M000077.html +18 -0
  49. data/doc/api/classes/Rumai/Chain.src/M000078.html +18 -0
  50. data/doc/api/classes/Rumai/Chain.src/M000079.html +18 -0
  51. data/doc/api/classes/Rumai/Client.html +463 -0
  52. data/doc/api/classes/Rumai/Client.src/M000114.html +18 -0
  53. data/doc/api/classes/Rumai/Client.src/M000115.html +18 -0
  54. data/doc/api/classes/Rumai/Client.src/M000116.html +18 -0
  55. data/doc/api/classes/Rumai/Client.src/M000117.html +39 -0
  56. data/doc/api/classes/Rumai/Client.src/M000118.html +28 -0
  57. data/doc/api/classes/Rumai/Client.src/M000119.html +19 -0
  58. data/doc/api/classes/Rumai/Client.src/M000120.html +18 -0
  59. data/doc/api/classes/Rumai/Client.src/M000121.html +18 -0
  60. data/doc/api/classes/Rumai/Client.src/M000122.html +18 -0
  61. data/doc/api/classes/Rumai/Client.src/M000123.html +19 -0
  62. data/doc/api/classes/Rumai/Client.src/M000124.html +20 -0
  63. data/doc/api/classes/Rumai/Client.src/M000125.html +20 -0
  64. data/doc/api/classes/Rumai/Client.src/M000126.html +22 -0
  65. data/doc/api/classes/Rumai/Client.src/M000127.html +18 -0
  66. data/doc/api/classes/Rumai/Client.src/M000128.html +20 -0
  67. data/doc/api/classes/Rumai/Client.src/M000129.html +18 -0
  68. data/doc/api/classes/Rumai/Client.src/M000130.html +22 -0
  69. data/doc/api/classes/Rumai/ClientContainer.html +180 -0
  70. data/doc/api/classes/Rumai/ClientContainer.src/M000027.html +18 -0
  71. data/doc/api/classes/Rumai/ClientContainer.src/M000028.html +18 -0
  72. data/doc/api/classes/Rumai/ClientContainer.src/M000029.html +18 -0
  73. data/doc/api/classes/Rumai/ExportInstMethods.html +119 -0
  74. data/doc/api/classes/Rumai/IXP.html +149 -0
  75. data/doc/api/classes/Rumai/IXP/Agent.html +447 -0
  76. data/doc/api/classes/Rumai/IXP/Agent.src/M000046.html +47 -0
  77. data/doc/api/classes/Rumai/IXP/Agent.src/M000047.html +44 -0
  78. data/doc/api/classes/Rumai/IXP/Agent.src/M000048.html +39 -0
  79. data/doc/api/classes/Rumai/IXP/Agent.src/M000049.html +20 -0
  80. data/doc/api/classes/Rumai/IXP/Agent.src/M000050.html +22 -0
  81. data/doc/api/classes/Rumai/IXP/Agent.src/M000051.html +20 -0
  82. data/doc/api/classes/Rumai/IXP/Agent.src/M000052.html +33 -0
  83. data/doc/api/classes/Rumai/IXP/Agent.src/M000053.html +19 -0
  84. data/doc/api/classes/Rumai/IXP/Agent.src/M000054.html +18 -0
  85. data/doc/api/classes/Rumai/IXP/Agent.src/M000055.html +21 -0
  86. data/doc/api/classes/Rumai/IXP/Agent.src/M000056.html +20 -0
  87. data/doc/api/classes/Rumai/IXP/Agent.src/M000057.html +20 -0
  88. data/doc/api/classes/Rumai/IXP/Agent.src/M000058.html +22 -0
  89. data/doc/api/classes/Rumai/IXP/Agent.src/M000059.html +23 -0
  90. data/doc/api/classes/Rumai/IXP/Agent.src/M000060.html +19 -0
  91. data/doc/api/classes/Rumai/IXP/Agent/FidStream.html +254 -0
  92. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000062.html +22 -0
  93. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000063.html +21 -0
  94. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000064.html +42 -0
  95. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000065.html +26 -0
  96. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000066.html +36 -0
  97. data/doc/api/classes/Rumai/IXP/Agent/MODES.html +130 -0
  98. data/doc/api/classes/Rumai/IXP/Agent/MODES.src/M000061.html +22 -0
  99. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.html +203 -0
  100. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000068.html +23 -0
  101. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000069.html +32 -0
  102. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000070.html +20 -0
  103. data/doc/api/classes/Rumai/IXP/Error.html +117 -0
  104. data/doc/api/classes/Rumai/IXP/Fcall.html +261 -0
  105. data/doc/api/classes/Rumai/IXP/Fcall.src/M000071.html +20 -0
  106. data/doc/api/classes/Rumai/IXP/Fcall.src/M000072.html +25 -0
  107. data/doc/api/classes/Rumai/IXP/Fcall.src/M000073.html +18 -0
  108. data/doc/api/classes/Rumai/IXP/Fcall.src/M000074.html +18 -0
  109. data/doc/api/classes/Rumai/IXP/Qid.html +184 -0
  110. data/doc/api/classes/Rumai/IXP/Rattach.html +119 -0
  111. data/doc/api/classes/Rumai/IXP/Rauth.html +119 -0
  112. data/doc/api/classes/Rumai/IXP/Rclunk.html +119 -0
  113. data/doc/api/classes/Rumai/IXP/Rcreate.html +119 -0
  114. data/doc/api/classes/Rumai/IXP/Rerror.html +119 -0
  115. data/doc/api/classes/Rumai/IXP/Rflush.html +119 -0
  116. data/doc/api/classes/Rumai/IXP/Ropen.html +119 -0
  117. data/doc/api/classes/Rumai/IXP/Rread.html +119 -0
  118. data/doc/api/classes/Rumai/IXP/Rremove.html +119 -0
  119. data/doc/api/classes/Rumai/IXP/Rstat.html +119 -0
  120. data/doc/api/classes/Rumai/IXP/Rversion.html +119 -0
  121. data/doc/api/classes/Rumai/IXP/Rwalk.html +119 -0
  122. data/doc/api/classes/Rumai/IXP/Rwrite.html +119 -0
  123. data/doc/api/classes/Rumai/IXP/Rwstat.html +119 -0
  124. data/doc/api/classes/Rumai/IXP/Stat.html +248 -0
  125. data/doc/api/classes/Rumai/IXP/Stat.src/M000076.html +18 -0
  126. data/doc/api/classes/Rumai/IXP/Stream.html +158 -0
  127. data/doc/api/classes/Rumai/IXP/Stream.src/M000045.html +19 -0
  128. data/doc/api/classes/Rumai/IXP/Struct.html +257 -0
  129. data/doc/api/classes/Rumai/IXP/Struct.src/M000030.html +19 -0
  130. data/doc/api/classes/Rumai/IXP/Struct.src/M000031.html +18 -0
  131. data/doc/api/classes/Rumai/IXP/Struct.src/M000032.html +18 -0
  132. data/doc/api/classes/Rumai/IXP/Struct.src/M000033.html +18 -0
  133. data/doc/api/classes/Rumai/IXP/Struct.src/M000034.html +20 -0
  134. data/doc/api/classes/Rumai/IXP/Struct.src/M000035.html +57 -0
  135. data/doc/api/classes/Rumai/IXP/Struct/Field.html +304 -0
  136. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000036.html +21 -0
  137. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000037.html +21 -0
  138. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000038.html +20 -0
  139. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000039.html +29 -0
  140. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000040.html +18 -0
  141. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000041.html +18 -0
  142. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.html +152 -0
  143. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000043.html +24 -0
  144. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000044.html +25 -0
  145. data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.html +137 -0
  146. data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.src/M000042.html +18 -0
  147. data/doc/api/classes/Rumai/IXP/Tattach.html +120 -0
  148. data/doc/api/classes/Rumai/IXP/Tauth.html +119 -0
  149. data/doc/api/classes/Rumai/IXP/Tclunk.html +119 -0
  150. data/doc/api/classes/Rumai/IXP/Tcreate.html +120 -0
  151. data/doc/api/classes/Rumai/IXP/Terror.html +145 -0
  152. data/doc/api/classes/Rumai/IXP/Terror.src/M000075.html +18 -0
  153. data/doc/api/classes/Rumai/IXP/Tflush.html +119 -0
  154. data/doc/api/classes/Rumai/IXP/Topen.html +193 -0
  155. data/doc/api/classes/Rumai/IXP/Tread.html +119 -0
  156. data/doc/api/classes/Rumai/IXP/Tremove.html +119 -0
  157. data/doc/api/classes/Rumai/IXP/Tstat.html +119 -0
  158. data/doc/api/classes/Rumai/IXP/Tversion.html +137 -0
  159. data/doc/api/classes/Rumai/IXP/Twalk.html +120 -0
  160. data/doc/api/classes/Rumai/IXP/Twrite.html +120 -0
  161. data/doc/api/classes/Rumai/IXP/Twstat.html +119 -0
  162. data/doc/api/classes/Rumai/Node.html +461 -0
  163. data/doc/api/classes/Rumai/Node.src/M000097.html +18 -0
  164. data/doc/api/classes/Rumai/Node.src/M000098.html +18 -0
  165. data/doc/api/classes/Rumai/Node.src/M000099.html +22 -0
  166. data/doc/api/classes/Rumai/Node.src/M000100.html +18 -0
  167. data/doc/api/classes/Rumai/Node.src/M000101.html +18 -0
  168. data/doc/api/classes/Rumai/Node.src/M000102.html +18 -0
  169. data/doc/api/classes/Rumai/Node.src/M000103.html +18 -0
  170. data/doc/api/classes/Rumai/Node.src/M000104.html +22 -0
  171. data/doc/api/classes/Rumai/Node.src/M000105.html +18 -0
  172. data/doc/api/classes/Rumai/Node.src/M000106.html +18 -0
  173. data/doc/api/classes/Rumai/Node.src/M000107.html +18 -0
  174. data/doc/api/classes/Rumai/Node.src/M000108.html +18 -0
  175. data/doc/api/classes/Rumai/Node.src/M000109.html +18 -0
  176. data/doc/api/classes/Rumai/Node.src/M000110.html +18 -0
  177. data/doc/api/classes/Rumai/Node.src/M000111.html +18 -0
  178. data/doc/api/classes/Rumai/Node.src/M000112.html +20 -0
  179. data/doc/api/classes/Rumai/Node.src/M000113.html +27 -0
  180. data/doc/api/classes/Rumai/View.html +436 -0
  181. data/doc/api/classes/Rumai/View.src/M000131.html +18 -0
  182. data/doc/api/classes/Rumai/View.src/M000132.html +18 -0
  183. data/doc/api/classes/Rumai/View.src/M000133.html +18 -0
  184. data/doc/api/classes/Rumai/View.src/M000134.html +18 -0
  185. data/doc/api/classes/Rumai/View.src/M000135.html +18 -0
  186. data/doc/api/classes/Rumai/View.src/M000136.html +18 -0
  187. data/doc/api/classes/Rumai/View.src/M000137.html +18 -0
  188. data/doc/api/classes/Rumai/View.src/M000138.html +28 -0
  189. data/doc/api/classes/Rumai/View.src/M000139.html +18 -0
  190. data/doc/api/classes/Rumai/View.src/M000140.html +18 -0
  191. data/doc/api/classes/Rumai/View.src/M000141.html +18 -0
  192. data/doc/api/classes/Rumai/View.src/M000142.html +18 -0
  193. data/doc/api/classes/Rumai/View.src/M000143.html +29 -0
  194. data/doc/api/classes/Rumai/View.src/M000144.html +20 -0
  195. data/doc/api/classes/Rumai/View.src/M000145.html +33 -0
  196. data/doc/api/classes/Rumai/View.src/M000146.html +50 -0
  197. data/doc/api/classes/String.html +169 -0
  198. data/doc/api/classes/String.src/M000005.html +18 -0
  199. data/doc/api/classes/String.src/M000006.html +19 -0
  200. data/doc/api/classes/StringIO.html +120 -0
  201. data/doc/api/classes/Time.html +163 -0
  202. data/doc/api/classes/Time.src/M000003.html +18 -0
  203. data/doc/api/classes/Time.src/M000004.html +18 -0
  204. data/doc/api/created.rid +1 -0
  205. data/doc/api/files/lib/rumai/fs_rb.html +115 -0
  206. data/doc/api/files/lib/rumai/ixp/message_rb.html +120 -0
  207. data/doc/api/files/lib/rumai/ixp/message_spec_rb.html +175 -0
  208. data/doc/api/files/lib/rumai/ixp/message_spec_rb.src/M000001.html +37 -0
  209. data/doc/api/files/lib/rumai/ixp/transport_rb.html +115 -0
  210. data/doc/api/files/lib/rumai/ixp_rb.html +116 -0
  211. data/doc/api/files/lib/rumai/nfo_rb.html +107 -0
  212. data/doc/api/files/lib/rumai/wm_rb.html +115 -0
  213. data/doc/api/files/lib/rumai_rb.html +115 -0
  214. data/doc/api/fr_class_index.html +82 -0
  215. data/doc/api/fr_file_index.html +34 -0
  216. data/doc/api/fr_method_index.html +172 -0
  217. data/doc/api/index.html +24 -0
  218. data/doc/api/rdoc-style.css +208 -0
  219. data/doc/guide.erb +390 -0
  220. data/doc/guide.html +2035 -0
  221. data/doc/index.html +2035 -0
  222. data/lib/rumai.rb +9 -0
  223. data/lib/rumai/fs.rb +170 -0
  224. data/lib/rumai/ixp.rb +9 -0
  225. data/lib/rumai/ixp/message.rb +642 -0
  226. data/lib/rumai/ixp/message_spec.rb +247 -0
  227. data/lib/rumai/ixp/transport.rb +382 -0
  228. data/lib/rumai/nfo.rb +24 -0
  229. data/lib/rumai/wm.rb +780 -0
  230. 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