suzuna 0.0.1-freebsd

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +35 -0
  3. data/README.md +140 -0
  4. data/Rakefile +106 -0
  5. data/examples/gate.rb +47 -0
  6. data/gemstub.rb +23 -0
  7. data/lib/suzuna.rb +423 -0
  8. metadata +98 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fd5ab88833f46fc0c494ddfa17de68c30a41675a
4
+ data.tar.gz: c0f42f43c647683708e60c98511ff5ba3188b803
5
+ SHA512:
6
+ metadata.gz: 134e68effecf0a1f35173b638abaae7307bf969fa81ab5107f1cea8528509eb6fff3cea8f1c6090b7ed1d5ee7b9249d9f4cbfa5b47a3bdbb60645e20d457d1d9
7
+ data.tar.gz: 8e28be08ec031f7079985be4cd6d2cb1a976906efa2d5848f2d45fba61a5632c07f3c635783740a6fb5d6ffda8d0afb4e59aef061d166923bd829d4db18cde86
@@ -0,0 +1,35 @@
1
+ # suzuna License (2-clause BSD License)
2
+
3
+ ----
4
+
5
+ Copyright (c) 2014, dearblue
6
+ All rights reserved.
7
+
8
+ Redistribution and use in source and binary forms, with or without
9
+ modification, are permitted provided that the following conditions are met:
10
+
11
+ 1. Redistributions of source code must retain the above copyright notice,
12
+ this list of conditions and the following disclaimer.
13
+
14
+ 2. Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ ----
31
+
32
+ In Japanese.
33
+
34
+ 日本語訳については次のページが参考になるでしょう:
35
+ http://www.freebsd.org/ja/copyright/freebsd-license.html
@@ -0,0 +1,140 @@
1
+ # suzuna (スズナ)
2
+
3
+ The suzuna is a ruby library that provides an infrastructure for virtualized block devices.
4
+
5
+ "suzuna" is means "Turnip" in japanese.
6
+
7
+ ----
8
+
9
+ (in Japanese)
10
+
11
+ suzuna は ruby 拡張ライブラリで、仮想ブロックデバイス基盤を提供します。
12
+
13
+ 名称は春の七種の一つである『スズナ』から取りました。
14
+
15
+ ----
16
+
17
+ * product name: suzuna (スズナ / 菘 / カブ / Turnip)
18
+ * author: dearblue &lt;<dearblue@users.sourceforge.jp>&gt;
19
+ * license: 2-clause BSD License (二条項 BSD ライセンス)
20
+ * software quarity: PROTOTYPE
21
+ * users: rubyist
22
+ * release number: 0.0.1
23
+ * memory usage: 1 MB +
24
+ * installed size: under 1 MB
25
+ * project page: &lt;http://sourceforge.jp/projects/rutsubo/&gt;
26
+ * support ruby: ruby-2.0+ &lt;http://www.ruby-lang.org/&gt;
27
+ * support platforms:
28
+ * FreeBSD (GEOM Gate / ggate) (ggatel only)
29
+ * dependency libraries:
30
+ * ruby - gogyou-0.2+ &lt;https://rubygems.org/gems/gogyou&gt;
31
+
32
+
33
+ ## LIBRARY
34
+
35
+ ``require "suzuna"``
36
+
37
+
38
+ ## SYNOPSIS
39
+
40
+ ``module Suzuna``
41
+
42
+ ``module Suzuna::Template``
43
+
44
+ ``Suzuna.join(unit_object)``
45
+
46
+
47
+ ## DESCRIPTION
48
+
49
+ suzuna は ruby で GEOM Gate を用いて GEOM クラスを書くためのライブラリです。
50
+
51
+ ``module Suzuna`` は、suzuna の名前空間として使われるモジュールです。
52
+
53
+ ``module Suzuna::Template`` は、I/O 要求処理を担当するクラスに ``include`` することを想定したモジュールです (必ず必要なモジュールではありません)。このモジュールには、I/O 要求処理に必要となるインスタンスメソッドが予め定義してあります。ただし ``raise NotImplementedError`` を発生させるだけのメソッドがあるので、このようなメソッドは利用者側で実装する必要があります。このあたりは [EXAMPLE](#label-EXAMPLE) が参考になると思います。
54
+
55
+ ``Suzuna.join(unit_object)`` は、I/O 要求処理オブジェクトを与えて実際に GEOM Gate オブジェクトを作成するためのメソッドです。***このメソッドはスレッドを停止させます。現在の実装では、このメソッドから返ってくる場面は例外が発生した時のみとなります。***
56
+
57
+
58
+ ## EXAMPLE
59
+
60
+ 以下は FreeBSD 上でローカルな GEOM Gate (ggate) を用いた、ruby による仮想ブロックデバイスプログラミングです。
61
+
62
+ ``` ruby:ruby
63
+ #!ruby
64
+
65
+ require "stringio"
66
+ require "fattr"
67
+ require "suzuna"
68
+
69
+ class MyUnit
70
+ include Suzuna::Template
71
+
72
+ fattr(:mediasize) { @pool.size }
73
+ fattr sectorsize: 512,
74
+ timeout: 30,
75
+ flags: 0 # or Suzuna::G_GATE_FLAG_READONLY
76
+
77
+ def initialize
78
+ @pool = File.open("testsuzuna.img", File::BINARY | File::RDWR | File::CREAT)
79
+ @pool.truncate(80.MiB) unless @pool.size >= 80.MiB
80
+ end
81
+
82
+ def cleanup
83
+ @pool.flush rescue nil
84
+ @pool.close rescue nil
85
+ end
86
+
87
+ def read(offset, size, buf)
88
+ puts "#{File.basename caller(0, 1)[0]}: offset=#{offset}, size=#{size}"
89
+ @pool.pos = offset
90
+ @pool.read(size, buf)
91
+ buf.resize(size)
92
+ nil
93
+ end
94
+
95
+ def write(offset, buf)
96
+ puts "#{File.basename caller(0, 1)[0]}: offset=#{offset}, size=#{buf.bytesize}"
97
+ @pool.pos = offset
98
+ @pool.write(buf)
99
+ nil
100
+ end
101
+
102
+ def delete(offset, size)
103
+ puts "#{File.basename caller(0, 1)[0]}: offset=#{offset}, size=#{size}"
104
+ nil
105
+ end
106
+ end
107
+
108
+ Suzuna.join(MyUnit.new)
109
+ ```
110
+
111
+ これを管理者権限で実行すると `/dev/ggate0` が作成されて、`/dev/ada0` や `/dev/da0` などと同様の扱いをすることが出来ます。
112
+
113
+ 停止させる場合は、`Ctrl+C` をするか、他の(擬似)端末から `$ sudo ggate -du0` と入力します。
114
+
115
+ 環境によっては ``/usr/local/lib/ruby/gems/2.1/gems/suzuna-0.0.1-freebsd/lib/suzuna.rb:in `initialize': No such file or directory @ rb_sysopen - /dev/ggctl (Errno::ENOENT)`` などと例外が出るかもしれません。この場合はカーネルに `geom_gate.ko` を読み込ませてから試して下さい。
116
+
117
+ ``` shell:shell
118
+ $ sudo kldload geom_gate
119
+ ```
120
+
121
+
122
+ ## DEMERIT
123
+
124
+ * geom gate + ruby のため、実行性能はカーネルに置かれた geom オブジェクトよりもかなり劣ります。
125
+
126
+ 個人・研究用途としては十分かもしれませんが、大規模な用途には向かないでしょう。
127
+
128
+ * geom gate ネットワークデーモンとしての機能は持っていません。
129
+
130
+ ``ggated`` を組み合わせて利用できるかもしれません (試していません)。
131
+
132
+ * 現在の実装は、マルチスレッド化されていません。
133
+
134
+ I/O 要求の処理は単体のスレッドのみで行われます。
135
+
136
+ ## SEE ALSO
137
+
138
+ * [geom(4)](http://www.freebsd.org/cgi/man.cgi?sektion=4&query=geom),
139
+ [geom(8)](http://www.freebsd.org/cgi/man.cgi?sektion=8&query=geom),
140
+ [ggatel(8)](http://www.freebsd.org/cgi/man.cgi?sektion=8&query=ggatel)
@@ -0,0 +1,106 @@
1
+
2
+ require "rake/clean"
3
+
4
+ DOC = FileList["{README,LICENSE,CHANGELOG,Changelog}{,.ja}{,.txt,.rd,.rdoc,.md,.markdown}"] +
5
+ FileList["ext/**/{README,LICENSE,CHANGELOG,Changelog}{,.ja}{,.txt,.rd,.rdoc,.md,.markdown}"]
6
+ #EXT = FileList["ext/**/*.{h,hh,c,cc,cpp,cxx}"] +
7
+ # FileList["ext/externals/**/*"]
8
+ EXT = FileList["ext/**/*"]
9
+ BIN = FileList["bin/*"]
10
+ LIB = FileList["lib/**/*.rb"]
11
+ SPEC = FileList["spec/**/*"]
12
+ EXAMPLE = FileList["examples/**/*"]
13
+ RAKEFILE = [File.basename(__FILE__), "gemstub.rb"]
14
+ EXTRA = []
15
+
16
+ load "gemstub.rb"
17
+
18
+ EXTCONF = FileList["ext/extconf.rb"]
19
+ EXTCONF.reject! { |n| !File.file?(n) }
20
+ GEMSTUB.extensions += EXTCONF
21
+ GEMSTUB.executables += FileList["bin/*"].map { |n| File.basename n }
22
+
23
+ GEMFILE = "#{GEMSTUB.name}-#{GEMSTUB.version}.gem"
24
+ GEMSPEC = "#{GEMSTUB.name}.gemspec"
25
+
26
+ GEMSTUB.files += DOC + EXT + EXTCONF + BIN + LIB + SPEC + EXAMPLE + RAKEFILE + EXTRA
27
+ GEMSTUB.rdoc_options ||= %w(--charset UTF-8)
28
+ GEMSTUB.extra_rdoc_files += DOC + LIB + EXT.reject { |n| n.include?("/externals/") || !%w(.h .hh .c .cc .cpp .cxx).include?(File.extname(n)) }
29
+
30
+ CLEAN << GEMSPEC
31
+ CLOBBER << GEMFILE
32
+
33
+ task :default => :all
34
+
35
+ task :all => GEMFILE
36
+
37
+ task :rdoc => DOC + EXT + LIB do
38
+ sh *(%w(rdoc) + GEMSTUB.rdoc_options + DOC + EXT + LIB)
39
+ end
40
+
41
+ file GEMFILE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + EXAMPLE + RAKEFILE + [GEMSPEC] do
42
+ sh "gem build #{GEMSPEC}"
43
+ end
44
+
45
+ file GEMSPEC => RAKEFILE do
46
+ File.write(GEMSPEC, GEMSTUB.to_ruby, mode: "wb")
47
+ end
48
+
49
+
50
+ RUBYSET ||= nil
51
+
52
+ if RUBYSET && !RUBYSET.empty? && !EXTCONF.empty?
53
+ RUBY_VERSIONS = RUBYSET.map do |ruby|
54
+ ver = `#{ruby} --disable gem -rrbconfig -e "puts RbConfig::CONFIG['ruby_version']"`.chomp
55
+ raise "failed ruby checking - ``#{ruby}''" unless $?.success?
56
+ [ver, ruby]
57
+ end
58
+ SOFILES_SET = RUBY_VERSIONS.map { |(ver, ruby)| ["lib/#{ver}/#{GEMSTUB.name}.so", ruby] }
59
+ SOFILES = SOFILES_SET.map { |(lib, ruby)| lib }
60
+ platforms = RUBYSET.map { |ruby| `#{ruby} -rubygems -e "puts Gem::Platform.local.to_s"`.chomp }
61
+ platforms.uniq!
62
+ platforms.compact!
63
+ unless platforms.size == 1
64
+ raise "wrong platforms (#{RUBYSET.inspect} => #{platforms.inspect})"
65
+ end
66
+
67
+ GEMSTUB_NATIVE = GEMSTUB.dup
68
+ GEMSTUB_NATIVE.files += SOFILES
69
+ GEMSTUB_NATIVE.platform = platforms[0]
70
+ GEMFILE_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.version}-#{GEMSTUB_NATIVE.platform}.gem"
71
+ GEMSPEC_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.platform}.gemspec"
72
+
73
+ task :all => [GEMFILE, :native]
74
+
75
+ task :native => GEMFILE_NATIVE
76
+
77
+ file GEMFILE_NATIVE => DOC + EXT + [EXTCONF] + BIN + LIB + SPEC + EXAMPLE + SOFILES + RAKEFILE + [GEMSPEC_NATIVE] do
78
+ sh "gem build #{GEMSPEC_NATIVE}"
79
+ end
80
+
81
+ file GEMSPEC_NATIVE => __FILE__ do
82
+ File.write(GEMSPEC_NATIVE, GEMSTUB_NATIVE.to_ruby, mode: "wb")
83
+ end
84
+
85
+ SOFILES_SET.each do |(soname, ruby)|
86
+ sodir = File.dirname(soname)
87
+ makefile = File.join(sodir, "Makefile")
88
+
89
+ CLEAN << GEMSPEC_NATIVE << sodir
90
+ CLOBBER << GEMFILE_NATIVE
91
+
92
+ directory sodir
93
+
94
+ file soname => [makefile] + EXT do
95
+ cd sodir do
96
+ sh "make"
97
+ end
98
+ end
99
+
100
+ file makefile => [sodir] + [EXTCONF] do
101
+ cd sodir do
102
+ sh "#{ruby} ../../#{EXTCONF} \"--ruby=#{ruby}\""
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,47 @@
1
+ #!ruby
2
+
3
+ require "stringio"
4
+ require "fattr"
5
+ require "suzuna"
6
+
7
+ class MyUnit
8
+ include Suzuna::Template
9
+
10
+ fattr(:mediasize) { @pool.size }
11
+ fattr sectorsize: 512,
12
+ timeout: 30,
13
+ flags: 0 # or Suzuna::G_GATE_FLAG_READONLY
14
+
15
+ def initialize
16
+ #@pool = StringIO.new(String.alloc(80.MiB))
17
+ @pool = File.open("testsuzuna.img", File::BINARY | File::RDWR | File::CREAT)
18
+ @pool.truncate(80.MiB) unless @pool.size >= 80.MiB
19
+ end
20
+
21
+ def cleanup
22
+ @pool.flush rescue nil
23
+ @pool.close rescue nil
24
+ end
25
+
26
+ def read(offset, size, buf)
27
+ puts "#{File.basename caller(0, 1)[0]}: offset=#{offset}, size=#{size}"
28
+ @pool.pos = offset
29
+ @pool.read(size, buf)
30
+ buf.resize(size)
31
+ nil
32
+ end
33
+
34
+ def write(offset, buf)
35
+ puts "#{File.basename caller(0, 1)[0]}: offset=#{offset}, size=#{buf.bytesize}"
36
+ @pool.pos = offset
37
+ @pool.write(buf)
38
+ nil
39
+ end
40
+
41
+ def delete(offset, size)
42
+ puts "#{File.basename caller(0, 1)[0]}: offset=#{offset}, size=#{size}"
43
+ nil
44
+ end
45
+ end
46
+
47
+ Suzuna.join(MyUnit.new)
@@ -0,0 +1,23 @@
1
+
2
+ require_relative "lib/suzuna"
3
+
4
+ GEMSTUB = Gem::Specification.new do |s|
5
+ s.name = "suzuna"
6
+ s.version = Suzuna::VERSION
7
+ s.platform = "freebsd"
8
+ s.summary = "Soft volume infrastructure for ruby"
9
+ s.description = <<EOS
10
+ ``suzuna'' is software volume infrastructure for ruby.
11
+
12
+ Support platform is FreeBSD GEOM only.
13
+ EOS
14
+ s.license = "2-clause BSD License"
15
+ s.author = "dearblue"
16
+ s.email = "dearblue@users.sourceforge.jp"
17
+ s.homepage = "http://sourceforge.jp/projects/rutsubo/"
18
+
19
+ s.required_ruby_version = ">= 2.0"
20
+ s.add_development_dependency "rspec", "~> 2.14"
21
+ s.add_development_dependency "rake", "~> 10.0"
22
+ s.add_runtime_dependency "gogyou", "~> 0.2"
23
+ end
@@ -0,0 +1,423 @@
1
+ require "gogyou"
2
+
3
+ module Suzuna
4
+ Suzuna = self
5
+ VERSION = Gem::Version.new "0.0.1"
6
+
7
+ G_GATE_CTL_NAME = "/dev/ggctl"
8
+ G_GATE_TIMEOUT = 0
9
+ G_GATE_UNIT_AUTO = -1
10
+ G_GATE_PROVIDER_NAME = "ggate"
11
+
12
+ G_GATE_FLAG_READWRITE = 0x0000
13
+ G_GATE_FLAG_READONLY = 0x0001
14
+ G_GATE_FLAG_WRITEONLY = 0x0002
15
+ G_GATE_FLAG_DESTROY = 0x1000
16
+ G_GATE_USERFLAGS = G_GATE_FLAG_READONLY | G_GATE_FLAG_WRITEONLY
17
+
18
+ G_GATE_VERSION = 3
19
+
20
+ BIO_READ = 0x01
21
+ BIO_WRITE = 0x02
22
+ BIO_DELETE = 0x04
23
+
24
+ #
25
+ # suzuna 固有の例外が include しているモジュールです。
26
+ #
27
+ # rescue で一括して受け取るために用意されています。
28
+ #
29
+ module Exceptions
30
+ end
31
+
32
+ class DestroyedGate < Errno::ENXIO
33
+ include Exceptions
34
+ end
35
+
36
+ #
37
+ # I/O 要求処理の基本機能のみを実装したモジュールです。
38
+ #
39
+ # クラスやモジュールに include することを想定しています。
40
+ #
41
+ # 必要なインスタンスメソッドは include したクラスで再定義して下さい。
42
+ #
43
+ module Template
44
+ #
45
+ # このメソッドはデバイスの大きさを取得するために呼ばれます。
46
+ #
47
+ # /dev 以下に geom gate オブジェクトとして作成される前に呼ばれます。
48
+ #
49
+ # 戻り値として 0以上で sectorsize の整数倍となる整数を返して下さい。
50
+ #
51
+ def mediasize
52
+ raise NotImplementedError, "IMPLEMENT ME! #mediasize -> integer"
53
+ end
54
+
55
+ #
56
+ # このメソッドはデバイスの読み書き特性を取得するために呼ばれます。
57
+ #
58
+ # /dev 以下に geom gate オブジェクトとして作成される前に呼ばれます。
59
+ #
60
+ # 戻り値として G_GATE_FLAG_READWRITE、G_GATE_FLAG_READONLY、G_GATE_FLAG_WRITEONLY のいずれかを返して下さい。
61
+ #
62
+ def flags
63
+ G_GATE_FLAG_READONLY
64
+ end
65
+
66
+ #
67
+ # このメソッドはデバイスのセクタサイズ (読み書きの最小単位) を取得するために呼ばれます。
68
+ #
69
+ # /dev 以下に geom gate オブジェクトとして作成される前に呼ばれます。
70
+ #
71
+ # 戻り値として正の整数値を返して下さい。
72
+ #
73
+ def sectorsize
74
+ 512
75
+ end
76
+
77
+ #
78
+ # このメソッドはデバイスのテキスト情報を取得するために呼ばれます。
79
+ #
80
+ # /dev 以下に geom gate オブジェクトとして作成される前に呼ばれます。
81
+ #
82
+ # 戻り値として nil か 2048 バイト未満の文字列を返して下さい。
83
+ #
84
+ def info
85
+ "#{Suzuna}-#{VERSION} (powered by #{RUBY_ENGINE})"
86
+ end
87
+
88
+ #
89
+ # このメソッドはデバイスの要求処理の最大待機時間を取得するために呼ばれます。
90
+ #
91
+ # /dev 以下に geom gate オブジェクトとして作成される前に呼ばれます。
92
+ #
93
+ # 戻り値として正の整数値を返して下さい。0 は無制限とみなされます。
94
+ #
95
+ def timeout
96
+ 60
97
+ end
98
+
99
+ #
100
+ # このメソッドはデバイスのユニット番号を取得するために呼ばれます。
101
+ #
102
+ # /dev 以下に geom gate オブジェクトとして作成される前に呼ばれます。
103
+ #
104
+ # 戻り値として正の整数値か、G_GATE_UNIT_AUTO を返して下さい。
105
+ #
106
+ def unit
107
+ G_GATE_UNIT_AUTO
108
+ end
109
+
110
+ #
111
+ # このメソッドは geom gate オブジェクトが破棄されたあとに呼ばれます。
112
+ #
113
+ # 仮想デバイスの終了処理などを目的として用意されています。
114
+ #
115
+ # 戻り値は無視されます。
116
+ #
117
+ def cleanup
118
+ nil
119
+ end
120
+
121
+ #
122
+ # このメソッドは geom gate オブジェクトに対して読み込み要求があった時に呼ばれます。
123
+ #
124
+ # 文字列オブジェクトである buf に読み込んだデータを転写して下さい。
125
+ #
126
+ # 戻り値として、処理の成否である Errno::EXXX クラスかそのインスタンス、errno の整数値を返して下さい。
127
+ # 正常な場合には、nil を返すことで Errno::NOERROR と認識されます。
128
+ #
129
+ def read(offset, size, buf)
130
+ raise NotImplementedError, "IMPLEMENT ME! #read(offset, size, buf) -> errno"
131
+ end
132
+
133
+ #
134
+ # このメソッドは geom gate オブジェクトに対して書き込み要求があった時に呼ばれます。
135
+ #
136
+ # 文字列オブジェクトである buf を書き込む処理を行って下さい。
137
+ #
138
+ # 戻り値として、処理の成否である Errno::EXXX クラスかそのインスタンス、errno の整数値を返して下さい。
139
+ # 正常な場合には、nil を返すことで Errno::NOERROR と認識されます。
140
+ #
141
+ def write(offset, buf)
142
+ raise NotImplementedError, "IMPLEMENT ME! #write(offset, buf) -> errno"
143
+ end
144
+
145
+ #
146
+ # このメソッドは geom gate オブジェクトに対してセクタの削除(解放)要求があった時に呼ばれます。
147
+ #
148
+ # 戻り値として、処理の成否である Errno::EXXX クラスかそのインスタンス、errno の整数値を返して下さい。
149
+ # 正常な場合には、nil を返すことで Errno::NOERROR と認識されます。
150
+ #
151
+ def delete(offset, size)
152
+ raise NotImplementedError, "IMPLEMENT ME! #delete(offset, size) -> errno"
153
+ end
154
+ end
155
+
156
+ def self.join(unitobj)
157
+ unit = IOCTL::Create.post(unitobj.mediasize, unitobj.flags,
158
+ sectorsize: unitobj.sectorsize, info: unitobj.info,
159
+ timeout: unitobj.timeout, unit: unitobj.unit)
160
+
161
+ begin
162
+ mainloop(unitobj, unit)
163
+ ensure
164
+ IOCTL::Destroy.post(unit, true) rescue nil unless $!.kind_of?(DestroyedGate)
165
+ unitobj.cleanup
166
+ end
167
+ end
168
+
169
+ def self.mainloop(unitobj, unit)
170
+ ioc = IOCTL::IOReq.new
171
+ ioc.start.version = G_GATE_VERSION
172
+ ioc.start.unit = unit
173
+ bufsize = unitobj.sectorsize
174
+ buf = String.alloc(bufsize)
175
+ ioc.start.data = buf.to_ptr
176
+
177
+ while true
178
+ while true
179
+ ioc.start.data = buf.to_ptr
180
+ ioc.start.length = buf.bytesize
181
+ ioc.start.error = 0
182
+ begin
183
+ ioc.start.post
184
+ rescue Errno::ENXIO
185
+ raise DestroyedGate, "/dev/ggate#{unit}"
186
+ end
187
+
188
+ case ioc.start.error
189
+ when Errno::NOERROR::Errno
190
+ # nothing to do here
191
+ when Errno::ECANCELED::Errno, Errno::ENXIO::Errno
192
+ raise DestroyedGate, "/dev/ggate#{unit}"
193
+ when Errno::ENOMEM::Errno
194
+ buf.resize(bufsize = ioc.start.length)
195
+ unless buf.bytesize == bufsize
196
+ raise Errno::ENOMEM, <<-EOM.chomp
197
+ #{G_GATE_CTL_NAME} (require size = #{bufsize}, but allocated size = #{buf.bytesize})
198
+ EOM
199
+ end
200
+ break
201
+ else
202
+ raise SystemCallError.new("#{G_GATE_CTL_NAME} (ioctl)", ioc.start.error)
203
+ end
204
+
205
+ catch(:break) do
206
+ begin
207
+ case ioc.start.cmd
208
+ when BIO_READ
209
+ if ioc.start.length > bufsize
210
+ buf.resize(bufsize = ioc.start.length)
211
+ unless buf.bytesize == bufsize
212
+ ioc.done.error = Errno::ENOMEM::Errno
213
+ throw :break
214
+ end
215
+ end
216
+ ioc.done.error = err2code(unitobj.read(ioc.start.offset, ioc.start.length, buf))
217
+ ioc.done.data = buf.to_ptr
218
+ when BIO_DELETE
219
+ ioc.done.error = err2code(unitobj.delete(ioc.start.offset, ioc.start.length))
220
+ when BIO_WRITE
221
+ buf.resize(ioc.start.length)
222
+ ioc.done.error = err2code(unitobj.write(ioc.start.offset, buf))
223
+ buf.resize(bufsize)
224
+ ioc.done.data = buf.to_ptr
225
+ else
226
+ ioc.done.error = Errno::EOPNOTSUPP::Errno
227
+ end
228
+ rescue BasicObject
229
+ ioc.done.error = Errno::EFAULT::Errno
230
+ raise
231
+ ensure
232
+ #p err: SystemCallError.new(ioc.done.error)
233
+ ioc.done.post
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ raise Exception, "!!BUG!! - SHALL NOT REACHED HERE!"
240
+ end
241
+
242
+ def self.err2code(err)
243
+ case err
244
+ when nil
245
+ Errno::NOERROR::Errno
246
+ when Class
247
+ err::Errno
248
+ when Integer
249
+ err.to_i
250
+ else
251
+ err.errno
252
+ end
253
+ end
254
+
255
+ module IOCTL
256
+ module IOC
257
+ IOCPARM_SHIFT = 13
258
+ IOCPARM_MASK = ~(~0 << IOCPARM_SHIFT)
259
+ IOC_IN = 0x80000000
260
+ IOC_OUT = 0x40000000
261
+ IOC_INOUT = IOC_IN | IOC_OUT
262
+
263
+ def _IOC(inout, group, num, len)
264
+ inout.to_i | ((len.to_i & IOCPARM_MASK) << 16) | (group.to_i << 8) | num.to_i
265
+ end
266
+
267
+ def _IOWR(g, n, t)
268
+ _IOC(IOC_INOUT, g.to_i, n.to_i, t.bytesize)
269
+ end
270
+ end
271
+
272
+ NAME_MAX = 255
273
+
274
+ G_GATE_INFOSIZE = 2048
275
+
276
+ GG_MODIFY_MEDIASIZE = 0x01
277
+ GG_MODIFY_INFO = 0x02
278
+ GG_MODIFY_READPROV = 0x04
279
+ GG_MODIFY_READOFFSET = 0x08
280
+
281
+ @@devfd = File.open(G_GATE_CTL_NAME, File::RDWR)
282
+
283
+ # :nodoc:
284
+ def self.ioctl(req, data)
285
+ @@devfd.ioctl(req, data)
286
+ end
287
+
288
+ module CommonModule
289
+ def post
290
+ IOCTL.ioctl(self.class::REQ, to_buffer)
291
+ end
292
+ end
293
+
294
+ extend Gogyou
295
+
296
+ typedef :uint64_t, :off_t
297
+
298
+ Create = struct {
299
+ uint :version
300
+ off_t :mediasize
301
+ uint :sectorsize
302
+ uint :flags
303
+ uint :maxcount
304
+ uint :timeout
305
+ char :name, NAME_MAX
306
+ char :info, G_GATE_INFOSIZE
307
+ char :readprov, NAME_MAX
308
+ off_t :readoffset
309
+ int :unit
310
+ }
311
+
312
+ class Create
313
+ include CommonModule
314
+ extend IOC
315
+
316
+ REQ = _IOWR("m".ord, 0, self)
317
+
318
+ def self.post(mediasize, flags, sectorsize: 512, info: nil, timeout: G_GATE_TIMEOUT, unit: G_GATE_UNIT_AUTO)
319
+ ioc = IOCTL::Create.new
320
+ ioc.version = G_GATE_VERSION
321
+ ioc.unit = unit ? unit : G_GATE_UNIT_AUTO
322
+ ioc.mediasize = mediasize
323
+ ioc.sectorsize = sectorsize
324
+ ioc.timeout = timeout
325
+ ioc.flags = flags
326
+ ioc.maxcount = 0
327
+ ioc.info = info if info
328
+ ioc.post
329
+
330
+ if unit == G_GATE_UNIT_AUTO
331
+ puts "%s%u\n" % [G_GATE_PROVIDER_NAME, ioc.unit]
332
+ end
333
+
334
+ ioc.unit
335
+ end
336
+ end
337
+
338
+ Modify = struct {
339
+ uint :version
340
+ int :unit
341
+ uint32_t :modify
342
+ off_t :mediasize
343
+ char :info, G_GATE_INFOSIZE
344
+ char :readprov, NAME_MAX
345
+ off_t :readoffset
346
+ }
347
+
348
+ class Modify
349
+ include CommonModule
350
+ extend IOC
351
+
352
+ REQ = _IOWR("m".ord, 1, self)
353
+ end
354
+
355
+ Destroy = struct {
356
+ uint :version
357
+ int :unit
358
+ int :force
359
+ char :name, NAME_MAX
360
+ }
361
+
362
+ class Destroy
363
+ include CommonModule
364
+ extend IOC
365
+
366
+ REQ = _IOWR("m".ord, 2, self)
367
+
368
+ def self.post(unit, force = false)
369
+ cmd = IOCTL::Destroy.new
370
+ cmd.version = G_GATE_VERSION
371
+ cmd.unit = unit
372
+ cmd.force = force ? 1 : 0
373
+ cmd.post
374
+ end
375
+ end
376
+
377
+ Cancel = struct {
378
+ uint :version
379
+ int :unit
380
+ uintptr_t :seq
381
+ char :name, NAME_MAX
382
+ }
383
+
384
+ class Cancel
385
+ include CommonModule
386
+ extend IOC
387
+
388
+ REQ = _IOWR("m".ord, 3, self)
389
+ end
390
+
391
+ CtlIO = struct {
392
+ uint :version
393
+ int :unit
394
+ uintptr_t :seq
395
+ uint :cmd
396
+ off_t :offset
397
+ off_t :length
398
+ uintptr_t :data # void *gctl_data
399
+ int :error
400
+ }
401
+
402
+ class Start < CtlIO
403
+ include CommonModule
404
+ extend IOC
405
+
406
+ REQ = _IOWR("m".ord, 4, self)
407
+ end
408
+
409
+ class Done < CtlIO
410
+ include CommonModule
411
+ extend IOC
412
+
413
+ REQ = _IOWR("m".ord, 5, self)
414
+ end
415
+
416
+ IOReq = struct {
417
+ union {
418
+ Start :start
419
+ Done :done
420
+ }
421
+ }
422
+ end
423
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: suzuna
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: freebsd
6
+ authors:
7
+ - dearblue
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gogyou
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ description: |
56
+ ``suzuna'' is software volume infrastructure for ruby.
57
+
58
+ Support platform is FreeBSD GEOM only.
59
+ email: dearblue@users.sourceforge.jp
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files:
63
+ - LICENSE.md
64
+ - README.md
65
+ - lib/suzuna.rb
66
+ files:
67
+ - LICENSE.md
68
+ - README.md
69
+ - Rakefile
70
+ - examples/gate.rb
71
+ - gemstub.rb
72
+ - lib/suzuna.rb
73
+ homepage: http://sourceforge.jp/projects/rutsubo/
74
+ licenses:
75
+ - 2-clause BSD License
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '2.0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.4.1
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Soft volume infrastructure for ruby
97
+ test_files: []
98
+ has_rdoc: