virtfs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![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
|
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
|