virtfs 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +154 -0
- data/Rakefile +5 -0
- data/lib/virtfs-nativefs-thick.rb +1 -0
- data/lib/virtfs-nativefs-thin.rb +1 -0
- data/lib/virtfs.rb +38 -0
- data/lib/virtfs/activation.rb +97 -0
- data/lib/virtfs/block_io.rb +140 -0
- data/lib/virtfs/byte_range.rb +71 -0
- data/lib/virtfs/context.rb +300 -0
- data/lib/virtfs/context_manager.rb +175 -0
- data/lib/virtfs/context_switch_class_methods.rb +96 -0
- data/lib/virtfs/delegate_module.rb +40 -0
- data/lib/virtfs/dir_instance_delegate.rb +3 -0
- data/lib/virtfs/exception.rb +13 -0
- data/lib/virtfs/file_instance_delegate.rb +3 -0
- data/lib/virtfs/file_modes_and_options.rb +293 -0
- data/lib/virtfs/find_class_methods.rb +106 -0
- data/lib/virtfs/io_buffer.rb +133 -0
- data/lib/virtfs/io_instance_delegate.rb +3 -0
- data/lib/virtfs/kernel.rb +146 -0
- data/lib/virtfs/nativefs/thick.rb +30 -0
- data/lib/virtfs/nativefs/thick/dir_class_methods.rb +38 -0
- data/lib/virtfs/nativefs/thick/file_class_methods.rb +178 -0
- data/lib/virtfs/nativefs/thin.rb +32 -0
- data/lib/virtfs/nativefs/thin/dir.rb +30 -0
- data/lib/virtfs/nativefs/thin/dir_class_methods.rb +41 -0
- data/lib/virtfs/nativefs/thin/file.rb +112 -0
- data/lib/virtfs/nativefs/thin/file_class_methods.rb +181 -0
- data/lib/virtfs/protofs/protofs.rb +7 -0
- data/lib/virtfs/protofs/protofs_base.rb +12 -0
- data/lib/virtfs/protofs/protofs_dir.rb +13 -0
- data/lib/virtfs/protofs/protofs_dir_class.rb +31 -0
- data/lib/virtfs/protofs/protofs_file.rb +27 -0
- data/lib/virtfs/protofs/protofs_file_class.rb +136 -0
- data/lib/virtfs/stat.rb +100 -0
- data/lib/virtfs/thin_dir_delegator.rb +79 -0
- data/lib/virtfs/thin_file_delegator.rb +77 -0
- data/lib/virtfs/thin_io_delegator_methods.rb +301 -0
- data/lib/virtfs/thin_io_delegator_methods_bufferio.rb +337 -0
- data/lib/virtfs/v_dir.rb +238 -0
- data/lib/virtfs/v_file.rb +480 -0
- data/lib/virtfs/v_io.rb +243 -0
- data/lib/virtfs/v_pathname.rb +128 -0
- data/lib/virtfs/version.rb +3 -0
- data/spec/activate_spec.rb +202 -0
- data/spec/chroot_spec.rb +120 -0
- data/spec/context_manager_class_spec.rb +246 -0
- data/spec/context_manager_instance_spec.rb +255 -0
- data/spec/context_spec.rb +335 -0
- data/spec/data/UTF-16LE-data.txt +0 -0
- data/spec/data/UTF-8-data.txt +212 -0
- data/spec/dir_class_spec.rb +506 -0
- data/spec/dir_instance_spec.rb +208 -0
- data/spec/file_class_spec.rb +2106 -0
- data/spec/file_instance_spec.rb +154 -0
- data/spec/file_modes_and_options_spec.rb +1556 -0
- data/spec/find_spec.rb +142 -0
- data/spec/io_bufferio_size_shared_examples.rb +371 -0
- data/spec/io_bufferio_size_spec.rb +861 -0
- data/spec/io_bufferio_spec.rb +801 -0
- data/spec/io_class_spec.rb +145 -0
- data/spec/io_instance_spec.rb +516 -0
- data/spec/kernel_spec.rb +285 -0
- data/spec/mount_spec.rb +186 -0
- data/spec/nativefs_local_root_spec.rb +132 -0
- data/spec/path_spec.rb +39 -0
- data/spec/spec_helper.rb +126 -0
- data/tasks/rspec.rake +3 -0
- data/tasks/yard.rake +7 -0
- data/test/UTF-8-demo.txt +212 -0
- data/test/bench.rb +18 -0
- data/test/bio_internal_test.rb +45 -0
- data/test/delegate_io.rb +31 -0
- data/test/delegate_module.rb +62 -0
- data/test/encode_test.rb +42 -0
- data/test/enoent_test.rb +30 -0
- data/test/namespace_test.rb +42 -0
- data/test/read_block_valid_encoding.rb +44 -0
- data/test/read_test.rb +78 -0
- data/test/stream_readers.rb +46 -0
- data/test/utf-16-demo.txt +0 -0
- data/test/utf8_to_utf16.rb +77 -0
- data/test/wrapper_test.rb +34 -0
- data/virtfs.gemspec +29 -0
- metadata +230 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# VirtFS
|
2
|
+
|
3
|
+
[](https://travis-ci.org/ManageIQ/virtfs)
|
4
|
+
[](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
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "virtfs/nativefs/thick"
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "virtfs/nativefs/thin"
|
data/lib/virtfs.rb
ADDED
@@ -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
|