From 68a5b0f086c9d54f271cdff231a1a2c2a40b0aa6 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Tue, 6 Sep 2022 12:33:23 -0400 Subject: [PATCH] [rubygems/rubygems] Mask the file mode when extracting files When extracting files from the tarball, a mode is retrieved from the header. Occasionally you'll encounter a gem that was packaged on a system whose permission bits result in a value that is larger than the value that File.chmod will allow (anything >= 2^16). In that case the extraction fails with a RangeError, which is pretty esoteric. If you extract the tarball with the tar and gunzip utilities, the file permissions end up being just the bottom 16 bits masked off from the original value. I've mirrored that behavior here. Per the tar spec: > Modes which are not supported by the operating system restoring > files from the archive will be ignored. I think that basically means what I've done here. --- This commit also changes the behavior very slightly with regard to when the chmod is called. Previously it was called while the file descriptor was still open, but after the write call. When write flushes, the file permissions are changed to the mode value from the File.open call, undoing the changes made by FileUtils.chmod. CRuby appears to flush the buffer after the chmod call, whereas TruffleRuby flushes before the chmod call. So the file permissions can change depending on implementation. Both implementations end up getting the correct file permissions for the bottom 9 bits (user, group, world), but differ with regard to the sticky bit in the next 3. To get consistent behavior, this commit changes it to close the file descriptor before attempting to chmod anything, which makes it consistent because the write flushes in both cases. https://github.com/rubygems/rubygems/commit/22ce076e99 --- lib/rubygems/package.rb | 13 +++++++++---- test/rubygems/packages/Bluebie-legs-0.6.2.gem | Bin 0 -> 14336 bytes test/rubygems/test_gem_package.rb | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 test/rubygems/packages/Bluebie-legs-0.6.2.gem diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index 084dc5d2d9..4672866985 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -444,10 +444,10 @@ EOM directories << mkdir end - File.open destination, "wb" do |out| - out.write entry.read + if entry.file? + File.open(destination, "wb") {|out| out.write entry.read } FileUtils.chmod file_mode(entry.header.mode), destination - end if entry.file? + end verbose destination end @@ -467,7 +467,12 @@ EOM end def file_mode(mode) # :nodoc: - ((mode & 0111).zero? ? data_mode : prog_mode) || mode + ((mode & 0111).zero? ? data_mode : prog_mode) || + # If we're not using one of the default modes, then we're going to fall + # back to the mode from the tarball. In this case we need to mask it down + # to fit into 2^16 bits (the maximum value for a mode in CRuby since it + # gets put into an unsigned short). + (mode & ((1 << 16) - 1)) end ## diff --git a/test/rubygems/packages/Bluebie-legs-0.6.2.gem b/test/rubygems/packages/Bluebie-legs-0.6.2.gem new file mode 100644 index 0000000000000000000000000000000000000000..60918f3bc53abc47d783d04e26a33bdba0d51bfe GIT binary patch literal 14336 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 literal 0 HcmV?d00001 diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb index 9295f42dba..eebe4d86d0 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -510,6 +510,21 @@ class TestGemPackage < Gem::Package::TarTestCase assert_path_exist @destination end + def test_extract_file_permissions + pend "chmod not supported" if win_platform? + + gem_with_long_permissions = File.expand_path("packages/Bluebie-legs-0.6.2.gem", __dir__) + + package = Gem::Package.new gem_with_long_permissions + + package.extract_files @destination + + filepath = File.join @destination, "README.rdoc" + assert_path_exist filepath + + assert_equal 0104444, File.stat(filepath).mode + end + def test_extract_tar_gz_absolute package = Gem::Package.new @gem