sys-filesystem 1.5.5 → 1.6.0

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.
@@ -16,11 +16,184 @@ RSpec.describe Sys::Filesystem, :unix do
16
16
  let(:darwin) { RbConfig::CONFIG['host_os'] =~ /mac|darwin/i }
17
17
  let(:root) { '/' }
18
18
 
19
+ def expected_case_insensitive(stat)
20
+ if stat.path =~ /\w+/
21
+ File.identical?(stat.path, stat.path.swapcase)
22
+ else
23
+ zfs_case = zfs_case_insensitive(stat)
24
+ return zfs_case unless zfs_case.nil?
25
+
26
+ if darwin
27
+ true
28
+ else
29
+ false
30
+ end
31
+ end
32
+ end
33
+
34
+ def zfs_case_insensitive(stat)
35
+ return nil unless stat.base_type == 'zfs'
36
+
37
+ mount_point = described_class.mount_point(stat.path)
38
+ mount = described_class.mounts.find{ |mnt| mnt.mount_point == mount_point }
39
+ return nil unless mount
40
+
41
+ value = described_class.send(:zfs_property, mount.name, 'casesensitivity')
42
+ return nil unless value
43
+
44
+ case value.strip
45
+ when 'insensitive'
46
+ true
47
+ when 'sensitive'
48
+ false
49
+ else
50
+ nil
51
+ end
52
+ rescue SystemCallError
53
+ nil
54
+ end
55
+
56
+ def write_struct_members(struct, values)
57
+ values.each do |member, value|
58
+ struct[member] = value if struct.members.include?(member)
59
+ end
60
+ end
61
+
62
+ def stub_stat_syscalls
63
+ statvfs_values = {
64
+ f_bsize: 1_048_576,
65
+ f_frsize: 4096,
66
+ f_blocks: 1_000_000,
67
+ f_bfree: 400_000,
68
+ f_bavail: 300_000,
69
+ f_files: 2_000_000,
70
+ f_ffree: 1_500_000,
71
+ f_favail: 1_250_000,
72
+ f_fsid: 1234,
73
+ f_flag: 1,
74
+ f_namemax: 255,
75
+ f_owner: 501,
76
+ f_type: 42,
77
+ f_syncreads: 10,
78
+ f_asyncreads: 20,
79
+ f_syncwrites: 30,
80
+ f_asyncwrites: 40
81
+ }
82
+
83
+ statfs_values = {
84
+ f_flags: 1,
85
+ f_namemax: 255,
86
+ f_fstypename: 'fixturefs',
87
+ f_mntfromname: '/dev/fixture',
88
+ f_mntonname: root,
89
+ f_type: 42,
90
+ f_owner: 501,
91
+ f_syncreads: 10,
92
+ f_asyncreads: 20,
93
+ f_syncwrites: 30,
94
+ f_asyncwrites: 40
95
+ }
96
+
97
+ allow(described_class).to receive(:statvfs) do |_path, fs|
98
+ write_struct_members(fs, statvfs_values)
99
+ 0
100
+ end
101
+
102
+ if described_class.respond_to?(:statfs, true)
103
+ allow(described_class).to receive(:statfs) do |_path, fs|
104
+ write_struct_members(fs, statfs_values)
105
+ 0
106
+ end
107
+ end
108
+
109
+ allow(described_class).to receive(:enrich_mount_metadata)
110
+ end
111
+
19
112
  before do
20
113
  @stat = described_class.stat(root)
21
114
  @size = 58720256
22
115
  end
23
116
 
