virtfs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +154 -0
  8. data/Rakefile +5 -0
  9. data/lib/virtfs-nativefs-thick.rb +1 -0
  10. data/lib/virtfs-nativefs-thin.rb +1 -0
  11. data/lib/virtfs.rb +38 -0
  12. data/lib/virtfs/activation.rb +97 -0
  13. data/lib/virtfs/block_io.rb +140 -0
  14. data/lib/virtfs/byte_range.rb +71 -0
  15. data/lib/virtfs/context.rb +300 -0
  16. data/lib/virtfs/context_manager.rb +175 -0
  17. data/lib/virtfs/context_switch_class_methods.rb +96 -0
  18. data/lib/virtfs/delegate_module.rb +40 -0
  19. data/lib/virtfs/dir_instance_delegate.rb +3 -0
  20. data/lib/virtfs/exception.rb +13 -0
  21. data/lib/virtfs/file_instance_delegate.rb +3 -0
  22. data/lib/virtfs/file_modes_and_options.rb +293 -0
  23. data/lib/virtfs/find_class_methods.rb +106 -0
  24. data/lib/virtfs/io_buffer.rb +133 -0
  25. data/lib/virtfs/io_instance_delegate.rb +3 -0
  26. data/lib/virtfs/kernel.rb +146 -0
  27. data/lib/virtfs/nativefs/thick.rb +30 -0
  28. data/lib/virtfs/nativefs/thick/dir_class_methods.rb +38 -0
  29. data/lib/virtfs/nativefs/thick/file_class_methods.rb +178 -0
  30. data/lib/virtfs/nativefs/thin.rb +32 -0
  31. data/lib/virtfs/nativefs/thin/dir.rb +30 -0
  32. data/lib/virtfs/nativefs/thin/dir_class_methods.rb +41 -0
  33. data/lib/virtfs/nativefs/thin/file.rb +112 -0
  34. data/lib/virtfs/nativefs/thin/file_class_methods.rb +181 -0
  35. data/lib/virtfs/protofs/protofs.rb +7 -0
  36. data/lib/virtfs/protofs/protofs_base.rb +12 -0
  37. data/lib/virtfs/protofs/protofs_dir.rb +13 -0
  38. data/lib/virtfs/protofs/protofs_dir_class.rb +31 -0
  39. data/lib/virtfs/protofs/protofs_file.rb +27 -0
  40. data/lib/virtfs/protofs/protofs_file_class.rb +136 -0
  41. data/lib/virtfs/stat.rb +100 -0
  42. data/lib/virtfs/thin_dir_delegator.rb +79 -0
  43. data/lib/virtfs/thin_file_delegator.rb +77 -0
  44. data/lib/virtfs/thin_io_delegator_methods.rb +301 -0
  45. data/lib/virtfs/thin_io_delegator_methods_bufferio.rb +337 -0
  46. data/lib/virtfs/v_dir.rb +238 -0
  47. data/lib/virtfs/v_file.rb +480 -0
  48. data/lib/virtfs/v_io.rb +243 -0
  49. data/lib/virtfs/v_pathname.rb +128 -0
  50. data/lib/virtfs/version.rb +3 -0
  51. data/spec/activate_spec.rb +202 -0
  52. data/spec/chroot_spec.rb +120 -0
  53. data/spec/context_manager_class_spec.rb +246 -0
  54. data/spec/context_manager_instance_spec.rb +255 -0
  55. data/spec/context_spec.rb +335 -0
  56. data/spec/data/UTF-16LE-data.txt +0 -0
  57. data/spec/data/UTF-8-data.txt +212 -0
  58. data/spec/dir_class_spec.rb +506 -0
  59. data/spec/dir_instance_spec.rb +208 -0
  60. data/spec/file_class_spec.rb +2106 -0
  61. data/spec/file_instance_spec.rb +154 -0
  62. data/spec/file_modes_and_options_spec.rb +1556 -0
  63. data/spec/find_spec.rb +142 -0
  64. data/spec/io_bufferio_size_shared_examples.rb +371 -0
  65. data/spec/io_bufferio_size_spec.rb +861 -0
  66. data/spec/io_bufferio_spec.rb +801 -0
  67. data/spec/io_class_spec.rb +145 -0
  68. data/spec/io_instance_spec.rb +516 -0
  69. data/spec/kernel_spec.rb +285 -0
  70. data/spec/mount_spec.rb +186 -0
  71. data/spec/nativefs_local_root_spec.rb +132 -0
  72. data/spec/path_spec.rb +39 -0
  73. data/spec/spec_helper.rb +126 -0
  74. data/tasks/rspec.rake +3 -0
  75. data/tasks/yard.rake +7 -0
  76. data/test/UTF-8-demo.txt +212 -0
  77. data/test/bench.rb +18 -0
  78. data/test/bio_internal_test.rb +45 -0
  79. data/test/delegate_io.rb +31 -0
  80. data/test/delegate_module.rb +62 -0
  81. data/test/encode_test.rb +42 -0
  82. data/test/enoent_test.rb +30 -0
  83. data/test/namespace_test.rb +42 -0
  84. data/test/read_block_valid_encoding.rb +44 -0
  85. data/test/read_test.rb +78 -0
  86. data/test/stream_readers.rb +46 -0
  87. data/test/utf-16-demo.txt +0 -0
  88. data/test/utf8_to_utf16.rb +77 -0
  89. data/test/wrapper_test.rb +34 -0
  90. data/virtfs.gemspec +29 -0
  91. metadata +230 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6347d643cd70f383dfe6e5738525ca3ddfc0b40d
