sys-filesystem-1.4.4/0000755000004100000410000000000014514017636014557 5ustar www-datawww-datasys-filesystem-1.4.4/README.md0000644000004100000410000000424014514017636016036 0ustar www-datawww-data[![Ruby](https://github.com/djberg96/sys-filesystem/actions/workflows/ruby.yml/badge.svg)](https://github.com/djberg96/sys-filesystem/actions/workflows/ruby.yml) ## Description A cross platform Ruby interface for getting file system information. ## Installation `gem install sys-filesystem` ## Adding the trusted cert `gem cert --add <(curl -Ls https://raw.githubusercontent.com/djberg96/sys-filesystem/main/certs/djberg96_pub.pem)` ## Synopsis ```ruby require 'sys/filesystem' include Sys # Display information about a particular filesystem. p Filesystem.stat('/') # Sample output # # Describe all mount points on the system Filesystem.mounts{ |mount| p mount } # Find the mount point of any particular file puts Filesystem.mount_point('/home/djberge/some_file.txt') => '/home' ``` ## Notes This is a pure Ruby implementation that uses FFI. This means it should work with JRuby, too. ## Sample code Run 'rake example' if you want to see a basic sample run. The actual code is 'example_stat.rb' in the 'examples' directory. Modify it as you see fit. ## Known Bugs None that I'm aware of. Please report bugs on the project page at: https://github.com/djberg96/sys-filesystem ## Future Plans * Add better 64-bit support for Linux and BSD. * Other suggestions welcome. ## Acknowledgements * Mike Hall, for ideas and code that I borrowed from his 'filesystem' library. * Park Heesob, for implementation and API ideas for the MS Windows version. * Nobuyoshi Miyokawa, for adding the original FreeBSD and OS X support. ## License Apache-2.0 ## Copyright (C) 2003-2020 Daniel J. Berger All Rights Reserved ## Warranty This library is provided "as is" and without any express or implied warranties, including, without limitation, the implied warranties of merchantability and fitness for a particular purpose. ## Author Daniel J. Berger sys-filesystem-1.4.4/sys-filesystem.gemspec0000644000004100000410000000333614514017636021131 0ustar www-datawww-datarequire 'rubygems' Gem::Specification.new do |spec| spec.name = 'sys-filesystem' spec.version = '1.4.4' spec.author = 'Daniel J. Berger' spec.email = 'djberg96@gmail.com' spec.homepage = 'https://github.com/djberg96/sys-filesystem' spec.summary = 'A Ruby interface for getting file system information.' spec.license = 'Apache-2.0' spec.test_files = Dir['spec/*_spec.rb'] spec.files = Dir['**/*'].reject{ |f| f.include?('git') } spec.cert_chain = Dir['certs/*'] spec.add_dependency('ffi', '~> 1.1') spec.add_development_dependency('mkmf-lite', '~> 0.5') unless Gem.win_platform? spec.add_development_dependency('rake') spec.add_development_dependency('rspec', '~> 3.9') spec.add_development_dependency('rubocop') spec.add_development_dependency('rubocop-rspec') if RUBY_PLATFORM == 'java' && Gem.win_platform? spec.add_dependency('jruby-win32ole') end spec.metadata = { 'homepage_uri' => 'https://github.com/djberg96/sys-filesystem', 'bug_tracker_uri' => 'https://github.com/djberg96/sys-filesystem/issues', 'changelog_uri' => 'https://github.com/djberg96/sys-filesystem/blob/main/CHANGES.md', 'documentation_uri' => 'https://github.com/djberg96/sys-filesystem/wiki', 'source_code_uri' => 'https://github.com/djberg96/sys-filesystem', 'wiki_uri' => 'https://github.com/djberg96/sys-filesystem/wiki', 'rubygems_mfa_required' => 'true', 'github_repo' => 'https://github.com/djberg96/sys-filesystem' } spec.description = <<-EOF The sys-filesystem library provides a cross-platform interface for gathering filesystem information, such as disk space and mount point data. EOF end sys-filesystem-1.4.4/data.tar.gz.sig0000444000004100000410000000060014514017636017372 0ustar www-datawww-dataIL*c8zNɿ7eGE J F*f}*}MMTfkJ\5dDSSڍCT=23i7)ݲh=-;UerAh#RnǠ!(n}j!QI%pHxVtqcPGu(4j0,3$灬8>$ðA_Ns%eKKd xML=AzRlF&qqgR9:fԢyTd 4cXM; qO kp`+bnwI=0-$63ۜaBcYøHƖixcs G)HR&ysys-filesystem-1.4.4/spec/0000755000004100000410000000000014514017636015511 5ustar www-datawww-datasys-filesystem-1.4.4/spec/sys_filesystem_shared.rb0000644000004100000410000000060614514017636022450 0ustar www-datawww-data# frozen_string_literal: true require 'sys-filesystem' RSpec.shared_examples Sys::Filesystem do example 'version number is set to the expected value' do expect(Sys::Filesystem::VERSION).to eq('1.4.4') expect(Sys::Filesystem::VERSION).to be_frozen end example 'you cannot instantiate an instance' do expect{ described_class.new }.to raise_error(NoMethodError) end end sys-filesystem-1.4.4/spec/sys_filesystem_unix_spec.rb0000644000004100000410000004073014514017636023201 0ustar www-datawww-data# frozen_string_literal: true #################################################################### # sys_filesystem_unix_spec.rb # # Specs for the Sys::Filesystem.stat method and related stuff. # This test suite should be run via the 'rake spec' task. #################################################################### require 'spec_helper' require 'sys-filesystem' require 'pathname' RSpec.describe Sys::Filesystem, :unix => true do let(:solaris) { RbConfig::CONFIG['host_os'] =~ /sunos|solaris/i } let(:linux) { RbConfig::CONFIG['host_os'] =~ /linux/i } let(:bsd) { RbConfig::CONFIG['host_os'] =~ /bsd/i } let(:darwin) { RbConfig::CONFIG['host_os'] =~ /mac|darwin/i } let(:root) { '/' } before do @stat = described_class.stat(root) @size = 58720256 end example 'stat path works as expected' do expect(@stat).to respond_to(:path) expect(@stat.path).to eq(root) end example 'stat block_size works as expected' do expect(@stat).to respond_to(:block_size) expect(@stat.block_size).to be_a(Numeric) end example 'stat fragment_size works as expected' do expect(@stat).to respond_to(:fragment_size) expect(@stat.fragment_size).to be_a(Numeric) end example 'stat fragment_size is a plausible value' do expect(@stat.fragment_size).to be >= 512 expect(@stat.fragment_size).to be <= 16384 end example 'stat blocks works as expected' do expect(@stat).to respond_to(:blocks) expect(@stat.blocks).to be_a(Numeric) end example 'stat blocks_free works as expected' do expect(@stat).to respond_to(:blocks_free) expect(@stat.blocks_free).to be_a(Numeric) end example 'stat blocks_available works as expected' do expect(@stat).to respond_to(:blocks_available) expect(@stat.blocks_available).to be_a(Numeric) end example 'stat files works as expected' do expect(@stat).to respond_to(:files) expect(@stat.files).to be_a(Numeric) end example 'stat inodes is an alias for files' do expect(@stat).to respond_to(:inodes) expect(@stat.method(:inodes)).to eq(@stat.method(:files)) end example 'stat files tree works as expected' do expect(@stat).to respond_to(:files_free) expect(@stat.files_free).to be_a(Numeric) end example 'stat inodes_free is an alias for files_free' do expect(@stat).to respond_to(:inodes_free) expect(@stat.method(:inodes_free)).to eq(@stat.method(:files_free)) end example 'stat files_available works as expected' do expect(@stat).to respond_to(:files_available) expect(@stat.files_available).to be_a(Numeric) end example 'stat inodes_available is an alias for files_available' do expect(@stat).to respond_to(:inodes_available) expect(@stat.method(:inodes_available)).to eq(@stat.method(:files_available)) end example 'stat filesystem_id works as expected' do expect(@stat).to respond_to(:filesystem_id) expect(@stat.filesystem_id).to be_a(Integer) end example 'stat flags works as expected' do expect(@stat).to respond_to(:flags) expect(@stat.flags).to be_a(Numeric) end example 'stat name_max works as expected' do expect(@stat).to respond_to(:name_max) expect(@stat.name_max).to be_a(Numeric) end example 'stat base_type works as expected' do skip 'base_type test skipped except on Solaris' unless solaris expect(@stat).to respond_to(:base_type) expect(@stat.base_type).to be_a(String) end example 'stat constants are defined' do expect(Sys::Filesystem::Stat::RDONLY).not_to be_nil expect(Sys::Filesystem::Stat::NOSUID).not_to be_nil end example 'stat constants for solaris are defined' do skip 'NOTRUNC test skipped except on Solaris' unless solaris expect(Sys::Filesystem::Stat::NOTRUNC).not_to be_nil end example 'stat bytes_total works as expected' do expect(@stat).to respond_to(:bytes_total) expect(@stat.bytes_total).to be_a(Numeric) end example 'stat bytes_free works as expected' do expect(@stat).to respond_to(:bytes_free) expect(@stat.bytes_free).to be_a(Numeric) expect(@stat.blocks_free * @stat.fragment_size).to eq(@stat.bytes_free) end example 'stat bytes_available works as expected' do expect(@stat).to respond_to(:bytes_available) expect(@stat.bytes_available).to be_a(Numeric) expect(@stat.blocks_available * @stat.fragment_size).to eq(@stat.bytes_available) end example 'stat bytes works as expected' do expect(@stat).to respond_to(:bytes_used) expect(@stat.bytes_used).to be_a(Numeric) end example 'stat percent_used works as expected' do expect(@stat).to respond_to(:percent_used) expect(@stat.percent_used).to be_a(Float) end example 'stat singleton method requires an argument' do expect{ described_class.stat }.to raise_error(ArgumentError) end example 'stat case_insensitive method works as expected' do expected = darwin ? true : false expect(@stat.case_insensitive?).to eq(expected) expect(described_class.stat(Dir.home).case_insensitive?).to eq(expected) end example 'stat case_sensitive method works as expected' do expected = darwin ? false : true expect(@stat.case_sensitive?).to eq(expected) expect(described_class.stat(Dir.home).case_sensitive?).to eq(expected) end example 'numeric helper methods are defined' do expect(@size).to respond_to(:to_kb) expect(@size).to respond_to(:to_mb) expect(@size).to respond_to(:to_gb) expect(@size).to respond_to(:to_tb) end example 'to_kb works as expected' do expect(@size.to_kb).to eq(57344) end example 'to_mb works as expected' do expect(@size.to_mb).to eq(56) end example 'to_gb works as expected' do expect(@size.to_gb).to eq(0) end context 'Filesystem.stat(Pathname)' do before do @stat_pathname = described_class.stat(Pathname.new(root)) end example 'class returns expected value with pathname argument' do expect(@stat_pathname.class).to eq(@stat.class) end example 'path returns expected value with pathname argument' do expect(@stat_pathname.path).to eq(@stat.path) end example 'block_size returns expected value with pathname argument' do expect(@stat_pathname.block_size).to eq(@stat.block_size) end example 'fragment_size returns expected value with pathname argument' do expect(@stat_pathname.fragment_size).to eq(@stat.fragment_size) end example 'blocks returns expected value with pathname argument' do expect(@stat_pathname.blocks).to eq(@stat.blocks) end example 'blocks_free returns expected value with pathname argument' do expect(@stat_pathname.blocks_free).to eq(@stat.blocks_free) end example 'blocks_available returns expected value with pathname argument' do expect(@stat_pathname.blocks_available).to eq(@stat.blocks_available) end example 'files returns expected value with pathname argument' do expect(@stat_pathname.files).to eq(@stat.files) end example 'files_free returns expected value with pathname argument' do expect(@stat_pathname.files_free).to eq(@stat.files_free) end example 'files_available returns expected value with pathname argument' do expect(@stat_pathname.files_available).to eq(@stat.files_available) end example 'filesystem_id returns expected value with pathname argument' do expect(@stat_pathname.filesystem_id).to eq(@stat.filesystem_id) end example 'flags returns expected value with pathname argument' do expect(@stat_pathname.flags).to eq(@stat.flags) end example 'name_max returns expected value with pathname argument' do expect(@stat_pathname.name_max).to eq(@stat.name_max) end example 'base_type returns expected value with pathname argument' do expect(@stat_pathname.base_type).to eq(@stat.base_type) end end context 'Filesystem.stat(File)' do before do @stat_file = File.open(root){ |file| described_class.stat(file) } end example 'class returns expected value with file argument' do expect(@stat_file.class).to eq(@stat.class) end example 'path returns expected value with file argument' do expect(@stat_file.path).to eq(@stat.path) end example 'block_size returns expected value with file argument' do expect(@stat_file.block_size).to eq(@stat.block_size) end example 'fragment_size returns expected value with file argument' do expect(@stat_file.fragment_size).to eq(@stat.fragment_size) end example 'blocks returns expected value with file argument' do expect(@stat_file.blocks).to eq(@stat.blocks) end example 'blocks_free returns expected value with file argument' do expect(@stat_file.blocks_free).to eq(@stat.blocks_free) end example 'blocks_available returns expected value with file argument' do expect(@stat_file.blocks_available).to eq(@stat.blocks_available) end example 'files returns expected value with file argument' do expect(@stat_file.files).to eq(@stat.files) end example 'files_free returns expected value with file argument' do expect(@stat_file.files_free).to eq(@stat.files_free) end example 'files_available returns expected value with file argument' do expect(@stat_file.files_available).to eq(@stat.files_available) end example 'filesystem_id returns expected value with file argument' do expect(@stat_file.filesystem_id).to eq(@stat.filesystem_id) end example 'flags returns expected value with file argument' do expect(@stat_file.flags).to eq(@stat.flags) end example 'name_max returns expected value with file argument' do expect(@stat_file.name_max).to eq(@stat.name_max) end example 'base_type returns expected value with file argument' do expect(@stat_file.base_type).to eq(@stat.base_type) end end context 'Filesystem.stat(Dir)' do before do @stat_dir = Dir.open(root){ |dir| described_class.stat(dir) } end example 'class returns expected value with Dir argument' do expect(@stat_dir.class).to eq(@stat.class) end example 'path returns expected value with Dir argument' do expect(@stat_dir.path).to eq(@stat.path) end example 'block_size returns expected value with Dir argument' do expect(@stat_dir.block_size).to eq(@stat.block_size) end example 'fragment_size returns expected value with Dir argument' do expect(@stat_dir.fragment_size).to eq(@stat.fragment_size) end example 'blocks returns expected value with Dir argument' do expect(@stat_dir.blocks).to eq(@stat.blocks) end example 'blocks_free returns expected value with Dir argument' do expect(@stat_dir.blocks_free).to eq(@stat.blocks_free) end example 'blocks_available returns expected value with Dir argument' do expect(@stat_dir.blocks_available).to eq(@stat.blocks_available) end example 'files returns expected value with Dir argument' do expect(@stat_dir.files).to eq(@stat.files) end example 'files_free returns expected value with Dir argument' do expect(@stat_dir.files_free).to eq(@stat.files_free) end example 'files_available returns expected value with Dir argument' do expect(@stat_dir.files_available).to eq(@stat.files_available) end example 'filesystem_id returns expected value with Dir argument' do expect(@stat_dir.filesystem_id).to eq(@stat.filesystem_id) end example 'flags returns expected value with Dir argument' do expect(@stat_dir.flags).to eq(@stat.flags) end example 'name_max returns expected value with Dir argument' do expect(@stat_dir.name_max).to eq(@stat.name_max) end example 'base_type returns expected value with Dir argument' do expect(@stat_dir.base_type).to eq(@stat.base_type) end end context 'Filesystem::Mount' do let(:mount){ described_class.mounts[0] } before do @array = [] end example 'mounts singleton method works as expected without a block' do expect{ @array = described_class.mounts }.not_to raise_error expect(@array[0]).to be_a(Sys::Filesystem::Mount) end example 'mounts singleton method works as expected with a block' do expect{ described_class.mounts{ |m| @array << m } }.not_to raise_error expect(@array[0]).to be_a(Sys::Filesystem::Mount) end example 'calling the mounts singleton method a large number of times does not cause issues' do expect{ 1000.times{ @array = described_class.mounts } }.not_to raise_error end example 'mount name method works as expected' do expect(mount).to respond_to(:name) expect(mount.name).to be_a(String) end example 'mount fsname is an alias for name' do expect(mount).to respond_to(:fsname) expect(mount.method(:fsname)).to eq(mount.method(:name)) end example 'mount point method works as expected' do expect(mount).to respond_to(:mount_point) expect(mount.mount_point).to be_a(String) end example 'mount dir is an alias for mount_point' do expect(mount).to respond_to(:dir) expect(mount.method(:dir)).to eq(mount.method(:mount_point)) end example 'mount mount_type works as expected' do expect(mount).to respond_to(:mount_type) expect(mount.mount_type).to be_a(String) end example 'mount options works as expected' do expect(mount).to respond_to(:options) expect(mount.options).to be_a(String) end example 'mount opts is an alias for options' do expect(mount).to respond_to(:opts) expect(mount.method(:opts)).to eq(mount.method(:options)) end example 'mount time works as expected' do expected_class = solaris ? Time : NilClass expect(mount).to respond_to(:mount_time) expect(mount.mount_time).to be_a(expected_class) end example 'mount dump_frequency works as expected' do msg = 'dump_frequency test skipped on this platform' skip msg if solaris || bsd || darwin expect(mount).to respond_to(:dump_frequency) expect(mount.dump_frequency).to be_a(Numeric) end example 'mount freq is an alias for dump_frequency' do expect(mount).to respond_to(:freq) expect(mount.method(:freq)).to eq(mount.method(:dump_frequency)) end example 'mount pass_number works as expected' do msg = 'pass_number test skipped on this platform' skip msg if solaris || bsd || darwin expect(mount).to respond_to(:pass_number) expect(mount.pass_number).to be_a(Numeric) end example 'mount passno is an alias for pass_number' do expect(mount).to respond_to(:passno) expect(mount.method(:passno)).to eq(mount.method(:pass_number)) end example 'mount_point singleton method works as expected' do expect(described_class).to respond_to(:mount_point) expect{ described_class.mount_point(Dir.pwd) }.not_to raise_error expect(described_class.mount_point(Dir.pwd)).to be_a(String) end example 'mount singleton method is defined' do expect(described_class).to respond_to(:mount) end example 'umount singleton method is defined' do expect(described_class).to respond_to(:umount) end end context 'FFI' do before(:context) do require 'mkmf-lite' end let(:dummy) { Class.new { extend Mkmf::Lite } } example 'ffi functions are private' do expect(described_class.methods.include?('statvfs')).to be false expect(described_class.methods.include?('strerror')).to be false end example 'statfs struct is expected size' do header = bsd || darwin ? 'sys/mount.h' : 'sys/statfs.h' expect(Sys::Filesystem::Structs::Statfs.size).to eq(dummy.check_sizeof('struct statfs', header)) end example 'statvfs struct is expected size' do expect(Sys::Filesystem::Structs::Statvfs.size).to eq(dummy.check_sizeof('struct statvfs', 'sys/statvfs.h')) end example 'mnttab struct is expected size' do skip 'mnttab test skipped except on Solaris' unless solaris expect(Sys::Filesystem::Structs::Mnttab.size).to eq(dummy.check_sizeof('struct mnttab', 'sys/mnttab.h')) end example 'mntent struct is expected size' do skip 'mnttab test skipped except on Linux' unless linux expect(Sys::Filesystem::Structs::Mntent.size).to eq(dummy.check_sizeof('struct mntent', 'mntent.h')) end example 'a failed statvfs call behaves as expected' do msg = 'statvfs() function failed: No such file or directory' expect{ described_class.stat('/whatever') }.to raise_error(Sys::Filesystem::Error, msg) end end end sys-filesystem-1.4.4/spec/spec_helper.rb0000644000004100000410000000042514514017636020330 0ustar www-datawww-data# frozen_string_literal: true require 'rspec' require 'sys_filesystem_shared' RSpec.configure do |config| config.include_context(Sys::Filesystem) config.filter_run_excluding(:windows) unless Gem.win_platform? config.filter_run_excluding(:unix) if Gem.win_platform? end sys-filesystem-1.4.4/spec/sys_filesystem_windows_spec.rb0000644000004100000410000003535514514017636023717 0ustar www-datawww-data# frozen_string_literal: true #################################################################### # sys_filesystem_windows_spec.rb # # Specs for the Sys::Filesystem.stat method and related stuff. # This should be run via the 'rake spec' task. #################################################################### require 'spec_helper' require 'sys/filesystem' require 'pathname' RSpec.describe Sys::Filesystem, :windows => true do let(:root) { 'C:/' } before do @stat = described_class.stat(root) @size = 58720256 end example 'stat path works as expected' do expect(@stat).to respond_to(:path) expect(@stat.path).to eq(root) end example 'stat block_size works as expected' do expect(@stat).to respond_to(:block_size) expect(@stat.block_size).to be_a(Numeric) end example 'stat works with or without trailing slash on standard paths' do expect(described_class.stat('C:/').path).to eq('C:/') expect(described_class.stat('C:/Users').path).to eq('C:/Users') expect(described_class.stat('C:/Users/').path).to eq('C:/Users/') expect(described_class.stat('C:/Users/').path).to eq('C:/Users/') end example 'stat works with or without trailing slash on UNC paths' do expect(described_class.stat('//127.0.0.1/C$').path).to eq('//127.0.0.1/C$') expect(described_class.stat('//127.0.0.1/C$/').path).to eq('//127.0.0.1/C$/') expect(described_class.stat('\\\\127.0.0.1\\C$').path).to eq('\\\\127.0.0.1\\C$') expect(described_class.stat('\\\\127.0.0.1\\C$\\').path).to eq('\\\\127.0.0.1\\C$\\') end example 'stat fragment_size works as expected' do expect(@stat).to respond_to(:fragment_size) expect(@stat.fragment_size).to be_nil end example 'stat blocks works as expected' do expect(@stat).to respond_to(:blocks) expect(@stat.blocks).to be_a(Numeric) end example 'stat blocks_free works as expected' do expect(@stat).to respond_to(:blocks_free) expect(@stat.blocks_free).to be_a(Numeric) end example 'stat blocks_available works as expected' do expect(@stat).to respond_to(:blocks_available) expect(@stat.blocks_available).to be_a(Numeric) end example 'block stats return expected relative values' do expect(@stat.blocks >= @stat.blocks_free).to be true expect(@stat.blocks_free >= @stat.blocks_available).to be true end example 'stat files works as expected' do expect(@stat).to respond_to(:files) expect(@stat.files).to be_nil end example 'stat inodes is an alias for files' do expect(@stat.method(:inodes)).to eq(@stat.method(:files)) end example 'stat files_free works as expected' do expect(@stat).to respond_to(:files_free) expect(@stat.files_free).to be_nil end example 'stat inodes_free is an alias for files_free' do expect(@stat).to respond_to(:inodes_free) end example 'stat files available works as expected' do expect(@stat).to respond_to(:files_available) expect(@stat.files_available).to be_nil end example 'stat inodes_available is an alias for files_available' do expect(@stat.method(:inodes_available)).to eq(@stat.method(:files_available)) end example 'stat filesystem_id works as expected' do expect(@stat).to respond_to(:filesystem_id) expect(@stat.filesystem_id).to be_a(Integer) end example 'stat flags works as expected' do expect(@stat).to respond_to(:flags) expect(@stat.flags).to be_a(Numeric) end example 'stat name_max works as expected' do expect(@stat).to respond_to(:name_max) expect(@stat.name_max).to be_a(Numeric) end example 'stat base_type works as expected' do expect(@stat).to respond_to(:base_type) expect(@stat.base_type).to be_a(String) end example 'stat bytes_total basic functionality' do expect(@stat).to respond_to(:bytes_total) expect(@stat.bytes_total).to be_a(Numeric) end example 'stat bytes_free basic functionality' do expect(@stat).to respond_to(:bytes_free) expect(@stat.bytes_free).to be_a(Numeric) expect(@stat.blocks_free * @stat.block_size).to eq(@stat.bytes_free) end example 'stat bytes_available basic functionality' do expect(@stat).to respond_to(:bytes_available) expect(@stat.bytes_available).to be_a(Numeric) expect(@stat.blocks_available * @stat.block_size).to eq(@stat.bytes_available) end example 'stat bytes_used basic functionality' do expect(@stat).to respond_to(:bytes_used) expect(@stat.bytes_used).to be_a(Numeric) end example 'stat percent_used basic functionality' do expect(@stat).to respond_to(:percent_used) expect(@stat.percent_used).to be_a(Float) end example 'case_insensitive returns expected result' do expect(@stat).to respond_to(:case_insensitive?) expect(@stat.case_insensitive?).to be(true) end context 'Filesystem.stat(Pathname)' do before do @stat_pathname = described_class.stat(Pathname.new(root)) end example 'class returns expected value with pathname argument' do expect(@stat_pathname.class).to eq(@stat.class) end example 'path returns expected value with pathname argument' do expect(@stat_pathname.path).to eq(@stat.path) end example 'block_size returns expected value with pathname argument' do expect(@stat_pathname.block_size).to eq(@stat.block_size) end example 'fragment_size returns expected value with pathname argument' do expect(@stat_pathname.fragment_size).to eq(@stat.fragment_size) end example 'blocks returns expected value with pathname argument' do expect(@stat_pathname.blocks).to eq(@stat.blocks) end example 'blocks_free returns expected value with pathname argument' do expect(@stat_pathname.blocks_free).to eq(@stat.blocks_free) end example 'blocks_available returns expected value with pathname argument' do expect(@stat_pathname.blocks_available).to eq(@stat.blocks_available) end example 'files returns expected value with pathname argument' do expect(@stat_pathname.files).to eq(@stat.files) end example 'files_free returns expected value with pathname argument' do expect(@stat_pathname.files_free).to eq(@stat.files_free) end example 'files_available returns expected value with pathname argument' do expect(@stat_pathname.files_available).to eq(@stat.files_available) end example 'filesystem_id returns expected value with pathname argument' do expect(@stat_pathname.filesystem_id).to eq(@stat.filesystem_id) end example 'flags returns expected value with pathname argument' do expect(@stat_pathname.flags).to eq(@stat.flags) end example 'name_max returns expected value with pathname argument' do expect(@stat_pathname.name_max).to eq(@stat.name_max) end example 'base_type returns expected value with pathname argument' do expect(@stat_pathname.base_type).to eq(@stat.base_type) end end context 'Filesystem.stat(Dir)' do before do @stat_dir = Dir.open(root){ |dir| described_class.stat(dir) } end example 'stat class with Dir argument works as expected' do expect(@stat_dir.class).to eq(@stat.class) end example 'stat path with Dir argument works as expected' do expect(@stat_dir.path).to eq(@stat.path) end example 'stat block_size with Dir argument works as expected' do expect(@stat_dir.block_size).to eq(@stat.block_size) end example 'stat fragment_size with Dir argument works as expected' do expect(@stat_dir.fragment_size).to eq(@stat.fragment_size) end example 'stat blocks with Dir argument works as expected' do expect(@stat_dir.blocks).to eq(@stat.blocks) end example 'stat blocks_free with Dir argument works as expected' do expect(@stat_dir.blocks_free).to eq(@stat.blocks_free) end example 'stat blocks_available with Dir argument works as expected' do expect(@stat_dir.blocks_available).to eq(@stat.blocks_available) end example 'stat files with Dir argument works as expected' do expect(@stat_dir.files).to eq(@stat.files) end example 'stat files_free with Dir argument works as expected' do expect(@stat_dir.files_free).to eq(@stat.files_free) end example 'stat files_available with Dir argument works as expected' do expect(@stat_dir.files_available).to eq(@stat.files_available) end example 'stat filesystem_id with Dir argument works as expected' do expect(@stat_dir.filesystem_id).to eq(@stat.filesystem_id) end example 'stat flags with Dir argument works as expected' do expect(@stat_dir.flags).to eq(@stat.flags) end example 'stat name_max with Dir argument works as expected' do expect(@stat_dir.name_max).to eq(@stat.name_max) end example 'stat base_type with Dir argument works as expected' do expect(@stat_dir.base_type).to eq(@stat.base_type) end end context 'mount_point' do example 'mount_point singleton method basic functionality' do expect(described_class).to respond_to(:mount_point) expect{ described_class.mount_point(Dir.pwd) }.not_to raise_error expect(described_class.mount_point(Dir.pwd)).to be_a(String) end example 'mount_point singleton method returns expected value' do expect(described_class.mount_point('C:\\Users\\foo')).to eq('C:\\') expect(described_class.mount_point('//foo/bar/baz')).to eq('\\\\foo\\bar') end example 'mount_point works with Pathname object' do expect{ described_class.mount_point(Pathname.new('C:/Users/foo')) }.not_to raise_error expect(described_class.mount_point('C:\\Users\\foo')).to eq('C:\\') expect(described_class.mount_point('//foo/bar/baz')).to eq('\\\\foo\\bar') end end context 'filesystem constants are defined' do example 'CASE_SENSITIVE_SEARCH' do expect(Sys::Filesystem::CASE_SENSITIVE_SEARCH).not_to be_nil end example 'CASE_PRESERVED_NAMES' do expect(Sys::Filesystem::CASE_PRESERVED_NAMES).not_to be_nil end example 'UNICODE_ON_DISK' do expect(Sys::Filesystem::UNICODE_ON_DISK).not_to be_nil end example 'PERSISTENT_ACLS' do expect(Sys::Filesystem::PERSISTENT_ACLS).not_to be_nil end example 'FILE_COMPRESSION' do expect(Sys::Filesystem::FILE_COMPRESSION).not_to be_nil end example 'VOLUME_QUOTAS' do expect(Sys::Filesystem::VOLUME_QUOTAS).not_to be_nil end example 'SUPPORTS_SPARSE_FILES' do expect(Sys::Filesystem::SUPPORTS_SPARSE_FILES).not_to be_nil end example 'SUPPORTS_REPARSE_POINTS' do expect(Sys::Filesystem::SUPPORTS_REPARSE_POINTS).not_to be_nil end example 'SUPPORTS_REMOTE_STORAGE' do expect(Sys::Filesystem::SUPPORTS_REMOTE_STORAGE).not_to be_nil end example 'VOLUME_IS_COMPRESSED' do expect(Sys::Filesystem::VOLUME_IS_COMPRESSED).not_to be_nil end example 'SUPPORTS_OBJECT_IDS' do expect(Sys::Filesystem::SUPPORTS_OBJECT_IDS).not_to be_nil end example 'SUPPORTS_ENCRYPTION' do expect(Sys::Filesystem::SUPPORTS_ENCRYPTION).not_to be_nil end example 'NAMED_STREAMS' do expect(Sys::Filesystem::NAMED_STREAMS).not_to be_nil end example 'READ_ONLY_VOLUME' do expect(Sys::Filesystem::READ_ONLY_VOLUME).not_to be_nil end end example 'stat singleton method defaults to root path if proviced' do expect{ described_class.stat('C://Program Files') }.not_to raise_error end example 'stat singleton method accepts a Pathname object' do expect{ described_class.stat(Pathname.new('C://Program Files')) }.not_to raise_error end example 'stat singleton method requires a single argument' do expect{ described_class.stat }.to raise_error(ArgumentError) expect{ described_class.stat(Dir.pwd, Dir.pwd) }.to raise_error(ArgumentError) end example 'stat singleton method raises an error if path is not found' do expect{ described_class.stat('C://Bogus//Dir') }.to raise_error(Errno::ESRCH) end context 'Filesystem::Mount' do let(:mount){ described_class.mounts[0] } before do @array = [] end example 'mount singleton method exists' do expect(described_class).to respond_to(:mount) end example 'umount singleton method exists' do expect(described_class).to respond_to(:umount) end example 'mounts singleton method basic functionality' do expect(described_class).to respond_to(:mounts) expect{ described_class.mounts }.not_to raise_error expect{ described_class.mounts{} }.not_to raise_error end example 'mounts singleton method returns the expected value' do expect(described_class.mounts).to be_a(Array) expect(described_class.mounts[0]).to be_a(Sys::Filesystem::Mount) end example 'mounts singleton method works as expected when a block is provided' do expect(described_class.mounts{}).to be_nil expect{ described_class.mounts{ |mt| @array << mt } }.not_to raise_error expect(@array[0]).to be_a(Sys::Filesystem::Mount) end example 'mount name works as expected' do expect(mount).to respond_to(:name) expect(mount.name).to be_a(String) end example 'mount_time works as expected' do expect(mount).to respond_to(:mount_time) expect(mount.mount_time).to be_a(Time) end example 'mount type works as expected' do expect(mount).to respond_to(:mount_type) expect(mount.mount_type).to be_a(String) end example 'mount point works as expected' do expect(mount).to respond_to(:mount_point) expect(mount.mount_point).to be_a(String) end example 'mount options works as expected' do expect(mount).to respond_to(:options) expect(mount.options).to be_a(String) end example 'mount pass_number works as expected' do expect(mount).to respond_to(:pass_number) expect(mount.pass_number).to be_nil end example 'mount frequency works as expected' do expect(mount).to respond_to(:frequency) expect(mount.frequency).to be_nil end example 'mounts singleton method does not accept any arguments' do expect{ described_class.mounts('C:\\') }.to raise_error(ArgumentError) end end example 'custom Numeric#to_kb method works as expected' do expect(@size).to respond_to(:to_kb) expect(@size.to_kb).to eq(57344) end example 'custom Numeric#to_mb method works as expected' do expect(@size).to respond_to(:to_mb) expect(@size.to_mb).to eq(56) end example 'custom Numeric#to_gb method works as expected' do expect(@size).to respond_to(:to_gb) expect(@size.to_gb).to eq(0) end context 'FFI' do example 'internal ffi functions are not public' do expect(described_class.methods.include?(:GetVolumeInformationA)).to be(false) expect(described_class.instance_methods.include?(:GetVolumeInformationA)).to be(false) end end end sys-filesystem-1.4.4/certs/0000755000004100000410000000000014514017636015677 5ustar www-datawww-datasys-filesystem-1.4.4/certs/djberg96_pub.pem0000644000004100000410000000307614514017636020672 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MREwDwYDVQQDDAhkamJl cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t MB4XDTE4MDMxODE1MjIwN1oXDTI4MDMxNTE1MjIwN1owPzERMA8GA1UEAwwIZGpi ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALgfaroVM6CI06cxr0/h A+j+pc8fgpRgBVmHFaFunq28GPC3IvW7Nvc3Y8SnAW7pP1EQIbhlwRIaQzJ93/yj u95KpkP7tA9erypnV7dpzBkzNlX14ACaFD/6pHoXoe2ltBxk3CCyyzx70mTqJpph 75IB03ni9a8yqn8pmse+s83bFJOAqddSj009sGPcQO+QOWiNxqYv1n5EHcvj2ebO 6hN7YTmhx7aSia4qL/quc4DlIaGMWoAhvML7u1fmo53CYxkKskfN8MOecq2vfEmL iLu+SsVVEAufMDDFMXMJlvDsviolUSGMSNRTujkyCcJoXKYYxZSNtIiyd9etI0X3 ctu0uhrFyrMZXCedutvXNjUolD5r9KGBFSWH1R9u2I3n3SAyFF2yzv/7idQHLJJq 74BMnx0FIq6fCpu5slAipvxZ3ZkZpEXZFr3cIBtO1gFvQWW7E/Y3ijliWJS1GQFq 058qERadHGu1yu1dojmFRo6W2KZvY9al2yIlbkpDrD5MYQIDAQABo3cwdTAJBgNV HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUFZsMapgzJimzsbaBG2Tm8j5e AzgwHQYDVR0RBBYwFIESZGpiZXJnOTZAZ21haWwuY29tMB0GA1UdEgQWMBSBEmRq YmVyZzk2QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAW2tnYixXQtKxgGXq /3iSWG2bLwvxS4go3srO+aRXZHrFUMlJ5W0mCxl03aazxxKTsVVpZD8QZxvK91OQ h9zr9JBYqCLcCVbr8SkmYCi/laxIZxsNE5YI8cC8vvlLI7AMgSfPSnn/Epq1GjGY 6L1iRcEDtanGCIvjqlCXO9+BmsnCfEVehqZkQHeYczA03tpOWb6pon2wzvMKSsKH ks0ApVdstSLz1kzzAqem/uHdG9FyXdbTAwH1G4ZPv69sQAFAOCgAqYmdnzedsQtE 1LQfaQrx0twO+CZJPcRLEESjq8ScQxWRRkfuh2VeR7cEU7L7KqT10mtUwrvw7APf DYoeCY9KyjIBjQXfbj2ke5u1hZj94Fsq9FfbEQg8ygCgwThnmkTrrKEiMSs3alYR ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh -----END CERTIFICATE----- sys-filesystem-1.4.4/MANIFEST.md0000644000004100000410000000121014514017636016301 0ustar www-datawww-data* CHANGES.md * Gemfile * LICENSE * MANIFEST.md * Rakefile * README.md * sys-fileystem.gemspec * certs/djberg96_pub.pem * examples/example_mount.rb * examples/example_stat.rb * lib/sys-filesystem.rb * lib/sys/filesystem.rb * lib/sys/unix/sys/filesystem.rb * lib/sys/unix/sys/filesystem/constants.rb * lib/sys/unix/sys/filesystem/functions.rb * lib/sys/unix/sys/filesystem/structs.rb * lib/sys/windows/sys/filesystem.rb * lib/sys/windows/sys/filesystem/constants.rb * lib/sys/windows/sys/filesystem/functions.rb * lib/sys/windows/sys/filesystem/helper.rb * spec/spec_helper.rb * spec/sys_filesystem_unix_spec.rb * spec/sys_filesystem_windows_spec.rb sys-filesystem-1.4.4/metadata.gz.sig0000444000004100000410000000060014514017636017454 0ustar www-datawww-data#)Sa:=p:`s7TW<uVi!D)ނx 36I<'v&jRw[֙Rd?<.(nKEmI#WyzB`Zu^ST+:!_U'qv\TPg)pS}vYkFJϵey5+ G,xD)GD-TnepESl<Lfsys-filesystem-1.4.4/checksums.yaml.gz.sig0000444000004100000410000000060014514017636020622 0ustar www-datawww-data+jMjo:|b"Irdvlߊ-#)+^gȅÉn Dh>R aClɭ.KKUg;݈pKrqD zқ'MJl5[M<߸BM+ GܕZ]\#BeM!k-uЋu'ٱ$&װ;]T$0y&Ӣ0~"r7IftBwh 6ƯfQ׃Y4]|fŽ 9\c-SA4P'&[ Uli;~~ٳ8oM-ia 4/[1 n`%MF\T:ӼXxjfhQsys-filesystem-1.4.4/LICENSE0000644000004100000410000002367614514017636015602 0ustar www-datawww-data Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS sys-filesystem-1.4.4/examples/0000755000004100000410000000000014514017636016375 5ustar www-datawww-datasys-filesystem-1.4.4/examples/example_stat.rb0000644000004100000410000000175614514017636021421 0ustar www-datawww-data###################################################################### # example_stat.rb # # Example program that demonstrates the FileSystem.stat method. # Use the 'rake example' task to run this program. ###################################################################### require 'sys/filesystem' include Sys p Filesystem::VERSION stat = Filesystem.stat("/") puts "Path: " + stat.path puts "Block size: " + stat.block_size.to_s puts "Fragment size: " + stat.fragment_size.to_s puts "Blocks free: " + stat.blocks_free.to_s puts "Blocks available: " + stat.blocks_available.to_s puts "Bytes free: " + stat.bytes_free.to_s puts "Bytes available: " + stat.bytes_available.to_s puts "Files/Inodes: " + stat.files.to_s puts "Files/Inodes free: " + stat.files_free.to_s puts "Files/Inodes available: " + stat.files_available.to_s puts "File system id: " + stat.filesystem_id.to_s puts "Base type: " + stat.base_type if stat.base_type puts "Flags: " + stat.flags.to_s puts "Name max: " + stat.name_max.to_s sys-filesystem-1.4.4/examples/example_mount.rb0000644000004100000410000000360014514017636021576 0ustar www-datawww-data###################################################################### # example_mount.rb # # Example program that demonstrates the Filesystem.mount method. # Simulates the `mount` command in ruby ###################################################################### require 'optparse' options = {:mount_options => []} OptionParser.new do |opts| opts.banner = "Usage: #$0 [-o options] [-t external_type] special node" opts.on("-o=OPTIONS", "Set one or many mount options (comma delimited)") do |cli_opts| options[:mount_options] += cli_opts.split(',') end opts.on("-r", "Set readonly flag") do options[:read_only] = true end opts.on("-t=EXTERNAL_TYPE", "Set the filesystem type") do |type| options[:type] = type end opts.on("-v", "--version", "Display version") do options[:version] = true end opts.separator "" opts.separator "Examples:" opts.separator "" opts.separator " NFS: ruby #$0 -t nfs 192.168.0.10:/var/nfs /mnt/nfs" opts.separator "" opts.separator " SMB: ruby #$0 -t cifs //192.168.0.10/share /mnt/smb/ -o username=user,password=pass,domain=example.com" opts.separator "" end.parse! require 'sys/filesystem' include Sys if options[:version] puts "Sys::Filesystem::VERSION: #{Filesystem::VERSION}" exit end def die msg warn msg exit 1 end mount_flags = options[:read_only] ? Filesystem::MNT_RDONLY : 0 mnt_path, mnt_point = ARGV[0,2] case options[:type] when "cifs" # keep mnt_path as is when "nfs" host, path, err = mnt_path.split(":") die "ERROR: mount_pount '#{mnt_path}' should only contain 1 ':'" if err mnt_path = ":#{path}" options[:mount_options] << "addr=#{host}" else die "ERROR: unknown mount type!" end Filesystem.mount mnt_path, mnt_point, options[:type], mount_flags, options[:mount_options].join(',') sys-filesystem-1.4.4/Rakefile0000644000004100000410000000206714514017636016231 0ustar www-datawww-datarequire 'rake' require 'rake/clean' require 'rspec/core/rake_task' require 'rubocop/rake_task' CLEAN.include('**/*.gem', '**/*.rbc', '**/*.rbx', '**/*.lock') namespace :gem do desc "Build the sys-filesystem gem" task :create => [:clean] do require 'rubygems/package' spec = Gem::Specification.load('sys-filesystem.gemspec') spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem') Gem::Package.build(spec) end desc "Install the sys-filesystem gem" task :install => [:create] do file = Dir['*.gem'].first sh "gem install -l #{file}" end end desc "Run the example program" task :example do sh "ruby -Ilib -Ilib/unix -Ilib/windows examples/example_stat.rb" end RuboCop::RakeTask.new namespace :rubocop do RuboCop::RakeTask.new(:unix) do |task| task.patterns = ['lib/sys/unix/sys/**/*.rb', 'spec/*unix*'] end RuboCop::RakeTask.new(:windows) do |task| task.patterns = ['lib/sys/windows/sys/**/*.rb', 'spec/*windows*'] end end desc "Run the test suite" RSpec::Core::RakeTask.new(:spec) task :default => :spec sys-filesystem-1.4.4/lib/0000755000004100000410000000000014514017636015325 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys-filesystem.rb0000644000004100000410000000010114514017636020642 0ustar www-datawww-data# frozen_string_literal: true require_relative 'sys/filesystem' sys-filesystem-1.4.4/lib/sys/0000755000004100000410000000000014514017636016143 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/filesystem.rb0000644000004100000410000000374714514017636020667 0ustar www-datawww-data# frozen_string_literal: true require 'rbconfig' if File::ALT_SEPARATOR require_relative 'windows/sys/filesystem' else require_relative 'unix/sys/filesystem' end # Methods and properties universal to all platforms # The Sys module serves as a namespace only. module Sys # The Filesystem class serves as an abstract base class. Its methods # return objects of other types. Do not instantiate. class Filesystem # The version of the sys-filesystem library VERSION = '1.4.4' # Stat objects are returned by the Sys::Filesystem.stat method. Here # we're adding universal methods. class Stat # Returns true if the filesystem is case sensitive for the current path. # Typically this will be any path on MS Windows or Macs using HFS. # # For a root path (really any path without actual a-z characters) we # take a best guess based on the host operating system. However, as a # general rule, I do not recommend using this method for a root path. # def case_insensitive? if path =~ /\w+/ File.identical?(path, path.swapcase) elsif RbConfig::CONFIG['host_os'] =~ /darwin|mac|windows|mswin|mingw/i true # Assumes HFS/APFS on Mac else false end end # Opposite of case_insensitive? # def case_sensitive? !case_insensitive? end end end end # Reopen the Numeric class and add some convenient methods # for converting bytes to kb, mb, and gb. class Numeric # call-seq: # num.to_kb # # Returns +num+ in terms of kilobytes. def to_kb self / 1024 end # call-seq: # num.to_mb # # Returns +num+ in terms of megabytes. def to_mb self / 1048576 end # call-seq: # num.to_gb # # Returns +num+ in terms of gigabytes. def to_gb self / 1073741824 end # call-seq: # num.to_gb # # Returns +num+ in terms of terabytes. def to_tb self / 1099511627776 end end sys-filesystem-1.4.4/lib/sys/unix/0000755000004100000410000000000014514017636017126 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/unix/sys/0000755000004100000410000000000014514017636017744 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/unix/sys/filesystem/0000755000004100000410000000000014514017636022130 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/unix/sys/filesystem/structs.rb0000644000004100000410000001527214514017636024173 0ustar www-datawww-data# frozen_string_literal: true require 'ffi' require 'rbconfig' module Sys class Filesystem module Structs # The Statfs struct is a subclass of FFI::Struct that corresponds to a struct statfs. class Statfs < FFI::Struct # Private method that will determine the layout of the struct on Linux. def self.linux64? if RUBY_PLATFORM == 'java' ENV_JAVA['sun.arch.data.model'].to_i == 64 else RbConfig::CONFIG['host_os'] =~ /linux/i && (RbConfig::CONFIG['arch'] =~ /64/ || RbConfig::CONFIG['DEFS'] =~ /64/) end end private_class_method :linux64? # FreeBSD 12.0 MNAMELEN from 88 => 1024. MNAMELEN = if RbConfig::CONFIG['host_os'] =~ /freebsd(.*)/i Regexp.last_match(1).to_f < 12.0 ? 88 : 1024 else 88 end case RbConfig::CONFIG['host_os'] when /bsd/i layout( :f_version, :uint32, :f_type, :uint32, :f_flags, :uint64, :f_bsize, :uint64, :f_iosize, :int64, :f_blocks, :uint64, :f_bfree, :uint64, :f_bavail, :int64, :f_files, :uint64, :f_ffree, :uint64, :f_syncwrites, :uint64, :f_asyncwrites, :uint64, :f_syncreads, :uint64, :f_asyncreads, :uint64, :f_spare, [:uint64, 10], :f_namemax, :uint32, :f_owner, :int32, :f_fsid, [:int32, 2], :f_charspare, [:char, 80], :f_fstypename, [:char, 16], :f_mntfromname, [:char, MNAMELEN], :f_mntonname, [:char, MNAMELEN] ) when /linux/i if linux64? layout( :f_type, :ulong, :f_bsize, :ulong, :f_blocks, :uint64, :f_bfree, :uint64, :f_bavail, :uint64, :f_files, :uint64, :f_ffree, :uint64, :f_fsid, [:int, 2], :f_namelen, :ulong, :f_frsize, :ulong, :f_flags, :ulong, :f_spare, [:ulong, 4] ) else layout( :f_type, :ulong, :f_bsize, :ulong, :f_blocks, :uint32, :f_bfree, :uint32, :f_bavail, :uint32, :f_files, :uint32, :f_ffree, :uint32, :f_fsid, [:int, 2], :f_namelen, :ulong, :f_frsize, :ulong, :f_flags, :ulong, :f_spare, [:ulong, 4] ) end else layout( :f_bsize, :uint32, :f_iosize, :int32, :f_blocks, :uint64, :f_bfree, :uint64, :f_bavail, :uint64, :f_files, :uint64, :f_ffree, :uint64, :f_fsid, [:int32, 2], :f_owner, :int32, :f_type, :uint32, :f_flags, :uint32, :f_fssubtype, :uint32, :f_fstypename, [:char, 16], :f_mntonname, [:char, 1024], :f_mntfromname, [:char, 1024], :f_reserved, [:uint32, 8] ) end end # The Statvfs struct represents struct statvfs from sys/statvfs.h. class Statvfs < FFI::Struct # Private method that will determine the layout of the struct on Linux. def self.linux64? if RUBY_PLATFORM == 'java' ENV_JAVA['sun.arch.data.model'].to_i == 64 else RbConfig::CONFIG['host_os'] =~ /linux/i && (RbConfig::CONFIG['arch'] =~ /64/ || RbConfig::CONFIG['DEFS'] =~ /64/) end end if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i layout( :f_bsize, :ulong, :f_frsize, :ulong, :f_blocks, :uint, :f_bfree, :uint, :f_bavail, :uint, :f_files, :uint, :f_ffree, :uint, :f_favail, :uint, :f_fsid, :ulong, :f_flag, :ulong, :f_namemax, :ulong ) elsif RbConfig::CONFIG['host'] =~ /bsd/i layout( :f_bavail, :uint64, :f_bfree, :uint64, :f_blocks, :uint64, :f_favail, :uint64, :f_ffree, :uint64, :f_files, :uint64, :f_bsize, :ulong, :f_flag, :ulong, :f_frsize, :ulong, :f_fsid, :ulong, :f_namemax, :ulong ) elsif RbConfig::CONFIG['host'] =~ /sunos|solaris/i layout( :f_bsize, :ulong, :f_frsize, :ulong, :f_blocks, :uint64_t, :f_bfree, :uint64_t, :f_bavail, :uint64_t, :f_files, :uint64_t, :f_ffree, :uint64_t, :f_favail, :uint64_t, :f_fsid, :ulong, :f_basetype, [:char, 16], :f_flag, :ulong, :f_namemax, :ulong, :f_fstr, [:char, 32], :f_filler, [:ulong, 16] ) elsif !linux64? layout( :f_bsize, :ulong, :f_frsize, :ulong, :f_blocks, :uint, :f_bfree, :uint, :f_bavail, :uint, :f_files, :uint, :f_ffree, :uint, :f_favail, :uint, :f_fsid, :ulong, :f_unused, :int, :f_flag, :ulong, :f_namemax, :ulong, :f_spare, [:int, 6] ) else layout( :f_bsize, :ulong, :f_frsize, :ulong, :f_blocks, :uint64, :f_bfree, :uint64, :f_bavail, :uint64, :f_files, :uint64, :f_ffree, :uint64, :f_favail, :uint64, :f_fsid, :ulong, :f_flag, :ulong, :f_namemax, :ulong, :f_spare, [:int, 6] ) end end # The Mnttab struct represents struct mnnttab from sys/mnttab.h on Solaris. class Mnttab < FFI::Struct layout( :mnt_special, :string, :mnt_mountp, :string, :mnt_fstype, :string, :mnt_mntopts, :string, :mnt_time, :string ) end # The Mntent struct represents struct mntent from sys/mount.h on Unix. class Mntent < FFI::Struct layout( :mnt_fsname, :string, :mnt_dir, :string, :mnt_type, :string, :mnt_opts, :string, :mnt_freq, :int, :mnt_passno, :int ) end end end end sys-filesystem-1.4.4/lib/sys/unix/sys/filesystem/functions.rb0000644000004100000410000000466114514017636024474 0ustar www-datawww-data# frozen_string_literal: true require 'ffi' module Sys class Filesystem # A scoped module for internal FFI functions to be used by the main code. module Functions extend FFI::Library ffi_lib FFI::Library::LIBC def self.linux64? if RUBY_PLATFORM == 'java' RbConfig::CONFIG['host_os'] =~ /linux/i && ENV_JAVA['sun.arch.data.model'].to_i == 64 else RbConfig::CONFIG['host_os'] =~ /linux/i && (RbConfig::CONFIG['arch'] =~ /64/ || RbConfig::CONFIG['DEFS'] =~ /64/) end end def self.solaris? RbConfig::CONFIG['host_os'] =~ /sunos|solaris/i end private_class_method :linux64? if linux64? || solaris? attach_function(:statvfs, :statvfs64, %i[string pointer], :int) else attach_function(:statvfs, %i[string pointer], :int) end attach_function(:strerror, [:int], :string) attach_function(:mount_c, :mount, %i[string string string ulong string], :int) begin attach_function(:umount_c, :umount, [:string], :int) rescue FFI::NotFoundError if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach|bsd/i attach_function(:umount_c, :unmount, [:string], :int) end end private_class_method :statvfs, :strerror, :mount_c, :umount_c begin if RbConfig::CONFIG['host_os'] =~ /sunos|solaris/i attach_function(:fopen, %i[string string], :pointer) attach_function(:fclose, [:pointer], :int) attach_function(:getmntent, %i[pointer pointer], :int) private_class_method :fopen, :fclose, :getmntent else attach_function(:getmntent, [:pointer], :pointer) attach_function(:setmntent, %i[string string], :pointer) attach_function(:endmntent, [:pointer], :int) attach_function(:umount2, %i[string int], :int) private_class_method :getmntent, :setmntent, :endmntent, :umount2 end rescue FFI::NotFoundError if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i begin attach_function(:getmntinfo, :getmntinfo64, %i[pointer int], :int) rescue FFI::NotFoundError attach_function(:getmntinfo, %i[pointer int], :int) # Big Sur and later end else attach_function(:getmntinfo, %i[pointer int], :int) end private_class_method :getmntinfo end end end end sys-filesystem-1.4.4/lib/sys/unix/sys/filesystem/constants.rb0000644000004100000410000000531214514017636024472 0ustar www-datawww-data# frozen_string_literal: true module Sys class Filesystem module Constants MNT_RDONLY = 0x00000001 # read only filesystem MNT_SYNCHRONOUS = 0x00000002 # file system written synchronously MNT_NOEXEC = 0x00000004 # can't exec from filesystem MNT_NOSUID = 0x00000008 # don't honor setuid bits on fs MNT_NODEV = 0x00000010 # don't interpret special files MNT_UNION = 0x00000020 # union with underlying filesystem MNT_ASYNC = 0x00000040 # file system written asynchronously MNT_CPROTECT = 0x00000080 # file system supports content protection MNT_EXPORTED = 0x00000100 # file system is exported MNT_QUARANTINE = 0x00000400 # file system is quarantined MNT_LOCAL = 0x00001000 # filesystem is stored locally MNT_QUOTA = 0x00002000 # quotas are enabled on filesystem MNT_ROOTFS = 0x00004000 # identifies the root filesystem MNT_DOVOLFS = 0x00008000 # FS supports volfs (deprecated) MNT_DONTBROWSE = 0x00100000 # FS is not appropriate path to user data MNT_IGNORE_OWNERSHIP = 0x00200000 # VFS will ignore ownership info on FS objects MNT_AUTOMOUNTED = 0x00400000 # filesystem was mounted by automounter MNT_JOURNALED = 0x00800000 # filesystem is journaled MNT_NOUSERXATTR = 0x01000000 # Don't allow user extended attributes MNT_DEFWRITE = 0x02000000 # filesystem should defer writes MNT_MULTILABEL = 0x04000000 # MAC support for individual labels MNT_NOATIME = 0x10000000 # disable update of file access time MNT_VISFLAGMASK = ( MNT_RDONLY | MNT_SYNCHRONOUS | MNT_NOEXEC | MNT_NOSUID | MNT_NODEV | MNT_UNION | MNT_ASYNC | MNT_EXPORTED | MNT_QUARANTINE | MNT_LOCAL | MNT_QUOTA | MNT_ROOTFS | MNT_DOVOLFS | MNT_DONTBROWSE | MNT_IGNORE_OWNERSHIP | MNT_AUTOMOUNTED | MNT_JOURNALED | MNT_NOUSERXATTR | MNT_DEFWRITE | MNT_MULTILABEL | MNT_NOATIME | MNT_CPROTECT ) MS_RDONLY = 1 MS_NOSUID = 2 MS_NODEV = 4 MS_NOEXEC = 8 MS_SYNCHRONOUS = 16 MS_REMOUNT = 32 MS_MANDLOCK = 64 MS_DIRSYNC = 128 MS_NOATIME = 1024 MS_NODIRATIME = 2048 MS_BIND = 4096 MS_MOVE = 8192 MS_REC = 16384 MS_SILENT = 32768 MS_POSIXACL = 1 << 16 MS_UNBINDABLE = 1 << 17 MS_PRIVATE = 1 << 18 MS_SLAVE = 1 << 19 MS_SHARED = 1 << 20 MS_RELATIME = 1 << 21 MS_KERNMOUNT = 1 << 22 MS_I_VERSION = 1 << 23 MS_STRICTATIME = 1 << 24 MS_ACTIVE = 1 << 30 MS_NOUSER = 1 << 31 MNT_FORCE = 1 MNT_DETACH = 2 MNT_EXPIRE = 4 UMOUNT_NOFOLLOW = 8 end end end sys-filesystem-1.4.4/lib/sys/unix/sys/filesystem.rb0000644000004100000410000003046514514017636022465 0ustar www-datawww-datarequire_relative 'filesystem/constants' require_relative 'filesystem/structs' require_relative 'filesystem/functions' # The Sys module serves as a namespace only. module Sys # The Filesystem class serves as an abstract base class. Its methods # return objects of other types. Do not instantiate. class Filesystem include Sys::Filesystem::Constants include Sys::Filesystem::Structs extend Sys::Filesystem::Functions private_class_method :new # Readable versions of constant names OPT_NAMES = { MNT_RDONLY => 'read-only', MNT_SYNCHRONOUS => 'synchronous', MNT_NOEXEC => 'noexec', MNT_NOSUID => 'nosuid', MNT_NODEV => 'nodev', MNT_UNION => 'union', MNT_ASYNC => 'asynchronous', MNT_CPROTECT => 'content-protection', MNT_EXPORTED => 'exported', MNT_QUARANTINE => 'quarantined', MNT_LOCAL => 'local', MNT_QUOTA => 'quotas', MNT_ROOTFS => 'rootfs', MNT_DONTBROWSE => 'nobrowse', MNT_IGNORE_OWNERSHIP => 'noowners', MNT_AUTOMOUNTED => 'automounted', MNT_JOURNALED => 'journaled', MNT_NOUSERXATTR => 'nouserxattr', MNT_DEFWRITE => 'defwrite', MNT_NOATIME => 'noatime' }.freeze private_constant :OPT_NAMES # File used to read mount informtion from. if File.exist?('/etc/mtab') MOUNT_FILE = '/etc/mtab'.freeze elsif File.exist?('/etc/mnttab') MOUNT_FILE = '/etc/mnttab'.freeze elsif File.exist?('/proc/mounts') MOUNT_FILE = '/proc/mounts'.freeze else MOUNT_FILE = 'getmntinfo'.freeze end private_constant :MOUNT_FILE # The error raised if any of the Filesystem methods fail. class Error < StandardError; end # Stat objects are returned by the Sys::Filesystem.stat method. class Stat # Read-only filesystem RDONLY = 1 # Filesystem does not support suid or sgid semantics. NOSUID = 2 # Filesystem does not truncate file names longer than +name_max+. NOTRUNC = 3 # The path of the filesystem. attr_accessor :path # The preferred system block size. attr_accessor :block_size # The fragment size, i.e. fundamental filesystem block size. attr_accessor :fragment_size # The total number of +fragment_size+ blocks in the filesystem. attr_accessor :blocks # The total number of free blocks in the filesystem. attr_accessor :blocks_free # The number of free blocks available to unprivileged processes. attr_accessor :blocks_available # The total number of files/inodes that can be created. attr_accessor :files # The total number of files/inodes on the filesystem. attr_accessor :files_free # The number of free files/inodes available to unprivileged processes. attr_accessor :files_available # The filesystem identifier. attr_accessor :filesystem_id # A bit mask of flags. attr_accessor :flags # The maximum length of a file name permitted on the filesystem. attr_accessor :name_max # The filesystem type, e.g. UFS. attr_accessor :base_type alias inodes files alias inodes_free files_free alias inodes_available files_available # Creates a new Sys::Filesystem::Stat object. This is meant for # internal use only. Do not instantiate directly. # def initialize @path = nil @block_size = nil @fragment_size = nil @blocks = nil @blocks_free = nil @blocks_available = nil @files = nil @files_free = nil @files_available = nil @filesystem_id = nil @flags = nil @name_max = nil @base_type = nil end # Returns the total space on the partition. def bytes_total blocks * fragment_size end # Returns the total amount of free space on the partition. def bytes_free blocks_free * fragment_size end # Returns the amount of free space available to unprivileged processes. def bytes_available blocks_available * fragment_size end # Returns the total amount of used space on the partition. def bytes_used bytes_total - bytes_free end # Returns the percentage of the partition that has been used. def percent_used 100 - (100.0 * bytes_free.to_f / bytes_total.to_f) end end # Mount objects are returned by the Sys::Filesystem.mounts method. class Mount # The name of the mounted resource. attr_accessor :name # The mount point/directory. attr_accessor :mount_point # The type of filesystem mount, e.g. ufs, nfs, etc. attr_accessor :mount_type # A list of comma separated options for the mount, e.g. nosuid, etc. attr_accessor :options # The time the filesystem was mounted. May be nil. attr_accessor :mount_time # The dump frequency in days. May be nil. attr_accessor :dump_frequency # The pass number of the filessytem check. May be nil. attr_accessor :pass_number alias fsname name alias dir mount_point alias opts options alias passno pass_number alias freq dump_frequency # Creates a Sys::Filesystem::Mount object. This is meant for internal # use only. Do no instantiate directly. # def initialize @name = nil @mount_point = nil @mount_type = nil @options = nil @mount_time = nil @dump_frequency = nil @pass_number = nil end end # Returns a Sys::Filesystem::Stat object containing information about the # +path+ on the filesystem. # # Examples: # # # path # Sys::Filesystem.stat("path") # # # Pathname # pathname = Pathname.new("path") # Sys::Filesystem.stat(pathname) # # # File # file = File.open("file", "r") # Sys::Filesystem.stat(file) # # # Dir # dir = Dir.open("/") # Sys::Filesystem.stat(dir) # def self.stat(path) path = path.path if path.respond_to?(:path) # File, Dir path = path.to_s if path.respond_to?(:to_s) # Pathname fs = Statvfs.new if statvfs(path, fs) < 0 raise Error, "statvfs() function failed: #{strerror(FFI.errno)}" end obj = Sys::Filesystem::Stat.new obj.path = path obj.block_size = fs[:f_bsize] obj.fragment_size = fs[:f_frsize] obj.blocks = fs[:f_blocks] obj.blocks_free = fs[:f_bfree] obj.blocks_available = fs[:f_bavail] obj.files = fs[:f_files] obj.files_free = fs[:f_ffree] obj.files_available = fs[:f_favail] obj.filesystem_id = fs[:f_fsid] obj.flags = fs[:f_flag] obj.name_max = fs[:f_namemax] # OSX does things a little differently if RbConfig::CONFIG['host_os'] =~ /darwin|osx|mach/i obj.block_size /= 256 end if fs.members.include?(:f_basetype) obj.base_type = fs[:f_basetype].to_s end obj.freeze end # In block form, yields a Sys::Filesystem::Mount object for each mounted # filesytem on the host. Otherwise it returns an array of Mount objects. # # Example: # # Sys::Filesystem.mounts{ |fs| # p fs.name # => '/dev/dsk/c0t0d0s0' # p fs.mount_time # => Thu Dec 11 15:07:23 -0700 2008 # p fs.mount_type # => 'ufs' # p fs.mount_point # => '/' # p fs.options # => local, noowner, nosuid # } # def self.mounts array = block_given? ? nil : [] if respond_to?(:getmntinfo, true) buf = FFI::MemoryPointer.new(:pointer) num = getmntinfo(buf, 2) if num == 0 raise Error, "getmntinfo() function failed: #{strerror(FFI.errno)}" end ptr = buf.get_pointer(0) num.times do mnt = Statfs.new(ptr) obj = Sys::Filesystem::Mount.new obj.name = mnt[:f_mntfromname].to_s obj.mount_point = mnt[:f_mntonname].to_s obj.mount_type = mnt[:f_fstypename].to_s string = '' flags = mnt[:f_flags] & MNT_VISFLAGMASK OPT_NAMES.each do |key, val| if flags & key > 0 if string.empty? string << val else string << ", #{val}" end end flags &= ~key end obj.options = string if block_given? yield obj.freeze else array << obj.freeze end ptr += Statfs.size end else begin if respond_to?(:setmntent, true) method_name = 'setmntent' fp = setmntent(MOUNT_FILE, 'r') else method_name = 'fopen' fp = fopen(MOUNT_FILE, 'r') end if fp.null? raise SystemCallError.new(method_name, FFI.errno) end if RbConfig::CONFIG['host_os'] =~ /sunos|solaris/i mt = Mnttab.new while getmntent(fp, mt) == 0 obj = Sys::Filesystem::Mount.new obj.name = mt[:mnt_special].to_s obj.mount_point = mt[:mnt_mountp].to_s obj.mount_type = mt[:mnt_fstype].to_s obj.options = mt[:mnt_mntopts].to_s obj.mount_time = Time.at(Integer(mt[:mnt_time])) if block_given? yield obj.freeze else array << obj.freeze end end else while ptr = getmntent(fp) break if ptr.null? mt = Mntent.new(ptr) obj = Sys::Filesystem::Mount.new obj.name = mt[:mnt_fsname] obj.mount_point = mt[:mnt_dir] obj.mount_type = mt[:mnt_type] obj.options = mt[:mnt_opts] obj.mount_time = nil obj.dump_frequency = mt[:mnt_freq] obj.pass_number = mt[:mnt_passno] if block_given? yield obj.freeze else array << obj.freeze end end end ensure if fp && !fp.null? if respond_to?(:endmntent, true) endmntent(fp) else fclose(fp) end end end end array end # Returns the mount point of the given +file+, or itself if it cannot # be found. # # Example: # # Sys::Filesystem.mount_point('/home/some_user') # => /home # def self.mount_point(file) dev = File.stat(file).dev val = file mounts.each do |mnt| mp = mnt.mount_point begin if File.stat(mp).dev == dev val = mp break end rescue Errno::EACCES next end end val end # Attach the filesystem specified by +source+ to the location (a directory # or file) specified by the pathname in +target+. # # Note that the +source+ is often a pathname referring to a device, but # can also be the pathname of a directory or file, or a dummy string. # # By default this method will assume 'ext2' as the filesystem type, but # you should update this as needed. # # Typically requires admin privileges. # # Example: # # Sys::Filesystem.mount('/dev/loop0', '/home/you/tmp', 'ext4', Sys::Filesystem::MNT_RDONLY) # def self.mount(source, target, fstype = 'ext2', flags = 0, data = nil) if mount_c(source, target, fstype, flags, data) != 0 raise Error, "mount() function failed: #{strerror(FFI.errno)}" end self end # Removes the attachment of the (topmost) filesystem mounted on target. # Additional flags may be provided for operating systems that support # the umount2 function. Otherwise this argument is ignored. # # Typically requires admin privileges. # def self.umount(target, flags = nil) if flags && respond_to?(:umount2) function = 'umount2' rv = umount2_c(target, flags) else function = 'umount' rv = umount_c(target) end if rv != 0 raise Error, "#{function} function failed: " + strerror(FFI.errno) end self end end end sys-filesystem-1.4.4/lib/sys/windows/0000755000004100000410000000000014514017636017635 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/windows/sys/0000755000004100000410000000000014514017636020453 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/windows/sys/filesystem/0000755000004100000410000000000014514017636022637 5ustar www-datawww-datasys-filesystem-1.4.4/lib/sys/windows/sys/filesystem/helper.rb0000644000004100000410000000047214514017636024446 0ustar www-datawww-data# Reopen core Ruby classes here and add some custom methods. class String # Convenience method for converting strings to UTF-16LE for wide character # functions that require it. #-- # TODO: Use a refinement. def wincode (tr(File::SEPARATOR, File::ALT_SEPARATOR) + 0.chr).encode('UTF-16LE') end end sys-filesystem-1.4.4/lib/sys/windows/sys/filesystem/functions.rb0000644000004100000410000000243614514017636025201 0ustar www-datawww-datarequire 'ffi' module Sys class Filesystem # Wrapper module for Windows related FFI functions. module Functions extend FFI::Library ffi_lib :kernel32 # Make FFI functions private module FFI::Library def attach_pfunc(*args) attach_function(*args) private args[0] end end attach_pfunc :DeleteVolumeMountPointA, [:string], :bool attach_pfunc :GetDiskFreeSpaceW, %i[buffer_in pointer pointer pointer pointer], :bool attach_pfunc :GetDiskFreeSpaceExW, %i[buffer_in pointer pointer pointer], :bool attach_pfunc :GetLogicalDriveStringsA, %i[ulong pointer], :ulong attach_pfunc :GetVolumeInformationA, %i[buffer_in pointer ulong pointer pointer pointer pointer ulong], :bool attach_pfunc :GetVolumeInformationW, %i[buffer_in pointer ulong pointer pointer pointer pointer ulong], :bool attach_pfunc :GetVolumeNameForVolumeMountPointW, %i[buffer_in buffer_in ulong], :bool attach_pfunc :QueryDosDeviceA, %i[buffer_in buffer_out ulong], :ulong attach_pfunc :SetVolumeMountPointW, %i[buffer_in buffer_in], :bool ffi_lib :shlwapi attach_pfunc :PathStripToRootW, [:pointer], :bool end end end sys-filesystem-1.4.4/lib/sys/windows/sys/filesystem/constants.rb0000644000004100000410000000133614514017636025203 0ustar www-datawww-datamodule Sys class Filesystem module Constants MAXPATH = 260 CASE_SENSITIVE_SEARCH = 0x00000001 CASE_PRESERVED_NAMES = 0x00000002 UNICODE_ON_DISK = 0x00000004 PERSISTENT_ACLS = 0x00000008 FILE_COMPRESSION = 0x00000010 VOLUME_QUOTAS = 0x00000020 SUPPORTS_SPARSE_FILES = 0x00000040 SUPPORTS_REPARSE_POINTS = 0x00000080 SUPPORTS_REMOTE_STORAGE = 0x00000100 VOLUME_IS_COMPRESSED = 0x00008000 SUPPORTS_OBJECT_IDS = 0x00010000 SUPPORTS_ENCRYPTION = 0x00020000 NAMED_STREAMS = 0x00040000 READ_ONLY_VOLUME = 0x00080000 end end end sys-filesystem-1.4.4/lib/sys/windows/sys/filesystem.rb0000644000004100000410000003520614514017636023172 0ustar www-datawww-datarequire_relative 'filesystem/constants' require_relative 'filesystem/functions' require_relative 'filesystem/helper' require 'socket' require 'win32ole' require 'date' require 'time' # The Sys module serves as a namespace only. module Sys # The Filesystem class encapsulates information about your filesystem. class Filesystem include Sys::Filesystem::Constants extend Sys::Filesystem::Functions # Error typically raised if any of the Sys::Filesystem methods fail. class Error < StandardError; end private_class_method :new # Mount objects are returned by the Sys::Filesystem.mounts method. class Mount # The name of the volume. This is the device mapping. attr_reader :name # The last time the volume was mounted. For MS Windows this equates # to your system's boot time. attr_reader :mount_time # The type of mount, e.g. NTFS, UDF, etc. attr_reader :mount_type # The volume mount point, e.g. 'C:\' attr_reader :mount_point # Various comma separated options that reflect the volume's features attr_reader :options # Always nil on MS Windows. Provided for interface compatibility only. attr_reader :pass_number # Always nil on MS Windows. Provided for interface compatibility only. attr_reader :frequency alias fsname name alias dir mount_point alias opts options alias passno pass_number alias freq frequency end # Stat objects are returned by the Sys::Filesystem.stat method. class Stat # The path of the file system. attr_reader :path # The file system block size. MS Windows typically defaults to 4096. attr_reader :block_size # Fragment size. Meaningless at the moment. attr_reader :fragment_size # The total number of blocks available (used or unused) on the file # system. attr_reader :blocks # The total number of unused blocks. attr_reader :blocks_free # The total number of unused blocks available to unprivileged processes. attr_reader :blocks_available # Total number of files/inodes that can be created on the file system. # This attribute is always nil on MS Windows. attr_reader :files # Total number of free files/inodes that can be created on the file # system. This attribute is always nil on MS Windows. attr_reader :files_free # Total number of available files/inodes for unprivileged processes # that can be created on the file system. This attribute is always # nil on MS Windows. attr_reader :files_available # The file system volume id. attr_reader :filesystem_id # A bit mask of file system flags. attr_reader :flags # The maximum length of a file name permitted on the file system. attr_reader :name_max # The file system type, e.g. NTFS, FAT, etc. attr_reader :base_type # The total amount of free space on the partition. attr_reader :bytes_free # The amount of free space available to unprivileged processes. attr_reader :bytes_available alias inodes files alias inodes_free files_free alias inodes_available files_available # Returns the total space on the partition. def bytes_total blocks * block_size end # Returns the total amount of used space on the partition. def bytes_used bytes_total - bytes_free end # Returns the percentage of the partition that has been used. def percent_used 100 - (100.0 * bytes_free.to_f / bytes_total.to_f) end end # Yields a Filesystem::Mount object for each volume on your system in # block form. Returns an array of Filesystem::Mount objects in non-block # form. # # Example: # # Sys::Filesystem.mounts{ |mount| # p mt.name # => \\Device\\HarddiskVolume1 # p mt.mount_point # => C:\ # p mt.mount_time # => Thu Dec 18 20:12:08 -0700 2008 # p mt.mount_type # => NTFS # p mt.options # => casepres,casesens,ro,unicode # p mt.pass_number # => nil # p mt.dump_freq # => nil # } # # This method is a bit of a fudge for MS Windows in the name of interface # compatibility because this method deals with volumes, not actual mount # points. But, I believe it provides the sort of information many users # want at a glance. # # The possible values for the +options+ and their meanings are as follows: # # casepres => The filesystem preserves the case of file names when it places a name on disk. # casesens => The filesystem supports case-sensitive file names. # compression => The filesystem supports file-based compression. # namedstreams => The filesystem supports named streams. # pacls => The filesystem preserves and enforces access control lists. # ro => The filesystem is read-only. # encryption => The filesystem supports the Encrypted File System (EFS). # objids => The filesystem supports object identifiers. # rpoints => The filesystem supports reparse points. # sparse => The filesystem supports sparse files. # unicode => The filesystem supports Unicode in file names as they appear on disk. # compressed => The filesystem is compressed. # #-- # I couldn't really find a good reason to use the wide functions for this # method. If you have one, patches welcome. #-- # rubocop:disable Metrics/BlockLength # def self.mounts # First call, get needed buffer size buffer = 0.chr length = GetLogicalDriveStringsA(buffer.size, buffer) if length == 0 raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno) else buffer = 0.chr * length end mounts = block_given? ? nil : [] # Try again with new buffer size if GetLogicalDriveStringsA(buffer.size, buffer) == 0 raise SystemCallError.new('GetLogicalDriveStrings', FFI.errno) end drives = buffer.split(0.chr) boot_time = get_boot_time drives.each do |drive| mount = Mount.new volume = FFI::MemoryPointer.new(:char, MAXPATH) fsname = FFI::MemoryPointer.new(:char, MAXPATH) mount.instance_variable_set(:@mount_point, drive) mount.instance_variable_set(:@mount_time, boot_time) volume_serial_number = FFI::MemoryPointer.new(:ulong) max_component_length = FFI::MemoryPointer.new(:ulong) filesystem_flags = FFI::MemoryPointer.new(:ulong) bool = GetVolumeInformationA( drive, volume, volume.size, volume_serial_number, max_component_length, filesystem_flags, fsname, fsname.size ) # Skip unmounted floppies or cd-roms, or inaccessible drives unless bool if [5, 21].include?(FFI.errno) # ERROR_NOT_READY or ERROR_ACCESS_DENIED next else raise SystemCallError.new('GetVolumeInformation', FFI.errno) end end filesystem_flags = filesystem_flags.read_ulong fsname = fsname.read_string name = 0.chr * MAXPATH if QueryDosDeviceA(drive[0, 2], name, name.size) == 0 raise SystemCallError.new('QueryDosDevice', FFI.errno) end mount.instance_variable_set(:@name, name.strip) mount.instance_variable_set(:@mount_type, fsname) mount.instance_variable_set(:@options, get_options(filesystem_flags)) if block_given? yield mount else mounts << mount end end mounts # Nil if the block form was used. end # rubocop:enable Metrics/BlockLength # Returns the mount point for the given +file+. For MS Windows this # means the root of the path. # # Example: # # File.mount_point("C:\\Documents and Settings") # => "C:\\' # def self.mount_point(file) wfile = FFI::MemoryPointer.from_string(file.to_s.wincode) if PathStripToRootW(wfile) wfile.read_string(wfile.size).split("\000\000").first.tr(0.chr, '') end end # Returns a Filesystem::Stat object that contains information about the # +path+ file system. On Windows this will default to using the root # path for volume information. # # Examples: # # # Regular directory # Sys::Filesystem.stat("C:\\") # Sys::Filesystem.stat("C:\\Documents and Settings\\some_user") # # # Pathname # pathname = Pathname.new("C:\\") # Sys::Filesystem.stat(pathname) # # # Dir # dir = Dir.open("C:\\") # Sys::Filesystem.stat(dir) # # Note that on Windows you cannot stat a regular file because # Windows won't like it. It must be a directory in some form. # def self.stat(path) path = path.path if path.respond_to?(:path) # Dir path = path.to_s if path.respond_to?(:to_s) # Pathname bytes_avail = FFI::MemoryPointer.new(:ulong_long) bytes_free = FFI::MemoryPointer.new(:ulong_long) total_bytes = FFI::MemoryPointer.new(:ulong_long) mpoint = mount_point(path).to_s mpoint << '/' unless mpoint.end_with?('/') wpath = path.to_s.wincode # We need this call for the 64 bit support unless GetDiskFreeSpaceExW(wpath, bytes_avail, total_bytes, bytes_free) raise SystemCallError.new('GetDiskFreeSpaceEx', FFI.errno) end bytes_avail = bytes_avail.read_ulong_long bytes_free = bytes_free.read_ulong_long total_bytes = total_bytes.read_ulong_long sectors_ptr = FFI::MemoryPointer.new(:ulong_long) bytes_ptr = FFI::MemoryPointer.new(:ulong_long) free_ptr = FFI::MemoryPointer.new(:ulong_long) total_ptr = FFI::MemoryPointer.new(:ulong_long) # We need this call for the total/cluster info, which is not in the Ex call. unless GetDiskFreeSpaceW(wpath, sectors_ptr, bytes_ptr, free_ptr, total_ptr) raise SystemCallError.new('GetDiskFreeSpace', FFI.errno) end sectors_per_cluster = sectors_ptr.read_ulong_long bytes_per_sector = bytes_ptr.read_ulong_long free_ptr.free total_ptr.free block_size = sectors_per_cluster * bytes_per_sector blocks_avail = bytes_avail / block_size blocks_free = bytes_free / block_size total_blocks = total_bytes / block_size vol_name_ptr = FFI::MemoryPointer.new(:char, MAXPATH) base_type_ptr = FFI::MemoryPointer.new(:char, MAXPATH) vol_serial_ptr = FFI::MemoryPointer.new(:ulong) name_max_ptr = FFI::MemoryPointer.new(:ulong) flags_ptr = FFI::MemoryPointer.new(:ulong) bool = GetVolumeInformationW( mpoint.wincode, vol_name_ptr, vol_name_ptr.size, vol_serial_ptr, name_max_ptr, flags_ptr, base_type_ptr, base_type_ptr.size ) unless bool raise SystemCallError.new('GetVolumeInformation', FFI.errno) end vol_serial = vol_serial_ptr.read_ulong name_max = name_max_ptr.read_ulong flags = flags_ptr.read_ulong base_type = base_type_ptr.read_string(base_type_ptr.size).tr(0.chr, '') # Lets explicitly free our pointers vol_name_ptr.free vol_serial_ptr.free name_max_ptr.free flags_ptr.free base_type_ptr.free sectors_ptr.free bytes_ptr.free stat_obj = Stat.new stat_obj.instance_variable_set(:@path, path) stat_obj.instance_variable_set(:@block_size, block_size) stat_obj.instance_variable_set(:@blocks, total_blocks) stat_obj.instance_variable_set(:@blocks_available, blocks_avail) stat_obj.instance_variable_set(:@blocks_free, blocks_free) stat_obj.instance_variable_set(:@name_max, name_max) stat_obj.instance_variable_set(:@base_type, base_type) stat_obj.instance_variable_set(:@flags, flags) stat_obj.instance_variable_set(:@filesystem_id, vol_serial) stat_obj.instance_variable_set(:@bytes_free, bytes_free) stat_obj.instance_variable_set(:@bytes_available, bytes_avail) stat_obj.freeze # Read-only object end # Associate a volume with a drive letter or a directory on another volume. # def self.mount(target, source) targetw = target.to_s.wincode sourcew = source.to_s.wincode volume_namew = (0.chr * 256).wincode unless GetVolumeNameForVolumeMountPointW(sourcew, volume_namew, volume_namew.size) raise SystemCallError.new('GetVolumeNameForVolumeMountPoint', FFI.errno) end unless SetVolumeMountPointW(targetw, volume_namew) raise SystemCallError.new('SetVolumeMountPoint', FFI.errno) end self end # Deletes a drive letter or mounted folder. # def self.umount(mount_point) unless DeleteVolumeMountPoint(mount_point) raise SystemCallError.new('DeleteVolumeMountPoint', FFI.errno) end self end # This method is used to get the boot time of the system, which is used # for the mount_time attribute within the File.mounts method. # def self.get_boot_time host = Socket.gethostname cs = "winmgmts://#{host}/root/cimv2" begin wmi = WIN32OLE.connect(cs) rescue WIN32OLERuntimeError => e raise Error, e else query = 'select LastBootupTime from Win32_OperatingSystem' ole = wmi.ExecQuery(query).ItemIndex(0) time_array = Time.parse(ole.LastBootupTime.split('.').first) Time.mktime(*time_array) end end private_class_method :get_boot_time # Private method that converts filesystem flags into a comma separated # list of strings. The presentation is meant as a rough analogue to the # way options are presented for Unix filesystems. # def self.get_options(flags) str = '' str << ' casepres' if CASE_PRESERVED_NAMES & flags > 0 str << ' casesens' if CASE_SENSITIVE_SEARCH & flags > 0 str << ' compression' if FILE_COMPRESSION & flags > 0 str << ' namedstreams' if NAMED_STREAMS & flags > 0 str << ' pacls' if PERSISTENT_ACLS & flags > 0 str << ' ro' if READ_ONLY_VOLUME & flags > 0 str << ' encryption' if SUPPORTS_ENCRYPTION & flags > 0 str << ' objids' if SUPPORTS_OBJECT_IDS & flags > 0 str << ' rpoints' if SUPPORTS_REPARSE_POINTS & flags > 0 str << ' sparse' if SUPPORTS_SPARSE_FILES & flags > 0 str << ' unicode' if UNICODE_ON_DISK & flags > 0 str << ' compressed' if VOLUME_IS_COMPRESSED & flags > 0 str.tr!(' ', ',') str[1..-1] # Ignore the first comma end private_class_method :get_options end end sys-filesystem-1.4.4/Gemfile0000644000004100000410000000004614514017636016052 0ustar www-datawww-datasource 'https://rubygems.org' gemspec sys-filesystem-1.4.4/CHANGES.md0000644000004100000410000001477314514017636016165 0ustar www-datawww-data## 1.4.4 - 12-Sep-2023 * Yet another fix for 32-bit vs 64-bit linux, specifically for the Statvfs struct. Thanks go to Josh Cooper for the spot and the patch. ## 1.4.3 - 20-Oct-2021 * Another fix for 32-bit vs 64-bit Linux since it was realized we cannot always rely on the host architecture information. Handling for JRuby was improved as well. Thanks go to Scott Bradnick for the spot. * The constructor is now explicitly private. This class was never meant to actually be instantiated. * Updated the MANIFEST.md file. ## 1.4.2 - 22-Jul-2021 * Fixed support for 32-bit Linux. Thanks go to ciprianbadescu for the spot. ## 1.4.1 - 30-Dec-2020 * Fix an FFI function declaration bug for Big Sur and later on Mac. Thanks go to Roman Gaufman for the spot and Martins Polakovs for testing. * Fixed the changelog metadata URI. * Added a .gitignore file. ## 1.4.0 - 6-Sep-2020 * The Sys::Filesystem.stat method now accepts a Pathname and Dir object as an argument. On most platforms it will also accept a File object. Thanks go to hirura for the patch. * Fixed the statvfs struct layout on i686 platforms. Thanks go to Filipovici-Andrei for the patch. * Switched from test-unit to rspec, and updated the tests. ## 1.3.4 - 17-Mar-2020 * Properly include a LICENSE file as per the Apache-2.0 license. ## 1.3.3 - 18-Feb-2020 * Linux now uses statvfs64 under the hood. This fixes a potential issue with 32 bit systems. Thanks go to Tom Smyth for the spot. ## 1.3.2 - 8-Dec-2019 * Renamed various text files to include .rdoc extension so that github renders them nicely. ## 1.3.1 - 15-Oct-2019 * Fixed a bug where the Statvfs and Statfs structs were not allocated properly on Linux, which could cause issues. Thanks go to Daniele Orlandi for the spot. * Added specs to ensure that the FFI struct size matches the C struct size. This required adding mkmf-lite as a development dependency. * Added a slightly more pessimistic version requirement for test-unit. ## 1.3.0 - 3-Oct-2019 * Added the mount and umount singleton methods. * Changed an internal class variable to a frozen constant. ## 1.2.0 - 1-Jan-2019 * Changed the license to Apache-2.0. * Added the case_sensitive? and case_insensitive? instance methods to the Filesystem::Stat class. * Refactored the Numeric helper methods into a single location. * Fixed the deprecation warnings when running the tests on Windows. * Added metadata to the gemspec. ## 1.1.9 - 26-Jan-2018 * The bytes_free and bytes_total methods were updated to use the fragment size instead of the block size. This fixes Macs using APFS, as well as Solaris 10. Thanks go to Mikhail Vasin for the spot. * Removed the code specific to FreeBSD 10, which was ultimately an attempt to address the issue now resolved by the above change. * Updated the cert again. ## 1.1.8 - 2-Sep-2017 * The stat and mount_point methods now call to_s on the string argument explicitly so that Pathname objects will now work with String#wincode. * Updated some tests to avoid Fixnum warnings for Ruby 2.4. * Updated the cert. ## 1.1.7 - 1-Aug-2016 * Fixed an issue in the stat method for MS Windows where the blocks_avail value was not correct. Thanks go to Maxime Lapointe for the spot. ## 1.1.6 - 17-May-2016 * On versions that use setmntent or fopen, explicitly raise an error if either of those calls return a null value. ## 1.1.5 - 5-Dec-2015 * This gem is now signed. * The gem related tasks in the Rakefile now assume Rubygems 2.x. * Updates to the gemspec, added cert_chain, removed rubyforge_project. * Internal reorganization, and use of relative_require as appropriate. * Added a sys-filesystem.rb file for convenience. ## 1.1.4 - 15-Mar-2015 * The File.mounts method no longer raises an error if a mount point is not accessible. Thanks go to Michael Pope for the patch. * Some internal code reorganization. ## 1.1.3 - 1-Oct-2014 * Now ignores EPERM errors when trying to find the mount point for a path. Thanks go to petersen for the patch. * The Filesystem.stat method now defaults to using the root path on Windows for volume information. ## 1.1.2 - 9-May-2014 * Added the percent_used, bytes_total, bytes_free and bytes_used methods to the Filesystem::Stat class. Thanks go to xanview for the suggestion. * Changed File.exists? to File.exist? to avoid warnings in Ruby 2.1.x. * The convenience methods to_mb, to_gb, etc, are now defined in Numeric instead of Fixnum. * Added the to_tb method for terabytes. * Minor updates to the gem:create and gem:install Rake tasks. ## 1.1.1 - 3-Oct-2013 * Solaris now always uses statvfs64 on Solaris for better 64-bit support. Thanks go to Jeff Shantz for the spot. ## 1.1.0 - 19-Jan-2013 * Converted the Windows source code to use FFI. Consequently, there is now a single gem rather than separate gems for Windows and Unix. * Revamped the Windows tests. ## 1.0.0 - 11-Jan-2012 * Converted everything from C to FFI for the Unix flavors. The Windows source code remains untouched. ## 0.3.4 - 19-Nov-2010 * Fixed a bug where negative block counts were happening on very large hard drives. Thanks go to Jonas Pfenniger for the spot. * Refactored the clean task in the Rakefile. * Some cosmetic source code changes. ## 0.3.3 - 21-May-2010 * Added a workaround for the Sys::Filesystem#block_size member to deal with a bug in OS X. Thanks go to Josh Pasqualetto for the spot. ## 0.3.2 - 29-Dec-2009 * Source has been moved to github. * Added the 'gem' task and removed build logic from the gemspec. * Updated the install task. * Minor correction to the manifest. * Removed some junk build files that were inadvertently included in the last gem. ## 0.3.1 - 5-Aug-2009 * Now compatible with Ruby 1.9.x. * Changed license to Artistic 2.0 * Updated the gemspec, including the explicit addition of a license and test-unit as a development dependency, as well as an updated description. ## 0.3.0 - 26-Feb-2009 * Added support for OS X and FreeBSD thanks to an awesome patch by Nobuyoshi Miyokawa. * Added the Filesystem.mount_point method that takes a file and returns the mount point it's sitting on. ## 0.2.0 - 30-Dec-2008 * Added the Filesystem.mounts method for iterating over mount or volume information. ## 0.1.1 - 28-Mar-2007 * Bug fix for BSD flavors. Thanks go to Jeremy Kemper and Ole Christian Rynning for the spot. * Bug fix for OS X (along the same lines as the BSD fix). Thanks go to Aurelian Dehay for the spot. * Some Rdoc improvements for the C extension. * Tweaks to the gemspec. * Added synopsis to the README. ## 0.1.0 - 17-Nov-2006 * Initial release. Alpha. Code is stable, but API is not.