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