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.
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,106 @@
1
+ module VirtFS
2
+ # VirtFS Find Class representation - implements the core Ruby Find methods,
3
+ # dispatching to underlying mounted VirtFS filesystems
4
+ module FindClassMethods
5
+ #
6
+ # Modified version of Find.find:
7
+ # - Accepts only a single path.
8
+ # - Can be restricted by depth - optimization for glob searches.
9
+ # - Will work with VirtFS, even when it's not active.
10
+ #
11
+ # @param path [String] starting directory of the find
12
+ # @param max_depth [Integer] max number of levels to decend befroelookup
13
+ # @yield files found
14
+ #
15
+ def find(path, max_depth = nil)
16
+ raise SystemCallError.new(path, Errno::ENOENT::Errno) unless VirtFS::VFile.exist?(path)
17
+ block_given? || (return enum_for(__method__, path, max_depth))
18
+
19
+ depths = [0]
20
+ paths = [path.dup]
21
+
22
+ while (file = paths.shift)
23
+ depth = depths.shift
24
+ catch(:prune) do
25
+ yield file.dup.taint
26
+ begin
27
+ s = VirtFS::VFile.lstat(file)
28
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
29
+ next
30
+ end
31
+ if s.directory?
32
+ next if depth + 1 > max_depth if max_depth
33
+ begin
34
+ fs = VirtFS::VDir.entries(file)
35
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
36
+ next
37
+ end
38
+ fs.sort!
39
+ fs.reverse_each do |f|
40
+ next if f == "." || f == ".."
41
+ f = VfsRealFile.join(file, f)
42
+ paths.unshift f.untaint
43
+ depths.unshift depth + 1
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # Implementation of Find.prune
51
+ #
52
+ # @raise [RuntimeError] always
53
+ def prune
54
+ throw :prune
55
+ end
56
+
57
+ GLOB_CHARS = '*?[{'
58
+ def glob_str?(str)
59
+ str.gsub(/\\./, "X").count(GLOB_CHARS) != 0
60
+ end
61
+
62
+ # Returns files matching glob pattern
63
+ #
64
+ # @api private
65
+ # @param glob_pattern [String,Regex] pattern to search for
66
+ # @return [String] paths to files found
67
+ #
68
+ def dir_and_glob(glob_pattern)
69
+ glob_path = Pathname.new(glob_pattern)
70
+
71
+ if glob_path.absolute?
72
+ search_path = VfsRealFile::SEPARATOR
73
+ specified_path = VfsRealFile::SEPARATOR
74
+ else
75
+ search_path = dir_getwd
76
+ specified_path = nil
77
+ end
78
+
79
+ components = glob_path.each_filename.to_a
80
+ while (comp = components.shift)
81
+ if glob_str?(comp)
82
+ components.unshift(comp)
83
+ break
84
+ end
85
+ search_path = VfsRealFile.join(search_path, comp)
86
+ if specified_path
87
+ specified_path = VfsRealFile.join(specified_path, comp)
88
+ else
89
+ specified_path = comp
90
+ end
91
+ end
92
+ return normalize_path(search_path), specified_path, VfsRealFile.join(components)
93
+ end
94
+
95
+ # Return max levels which glob pattern may resolve to
96
+ #
97
+ # @api private
98
+ # @param glob_pattern [String,Regex] pattern to search for
99
+ # @return [Integer] max levels which pattern may match
100
+ def glob_depth(glob_pattern)
101
+ path_components = Pathname(glob_pattern).each_filename.to_a
102
+ return nil if path_components.include?('**')
103
+ path_components.length
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,133 @@
1
+ module VirtFS
2
+ # I/O Buffer utility class, provides a fixed length byte buffer
3
+ # for I/O operations
4
+ class IOBuffer
5
+ attr_accessor :min_buf_sz, :external_encoding, :sync
6
+ attr_reader :buffer, :range
7
+
8
+ MAX_CHAR_LEN = 8
9
+
10
+ def initialize(io_obj, min_buf_sz)
11
+ @io_obj = io_obj
12
+ @min_buf_sz = min_buf_sz
13
+ @binary_encoding = Encoding.find("ASCII-8BIT")
14
+ @buffer = ""
15
+ @range = ByteRange.new
16
+ @write_range = ByteRange.new
17
+ @sync = false
18
+ end
19
+
20
+ def cover_range(pos, len)
21
+ len = adjust_len_to_eof(len, pos)
22
+ end_pos = pos + len - 1
23
+
24
+ return if @range.include?(pos) && @range.include?(end_pos)
25
+
26
+ if @range.include?(pos)
27
+ truncate_left(pos)
28
+ extend_right(len)
29
+ return
30
+ end
31
+
32
+ flush # If write data, flush.
33
+ raw_read_len = adjust_len_to_eof([@min_buf_sz, len].max, pos)
34
+ @buffer = @io_obj.fs_io_obj.raw_read(pos, raw_read_len)
35
+ @range.set(pos, pos + raw_read_len - 1)
36
+ end
37
+
38
+ def extend_right(len)
39
+ raw_read_len = adjust_len_to_eof([@min_buf_sz, len].max, @range.last)
40
+ rv = @io_obj.fs_io_obj.raw_read(@range.last + 1, raw_read_len)
41
+ @buffer << rv
42
+ @range.last += raw_read_len
43
+ end
44
+
45
+ def truncate_left(pos)
46
+ flush if @write_range.include?(pos) # If pos is within write_range, flush.
47
+ offset = buf_offset(pos)
48
+ return if offset == 0
49
+ @buffer = @buffer[offset..-1]
50
+ @range.first += offset
51
+ end
52
+
53
+ def prepend_bytes(str)
54
+ str = str.dup
55
+ str.force_encoding(@binary_encoding)
56
+ prepend(str)
57
+ end
58
+
59
+ def prepend_str(str)
60
+ str = str.dup
61
+ str.encode!(@io_obj.external_encoding) if @io_obj.external_encoding
62
+ str.force_encoding(@binary_encoding)
63
+ prepend(str)
64
+ end
65
+
66
+ # for unget, data does not get written - never in write_range.
67
+ def prepend(str)
68
+ @buffer.insert(0, str)
69
+ @range.first -= str.bytesize
70
+ str.bytesize
71
+ end
72
+
73
+ def write_to_buffer(pos, str)
74
+ len = str.bytesize
75
+ end_pos = pos + len - 1
76
+ flush unless @write_range.contiguous?(pos, end_pos)
77
+ @write_range.expand(pos, end_pos)
78
+ @range.expand(@write_range)
79
+ @buffer[buf_offset(pos), len] = str
80
+ flush if @sync
81
+ len
82
+ end
83
+
84
+ def adjust_len_to_eof(len, pos)
85
+ return @io_obj.end_byte_addr - pos + 1 if (pos + len - 1) > @io_obj.end_byte_addr
86
+ len
87
+ end
88
+
89
+ def get_byte(pos)
90
+ cover_range(pos, 1)
91
+ @buffer.getbyte(buf_offset(pos))
92
+ end
93
+
94
+ def get_char(pos)
95
+ max_char_len = adjust_len_to_eof(MAX_CHAR_LEN, pos)
96
+ cover_range(pos, max_char_len)
97
+ offset = buf_offset(pos)
98
+ (1..max_char_len).each do |len|
99
+ char = @buffer[offset, len]
100
+ char.force_encoding(@io_obj.external_encoding)
101
+ return char if char.valid_encoding?
102
+ end
103
+ raise "Invalid byte sequence"
104
+ end
105
+
106
+ def get_str(pos, len)
107
+ len = adjust_len_to_eof(len, pos)
108
+ cover_range(pos, len)
109
+ @buffer[buf_offset(pos), len]
110
+ end
111
+
112
+ def flush
113
+ return if @write_range.empty?
114
+ offset = buf_offset(@write_range.first)
115
+ length = @write_range.length
116
+ rv = @io_obj.fs_io_obj.raw_write(@write_range.first, @buffer[offset, length])
117
+ @write_range.clear
118
+ rv
119
+ end
120
+
121
+ def available_bytes(pos)
122
+ @range.last - pos + 1
123
+ end
124
+
125
+ def at_eof?
126
+ @range.last >= @io_obj.end_byte_addr
127
+ end
128
+
129
+ def buf_offset(pos)
130
+ pos - @range.first
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,3 @@
1
+ module VirtFS
2
+ IOInstanceDelegate = delegate_module(VfsRealIO)
3
+ end
@@ -0,0 +1,146 @@
1
+ require "pathname"
2
+
3
+ module VirtFS
4
+ module Kernel # rubocop:disable ModuleLength
5
+ @kernel_mutex = Mutex.new
6
+
7
+ def self.inject # rubocop:disable AbcSize
8
+ @kernel_mutex.synchronize do
9
+ return false if @injected
10
+
11
+ #
12
+ # If the 'require' method is implemented in ruby (method(:require).source_location != nil)
13
+ # and 'gem_original_require' is defined, then rubygems is active. Replace gem_original_require
14
+ # with our VirtFS aware implementation of require - the rubygems require will call us.
15
+ #
16
+ # If the 'require' method is implemented in 'C' (method(:require).source_location == nil)
17
+ # then rubygems is not active (even if 'gem_original_require' is defined). Replace 'require'
18
+ # with our VirtFS aware implementation of require
19
+ #
20
+ @rubygems_active = method(:require).source_location && private_method_defined?(:gem_original_require)
21
+
22
+ ::Kernel.module_exec(@rubygems_active) do |gems_active|
23
+ if gems_active
24
+ alias_method :virtfs_original_require, :gem_original_require
25
+ else
26
+ alias_method :virtfs_original_require, :require
27
+ end
28
+ private :virtfs_original_require
29
+
30
+ alias_method :virtfs_original_load, :load
31
+ private :virtfs_original_load
32
+
33
+ define_method(:virtfs_require) do |file_name|
34
+ VirtFS::Kernel.virtfs_require(file_name)
35
+ end
36
+ private :virtfs_require
37
+
38
+ define_method(:virtfs_load) do |file_name, wrap = false|
39
+ VirtFS::Kernel.virtfs_load(file_name, wrap)
40
+ end
41
+ private :virtfs_load
42
+ end
43
+ @injected = true
44
+ end
45
+ end
46
+
47
+ def self.withdraw
48
+ @kernel_mutex.synchronize do
49
+ return false unless @injected
50
+ raise "Cannot withdraw while VirtFS::Kernel is enabled" if @enabled
51
+
52
+ ::Kernel.module_eval do
53
+ remove_method :virtfs_original_require
54
+ remove_method :virtfs_original_load
55
+ remove_method :virtfs_require
56
+ remove_method :virtfs_load
57
+ end
58
+ @injected = false
59
+ end
60
+ true
61
+ end
62
+
63
+ def self.enable
64
+ @kernel_mutex.synchronize do
65
+ return false if @enabled
66
+
67
+ inject unless @injected
68
+
69
+ ::Kernel.module_exec(@rubygems_active) do |gems_active|
70
+ if gems_active
71
+ alias_method :gem_original_require, :virtfs_require
72
+ else
73
+ alias_method :require, :virtfs_require
74
+ end
75
+
76
+ alias_method :load, :virtfs_load
77
+ end
78
+ @enabled = true
79
+ end
80
+ true
81
+ end
82
+
83
+ def self.disable
84
+ @kernel_mutex.synchronize do
85
+ return false unless @enabled
86
+
87
+ ::Kernel.module_exec(@rubygems_active) do |gems_active|
88
+ if gems_active
89
+ alias_method :gem_original_require, :virtfs_original_require
90
+ else
91
+ alias_method :require, :virtfs_original_require
92
+ end
93
+
94
+ alias_method :load, :virtfs_original_load
95
+ end
96
+ @enabled = false
97
+ end
98
+ true
99
+ end
100
+
101
+ def self.virtfs_load(file_name, wrap)
102
+ file_path = ::Pathname.new(file_name)
103
+ return virtfs_original_load(file_name, wrap) unless file_path.extname == ".rb"
104
+ file_path = canonical_path(file_path)
105
+ raise LoadError, "cannot load such file -- #{file_name}" unless file_path
106
+ eval_file(file_path, wrap)
107
+ true
108
+ end
109
+
110
+ def self.virtfs_require(lib_name)
111
+ lib_path = canonical_path(::Pathname.new(lib_name))
112
+ raise LoadError, "cannot load such file -- #{lib_name}" unless lib_path
113
+ return virtfs_original_require(lib_name) unless lib_path.extname == ".rb"
114
+ return false if already_loaded(lib_path)
115
+ eval_file(lib_path, false)
116
+ $LOADED_FEATURES << lib_path.to_path
117
+ true
118
+ end
119
+
120
+ def self.canonical_path(path)
121
+ has_ext = path.extname != ""
122
+ return path if path.absolute?
123
+ $LOAD_PATH.each do |dir|
124
+ full_path = path.expand_path(dir)
125
+ if has_ext
126
+ return full_path if full_path.file?
127
+ else
128
+ %w(.rb .so .o .dll).each do |ext|
129
+ ext_path = full_path.sub_ext(ext)
130
+ return ext_path if ext_path.file?
131
+ end
132
+ end
133
+ end
134
+ nil
135
+ end
136
+
137
+ def self.already_loaded(canonical_name)
138
+ $LOADED_FEATURES.include?(canonical_name.to_path)
139
+ end
140
+
141
+ def self.eval_file(file_path, wrap)
142
+ eval_binding = wrap ? Module.new.send(:binding) : TOPLEVEL_BINDING
143
+ eval(file_path.read, eval_binding, file_path.to_path) # rubocop:disable Lint/Eval
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'thick/file_class_methods'
2
+ require_relative 'thick/dir_class_methods'
3
+
4
+ module VirtFS::NativeFS # rubocop:disable Style/ClassAndModuleChildren
5
+ class Thick
6
+ attr_accessor :mount_point, :name
7
+
8
+ include FileClassMethods
9
+ include DirClassMethods
10
+
11
+ def initialize(root = VfsRealFile::SEPARATOR)
12
+ @mount_point = nil
13
+ @name = self.class.name
14
+ @root = root
15
+ end
16
+
17
+ def thin_interface?
18
+ false
19
+ end
20
+
21
+ def umount
22
+ @mount_point = nil
23
+ end
24
+
25
+ def apply_root(path)
26
+ VfsRealFile.join(@root, path)
27
+ end
28
+ private :apply_root
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ #
2
+ # Dir class methods - are instance methods of filesystem instance.
3
+ #
4
+ module VirtFS::NativeFS # rubocop:disable Style/ClassAndModuleChildren
5
+ class Thick
6
+ module DirClassMethods
7
+ def dir_delete(p)
8
+ VfsRealDir.delete(apply_root(p))
9
+ end
10
+
11
+ def dir_entries(p)
12
+ VfsRealDir.entries(apply_root(p))
13
+ end
14
+
15
+ def dir_exist?(p)
16
+ VfsRealDir.exist?(apply_root(p))
17
+ end
18
+
19
+ def dir_foreach(p, &block)
20
+ VfsRealDir.foreach(apply_root(p), &block)
21
+ end
22
+
23
+ def dir_mkdir(p, permissions)
24
+ VfsRealDir.mkdir(apply_root(p), permissions)
25
+ end
26
+
27
+ def dir_new(fs_rel_path, hash_args, _open_path, cwd)
28
+ owd = VfsRealDir.getwd
29
+ begin
30
+ VfsRealDir.chdir(cwd)
31
+ return VfsRealDir.new(apply_root(fs_rel_path), hash_args)
32
+ ensure
33
+ VfsRealDir.chdir(owd)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end