(FACT-1778) Attempt to canonicalize search paths
Attempt to canonicalize search paths, then fall back to using absolute
path. This preserves the fix for FACT-1510, but also ensures we
deduplicate most paths.
This primarily fixes that in Puppet we load `lib/facter` from both
Puppet's search paths and Facter's internal handling of Ruby's LOAD_PATH.
Previously canonicalization would ensure that the resulting file paths for
custom facts were identical, so when we check whether we've previously
loaded a file we would identify duplicates and avoid loading them again.
The fix for FACT-1510 broke that. This is the least-impact approach to
fixing it.
Also use forward-slash explicitly when working with paths that come from
Ruby, to avoid inconsistent representations when we can't determine the
canonical path.
Michael Smith
6 years ago
172 | 172 | } |
173 | 173 | |
174 | 174 | namespace facter { namespace ruby { |
175 | ||
176 | static string canonicalize(string p) | |
177 | { | |
178 | // Get the canonical/absolute directory name | |
179 | // If it can be resolved, use canonical to avoid duplicate search paths. | |
180 | // Fall back to absolute because canonical on Windows won't recognize paths as valid if | |
181 | // they resolve to symlinks to non-NTFS volumes. | |
182 | boost::system::error_code ec; | |
183 | auto directory = canonical(p, ec); | |
184 | if (ec) { | |
185 | return absolute(p).string(); | |
186 | } | |
187 | return directory.string(); | |
188 | } | |
175 | 189 | |
176 | 190 | map<VALUE, module*> module::_instances; |
177 | 191 | |
302 | 316 | { |
303 | 317 | for (auto dir : paths) { |
304 | 318 | _additional_search_paths.emplace_back(dir); |
305 | ||
306 | // Get the absolute directory name | |
307 | // Absolute is used over canonical because canonical on Windows won't recognize paths as valid if | |
308 | // they resolve to symlinks to non-NTFS volumes. | |
309 | path directory = absolute(_additional_search_paths.back()); | |
310 | ||
311 | _search_paths.push_back(directory.string()); | |
319 | _search_paths.emplace_back(canonicalize(_additional_search_paths.back())); | |
312 | 320 | } |
313 | 321 | } |
314 | 322 | |
759 | 767 | if (!ruby.is_string(argv[i])) { |
760 | 768 | continue; |
761 | 769 | } |
770 | ||
762 | 771 | instance->_additional_search_paths.emplace_back(ruby.to_string(argv[i])); |
763 | ||
764 | // Get the absolute directory name | |
765 | path directory = absolute(instance->_additional_search_paths.back()); | |
766 | ||
767 | instance->_search_paths.push_back(directory.string()); | |
772 | instance->_search_paths.emplace_back(canonicalize(instance->_additional_search_paths.back())); | |
768 | 773 | } |
769 | 774 | return ruby.nil_value(); |
770 | 775 | }); |
952 | 957 | |
953 | 958 | // Look for "facter" subdirectories on the load path |
954 | 959 | for (auto const& directory : ruby.get_load_path()) { |
955 | // Get the absolute directory name | |
956 | 960 | boost::system::error_code ec; |
957 | path dir = absolute(directory); | |
961 | // Use forward-slash to keep this consistent with Ruby conventions. | |
962 | auto dir = canonicalize(directory) + "/facter"; | |
958 | 963 | |
959 | 964 | // Ignore facter itself if it's on the load path |
960 | if (is_regular_file(dir / "facter.rb", ec)) { | |
965 | if (is_regular_file(dir, ec)) { | |
961 | 966 | continue; |
962 | 967 | } |
963 | 968 | |
964 | dir = dir / "facter"; | |
965 | 969 | if (!is_directory(dir, ec)) { |
966 | 970 | continue; |
967 | 971 | } |
968 | _search_paths.push_back(dir.string()); | |
972 | _search_paths.push_back(dir); | |
969 | 973 | } |
970 | 974 | |
971 | 975 | // Append the FACTERLIB paths |
979 | 983 | // Insert the given paths last |
980 | 984 | _search_paths.insert(_search_paths.end(), paths.begin(), paths.end()); |
981 | 985 | |
982 | // Do a absolute transform | |
986 | // Do a canonical/absolute transform | |
983 | 987 | transform(_search_paths.begin(), _search_paths.end(), _search_paths.begin(), [](string const& directory) -> string { |
984 | // Get the absolute directory name | |
985 | path dir = absolute(directory); | |
986 | return dir.string(); | |
988 | return canonicalize(directory); | |
987 | 989 | }); |
988 | 990 | |
989 | 991 | // Remove anything that is empty using the erase-remove idiom. |