117
+ context 'platform constants' do
118
+ example 'freebsd mount constants match sys/mount.h' do
119
+ skip 'FreeBSD constant test skipped except on FreeBSD' unless RbConfig::CONFIG['host_os'] =~ /freebsd/i
120
+
121
+ expect(described_class::MNT_RDONLY).to eq(0x0000000000000001)
122
+ expect(described_class::MNT_NFS4ACLS).to eq(0x0000000000000010)
123
+ expect(described_class.const_defined?(:MNT_NODEV)).to be(false)
124
+ expect(described_class::MNT_LOCAL).to eq(0x0000000000001000)
125
+ expect(described_class::MNT_ROOTFS).to eq(0x0000000000004000)
126
+ expect(described_class::MNT_NOATIME).to eq(0x0000000010000000)
127
+ expect(described_class::MNT_FORCE).to eq(0x0000000000080000)
128
+ expect(described_class::MNT_BYFSID).to eq(0x0000000008000000)
129
+ end
130
+
131
+ example 'freebsd mount option names decode nfsv4acls distinctly from nodev' do
132
+ skip 'FreeBSD option-name test skipped except on FreeBSD' unless RbConfig::CONFIG['host_os'] =~ /freebsd/i
133
+
134
+ expect(described_class.send(:decode_mount_options, described_class::MNT_NFS4ACLS)).to eq('nfsv4acls')
135
+ expect(described_class::Constants::MOUNT_OPTION_NAMES.value?('nodev')).to be(false)
136
+ end
137
+
138
+ example 'freebsd root mount options agree with stat flags' do
139
+ skip 'FreeBSD root option test skipped except on FreeBSD' unless RbConfig::CONFIG['host_os'] =~ /freebsd/i
140
+
141
+ expect(@stat.mount_options).to eq(described_class.send(:decode_mount_options, @stat.flags))
142
+ end
143
+
144
+ example 'linux mount constants expose linux flags' do
145
+ skip 'Linux constant test skipped except on Linux' unless linux
146
+
147
+ expect(described_class::MS_RDONLY).to eq(1)
148
+ expect(described_class::MS_NODEV).to eq(4)
149
+ expect(described_class::MNT_NODEV).to eq(described_class::MS_NODEV)
150
+ expect(described_class::MNT_FORCE).to eq(1)
151
+ expect(described_class::MNT_DETACH).to eq(2)
152
+ expect(described_class::UMOUNT_NOFOLLOW).to eq(8)
153
+ end
154
+
155
+ example 'darwin mount constants expose darwin flags' do
156
+ skip 'Darwin constant test skipped except on Darwin' unless darwin
157
+
158
+ expect(described_class::MNT_NODEV).to eq(0x00000010)
159
+ expect(described_class::MNT_CPROTECT).to eq(0x00000080)
160
+ expect(described_class::MNT_QUARANTINE).to eq(0x00000400)
161
+ expect(described_class::MNT_FORCE).to eq(1)
162
+ end
163
+
164
+ example 'generic constants avoid darwin-specific flags' do
165
+ command = [
166
+ RbConfig.ruby,
167
+ '-Ilib',
168
+ '-e',
169
+ 'require "sys/unix/sys/filesystem/constants/generic"; ' \
170
+ 'c = Sys::Filesystem::Constants; ' \
171
+ 'abort unless c::MNT_RDONLY == 1; ' \
172
+ 'abort unless c::MOUNT_OPTION_NAMES[c::MNT_NOSUID] == "nosuid"; ' \
173
+ 'abort if c.const_defined?(:MNT_CPROTECT); ' \
174
+ 'abort if c.const_defined?(:MNT_QUARANTINE)'
175
+ ]
176
+
177
+ expect(system(*command)).to be(true)
178
+ end
179
+
180
+ example 'dragonfly constants use the shared bsd set' do
181
+ command = [
182
+ RbConfig.ruby,
183
+ '-Ilib',
184
+ '-e',
185
+ 'require "sys/unix/sys/filesystem/constants/dragonfly"; ' \
186
+ 'c = Sys::Filesystem::Constants; ' \
187
+ 'abort unless c::MNT_RDONLY == 1; ' \
188
+ 'abort unless c::MNT_LOCAL == 0x1000; ' \
189
+ 'abort unless c::MOUNT_OPTION_NAMES[c::MNT_NOATIME] == "noatime"; ' \
190
+ 'abort if c.const_defined?(:MNT_CPROTECT)'
191
+ ]
192
+
193
+ expect(system(*command)).to be(true)
194
+ end
195
+ end
196
+
24
197
  example 'stat path works as expected' do
25
198
  expect(@stat).to respond_to(:path)
26
199
  expect(@stat.path).to eq(root)
@@ -102,6 +275,87 @@ RSpec.describe Sys::Filesystem, :unix do
102
275
  expect(@stat.name_max).to be_a(Numeric)
