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 = 128;
27const CONF_PENDING_MAX: usize = 4096;
31const CONF_BYTES_MAX: usize = 1024 * 1024;
33
34pub fn default_library_paths(architecture: &Architecture) -> Vec<PathBuf> {
37 let mut paths = Vec::new();
38 if let Some(tuple) = architecture.machine.debian_multiarch() {
39 paths.push(PathBuf::from(format!("/lib/{tuple}")));
40 paths.push(PathBuf::from(format!("/usr/lib/{tuple}")));
41 }
42 if architecture.class == ElfClass::Elf64 {
43 paths.push(PathBuf::from("/lib64"));
44 paths.push(PathBuf::from("/usr/lib64"));
45 }
46 paths.push(PathBuf::from("/lib"));
47 paths.push(PathBuf::from("/usr/lib"));
48
49 paths
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54enum Directive {
55 Directory(PathBuf),
57 Include(PathBuf),
59}
60
61pub fn parse_ld_so_conf(root: &SourceRoot) -> Vec<PathBuf> {
66 let mut paths: Vec<PathBuf> = Vec::new();
67 let mut visited: Vec<PathBuf> = Vec::new();
68 let mut pending = vec![(Directive::Include(PathBuf::from("/etc/ld.so.conf")), 0usize)];
69
70 while let Some((directive, depth)) = pending.pop() {
71 let file = match directive {
72 Directive::Directory(dir) => {
73 if !paths.contains(&dir) && paths.len() < CONF_DIRECTORIES_MAX {
74 paths.push(dir);
75 }
76 continue;
77 }
78 Directive::Include(file) => file,
79 };
80
81 if depth > CONF_DEPTH_MAX || visited.contains(&file) || visited.len() == CONF_FILES_MAX {
83 continue;
84 }
85 visited.push(file.clone());
86
87 for directive in read_conf(root, &file).into_iter().rev() {
88 if pending.len() >= CONF_PENDING_MAX {
89 break;
90 }
91 pending.push((directive, depth + 1));
92 }
93 }
94
95 paths
96}
97
98fn read_conf(root: &SourceRoot, logical: &Path) -> Vec<Directive> {
102 assert!(logical.is_absolute());
103
104 let Ok(Some(bytes)) = root.read_bounded(logical, CONF_BYTES_MAX) else {
105 return Vec::new();
106 };
107 let Ok(text) = String::from_utf8(bytes) else {
108 return Vec::new();
109 };
110
111 let mut directives = Vec::new();
112 for line in text.lines() {
113 let line = line.split('#').next().unwrap_or("").trim();
114 if line.is_empty() {
115 continue;
116 }
117 if let Some(rest) = line
118 .strip_prefix("include ")
119 .or_else(|| line.strip_prefix("include\t"))
120 {
121 for pattern in rest.split_whitespace() {
122 let included = expand_include(root, logical, pattern);
123 directives.extend(included.into_iter().map(Directive::Include));
124 }
125 continue;
126 }
127 if line.starts_with("hwcap ") {
128 continue;
130 }
131 directives.push(Directive::Directory(normalize_absolute(Path::new(line))));
132 }
133 directives
134}
135
136fn expand_include(root: &SourceRoot, current: &Path, pattern: &str) -> Vec<PathBuf> {
138 assert!(current.is_absolute());
139
140 let pattern_path = if Path::new(pattern).is_absolute() {
141 PathBuf::from(pattern)
142 } else {
143 logical_parent(current).join(pattern)
144 };
145 let pattern_path = normalize_absolute(&pattern_path);
146
147 let Some(name) = pattern_path.file_name().and_then(|n| n.to_str()) else {
148 return Vec::new();
149 };
150 if !name.contains('*') {
151 return vec![pattern_path];
152 }
153
154 let dir = logical_parent(&pattern_path);
155 let Ok(entries) = root.read_dir(&dir) else {
156 return Vec::new();
157 };
158 let (prefix, suffix) = name.split_once('*').unwrap_or((name, ""));
159 entries
160 .into_iter()
161 .take(CONF_PENDING_MAX)
162 .filter_map(|entry| {
163 let entry = entry.to_str()?.to_string();
164 let fits = entry.len() >= prefix.len() + suffix.len();
167 let matches = entry.starts_with(prefix) && entry.ends_with(suffix);
168 (fits && matches).then(|| dir.join(entry))
169 })
170 .collect()
171}