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,238 @@
1
+ module VirtFS
2
+ # VirtFS Dir representation - implements the core Ruby Dir methods, dispatching
3
+ # to underlying mounted VirtFS filesystems
4
+ class VDir # rubocop:disable ClassLength
5
+ attr_accessor :fs_mod_obj
6
+
7
+ include DirInstanceDelegate
8
+
9
+ VfsRealDir.constants.each { |cn| const_set(cn, VfsRealDir.const_get(cn)) }
10
+
11
+ # VDir initializer
12
+ #
13
+ # @param dir_obj [VirtFS::FS::Dir] handle to filesystem specific dir obj
14
+ # @param path [String] path at which the dir resides
15
+ #
16
+ def initialize(dir_obj, path)
17
+ @open_path = path
18
+ __setobj__(dir_obj)
19
+ end
20
+
21
+ #
22
+ # Some methods need to return the Dir object. Methods in the delegator
23
+ # object can't do that, so we intercept them and do it here.
24
+ #
25
+
26
+ def each
27
+ return self if (rv = super) == __getobj__
28
+ rv
29
+ end
30
+
31
+ # @return [String] path which dir resides
32
+ def path
33
+ @open_path
34
+ end
35
+ alias_method :to_path, :path
36
+
37
+ def rewind
38
+ super
39
+ self
40
+ end
41
+
42
+ def seek(*args)
43
+ super
44
+ self
45
+ end
46
+
47
+ # Class methods
48
+ class << self
49
+ # Return dir entries matching the specified glob pattern
50
+ #
51
+ # @param glob_pattern [String,Regex] dir entry pattern to match
52
+ # @see Dir.[]
53
+ #
54
+ def [](glob_pattern)
55
+ glob(glob_pattern, 0)
56
+ end
57
+
58
+ # Change working directory to specified dir
59
+ #
60
+ # @param dir [String] path to change working directory to
61
+ # @see Dir.chdir
62
+ #
63
+ def chdir(dir = nil)
64
+ dir ||= VfsRealDir.home
65
+ raise SystemCallError.new(dir, Errno::ENOENT::Errno) unless exist?(dir)
66
+ if block_given?
67
+ pwd = getwd
68
+ begin
69
+ VirtFS.dir_chdir(dir)
70
+ return yield(getwd)
71
+ ensure
72
+ VirtFS.dir_chdir(pwd)
73
+ end
74
+ end
75
+ VirtFS.dir_chdir(dir)
76
+ 0
77
+ end
78
+
79
+ # Change root dir to specified dir
80
+ #
81
+ # @param dir [String] dir to change root dir to
82
+ # @see Dir.chroot
83
+ #
84
+ def chroot(dir)
85
+ VirtFS.dir_chroot(dir)
86
+ 0
87
+ end
88
+
89
+ # Delete specified dir
90
+ #
91
+ # @param dir [String] dir to delete
92
+ # @see Dir.delete
93
+ #
94
+ def delete(dir)
95
+ VirtFS.fs_lookup_call(dir, true) { |p| dir_delete(p) }
96
+ 0
97
+ end
98
+ alias_method :unlink, :delete
99
+ alias_method :rmdir, :delete
100
+
101
+ # Return array containing entries in specified dir
102
+ #
103
+ # @param dir [String] dir which to enumerate entries
104
+ #
105
+ # @return [Array<DirEntry>] array of dir entry instances
106
+ #
107
+ # @see Dir.entries
108
+ #
109
+ def entries(dir)
110
+ VirtFS.fs_lookup_call(dir) { |p| dir_entries(p) }
111
+ end
112
+
113
+ # Return bool indicating if specified dir exists
114
+ #
115
+ # @param dir [String] directory path to verify
116
+ # @return [Boolean] indicating if dir exists
117
+ #
118
+ def exist?(dir)
119
+ begin
120
+ fs, p = VirtFS.path_lookup(dir)
121
+ rescue Errno::ENOENT
122
+ return false
123
+ end
124
+ VirtFS.fs_call(fs) { dir_exist?(p) }
125
+ end
126
+ alias_method :exists?, :exist?
127
+
128
+ # Invoke block for each entry in dir
129
+ #
130
+ # @param dir [String] dir which to lookup entries
131
+ # @yield block to invoke
132
+ def foreach(dir, &block)
133
+ VirtFS.fs_lookup_call(dir) { |p| dir_foreach(p, &block) }
134
+ end
135
+
136
+ # @return [String] current working directory
137
+ def getwd
138
+ VirtFS.dir_getwd
139
+ end
140
+ alias_method :pwd, :getwd
141
+
142
+ # Return directory entries matching specified glob pattern
143
+ #
144
+ # @param glob_pattern [String] pattern to match
145
+ # @param flags [Integer] file match flags
146
+ # @yield block invoked with each match if specified
147
+ #
148
+ # @see VfsRealFile.fnmatch
149
+ # @see FindClassMethods#dir_and_glob which does most of the work regarding globbing
150
+ # @see FindClassMethods#find which retrieves stats information & dir entries for found files
151
+ #
152
+ def glob(glob_pattern, flags = 0)
153
+ search_path, specified_path, glob = VirtFS.dir_and_glob(glob_pattern)
154
+
155
+ unless exist?(search_path)
156
+ return [] unless block_given?
157
+ return false
158
+ end
159
+
160
+ ra = [] unless block_given?
161
+ VirtFS.find(search_path, VirtFS.glob_depth(glob)) do |p|
162
+ next if p == search_path
163
+
164
+ if search_path == VfsRealFile::SEPARATOR
165
+ p.sub!(VfsRealFile::SEPARATOR, "")
166
+ else
167
+ p.sub!("#{search_path}#{VfsRealFile::SEPARATOR}", "")
168
+ end
169
+
170
+ next if p == ""
171
+ next unless VfsRealFile.fnmatch(glob, p, flags)
172
+
173
+ p = VfsRealFile.join(specified_path, p) if specified_path
174
+ block_given? ? yield(p) : ra << p
175
+ end
176
+ block_given? ? false : ra.sort_by(&:downcase)
177
+ end
178
+
179
+ def home(*args)
180
+ VfsRealDir.home(*args)
181
+ end
182
+
183
+ # Make new dir at specified path
184
+ #
185
+ # @param dir [String] path to create
186
+ # @param permissions [Integer] initial permission to assign to dir
187
+ #
188
+ def mkdir(dir, permissions = 0700)
189
+ VirtFS.fs_lookup_call(dir, true) { |p| dir_mkdir(p, permissions) }
190
+ 0
191
+ end
192
+
193
+ # Instantiate new directory instance.
194
+ #
195
+ # @param dir [String] path to dir to instantiate
196
+ # @param hash_args [Hash] args to use when creating Dir instance
197
+ #
198
+ # @see VirtFS.fs_call
199
+ # @see ThinDirDelegator
200
+ #
201
+ def new(dir, hash_args = {})
202
+ fs, p = VirtFS.path_lookup(dir)
203
+ fs_obj = VirtFS.fs_call(fs) { dir_new(p, hash_args, dir, VDir.getwd) }
204
+
205
+ obj = allocate
206
+ if fs.thin_interface?
207
+ obj.send(:initialize, ThinDirDelegator.new(fs_obj, dir, p, hash_args), dir)
208
+ else
209
+ obj.send(:initialize, fs_obj, dir)
210
+ end
211
+
212
+ # fs_mod_obj always points to the fs module's file object
213
+ # for use by fs-specific extension modules
214
+ obj.fs_mod_obj = fs_obj
215
+ obj.extend(fs_obj.extension_module) if fs_obj.respond_to?(:extension_module) # fs-specific extension module
216
+ obj
217
+ end
218
+
219
+ # Open specified existing dir and invoke block with it before closing
220
+ #
221
+ # @param dir [String] path to dir to instantiate
222
+ # @param hash_args [Hash] args to use when creating Dir instance
223
+ #
224
+ # @yield the directory instance
225
+ # @see .new
226
+ #
227
+ def open(dir, hash_args = {})
228
+ dir_obj = new(dir, hash_args)
229
+ return dir_obj unless block_given?
230
+ begin
231
+ return yield(dir_obj)
232
+ ensure
233
+ dir_obj.close
234
+ end
235
+ end
236
+ end # class methods
237
+ end
238
+ end
@@ -0,0 +1,480 @@
1
+ module VirtFS
2
+ # VirtFS File representation - implements the core Ruby File methods, dispatching
3
+ # to underlying mounted VirtFS filesystems
4
+ #
5
+ class VFile < VIO # rubocop:disable ClassLength
6
+ attr_accessor :fs_mod_obj
7
+
8
+ include FileInstanceDelegate
9
+
10
+ VfsRealFile.constants.each { |cn| const_set(cn, VfsRealFile.const_get(cn)) }
11
+
12
+ # VFile initializer
13
+ #
14
+ # @param file_obj [VirtFS::FS::File] handle to filesystem specific file obj
15
+ # @param path [String] path at which the file resides
16
+ #
17
+ def initialize(file_obj, path)
18
+ @open_path = path
19
+ __setobj__(file_obj)
20
+ end
21
+
22
+ #
23
+ # Some methods need to return the File object. Methods in the delegator
24
+ # object can't do that, so we intercept them and do it here.
25
+ #
26
+
27
+ def <<(obj)
28
+ super
29
+ self
30
+ end
31
+
32
+ def binmode
33
+ super
34
+ self
35
+ end
36
+
37
+ def each(*args)
38
+ return self if (rv = super) == __getobj__
39
+ rv
40
+ end
41
+
42
+ def each_byte
43
+ return self if (rv = super) == __getobj__
44
+ rv
45
+ end
46
+
47
+ def each_char
48
+ return self if (rv = super) == __getobj__
49
+ rv
50
+ end
51
+
52
+ def each_codepoint
53
+ return self if (rv = super) == __getobj__
54
+ rv
55
+ end
56
+
57
+ def flush
58
+ return self if (rv = super) == __getobj__
59
+ rv
60
+ end
61
+
62
+ # @return [String] path which dir resides
63
+ def path
64
+ @open_path
65
+ end
66
+ alias_method :to_path, :path
67
+
68
+ # Reopens file with the given args
69
+ def reopen(*args)
70
+ new_path = nil
71
+ if !args[0].respond_to?(:to_str) && args[0].respond_to?(:__getobj__)
72
+ # Given an IO object.
73
+ to_obj = args[0]
74
+ args = [to_obj.__getobj__]
75
+ new_path = to_obj.path
76
+ end
77
+ new_obj = __getobj__.reopen(*args)
78
+ __setobj__(new_obj)
79
+ @open_path = new_path || new_obj.path
80
+ self
81
+ end
82
+
83
+ def set_encoding(*args)
84
+ super
85
+ self
86
+ end
87
+
88
+ def to_io
89
+ self
90
+ end
91
+
92
+ def min_read_buf_sz=(val)
93
+ __getobj__.send(:min_read_buf_sz=, val)
94
+ rescue
95
+ # ignore
96
+ end
97
+ private :min_read_buf_sz=
98
+
99
+ # Class methods
100
+ class << self
101
+ # @return absolute path of file (across mounted filesystems)
102
+ def absolute_path(f, dirstring = nil)
103
+ dir = dirstring || VirtFS.dir_getwd
104
+ VfsRealFile.absolute_path(f, dir)
105
+ end
106
+
107
+ # @return [Time] access time of the file
108
+ def atime(f)
109
+ VirtFS.fs_lookup_call(f) { |p| file_atime(p) }
110
+ end
111
+
112
+ # @return [String] base name of the file
113
+ def basename(*args)
114
+ VfsRealFile.basename(*args)
115
+ end
116
+
117
+ # @return [Boolean] indicating if file is a block device
118
+ def blockdev?(f)
119
+ VirtFS.fs_lookup_call(f) { |p| file_blockdev?(p) }
120
+ end
121
+
122
+ # @return [Boolean] indicating if file is a char device
123
+ def chardev?(f)
124
+ VirtFS.fs_lookup_call(f) { |p| file_chardev?(p) }
125
+ end
126
+
127
+ # Change File ACLs
128
+ #
129
+ # @param permission [Integer] new acl to assign to file(s)
130
+ def chmod(permission, *files)
131
+ nfp = 0
132
+ files.each do |f|
133
+ nfp += VirtFS.fs_lookup_call(f) { |p| file_chmod(permission, p) }
134
+ end
135
+ nfp
136
+ end
137
+
138
+ # Change ownership / group ownership of file
139
+ #
140
+ # @param owner [Integer,String] new owner of the file(s)
141
+ # @param group [Integer,String] new group owner of the file(s)
142
+ def chown(owner, group, *files)
143
+ owner = owner.to_int
144
+ group = group.to_int
145
+ nfp = 0
146
+ files.each do |f|
147
+ nfp += VirtFS.fs_lookup_call(f) { |p| file_chown(owner, group, p) }
148
+ end
149
+ nfp
150
+ end
151
+
152
+ # @return ]Time] change time of time
153
+ def ctime(f)
154
+ VirtFS.fs_lookup_call(f) { |p| file_ctime(p) }
155
+ end
156
+
157
+ # Delete specified files
158
+ def delete(*files)
159
+ nfp = 0
160
+ files.each do |f|
161
+ nfp += VirtFS.fs_lookup_call(f, false, false) { |p| file_delete(p) }
162
+ end
163
+ nfp
164
+ end
165
+ alias_method :unlink, :delete
166
+
167
+ # @return [Boolean] indiciating if file is a directory
168
+ def directory?(f)
169
+ VirtFS.fs_lookup_call(f) { |p| file_directory?(p) }
170
+ end
171
+
172
+ # @return [String] containg file directory name
173
+ def dirname(*args)
174
+ VfsRealFile.dirname(*args)
175
+ end
176
+
177
+ # @return [Boolean] indiciating if file is executable
178
+ def executable?(f)
179
+ VirtFS.fs_lookup_call(f) { |p| file_executable?(p) }
180
+ end
181
+
182
+ # @return [Boolean] indiciating if file is executable and real
183
+ def executable_real?(f)
184
+ VirtFS.fs_lookup_call(f) { |p| file_executable_real?(p) }
185
+ end
186
+
187
+ # @return [Boolean] indiciating if file exists
188
+ def exist?(f)
189
+ VirtFS.fs_lookup_call(f) { |p| file_exist?(p) }
190
+ end
191
+ alias_method :exists?, :exist?
192
+
193
+ # @return [String] full expanded path to file
194
+ def expand_path(f, dirstring = nil)
195
+ dir = dirstring || VirtFS.dir_getwd
196
+ VfsRealFile.expand_path(f, dir)
197
+ end
198
+
199
+ # @return [String] containg file extension name
200
+ def extname(*args)
201
+ VfsRealFile.extname(*args)
202
+ end
203
+
204
+ # @return [Boolean] indiciating if file is a regular file
205
+ def file?(f)
206
+ VirtFS.fs_lookup_call(f) { |p| file_file?(p) }
207
+ end
208
+
209
+ # @return [Array<String>] names of files matching given args
210
+ def fnmatch(*args)
211
+ VfsRealFile.fnmatch(*args)
212
+ end
213
+ alias_method :fnmatch?, :fnmatch
214
+
215
+ # @return type of file specified
216
+ def ftype(f)
217
+ VirtFS.fs_lookup_call(f) { |p| file_ftype(p) }
218
+ end
219
+
220
+ # @return [Boolean] indicating if file is group owned
221
+ def grpowned?(f)
222
+ VirtFS.fs_lookup_call(f) { |p| file_grpowned?(p) }
223
+ end
224
+
225
+ # @return [Boolean] indicating if files are identical
226
+ def identical?(fname1, fname2)
227
+ fs1, p1 = VirtFS.path_lookup(fname1)
228
+ fs2, p2 = VirtFS.path_lookup(fname2)
229
+ return false unless fs1 == fs2
230
+ VirtFS.fs_call(fs1) { file_identical?(p1, p2) }
231
+ end
232
+
233
+ # @return [String] containing joined path components
234
+ def join(*args)
235
+ VfsRealFile.join(*args)
236
+ end
237
+
238
+ # Invoke a 'lchmod' on the given files
239
+ #
240
+ # @param permission [Integer] new permission to assign to file(s)
241
+ #
242
+ def lchmod(permission, *files)
243
+ nfp = 0
244
+ files.each do |f|
245
+ nfp += VirtFS.fs_lookup_call(f) { |p| file_lchmod(permission, p) }
246
+ end
247
+ nfp
248
+ end
249
+
250
+ # Invoke a 'lchown' on the given files
251
+ #
252
+ # @param owner [String] new owner to assign to file(s)
253
+ # @param group [String] new group to assign to file(s)
254
+ #
255
+ def lchown(owner, group, *files)
256
+ nfp = 0
257
+ files.each do |f|
258
+ nfp += VirtFS.fs_lookup_call(f, false, false) { |p| file_lchown(owner, group, p) }
259
+ end
260
+ nfp
261
+ end
262
+
263
+ # Create a symbol link between files
264
+ #
265
+ # @param oname [String] file to link to
266
+ # @param nname [String] symbolic link to create
267
+ #
268
+ def link(oname, nname)
269
+ fs1, p1 = VirtFS.path_lookup(oname)
270
+ fs2, p2 = VirtFS.path_lookup(nname)
271
+ raise SystemCallError, "Can't hard link between filesystems" unless fs1 == fs2 # TODO: check exception
272
+ VirtFS.fs_call(fs1) { file_link(p1, p2) }
273
+ end
274
+
275
+ # @return [Stat] file stat for specified file
276
+ def lstat(f)
277
+ VirtFS.fs_lookup_call(f, false, false) { |p| file_lstat(p) }
278
+ end
279
+
280
+ # @return [Time] modification time of the specified file
281
+ def mtime(f)
282
+ VirtFS.fs_lookup_call(f) { |p| file_mtime(p) }
283
+ end
284
+
285
+ # @return [Boolean] indicating if file is owned
286
+ def owned?(f)
287
+ VirtFS.fs_lookup_call(f) { |p| file_owned?(p) }
288
+ end
289
+
290
+ # @return path to specified file object
291
+ def path(obj)
292
+ VfsRealFile.path(obj) # will check obj.to_path
293
+ end
294
+
295
+ # @return [Boolean] indicating if file is pipe
296
+ def pipe?(f)
297
+ VirtFS.fs_lookup_call(f) { |p| file_pipe?(p) }
298
+ end
299
+
300
+ # @return [Boolean] indicating if file is readable
301
+ def readable?(f)
302
+ VirtFS.fs_lookup_call(f) { |p| file_readable?(p) }
303
+ end
304
+
305
+ # @return [Boolean] indicating if file is real and readable
306
+ def readable_real?(f)
307
+ VirtFS.fs_lookup_call(f) { |p| file_readable_real?(p) }
308
+ end
309
+
310
+ # @return [String] name of file references by link
311
+ def readlink(f)
312
+ VirtFS.fs_lookup_call(f, false, false) { |p| file_readlink(p) }
313
+ end
314
+
315
+ # @return [String] real directory containing file
316
+ def realdirpath(path, relative_to = nil) # ???
317
+ VirtFS.expand_links(VirtFS.normalize_path(path, relative_to))
318
+ end
319
+
320
+ # @return [String] real path of the file
321
+ def realpath(path, relative_to = nil) # ???
322
+ VirtFS.expand_links(VirtFS.normalize_path(path, relative_to))
323
+ end
324
+
325
+ # Rename file
326
+ #
327
+ # @param oname [String] file to rename
328
+ # @param nname [String] new name to assign to file
329
+ #
330
+ def rename(oname, nname)
331
+ fs1, p1 = VirtFS.path_lookup(oname)
332
+ fs2, p2 = VirtFS.path_lookup(nname)
333
+ raise SystemCallError, "Can't rename between filesystems" unless fs1 == fs2 # TODO: check exception
334
+ VirtFS.fs_call(fs1) { file_rename(p1, p2) }
335
+ end
336
+
337
+ # @return [Boolean] indicating if file GID is set
338
+ def setgid?(f)
339
+ VirtFS.fs_lookup_call(f) { |p| file_setgid?(p) }
340
+ end
341
+
342
+ # @return [Boolean] indicating if file UID is set
343
+ def setuid?(f)
344
+ VirtFS.fs_lookup_call(f) { |p| file_setuid?(p) }
345
+ end
346
+
347
+ # @return [Integer] size of the file in bytes
348
+ def size(f)
349
+ VirtFS.fs_lookup_call(f) { |p| file_size(p) }
350
+ end
351
+
352
+ # @return [Integer,nil] same as #size but return nil if empty
353
+ def size?(f)
354
+ sz = size(f)
355
+ return nil if sz == 0
356
+ sz
357
+ end
358
+
359
+ # @return [Boolean] indicating if file is a socket
360
+ def socket?(f)
361
+ VirtFS.fs_lookup_call(f) { |p| file_socket?(p) }
362
+ end
363
+
364
+ # @return [Array<String>] split file path
365
+ def split(f)
366
+ VfsRealFile.split(f)
367
+ end
368
+
369
+ # @return [Stat] file stat correspond to file
370
+ def stat(f)
371
+ VirtFS.fs_lookup_call(f) { |p| file_stat(p) }
372
+ end
373
+
374
+ # @return [Boolean] indicating if file is sticky
375
+ def sticky?(f)
376
+ VirtFS.fs_lookup_call(f) { |p| file_sticky?(p) }
377
+ end
378
+
379
+ # Create new symlink to file
380
+ #
381
+ # @param oname [String] file to link to
382
+ # @param nname [String] symbollic link to create
383
+ #
384
+ def symlink(oname, nname)
385
+ #
386
+ # oname is the path to the original file in the global FS namespace.
387
+ # It is not modified and used as the link target.
388
+ #
389
+ VirtFS.fs_lookup_call(nname) { |p| file_symlink(oname, p) }
390
+ end
391
+
392
+ # @return [Boolean] indicating if file is symlink
393
+ def symlink?(f)
394
+ VirtFS.fs_lookup_call(f, false, false) { |p| file_symlink?(p) }
395
+ end
396
+
397
+ # Truncate file to the specified len
398
+ #
399
+ # @param f [String] file to truncate
400
+ # @param len [Integer] length to truncate file to (in bytes)
401
+ def truncate(f, len)
402
+ VirtFS.fs_lookup_call(f) { |p| file_truncate(p, len) }
403
+ end
404
+
405
+ # @return [Integer] umake of file
406
+ def umask(*args)
407
+ VfsRealFile.umask(*args)
408
+ end
409
+
410
+ # Update file time
411
+ #
412
+ # @param atime [Time] new access time to assign to file(s)
413
+ # @param mtime [Time] new modification time to assign to file(s)
414
+ def utime(atime, mtime, *files)
415
+ nfp = 0
416
+ files.each do |f|
417
+ nfp += VirtFS.fs_lookup_call(f) { |p| file_utime(atime, mtime, p) }
418
+ end
419
+ nfp
420
+ end
421
+
422
+ # @return [Boolean] indicating if file is world readable
423
+ def world_readable?(f)
424
+ VirtFS.fs_lookup_call(f) { |p| file_world_readable?(p) }
425
+ end
426
+
427
+ # @return [Boolean] indicating if file is world writable
428
+ def world_writable?(f)
429
+ VirtFS.fs_lookup_call(f) { |p| file_world_writable?(p) }
430
+ end
431
+
432
+ # @return [Boolean] indicating if file is writable
433
+ def writable?(f)
434
+ VirtFS.fs_lookup_call(f) { |p| file_writable?(p) }
435
+ end
436
+
437
+ # @return [Boolean] indicating if file is writable and real
438
+ def writable_real?(f)
439
+ VirtFS.fs_lookup_call(f) { |p| file_writable_real?(p) }
440
+ end
441
+
442
+ def zero?(f)
443
+ fs, p = VirtFS.path_lookup(f)
444
+ begin
445
+ VirtFS.fs_call(fs) { file_chardev?(p) }
446
+ return fs.file_size(p) == 0
447
+ rescue Errno::ENOENT
448
+ return false
449
+ end
450
+ end
451
+
452
+ # Instantiate new file instance.
453
+ #
454
+ # @param file_id [String] file identifier (usually path)
455
+ # @param args args to forward to file initializer
456
+ def new(file_id, *args) # rubocop:disable AbcSize
457
+ if file_id.respond_to?(:to_int)
458
+ fs_obj = VfsRealIO.new(file_id, *args)
459
+ else
460
+ parsed_args = FileModesAndOptions.new(*args)
461
+ fs, p = VirtFS.path_lookup(file_id, false, false)
462
+ fs_obj = VirtFS.fs_call(fs) { file_new(p, parsed_args, file_id, VDir.getwd) }
463
+ end
464
+
465
+ obj = allocate
466
+ if fs.thin_interface?
467
+ obj.send(:initialize, ThinFileDelegator.new(fs_obj, file_id, p, parsed_args), file_id)
468
+ else
469
+ obj.send(:initialize, fs_obj, file_id)
470
+ end
471
+
472
+ # fs_mod_obj always points to the fs module's file object
473
+ # for use by fs-specific extension modules
474
+ obj.fs_mod_obj = fs_obj
475
+ obj.extend(fs_obj.extension_module) if fs_obj.respond_to?(:extension_module) # fs-specific extension module
476
+ obj
477
+ end
478
+ end # class methods
479
+ end
480
+ end