103
276
  end
104
277
 
278
+ context 'bsd statfs enrichments' do
279
+ before do
280
+ skip 'statfs enrichment tests skipped except on BSD' unless bsd
281
+ end
282
+
283
+ example 'base_type is populated from statfs' do
284
+ expect(@stat.base_type).to be_a(String)
285
+ expect(@stat.base_type).not_to be_empty
286
+ end
287
+
288
+ example 'base_type matches the mount type' do
289
+ root_mount = described_class.mounts.find{ |mount| mount.mount_point == root }
290
+
291
+ expect(@stat.base_type).to eq(root_mount.mount_type)
292
+ end
293
+
294
+ example 'mount metadata matches the mount table' do
295
+ root_mount = described_class.mounts.find{ |mount| mount.mount_point == root }
296
+
297
+ expect(@stat.mount_source).to eq(root_mount.name)
298
+ expect(@stat.mount_point).to eq(root_mount.mount_point)
299
+ expect(@stat.mount_type).to eq(root_mount.mount_type)
300
+ expect(@stat.mount_options).to eq(root_mount.options)
301
+ end
302
+
303
+ example 'zfs dataset uses stat mount metadata when available' do
304
+ skip 'zfs dataset test skipped except on ZFS' unless @stat.base_type == 'zfs'
305
+
306
+ expect(described_class).not_to receive(:mounts)
307
+ expect(@stat.send(:zfs_dataset)).to eq(@stat.mount_source)
308
+ end
309
+
310
+ example 'filesystem_type is populated from statfs' do
311
+ expect(@stat.filesystem_type).to be_a(Numeric)
312
+ end
313
+
314
+ example 'owner is populated from statfs' do
315
+ expect(@stat.owner).to be_a(Numeric)
316
+ end
317
+
318
+ example 'read and write counters are populated from statfs' do
319
+ expect(@stat.sync_reads).to be_a(Numeric)
320
+ expect(@stat.async_reads).to be_a(Numeric)
321
+ expect(@stat.sync_writes).to be_a(Numeric)
322
+ expect(@stat.async_writes).to be_a(Numeric)
323
+ end
324
+ end
325
+
326
+ context 'zfs properties' do
327
+ before do
328
+ skip 'zfs property tests skipped except on ZFS' unless @stat.base_type == 'zfs'
329
+ end
330
+
331
+ example 'generic zfs_property returns property values' do
332
+ expect(@stat.zfs_property('casesensitivity')).to be_a(String)
333
+ expect(@stat.zfs_property(:compression)).to be_a(String)
334
+ end
335
+
336
+ example 'convenience methods match generic zfs_property' do
337
+ {
338
+ zfs_atime: 'atime',
339
+ zfs_casesensitivity: 'casesensitivity',
340
+ zfs_compression: 'compression',
341
+ zfs_compressratio: 'compressratio',
342
+ zfs_devices: 'devices',
343
+ zfs_exec: 'exec',
344
+ zfs_quota: 'quota',
345
+ zfs_readonly: 'readonly',
346
+ zfs_recordsize: 'recordsize',
347
+ zfs_reservation: 'reservation',
348
+ zfs_setuid: 'setuid'
349
+ }.each do |method_name, property|
350
+ expect(@stat.public_send(method_name)).to eq(@stat.zfs_property(property))
351
+ end
352
+ end
353
+
354
+ example 'unknown zfs properties return nil' do
355
+ expect(@stat.zfs_property('definitely_not_a_zfs_property')).to be_nil
356
+ end
357
+ end
358
+
105
359
  context 'dragonfly', :dragonfly do
106
360
  example 'owner works as expected' do
107
361
  expect(@stat).to respond_to(:owner)
@@ -171,15 +425,17 @@ RSpec.describe Sys::Filesystem, :unix do
171
425
  end
172
426
 
173
427
  example 'stat case_insensitive method works as expected' do
174
- expected = darwin ? true : false
175
- expect(@stat.case_insensitive?).to eq(expected)
176
- expect(described_class.stat(Dir.home).case_insensitive?).to eq(expected)
428
+ home_stat = described_class.stat(Dir.home)
429
+
430
+ expect(@stat.case_insensitive?).to eq(expected_case_insensitive(@stat))
431
+ expect(home_stat.case_insensitive?).to eq(expected_case_insensitive(home_stat))
177
432
  end
