elfpak_core/resolver/
search.rs1use crate::{
4 elf::{Architecture, ElfClass},
5 paths::{logical_parent, normalize_absolute},
6 source::SourceRoot,
7};
8use std::path::{Path, PathBuf};
9
10const CONF_DEPTH_MAX: usize = 8;
15
16const CONF_FILES_MAX: usize = 256;
19const CONF_DIRECTORIES_MAX: usize = 256;
20
21pub fn default_library_paths(architecture: &Architecture) -> Vec<PathBuf> {
24 let mut paths = Vec::new();
25 if let Some(tuple) = architecture.machine.debian_multiarch() {
26 paths.push(PathBuf::from(format!("/lib/{tuple}")));
27 paths.push(PathBuf::from(format!("/usr/lib/{tuple}")));
28 }
29 if architecture.class == ElfClass::Elf64 {
30 paths.push(PathBuf::from("/lib64"));
31 paths.push(PathBuf::from("/usr/lib64"));
32 }
33 paths.push(PathBuf::from("/lib"));
34 paths.push(PathBuf::from("/usr/lib"));
35
36 paths
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
41enum Directive {
42 Directory(PathBuf),
44 Include(PathBuf),
46}
47
48pub fn parse_ld_so_conf(root: &SourceRoot) -> Vec<PathBuf> {
53 let mut paths: Vec<PathBuf> = Vec::new();
54 let mut visited: Vec<PathBuf> = Vec::new();
55 let mut pending = vec![(Directive::Include(PathBuf::from("/etc/ld.so.conf")), 0usize)];
56
57 while let Some((directive, depth)) = pending.pop() {
58 let file = match directive {
59 Directive::Directory(dir) => {
60 if !paths.contains(&dir) && paths.len() < CONF_DIRECTORIES_MAX {
61 paths.push(dir);
62 }
63 continue;
64 }
65 Directive::Include(file) => file,
66 };
67
68 if depth > CONF_DEPTH_MAX || visited.contains(&file) || visited.len() == CONF_FILES_MAX {
70 continue;
71 }
72 visited.push(file.clone());
73
74 for directive in read_conf(root, &file).into_iter().rev() {
75 pending.push((directive, depth + 1));
76 }
77 }
78
79 paths
80}
81
82fn read_conf(root: &SourceRoot, logical: &Path) -> Vec<Directive> {
86 assert!(logical.is_absolute());
87
88 let Ok(Some(bytes)) = root.read(logical) else {
89 return Vec::new();
90 };
91 let Ok(text) = String::from_utf8(bytes) else {
92 return Vec::new();
93 };
94
95 let mut directives = Vec::new();
96 for line in text.lines() {
97 let line = line.split('#').next().unwrap_or("").trim();
98 if line.is_empty() {
99 continue;
100 }
101 if let Some(rest) = line
102 .strip_prefix("include ")
103 .or_else(|| line.strip_prefix("include\t"))
104 {
105 for pattern in rest.split_whitespace() {
106 let included = expand_include(root, logical, pattern);
107 directives.extend(included.into_iter().map(Directive::Include));
108 }
109 continue;
110 }
111 if line.starts_with("hwcap ") {
112 continue;
114 }
115 directives.push(Directive::Directory(normalize_absolute(Path::new(line))));
116 }
117 directives
118}
119
120fn expand_include(root: &SourceRoot, current: &Path, pattern: &str) -> Vec<PathBuf> {
122 assert!(current.is_absolute());
123
124 let pattern_path = if Path::new(pattern).is_absolute() {
125 PathBuf::from(pattern)
126 } else {
127 logical_parent(current).join(pattern)
128 };
129 let pattern_path = normalize_absolute(&pattern_path);
130
131 let Some(name) = pattern_path.file_name().and_then(|n| n.to_str()) else {
132 return Vec::new();
133 };
134 if !name.contains('*') {
135 return vec![pattern_path];
136 }
137
138 let dir = logical_parent(&pattern_path);
139 let Ok(entries) = root.read_dir(&dir) else {
140 return Vec::new();
141 };
142 let (prefix, suffix) = name.split_once('*').unwrap_or((name, ""));
143 entries
144 .into_iter()
145 .filter_map(|entry| {
146 let entry = entry.to_str()?.to_string();
147 let fits = entry.len() >= prefix.len() + suffix.len();
150 let matches = entry.starts_with(prefix) && entry.ends_with(suffix);
151 (fits && matches).then(|| dir.join(entry))
152 })
153 .collect()
154}