1use std::borrow::Cow;
2use std::ffi::{OsStr, OsString};
3#[cfg(unix)]
4use std::os::unix::ffi::{OsStrExt, OsStringExt};
5#[cfg(windows)]
6use std::os::windows::ffi::{OsStrExt, OsStringExt};
7use std::path::{Path, PathBuf};
8
9use rustc_middle::ty::Ty;
10
11use crate::*;
12
13pub enum PathConversion {
15 HostToTarget,
16 TargetToHost,
17}
18
19#[cfg(unix)]
20pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> {
21 interp_ok(OsStr::from_bytes(bytes))
22}
23#[cfg(not(unix))]
24pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> {
25 let s = std::str::from_utf8(bytes)
27 .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?;
28 interp_ok(OsStr::new(s))
29}
30
31impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
32pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
33 fn read_os_str_from_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a OsStr>
36 where
37 'tcx: 'a,
38 {
39 let this = self.eval_context_ref();
40 let bytes = this.read_c_str(ptr)?;
41 bytes_to_os_str(bytes)
42 }
43
44 fn read_os_str_from_wide_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, OsString>
47 where
48 'tcx: 'a,
49 {
50 #[cfg(windows)]
51 pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
52 interp_ok(OsString::from_wide(&u16_vec[..]))
53 }
54 #[cfg(not(windows))]
55 pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
56 let s = String::from_utf16(&u16_vec[..])
57 .map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?;
58 interp_ok(s.into())
59 }
60
61 let u16_vec = self.eval_context_ref().read_wide_str(ptr)?;
62 u16vec_to_osstring(u16_vec)
63 }
64
65 fn write_os_str_to_c_str(
69 &mut self,
70 os_str: &OsStr,
71 ptr: Pointer,
72 size: u64,
73 ) -> InterpResult<'tcx, (bool, u64)> {
74 let bytes = os_str.as_encoded_bytes();
75 self.eval_context_mut().write_c_str(bytes, ptr, size)
76 }
77
78 fn write_os_str_to_wide_str_helper(
81 &mut self,
82 os_str: &OsStr,
83 ptr: Pointer,
84 size: u64,
85 truncate: bool,
86 ) -> InterpResult<'tcx, (bool, u64)> {
87 #[cfg(windows)]
88 fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
89 interp_ok(os_str.encode_wide().collect())
90 }
91 #[cfg(not(windows))]
92 fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
93 os_str
97 .to_str()
98 .map(|s| s.encode_utf16().collect())
99 .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str))
100 .into()
101 }
102
103 let u16_vec = os_str_to_u16vec(os_str)?;
104 let (written, size_needed) = self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)?;
105 if truncate && !written && size > 0 {
106 let truncated_data = &u16_vec[..size.saturating_sub(1).try_into().unwrap()];
108 let (written, written_len) =
109 self.eval_context_mut().write_wide_str(truncated_data, ptr, size)?;
110 assert!(written && written_len == size);
111 }
112 interp_ok((written, size_needed))
113 }
114
115 fn write_os_str_to_wide_str(
119 &mut self,
120 os_str: &OsStr,
121 ptr: Pointer,
122 size: u64,
123 ) -> InterpResult<'tcx, (bool, u64)> {
124 self.write_os_str_to_wide_str_helper(os_str, ptr, size, false)
125 }
126
127 fn write_os_str_to_wide_str_truncated(
130 &mut self,
131 os_str: &OsStr,
132 ptr: Pointer,
133 size: u64,
134 ) -> InterpResult<'tcx, (bool, u64)> {
135 self.write_os_str_to_wide_str_helper(os_str, ptr, size, true)
136 }
137
138 fn alloc_os_str_as_c_str(
140 &mut self,
141 os_str: &OsStr,
142 memkind: MemoryKind,
143 ) -> InterpResult<'tcx, Pointer> {
144 let size = u64::try_from(os_str.len()).unwrap().strict_add(1); let this = self.eval_context_mut();
146
147 let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u8, size);
148 let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
149 let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr(), size).unwrap();
150 assert!(written);
151 interp_ok(arg_place.ptr())
152 }
153
154 fn alloc_os_str_as_wide_str(
156 &mut self,
157 os_str: &OsStr,
158 memkind: MemoryKind,
159 ) -> InterpResult<'tcx, Pointer> {
160 let size = u64::try_from(os_str.len()).unwrap().strict_add(1); let this = self.eval_context_mut();
162
163 let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size);
164 let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
165 let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size).unwrap();
166 assert!(written);
167 interp_ok(arg_place.ptr())
168 }
169
170 fn read_path_from_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, Cow<'a, Path>>
172 where
173 'tcx: 'a,
174 {
175 let this = self.eval_context_ref();
176 let os_str = this.read_os_str_from_c_str(ptr)?;
177
178 interp_ok(match this.convert_path(Cow::Borrowed(os_str), PathConversion::TargetToHost) {
179 Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)),
180 Cow::Owned(y) => Cow::Owned(PathBuf::from(y)),
181 })
182 }
183
184 fn read_path_from_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, PathBuf> {
186 let this = self.eval_context_ref();
187 let os_str = this.read_os_str_from_wide_str(ptr)?;
188
189 interp_ok(
190 this.convert_path(Cow::Owned(os_str), PathConversion::TargetToHost).into_owned().into(),
191 )
192 }
193
194 fn write_path_to_c_str(
197 &mut self,
198 path: &Path,
199 ptr: Pointer,
200 size: u64,
201 ) -> InterpResult<'tcx, (bool, u64)> {
202 let this = self.eval_context_mut();
203 let os_str =
204 this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
205 this.write_os_str_to_c_str(&os_str, ptr, size)
206 }
207
208 fn write_path_to_wide_str(
211 &mut self,
212 path: &Path,
213 ptr: Pointer,
214 size: u64,
215 ) -> InterpResult<'tcx, (bool, u64)> {
216 let this = self.eval_context_mut();
217 let os_str =
218 this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
219 this.write_os_str_to_wide_str(&os_str, ptr, size)
220 }
221
222 fn write_path_to_wide_str_truncated(
225 &mut self,
226 path: &Path,
227 ptr: Pointer,
228 size: u64,
229 ) -> InterpResult<'tcx, (bool, u64)> {
230 let this = self.eval_context_mut();
231 let os_str =
232 this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
233 this.write_os_str_to_wide_str_truncated(&os_str, ptr, size)
234 }
235
236 fn alloc_path_as_c_str(
239 &mut self,
240 path: &Path,
241 memkind: MemoryKind,
242 ) -> InterpResult<'tcx, Pointer> {
243 let this = self.eval_context_mut();
244 let os_str =
245 this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
246 this.alloc_os_str_as_c_str(&os_str, memkind)
247 }
248
249 fn alloc_path_as_wide_str(
252 &mut self,
253 path: &Path,
254 memkind: MemoryKind,
255 ) -> InterpResult<'tcx, Pointer> {
256 let this = self.eval_context_mut();
257 let os_str =
258 this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
259 this.alloc_os_str_as_wide_str(&os_str, memkind)
260 }
261
262 fn convert_path<'a>(
263 &self,
264 os_str: Cow<'a, OsStr>,
265 direction: PathConversion,
266 ) -> Cow<'a, OsStr> {
267 let this = self.eval_context_ref();
268 let target_os = &this.tcx.sess.target.os;
269
270 fn windows_to_unix<T>(path: &mut Vec<T>)
274 where
275 T: From<u8> + Copy + Eq,
276 {
277 let sep = T::from(b'/');
278 for c in path.iter_mut() {
280 if *c == b'\\'.into() {
281 *c = sep;
282 }
283 }
284 if path.get(0..4) == Some(&[sep, sep, b'?'.into(), sep]) {
287 path.splice(0..3, std::iter::empty());
289 }
290 else if path.get(1..3) == Some(&[b':'.into(), sep]) {
292 path.insert(0, sep);
295 }
296 }
297
298 fn unix_to_windows<T>(path: &mut Vec<T>)
302 where
303 T: From<u8> + Copy + Eq,
304 {
305 let sep = T::from(b'\\');
306 for c in path.iter_mut() {
308 if *c == b'/'.into() {
309 *c = sep;
310 }
311 }
312 if path.get(2..4) == Some(&[b':'.into(), sep]) && path[0] == sep {
315 path.remove(0);
317 }
318 else if path.first() == Some(&sep) && path.get(1) != Some(&sep) {
322 path.splice(0..0, [sep, sep, b'?'.into()]);
326 }
327 }
328
329 #[cfg(windows)]
332 return if target_os == "windows" {
333 os_str
335 } else {
336 let mut path: Vec<u16> = os_str.encode_wide().collect();
338 match direction {
339 PathConversion::HostToTarget => {
340 windows_to_unix(&mut path);
341 }
342 PathConversion::TargetToHost => {
343 unix_to_windows(&mut path);
344 }
345 }
346 Cow::Owned(OsString::from_wide(&path))
347 };
348 #[cfg(unix)]
349 return if target_os == "windows" {
350 let mut path: Vec<u8> = os_str.into_owned().into_encoded_bytes();
352 match direction {
353 PathConversion::HostToTarget => {
354 unix_to_windows(&mut path);
355 }
356 PathConversion::TargetToHost => {
357 windows_to_unix(&mut path);
358 }
359 }
360 Cow::Owned(OsString::from_vec(path))
361 } else {
362 os_str
364 };
365 }
366}