diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb index 481f913d0c..d2096a04cc 100644 --- a/test/fileutils/test_fileutils.rb +++ b/test/fileutils/test_fileutils.rb @@ -93,12 +93,24 @@ class TestFileUtils < Test::Unit::TestCase @@no_broken_symlink end + def has_capsh? + !!system('capsh', '--print', out: File::NULL, err: File::NULL) + end + + def has_root_file_capabilities? + !!system( + 'capsh', '--has-p=CAP_DAC_OVERRIDE', '--has-p=CAP_CHOWN', '--has-p=CAP_FOWNER', + out: File::NULL, err: File::NULL + ) + end + def root_in_posix? if /cygwin/ =~ RUBY_PLATFORM # FIXME: privilege if groups include root user? return Process.groups.include?(0) - end - if Process.respond_to?('uid') + elsif has_capsh? + return has_root_file_capabilities? + elsif Process.respond_to?('uid') return Process.uid == 0 else return false diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index de09811946..f3068cb189 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -186,6 +186,12 @@ class TestFileExhaustive < Test::Unit::TestCase @blockdev end + def root_without_capabilities? + return false unless Process.uid == 0 + return false unless system('command', '-v', 'capsh', out: File::NULL) + !system('capsh', '--has-p=CAP_DAC_OVERRIDE', out: File::NULL, err: File::NULL) + end + def test_path [regular_file, utf8_file].each do |file| assert_equal(file, File.open(file) {|f| f.path}) @@ -1538,8 +1544,17 @@ class TestFileExhaustive < Test::Unit::TestCase assert_equal(stat.size?, File.size?(f), f) assert_bool_equal(stat.socket?, File.socket?(f), f) assert_bool_equal(stat.setuid?, File.setuid?(f), f) - assert_bool_equal(stat.writable?, File.writable?(f), f) - assert_bool_equal(stat.writable_real?, File.writable_real?(f), f) + # It's possible in Linux to be uid 0, but not to have the CAP_DAC_OVERRIDE + # capability that allows skipping file permissions checks (e.g. some kinds + # of "rootless" container setups). In these cases, stat.writable? will be + # true (because it always returns true if Process.uid == 0), but + # File.writeable? will be false (because it actually asks the kernel to do + # an access check). + # Skip these two assertions in that case. + unless root_without_capabilities? + assert_bool_equal(stat.writable?, File.writable?(f), f) + assert_bool_equal(stat.writable_real?, File.writable_real?(f), f) + end assert_bool_equal(stat.executable?, File.executable?(f), f) assert_bool_equal(stat.executable_real?, File.executable_real?(f), f) assert_bool_equal(stat.zero?, File.zero?(f), f)