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,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