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,96 @@
1
+ module VirtFS
2
+ module ContextSwitchClassMethods
3
+ def context_manager
4
+ ContextManager
5
+ end
6
+
7
+ def context
8
+ context_manager.current.current_context
9
+ end
10
+
11
+ def context!
12
+ context_manager.current!.current_context
13
+ end
14
+
15
+ def mount(fs_instance, mount_point)
16
+ context.mount(fs_instance, mount_point)
17
+ end
18
+
19
+ def umount(mount_point)
20
+ context.umount(mount_point)
21
+ end
22
+
23
+ def mounted?(mount_point)
24
+ context.mounted?(mount_point)
25
+ end
26
+
27
+ def cwd=(dir)
28
+ context.restore_cwd_root(dir, nil)
29
+ end
30
+
31
+ def root
32
+ _cwd, root = context.cwd_root
33
+ root
34
+ end
35
+
36
+ def dir_chroot(dir)
37
+ context.chroot(dir)
38
+ end
39
+
40
+ def dir_chdir(dir)
41
+ context.chdir(dir)
42
+ end
43
+
44
+ def dir_getwd
45
+ context.getwd
46
+ end
47
+
48
+ #
49
+ # Expand symbolic links and perform mount indirection look up.
50
+ #
51
+ def path_lookup(path, raise_full_path = false, include_last = true)
52
+ context.path_lookup(path, raise_full_path, include_last)
53
+ end
54
+
55
+ #
56
+ # Expand symbolic links in the path.
57
+ # This must be done here, because a symlink in one file system
58
+ # can point to a file in another filesystem.
59
+ #
60
+ def expand_links(p, include_last = true)
61
+ context.expand_links(p, include_last)
62
+ end
63
+
64
+ def normalize_path(p, relative_to = nil)
65
+ # When running on windows, File.expand_path will add a drive letter.
66
+ # Remove it if it's there.
67
+ VfsRealFile.expand_path(p, relative_to || context.getwd).sub(/^[a-zA-Z]:/, "") # XXX
68
+ end
69
+
70
+ # Invoke block withing the given filesystem context
71
+ #
72
+ # @api private
73
+ # @param fs [VirtFS::FS] filesystem intstance through which to invoke block
74
+ # @param path [String] path to specify to block
75
+ #
76
+ # @raise [VirtFS::NotImplementedError] if filesystem method does not exist
77
+ #
78
+ def fs_call(fs, path = nil, &block)
79
+ block.arity < 1 ? fs.instance_eval(&block) : fs.instance_exec(path, &block)
80
+ rescue NoMethodError => err
81
+ STDOUT.puts err.to_s
82
+ STDOUT.puts err.backtrace.join("\n")
83
+ raise VirtFS::NotImplementedError.new(fs, err.name)
84
+ end
85
+
86
+ # Invoke block using fully resolved filesystem path
87
+ #
88
+ # @api private
89
+ # @see .fs_call
90
+ # @see Context#path_lookup
91
+ def fs_lookup_call(path, raise_full_path = false, include_last = true, &block)
92
+ fs, p = path_lookup(path, raise_full_path, include_last)
93
+ fs_call(fs, p, &block)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,40 @@
1
+ require "delegate"
2
+
3
+ module VirtFS
4
+ module DelegateModule
5
+ def delegate_module(superclass)
6
+ mod = Module.new
7
+ methods = superclass.instance_methods
8
+ methods -= ::Delegator.public_api
9
+ methods -= [:to_s, :inspect, :=~, :!~, :===]
10
+
11
+ mod.module_eval do
12
+ def __getobj__ # :nodoc:
13
+ unless defined?(@delegate_dc_obj)
14
+ return yield if block_given?
15
+ __raise__ ::ArgumentError, "not delegated"
16
+ end
17
+ @delegate_dc_obj
18
+ end
19
+
20
+ def __setobj__(obj) # :nodoc:
21
+ __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
22
+ @delegate_dc_obj = obj
23
+ end
24
+
25
+ methods.each do |method|
26
+ define_method(method, Delegator.delegating_block(method))
27
+ end
28
+ end
29
+
30
+ mod.define_singleton_method :public_instance_methods do |all = true|
31
+ super(all) - superclass.protected_instance_methods
32
+ end
33
+
34
+ mod.define_singleton_method :protected_instance_methods do |all = true|
35
+ super(all) | superclass.protected_instance_methods
36
+ end
37
+ mod
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module VirtFS
2
+ DirInstanceDelegate = delegate_module(VfsRealDir)
3
+ end
@@ -0,0 +1,13 @@
1
+ module VirtFS
2
+ class NotImplementedError < StandardError
3
+ def initialize(fs, method)
4
+ super "Feature: #{method} - not implemented in #{fs.name} filesystem"
5
+ end
6
+ end
7
+
8
+ class NoContextError < StandardError
9
+ def initialize
10
+ super "No filesystem context defined for thread group: #{Thread.current.group.inspect}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module VirtFS
2
+ FileInstanceDelegate = delegate_module(VfsRealFile)
3
+ end
@@ -0,0 +1,293 @@
1
+ module VirtFS
2
+
3
+ # Utilty class to convert String File Modes and Options to their
4
+ # corresponding Ruby Constants.
5
+ #
6
+ # *args --> mode="r" <,permission> <,options>
7
+ # mode --> String or Integer
8
+ # permission --> Integer
9
+ # options --> Hash
10
+ #
11
+ # 1 arg: <mode | options>
12
+ # 2 args: mode, <permissions | options>
13
+ # 3 args: mode, permissions, options
14
+ #
15
+ # mode string --> file-mode[:external-encoding[:internal-encoding]]
16
+ #
17
+ # file-mode mapped to binary:
18
+ # "r" --> File::RDONLY
19
+ # "r+" --> File::RDWR
20
+ # "w" --> File::WRONLY | File::TRUNC | File::CREAT
21
+ # "w+" --> File::RDWR | File::TRUNC | File::CREAT
22
+ # "a" --> File::WRONLY | File::APPEND | File::CREAT
23
+ # "a+" --> File::RDWR | File::APPEND | File::CREAT
24
+ #
25
+ # Options:
26
+ # :autoclose => If false, the underlying file will not be closed
27
+ # when this I/O object is finalized.
28
+ #
29
+ # :binmode => Opens the IO object in binary mode if true (same as mode: "b").
30
+ #
31
+ # :encoding => Specifies both external and internal encodings
32
+ # as "external:internal" (same format used in mode parameter).
33
+ #
34
+ # :external_encoding => Specifies the external encoding.
35
+ #
36
+ # :internal_encoding => Specifies the internal encoding.
37
+ #
38
+ # :mode => Specifies what would have been the mode parameter.
39
+ #
40
+ # :textmode => Open the file in text mode (the default).
41
+ #
42
+ class FileModesAndOptions # rubocop:disable ClassLength
43
+ attr_reader :external_encoding, :external_encoding_str
44
+ attr_reader :internal_encoding, :internal_encoding_str
45
+ attr_reader :permissions, :mode_bits, :options
46
+ attr_reader :args
47
+
48
+ BINARY_ENCODING = "ASCII-8BIT"
49
+
50
+ def initialize(*args) # rubocop:disable AbcSize, PerceivedComplexity
51
+ @args = args
52
+ @options = {}
53
+ @mode_bits = 0
54
+ @external_encoding_str = ""
55
+ @internal_encoding_str = ""
56
+ @binmode = false
57
+ @autoclose = true
58
+ @permissions = nil
59
+ @closed = false
60
+
61
+ process_args(args)
62
+
63
+ @mode_bits = VFile::RDONLY if @mode_bits == 0
64
+
65
+ if @external_encoding_str.empty? || @external_encoding_str.empty? == "-"
66
+ @external_encoding = Encoding.default_external
67
+ else
68
+ @external_encoding = Encoding.find(@external_encoding_str)
69
+ end
70
+
71
+ if @internal_encoding_str.empty? || @internal_encoding_str == "-"
72
+ @internal_encoding = Encoding.default_internal
73
+ else
74
+ @internal_encoding = Encoding.find(@internal_encoding_str)
75
+ end
76
+ end
77
+
78
+ def [](key)
79
+ @options[key]
80
+ end
81
+
82
+ def append?
83
+ @mode_bits & VFile::APPEND != 0
84
+ end
85
+
86
+ def autoclose?
87
+ @autoclose
88
+ end
89
+
90
+ def binmode?
91
+ @binmode
92
+ end
93
+
94
+ def binmode
95
+ set_encoding(BINARY_ENCODING, nil)
96
+ @binmode = true
97
+ end
98
+
99
+ def closed?
100
+ @closed
101
+ end
102
+
103
+ def create?
104
+ @mode_bits & VFile::CREAT != 0
105
+ end
106
+
107
+ def excl?
108
+ @mode_bits & VFile::EXCL != 0
109
+ end
110
+
111
+ def noctty?
112
+ @mode_bits & VFile::NOCTTY != 0
113
+ end
114
+
115
+ def nonblock?
116
+ @mode_bits & VFile::NONBLOCK != 0
117
+ end
118
+
119
+ def rdonly?
120
+ @mode_bits == VFile::RDONLY # VFile::RDONLY = 0
121
+ end
122
+
123
+ def rdwr?
124
+ @mode_bits & VFile::RDWR != 0
125
+ end
126
+
127
+ def trunc?
128
+ @mode_bits & VFile::TRUNC != 0
129
+ end
130
+
131
+ def wronly?
132
+ @mode_bits & VFile::WRONLY != 0
133
+ end
134
+
135
+ def read?
136
+ rdonly? || rdwr?
137
+ end
138
+
139
+ def write?
140
+ wronly? || rdwr?
141
+ end
142
+
143
+ def close_read
144
+ return unless rdonly?
145
+ close
146
+ end
147
+
148
+ def close_write
149
+ return unless wronly?
150
+ close
151
+ end
152
+
153
+ def close
154
+ @closed = true
155
+ end
156
+
157
+ def set_encoding(*args) # rubocop:disable AccessorMethodName, AbcSize, PerceivedComplexity, CyclomaticComplexity, MethodLength, LineLength
158
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1..2)" if args.length < 1 || args.length > 2
159
+ unless args[0].is_a?(Encoding) || args[0].respond_to?(:to_str)
160
+ raise TypeError, "no implicit conversion of #{args[0].class.name} into String"
161
+ end
162
+
163
+ if args.length == 2
164
+ unless args[1].is_a?(Encoding) || args[1].respond_to?(:to_str) || args[1].nil?
165
+ raise TypeError, "no implicit conversion of #{args[1].class.name} into String"
166
+ end
167
+ @external_encoding = args[0].is_a?(Encoding) ? args[0] : Encoding.find(args[0].to_str)
168
+ @internal_encoding = args[1].nil? || args[1].is_a?(Encoding) ? args[1] : Encoding.find(args[1].to_str)
169
+ return nil
170
+ end
171
+
172
+ if args[0].is_a?(Encoding)
173
+ @external_encoding = args[0]
174
+ return nil
175
+ end
176
+
177
+ if args[0].to_str.include?(":")
178
+ @external_encoding_str, @internal_encoding_str = args[0].split(":")
179
+ @external_encoding_str = @external_encoding_str.to_str
180
+ @internal_encoding_str = @internal_encoding_str.to_str
181
+ else
182
+ @external_encoding_str = @internal_encoding_str = args[0].to_str
183
+ end
184
+
185
+ @external_encoding = Encoding.find(@external_encoding_str) unless @external_encoding_str.empty?
186
+ @internal_encoding = Encoding.find(@internal_encoding_str) unless @internal_encoding_str.empty?
187
+ nil
188
+ end
189
+
190
+ private
191
+
192
+ def process_args(args) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
193
+ case args.length
194
+ when 0
195
+ # mode = "r"
196
+ when 1
197
+ # <mode | options>
198
+ if args[0].is_a?(Hash)
199
+ @options = args[0]
200
+ else
201
+ mode_arg(args[0])
202
+ end
203
+ when 2
204
+ # mode, <permissions | options>
205
+ mode_arg(args[0])
206
+ if args[1].is_a?(Hash)
207
+ @options = args[1]
208
+ else
209
+ @permissions = args[1].to_int
210
+ end
211
+ when 3
212
+ # mode, permissions, options
213
+ mode_arg(args[0])
214
+ @permissions = args[1].to_int
215
+ @options = args[2]
216
+ raise ArgumentError, "wrong number of arguments (4 for 1..3)" unless @options.is_a?(Hash)
217
+ end
218
+ process_options(@options)
219
+ end
220
+
221
+ def mode_arg(mode) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
222
+ if mode.respond_to?(:to_int)
223
+ @mode_bits = mode.to_int
224
+ else
225
+ @mode_string = mode.to_str
226
+ @file_mode, external_encoding_str, internal_encoding_str = @mode_string.split(":")
227
+
228
+ unless external_encoding_str.nil? || external_encoding_str.empty?
229
+ raise ArgumentError, "encoding specified twice" unless @external_encoding_str.empty?
230
+ @external_encoding_str = external_encoding_str.to_s
231
+ end
232
+ unless internal_encoding_str.nil? || internal_encoding_str.empty?
233
+ raise ArgumentError, "encoding specified twice" unless @internal_encoding_str.empty?
234
+ @internal_encoding_str = internal_encoding_str.to_s
235
+ end
236
+
237
+ mode_str_to_bits(@file_mode)
238
+ end
239
+ @mode_provided = true
240
+ end
241
+
242
+ def mode_str_to_bits(mode_str) # rubocop:disable AbcSize, CyclomaticComplexity
243
+ if mode_str[-1] == "b"
244
+ binmode
245
+ mode_str[-1] = ""
246
+ return if mode_str.empty?
247
+ end
248
+
249
+ @mode_bits = case mode_str
250
+ when "r" then VFile::RDONLY
251
+ when "r+" then VFile::RDWR
252
+ when "w" then VFile::WRONLY | VFile::TRUNC | VFile::CREAT
253
+ when "w+" then VFile::RDWR | VFile::TRUNC | VFile::CREAT
254
+ when "a" then VFile::WRONLY | VFile::APPEND | VFile::CREAT
255
+ when "a+" then VFile::RDWR | VFile::APPEND | VFile::CREAT
256
+ else
257
+ raise ArgumentError, "invalid access mode #{mode_str}"
258
+ end
259
+ end
260
+
261
+ def process_options(opts) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity, MethodLength
262
+ return if opts.empty?
263
+
264
+ @autoclose = opts[:autoclose] if opts.key?(:autoclose)
265
+ if opts.key?(:binmode)
266
+ raise ArgumentError, "binmode specified twice" if binmode?
267
+ binmode
268
+ end
269
+ if opts[:encoding]
270
+ raise ArgumentError, "encoding specified twice" unless @external_encoding_str.empty? && @internal_encoding_str.empty? # rubocop:disable LineLength
271
+ @external_encoding_str, @internal_encoding_str = opts[:encoding].split(":")
272
+ @external_encoding_str = @external_encoding_str.to_s
273
+ @internal_encoding_str = @internal_encoding_str.to_s
274
+ end
275
+ if opts[:external_encoding]
276
+ raise ArgumentError, "encoding specified twice" unless @external_encoding_str.empty?
277
+ @external_encoding_str = opts[:external_encoding].dup
278
+ end
279
+ if opts[:internal_encoding]
280
+ raise ArgumentError, "encoding specified twice" unless @internal_encoding_str.empty?
281
+ @internal_encoding_str = opts[:internal_encoding]
282
+ end
283
+ if opts[:mode]
284
+ # raise ArgumentError, "mode specified twice" if @mode_provided
285
+ mode_arg(opts[:mode])
286
+ end
287
+ if opts.key?(:textmode)
288
+ raise ArgumentError, "binmode and textmode are mutually exclusive" if binmode?
289
+ end
290
+ nil
291
+ end
292
+ end
293
+ end