From 989db5dfb590867fe2139a3564f162f4aa3a2154 Mon Sep 17 00:00:00 2001 From: danielnachun Date: Wed, 16 Feb 2022 21:29:08 -0800 Subject: [PATCH 1/3] keg_relocate.rb: add each_unique_binary_file --- Library/Homebrew/keg_relocate.rb | 42 ++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb index eaa3f3582b..ad779064bf 100644 --- a/Library/Homebrew/keg_relocate.rb +++ b/Library/Homebrew/keg_relocate.rb @@ -8,6 +8,8 @@ class Keg LIBRARY_PLACEHOLDER = "@@HOMEBREW_LIBRARY@@" PERL_PLACEHOLDER = "@@HOMEBREW_PERL@@" JAVA_PLACEHOLDER = "@@HOMEBREW_JAVA@@" + NULL_BYTE = "\x00" + NULL_BYTE_STRING = "\\x00" class Relocation extend T::Sig @@ -173,16 +175,42 @@ class Keg end alias generic_recursive_fgrep_args recursive_fgrep_args - def each_unique_file_matching(string) + def egrep_args + grep_bin = "grep" + grep_args = recursive_fgrep_args + grep_args += "Pa" + [grep_bin, grep_args] + end + alias generic_egrep_args egrep_args + + def each_unique_file(io, block) + hardlinks = Set.new + + until io.eof? + file = Pathname.new(io.readline.chomp) + # Don't return symbolic links. + next if file.symlink? + + # To avoid returning hardlinks, only return files with unique inodes. + # Hardlinks will have the same inode as the file they point to. + block.call file if hardlinks.add? file.stat.ino + end + end + + def each_unique_file_matching(string, &block) Utils.popen_read("fgrep", recursive_fgrep_args, string, to_s) do |io| - hardlinks = Set.new + each_unique_file(io, block) + end + end - until io.eof? - file = Pathname.new(io.readline.chomp) - next if file.symlink? + def each_unique_binary_file(&block) + grep_bin, grep_args = egrep_args - yield file if hardlinks.add? file.stat.ino - end + # We need to pass NULL_BYTE_STRING, the literal string "\x00", to grep + # rather than NULL_BYTE, a literal null byte, because grep will internally + # convert the literal string "\x00" to a null byte. + Utils.popen_read(grep_bin, grep_args, NULL_BYTE_STRING, to_s) do |io| + each_unique_file(io, block) end end From 94e22d49fa5f9a98c88ad6b0309589f0ea084b7b Mon Sep 17 00:00:00 2001 From: danielnachun Date: Wed, 16 Feb 2022 21:30:04 -0800 Subject: [PATCH 2/3] os/mac/keg_relocate.rb: override grep args --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 282d9be933..d59c3ee113 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -183,4 +183,10 @@ class Keg # it's wrong. -O is a BSD-grep-only option. "-lrO" end + + def egrep_args + grep_bin = "egrep" + grep_args = recursive_fgrep_args + [grep_bin, grep_args] + end end From dd898e58b836c0d39b705a5043dd90294d7543c1 Mon Sep 17 00:00:00 2001 From: danielnachun Date: Sat, 26 Feb 2022 17:23:41 -0800 Subject: [PATCH 3/3] test/keg_relocate/grep_spec.rb: add new unit tests --- .../Homebrew/test/keg_relocate/grep_spec.rb | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Library/Homebrew/test/keg_relocate/grep_spec.rb diff --git a/Library/Homebrew/test/keg_relocate/grep_spec.rb b/Library/Homebrew/test/keg_relocate/grep_spec.rb new file mode 100644 index 0000000000..993fa4b875 --- /dev/null +++ b/Library/Homebrew/test/keg_relocate/grep_spec.rb @@ -0,0 +1,58 @@ +# typed: false +# frozen_string_literal: true + +require "keg_relocate" + +describe Keg do + subject(:keg) { described_class.new(HOMEBREW_CELLAR/"foo/1.0.0") } + + let(:dir) { HOMEBREW_CELLAR/"foo/1.0.0" } + let(:text_file) { dir/"file.txt" } + let(:binary_file) { dir/"file.bin" } + + before do + dir.mkpath + end + + def setup_text_file + text_file.atomic_write <<~EOS + #{dir}/file.txt + /foo#{dir}/file.txt + foo/bar:#{dir}/file.txt + foo/bar:/foo#{dir}/file.txt + #{dir}/bar.txt:#{dir}/baz.txt + EOS + end + + def setup_binary_file + binary_file.atomic_write <<~EOS + \x00 + EOS + end + + describe "#each_unique_file_matching" do + specify "find string matches to path" do + setup_text_file + + string_matches = Set.new + keg.each_unique_file_matching(dir) do |file| + string_matches << file + end + + expect(string_matches.size).to eq 1 + end + end + + describe "#each_unique_binary_file" do + specify "find null bytes in binaries" do + setup_binary_file + + binary_matches = Set.new + keg.each_unique_binary_file do |file| + binary_matches << file + end + + expect(binary_matches.size).to eq 1 + end + end +end