178
433
 
179
434
  example 'stat case_sensitive method works as expected' do
180
- expected = darwin ? false : true
181
- expect(@stat.case_sensitive?).to eq(expected)
182
- expect(described_class.stat(Dir.home).case_sensitive?).to eq(expected)
435
+ home_stat = described_class.stat(Dir.home)
436
+
437
+ expect(@stat.case_sensitive?).to eq(!expected_case_insensitive(@stat))
438
+ expect(home_stat.case_sensitive?).to eq(!expected_case_insensitive(home_stat))
183
439
  end
184
440
 
185
441
  example 'numeric helper methods are defined' do
@@ -203,6 +459,8 @@ RSpec.describe Sys::Filesystem, :unix do
203
459
 
204
460
  context 'Filesystem.stat(Pathname)' do
205
461
  before do
462
+ stub_stat_syscalls
463
+ @stat = described_class.stat(root)
206
464
  @stat_pathname = described_class.stat(Pathname.new(root))
207
465
  end
208
466
 
@@ -265,6 +523,8 @@ RSpec.describe Sys::Filesystem, :unix do
265
523
 
266
524
  context 'Filesystem.stat(File)' do
267
525
  before do
526
+ stub_stat_syscalls
527
+ @stat = described_class.stat(root)
268
528
  @stat_file = File.open(root){ |file| described_class.stat(file) }
269
529
  end
270
530
 
@@ -327,6 +587,8 @@ RSpec.describe Sys::Filesystem, :unix do
327
587
 
328
588
  context 'Filesystem.stat(Dir)' do
329
589
  before do
590
+ stub_stat_syscalls
591
+ @stat = described_class.stat(root)
330
592
  @stat_dir = Dir.open(root){ |dir| described_class.stat(dir) }
331
593
  end
332
594
 
@@ -486,6 +748,23 @@ RSpec.describe Sys::Filesystem, :unix do
486
748
  example 'umount singleton method is defined' do
487
749
  expect(described_class).to respond_to(:umount)
488
750
  end
751
+
752
+ example 'freebsd nmount iovec is built as expected' do
753
+ skip 'nmount iovec test skipped except on FreeBSD' unless RbConfig::CONFIG['host_os'] =~ /freebsd/i
754
+
755
+ iov = described_class.send(
756
+ :nmount_iovec,
757
+ '/dev/md0',
758
+ '/mnt/test',
759
+ 'ufs',
760
+ readonly: ''
761
+ )
762
+
763
+ expect(iov[:count]).to eq(8)
764
+ expect(iov[:strings].map(&:read_string)).to eq(
765
+ %w[fstype ufs fspath /mnt/test from /dev/md0 readonly] + ['']
766
+ )
767
+ end
489
768
  end
490
769
 
491
770
  context 'FFI' do
@@ -496,8 +775,10 @@ RSpec.describe Sys::Filesystem, :unix do
496
775
  let(:dummy) { Class.new { extend Mkmf::Lite } }
497
776
 
498
777
  example 'ffi functions are private' do
499
- expect(described_class.methods.include?('statvfs')).to be false
500
- expect(described_class.methods.include?('strerror')).to be false
778
+ Sys::Filesystem::Functions.attached_functions.each_key do |function_name|
779
+ expect(described_class.methods).not_to include(function_name)
780
+ expect(described_class.private_methods).to include(function_name)
781
+ end
501
782
  end
502
783
 
503
784
  example 'statfs struct is expected size' do
@@ -514,6 +795,11 @@ RSpec.describe Sys::Filesystem, :unix do
514
795
  expect(Sys::Filesystem::Structs::Mntent.size).to eq(dummy.check_sizeof('struct mntent', 'mntent.h'))
515
796
  end
516
797
 
798
+ example 'iovec struct is expected size' do
799
+ skip 'iovec test skipped except on FreeBSD' unless RbConfig::CONFIG['host_os'] =~ /freebsd/i
800
+ expect(Sys::Filesystem::Structs::Iovec.size).to eq(dummy.check_sizeof('struct iovec', 'sys/uio.h'))
801
+ end
802
+
517
803
  example 'a failed statvfs call behaves as expected' do
