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.
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