4
+ data.tar.gz: 18b7fb18ba46b1958fc27e26cf2e071c5b502f3e
5
+ SHA512:
6
+ metadata.gz: 791b4b067addfe0070a50e8a78d8ad826e87452e9deed22dda8318c6ca414c7458aa5003ec30b71a74c942eefa01131f637f717fdd445051da140d0c05ecd269
7
+ data.tar.gz: 226904fefb083d84c7535e6e58e893f1c0f1e5b4061ef63f80cfdb0b9f90b974b1d6c8eeb18b2e37cf726ddbf08bea6afcf11d6e2498e23cfcb86eb11e6c8a99
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *swp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.2"
4
+ sudo: false
5
+ cache: bundler
6
+ env:
7
+ - FS_INTERFACE=thin
8
+ - FS_INTERFACE=thick
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in virtfs.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Richard Oliveri
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,154 @@
1
+ # VirtFS
2
+
3
+ [![Build Status](https://travis-ci.org/ManageIQ/virtfs.svg)](https://travis-ci.org/ManageIQ/virtfs)
4
+ [![Code Climate](https://codeclimate.com/github/ManageIQ/virtfs/badges/gpa.svg)](https://codeclimate.com/github/ManageIQ/virtfs)
5
+
6
+ A virtual (pseudo) filesystem facility for Ruby.
7
+
8
+ Provides an infrastructure where instances of various filesystem **plugins** can be _mounted_ within the filesystem namespace of a Ruby process, and accessed via Ruby `File` and `Dir` classes and objects.
9
+
10
+ Typically, these **plugins** implement a filesystem access paradigm on top of various storage mechanisms and/or programmatic data generation.
11
+
12
+ **For example:** a plugin can be implemented that stores and maintains filesystem-like information in a database - say Berkeley DB - we'll call that plugin `BdbFS`, which is also the name of the Ruby class implementing the plugin. Instantiating an instance of this class will return an instance of the `BdbFS` filesystem, which can then be mounted and accessed through the **VirtFS** facility.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'virtfs'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install virtfs
27
+
28
+ ## Usage
29
+ ### Mounting root
30
+ By default, **VirtFS** does not mount the native filesystem of the system on which the Ruby process is running. It needs to be done explicitly. This is accomplished through the use of a **plugin** that simply _wraps_ the native filsystem in a class that enables its access through **VirtFS**.
31
+
32
+ Typically, one of the first things a **VirtFS** client does, is _mount_ the native filesystem:
33
+ ```ruby
34
+ require "virtfs-nativefs-thick"
35
+
36
+ # Instantiate an instance of the native filesystem.
37
+ native_fs = VirtFS::NativeFS::Thick.new
38
+
39
+ # Mount the native filesystem on root "/"
40
+ VirtFS.mount(native_fs, "/")
41
+ ```
42
+ ### Access
43
+
44
+ Once the native filesystem is mounted, **VirtFS** can be accessed explicitly through the `VirtFS` class, or it can be _activated_, enabling access through Ruby's `File` and `Dir` classes.
45
+
46
+ #### Explicit access
47
+ ```ruby
48
+ require "virtfs-nativefs-thick"
49
+
50
+ # Instantiate an instance of the native filesystem
51
+ native_fs = VirtFS::NativeFS::Thick.new
52
+
53
+ # Mount the native filesystem on root "/"
54
+ VirtFS.mount(native_fs, "/")
55
+
56
+ VirtFS::VDir.chdir("/etc")
57
+ VirtFS::VDir.getwd # => "/etc"
58
+
59
+ VirtFS::VDir.foreach(this_dir) do |f|
60
+ puts f
61
+ end
62
+
63
+ # Note: this does not change the state of Ruby's standard filesystem.
64
+ Dir.getwd # => the original cwd of the ruby process.
65
+ ```
66
+ #### Activation
67
+ ```ruby
68
+ require "virtfs-nativefs-thick"
69
+
70
+ # Instantiate an instance of the native filesystem.
71
+ native_fs = VirtFS::NativeFS::Thick.new
72
+
73
+ # Mount the native filesystem on root "/"
74
+ VirtFS.mount(native_fs, "/")
75
+
76
+ VirtFS.activate!
77
+ VirtFS.activated? # => true
78
+
79
+ # Dir and File classes now go through VirtFS.
80
+ Dir.chdir("/etc")
81
+ Dir.getwd # => "/etc"
82
+
83
+ Dir.foreach(this_dir) do |f|
84
+ puts f
85
+ end
86
+
87
+ VirtFS.deactivate!
88
+ VirtFS.activated? # => false
89
+ ```
90
+ Activation can be confined to a block through the use of the `with` class method:
91
+ ```ruby
92
+ VirtFS.activated? # => false
93
+ VirtFS.with do
94
+ VirtFS.activated? # => true
95
+ end
96
+ VirtFS.activated? # => false
97
+ ```
98
+
99
+ ### Mounting other filesystems
100
+
101
+ Let's use the, yet to be implemented, `BdbFS` plugin as an example:
102
+ ```ruby
103
+ require "virtfs-nativefs-thick"
104
+ require "virtfs-bdbfs"
105
+
106
+ # The Berkeley DB file containing the filesystem data.
107
+ bdbfs_file = "/usr/me/my_bdbfs_file"
108
+
109
+ # Where to mount the BdbFS instance under the native filesystem.
110
+ bdbfs_mount_point = "/usr/me/bdbfs_root"
111
+
112
+ #
113
+ # Mount the native filesystem.
114
+ #
115
+ native_fs = VirtFS::NativeFS::Thick.new
116
+ VirtFS.mount(native_fs, "/")
117
+
118
+ #
119
+ # Instantiate a BdbFS instance and mount it.
120
+ #
121
+ bdb_fs = VirtFS::BdbFS.new(bdbfs_file)
122
+ VirtFS.mount(bdb_fs, bdbfs_mount_point)
123
+
124
+ VirtFS.with do
125
+ Dir.chdir(bdbfs_mount_point)
126
+ Dir.getwd # => "/usr/me/bdbfs_root"
127
+
128
+ # List the files at the root of the BdbFS.
129
+ Dir.foreach(this_dir) do |f|
130
+ puts f
131
+ end
132
+ end
133
+ ```
134
+
135
+ ## Plugins
136
+
137
+ There are two types of filesystem plugins for **VirtFS**; they are distinguished by the type of interface they present to the **VirtFS** infrastructure - the interface that **VirtFS** uses to call into the plugin.
138
+
139
+ * **Thin** interface plugins implement the minimum low-level methods required by **VirtFS**. Most of the common filesystem functionality is provided by the **VirtFS** infrastructure itself (like buffered IO and character encoding), making this type of plugin easier to implement.
140
+ * **Thick** interface plugins implement most, if not all, of the filesystem functionality within the plugin, overriding the common implementations within **VirtFS**. While these plugins require more work to implement, they do provide a measurable performance improvement.
141
+
142
+ In reality, most plugins will be of the **thin** type. More often than not, **thick** plugins will be used to interface **VirtFS** to an underlying technology that already implements the full filesystem interface. An obvious example of this is the plugin that interfaces with Ruby's native filesystem interface.
143
+
144
+ Notice in the examples above, when mounting the native filesystem on root, we used the `VirtFS::NativeFS::Thick` plugin class. This plugin passes most requests directly to the underlying Ruby filesystem implementation, bypassing the common implementations in **VirtFS**.
145
+
146
+ There is a **thin** version of this plugin: `VirtFS::NativeFS::Thin`. It only calls down into the underlying Ruby filesystem implementation for low-level operations, relying on the common functionality provided by **VirtFS** for everything else. While this plugin is less performant than its **thick** counterpart, it's extremely useful in testing the common filesystem functionality implemented within **VirtFS**. In fact the _spec_ tests for **VirtFS** are run using both **thick** and **thin** interfaces, to ensure the expected results are identical.
147
+
148
+ ## Contributing
149
+
150
+ 1. Fork it
151
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
152
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
153
+ 4. Push to the branch (`git push origin my-new-feature`)
154
+ 5. Create new Pull Request
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ Dir.glob('tasks/**/*.rake').each(&method(:import))
3
+
4
+ task :test => :spec
5
+ task :default => :spec
@@ -0,0 +1 @@
1
+ require_relative "virtfs/nativefs/thick"
@@ -0,0 +1 @@
1
+ require_relative "virtfs/nativefs/thin"
@@ -0,0 +1,38 @@
1
+ require 'pathname'
2
+
3
+ VfsRealDir = ::Dir
4
+ VfsRealFile = ::File
5
+ VfsRealIO = ::IO
6
+ VfsRealPathname = ::Pathname
7
+
8
+ require_relative 'virtfs/version.rb'
9
+ require_relative 'virtfs/exception.rb'
10
+ require_relative 'virtfs/context.rb'
11
+ require_relative 'virtfs/context_manager.rb'
12
+ require_relative 'virtfs/file_modes_and_options.rb'
13
+ require_relative 'virtfs/context_switch_class_methods.rb'
14
+ require_relative 'virtfs/find_class_methods.rb'
15
+ require_relative 'virtfs/activation.rb'
16
+ require_relative 'virtfs/delegate_module.rb'
17
+ require_relative 'virtfs/stat.rb'
18
+ require_relative 'virtfs/thin_dir_delegator.rb'
19
+ require_relative 'virtfs/thin_file_delegator.rb'
20
+ require_relative 'virtfs/kernel'
21
+ require_relative 'virtfs/v_pathname.rb'
22
+
23
+ # VirtFS - Ruby Virtual File System Interface
24
+ module VirtFS
25
+ @activated = false
26
+
27
+ extend Activation
28
+ extend DelegateModule
29
+ extend ContextSwitchClassMethods
30
+ extend FindClassMethods
31
+ end
32
+
33
+ require_relative 'virtfs/dir_instance_delegate.rb'
34
+ require_relative 'virtfs/file_instance_delegate.rb'
35
+ require_relative 'virtfs/io_instance_delegate.rb'
36
+ require_relative 'virtfs/v_io.rb'
37
+ require_relative 'virtfs/v_file.rb'
38
+ require_relative 'virtfs/v_dir.rb'
@@ -0,0 +1,97 @@
1
+ module VirtFS
2
+ module Activation
3
+ def activate_mutex
4
+ @activate_mutex ||= Mutex.new
5
+ end
6
+
7
+ # @return [Boolean] indicating if VirtFS is active
8
+ #
9
+ # @see .activate!
10
+ # @see .deactivate!
11
+ def activated?
12
+ @activated
13
+ end
14
+
15
+ # Overrides Ruby's native Dir, File, and IO classes
16
+ # with corresponding VirtFS classes
17
+ def activate!(enable_require = false)
18
+ activate_mutex.synchronize do
19
+ raise "VirtFS.activate! already activated" if @activated
20
+ @activated = true
21
+
22
+ Object.class_eval do
23
+ remove_const(:Dir)
24
+ remove_const(:File)
25
+ remove_const(:IO)
26
+ remove_const(:Pathname)
27
+
28
+ const_set(:Dir, VirtFS::VDir)
29
+ const_set(:File, VirtFS::VFile)
30
+ const_set(:IO, VirtFS::VIO)
31
+ const_set(:Pathname, VirtFS::VPathname)
32
+ end
33
+
34
+ if enable_require
35
+ VirtFS::Kernel.inject
36
+ VirtFS::Kernel.enable
37
+ end
38
+ end
39
+ true
40
+ end
41
+
42
+ # Restores Ruby's native Dir, File, and IO classes
43
+ # to their defaults
44
+ def deactivate!
45
+ activate_mutex.synchronize do
46
+ raise "VirtFS.deactivate! not activated" unless @activated
47
+ @activated = false
48
+
49
+ Object.class_eval do
50
+ remove_const(:Dir)
51
+ remove_const(:File)
52
+ remove_const(:IO)
53
+ remove_const(:Pathname)
54
+
55
+ const_set(:Dir, VfsRealDir)
56
+ const_set(:File, VfsRealFile)
57
+ const_set(:IO, VfsRealIO)
58
+ const_set(:Pathname, VfsRealPathname)
59
+ end
60
+ VirtFS::Kernel.disable
61
+ end
62
+ true
63
+ end
64
+
65
+ # Invokes the given block in an activated context
66
+ #
67
+ # @see .activate!
68
+ def with(enable_require = false)
69
+ if activated?
70
+ yield
71
+ else
72
+ begin
73
+ activate!(enable_require)
74
+ yield
75
+ ensure
76
+ deactivate!
77
+ end
78
+ end
79
+ end
80
+
81
+ # Invokes the given block in a deactivated context
82
+ #
83
+ # @see .deactivate!
84
+ def without
85
+ if !activated?
86
+ yield
87
+ else
88
+ begin
89
+ deactivate!
90
+ yield
91
+ ensure
92
+ activate!
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,140 @@
1
+ module VirtFS
2
+ # Block IO Adapter, used internally by VirtFS to perform read and write operations
3
+ # via an underlying 'block-like' interface.
4
+ #
5
+ # The class requires a handle to an object defining #raw_read and #raw_write methods
6
+ # facilitating raw block access.
7
+ #
8
+ # This class behaves similarily to a tradional disk device, providing a 'seek'
9
+ # position at which read/write operations occur after which the pos is updated
10
+ #
11
+ class BlockIO
12
+ MIN_BLOCKS_TO_CACHE = 64
13
+
14
+ # Optional absolute block offset, always applied
15
+ attr_accessor :offset
16
+
17
+ # BlockIO initializer
18
+ #
19
+ # @param io_obj [RawIO] instance of class defining #raw_read and #raw_write
20
+ #
21
+ def initialize(io_obj)
22
+ @io_obj = io_obj
23
+ @block_size = @io_obj.block_size # Size of block in bytes
24
+ @size = @io_obj.size # Size of file in bytes
25
+ @size_in_blocks = @size / @block_size # Size of file in blocks
26
+ @start_byte_addr = 0
27
+ @end_byte_addr = @size - 1
28
+ @lba_end = @size_in_blocks - 1
29
+ @seek_pos = 0
30
+ @cache_range = Range.new(-1, -1)
31
+ @offset = 0
32
+ end
33
+
34
+ # Read len bytes from the block device and update seek_pos
35
+ #
36
+ # @param len [Integer] number of bytes to read
37
+ # @return [Array<Byte>,nil] array of up to len bytes read from block device,
38
+ # or nil if no bytes can be read
39
+ def read(len)
40
+ return nil if @seek_pos >= @end_byte_addr
41
+ len = @end_byte_addr - @seek_pos if (@seek_pos + len) > @end_byte_addr
42
+
43
+ start_sector, start_offset = @seek_pos.divmod(@block_size)
44
+ end_sector = (@seek_pos + len - 1) / @block_size
45
+ num_sector = end_sector - start_sector + 1
46
+
47
+ rbuf = bread_cached(start_sector, num_sector)
48
+ @seek_pos += len
49
+
50
+ rbuf[start_offset, len]
51
+ end
52
+
53
+ # Write buffer of specified length to block device and update seek_pos
54
+ #
55
+ # @param buf [Array<Byte>] bytes to write to disk device
56
+ # @return [Integer,nil] number of bytes written to device, or nil if none
57
+ # can be written
58
+ def write(buf, len)
59
+ return nil if @seek_pos >= @end_byte_addr
60
+ len = @end_byte_addr - @seek_pos if (@seek_pos + len) > @end_byte_addr
61
+
62
+ start_sector, start_offset = @seek_pos.divmod(@block_size)
63
+ end_sector = (@seek_pos + len - 1) / @block_size
64
+ num_sector = end_sector - start_sector + 1
65
+
66
+ rbuf = bread(start_sector, num_sector)
67
+ rbuf[start_offset, len] = buf[0, len]
68
+
69
+ bwrite(start_sector, num_sector, rbuf)
70
+ @seek_pos += len
71
+
72
+ len
73
+ end
74
+
75
+ # Advance seek pointer via the specified mechanism
76
+ #
77
+ # @param amt [Integer] absolute amount which to advance seek pointer
78
+ # @param whence [IO::SEEK_SET, IO::SEEK_CUR, IO::SEEK_END] mechanism
79
+ # which to update seek pos
80
+ #
81
+ # @see ::IO::SEEK_SET
82
+ # @see ::IO::SEEK_CUR
83
+ # @see ::IO::SEEK_END
84
+ #
85
+ def seek(amt, whence = IO::SEEK_SET)
86
+ case whence
87
+ when IO::SEEK_CUR
88
+ @seek_pos += amt
89
+ when IO::SEEK_END
90
+ @seek_pos = @end_byte_addr + amt
91
+ when IO::SEEK_SET
92
+ @seek_pos = amt + @start_byte_addr
93
+ end
94
+ @seek_pos
95
+ end
96
+
97
+ # @return [Integer] size of block device
98
+ def size
99
+ @size
100
+ end
101
+
102
+ # Close block device, after this no additional read/writes can occur
103
+ def close
104
+ @io_obj.close
105
+ end
106
+
107
+ private
108
+
109
+ def bread(start_sector, num_sectors)
110
+ # $log.debug "RawBlockIO.bread: start_sector = #{start_sector}, num_sectors = #{num_sectors}, @lba_end = #{@lba_end}"
111
+ return nil if start_sector > @lba_end
112
+ num_sectors = @size_in_blocks - start_sector if (start_sector + num_sectors) > @size_in_blocks
113
+ @io_obj.raw_read(start_sector * @block_size + @offset, num_sectors * @block_size)
114
+ end
115
+
116
+ def bwrite(start_sector, num_sectors, buf)
117
+ return nil if start_sector > @lba_end
118
+ num_sectors = @size_in_blocks - start_sector if (start_sector + num_sectors) > @size_in_blocks
119
+ @io_obj.raw_write(buf, start_sector * @block_size + @offset, num_sectors * @block_size)
120
+ end
121
+
122
+ def bread_cached(start_sector, num_sectors)
123
+ # $log.debug "RawBlockIO.bread_cached: start_sector = #{start_sector}, num_sectors = #{num_sectors}"
124
+ if !@cache_range.include?(start_sector) || !@cache_range.include?(start_sector + num_sectors - 1)
125
+ sectors_to_read = [MIN_BLOCKS_TO_CACHE, num_sectors].max
126
+ @cache = bread(start_sector, sectors_to_read)
127
+ sectors_read = @cache.length / @block_size
128
+ end_sector = start_sector + sectors_read - 1
129
+ @cache_range = Range.new(start_sector, end_sector)
130
+ end
131
+
132
+ sector_offset = start_sector - @cache_range.first
133
+ buffer_offset = sector_offset * @block_size
134
+ length = num_sectors * @block_size
135
+ # $log.debug "RawBlockIO.bread_cached: buffer_offset = #{buffer_offset}, length = #{length}"
136
+
137
+ @cache[buffer_offset, length]
138
+ end
139
+ end
140
+ end