518
804
  msg = 'statvfs() function failed: No such file or directory'
519
805
  expect{ described_class.stat('/whatever') }.to raise_error(Sys::Filesystem::Error, msg)
@@ -523,6 +809,26 @@ RSpec.describe Sys::Filesystem, :unix do
523
809
  expect(Sys::Filesystem::Functions.attached_functions[:statvfs]).to be_a(FFI::Function)
524
810
  expect(Sys::Filesystem::Functions.attached_functions[:statvfs64]).to be_nil
525
811
  end
812
+
813
+ example 'freebsd nmount binding is private when available' do
814
+ skip 'nmount test skipped except on FreeBSD' unless RbConfig::CONFIG['host_os'] =~ /freebsd/i
815
+ expect(described_class.methods.include?('nmount_c')).to be false
816
+ expect(Sys::Filesystem::Functions.attached_functions[:nmount_c]).to be_a(FFI::Function)
817
+ end
818
+
819
+ example 'libzfs loading is limited to zfs-capable unix platforms' do
820
+ functions = Sys::Filesystem::Functions
821
+
822
+ allow(RbConfig::CONFIG).to receive(:[]).and_call_original
823
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('darwin24')
824
+ expect(functions.send(:zfs_supported?)).to be_falsey
825
+
826
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('freebsd15.0')
827
+ expect(functions.send(:zfs_supported?)).to be_truthy
828
+
829
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('linux-gnu')
830
+ expect(functions.send(:zfs_supported?)).to be_truthy
831
+ end
526
832
  end
527
833
 
528
834
  describe 'linux64? method' do
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'sys-filesystem'
5
- spec.version = '1.5.5'
5
+ spec.version = '1.6.0'
6
6
  spec.author = 'Daniel J. Berger'
7
7
  spec.email = 'djberg96@gmail.com'
8
8
  spec.homepage = 'https://github.com/djberg96/sys-filesystem'
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.add_development_dependency('rubocop')
20
20
  spec.add_development_dependency('rubocop-rspec')
21
21
 
22
- if RUBY_PLATFORM == 'java' && Gem.win_platform?
23
- spec.add_dependency('jruby-win32ole')
22
+ if Gem.win_platform?
23
+ spec.add_dependency('win32ole')
24
24
  end
25
25
 
26
26
  spec.metadata = {
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sys-filesystem
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -141,10 +141,17 @@ files:
141
141
  - lib/sys/filesystem.rb
142
142
  - lib/sys/unix/sys/filesystem.rb
143
143
  - lib/sys/unix/sys/filesystem/constants.rb
144
+ - lib/sys/unix/sys/filesystem/constants/bsd.rb
145
+ - lib/sys/unix/sys/filesystem/constants/darwin.rb
146
+ - lib/sys/unix/sys/filesystem/constants/dragonfly.rb
147
+ - lib/sys/unix/sys/filesystem/constants/freebsd.rb
148
+ - lib/sys/unix/sys/filesystem/constants/generic.rb
149
+ - lib/sys/unix/sys/filesystem/constants/linux.rb
144
150
  - lib/sys/unix/sys/filesystem/functions.rb
145
151
  - lib/sys/unix/sys/filesystem/structs.rb
146
152
  - lib/sys/windows/sys/filesystem.rb
147
153
  - lib/sys/windows/sys/filesystem/constants.rb
154
+ - lib/sys/windows/sys/filesystem/constants/windows.rb
148
155
  - lib/sys/windows/sys/filesystem/functions.rb
149
156
  - lib/sys/windows/sys/filesystem/helper.rb
150
157
  - spec/spec_helper.rb
@@ -179,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
186
  - !ruby/object:Gem::Version
180
187
  version: '0'
181
188
  requirements: []
182
- rubygems_version: 3.6.9
189
+ rubygems_version: 4.0.6
183
190
  specification_version: 4
184
191
  summary: A Ruby interface for getting file system information.
185
192
  test_files:
metadata.gz.sig CHANGED
Binary file