11use std:: ffi:: OsStr ;
2+ use std:: io;
23use std:: iter;
4+ use std:: path:: { self , Path , PathBuf } ;
35use std:: str;
46
57use rustc_span:: Symbol ;
@@ -21,6 +23,61 @@ fn is_dyn_sym(name: &str) -> bool {
2123 )
2224}
2325
26+ #[ cfg( windows) ]
27+ fn win_absolute < ' tcx > ( path : & Path ) -> InterpResult < ' tcx , io:: Result < PathBuf > > {
28+ // We are on Windows so we can simply lte the host do this.
29+ return Ok ( path:: absolute ( path) ) ;
30+ }
31+
32+ #[ cfg( unix) ]
33+ #[ allow( clippy:: get_first, clippy:: arithmetic_side_effects) ]
34+ fn win_absolute < ' tcx > ( path : & Path ) -> InterpResult < ' tcx , io:: Result < PathBuf > > {
35+ // We are on Unix, so we need to implement parts of the logic ourselves.
36+ let bytes = path. as_os_str ( ) . as_encoded_bytes ( ) ;
37+ // If it starts with `//` (these were backslashes but are already converted)
38+ // then this is a magic special path, we just leave it unchanged.
39+ if bytes. get ( 0 ) . copied ( ) == Some ( b'/' ) && bytes. get ( 1 ) . copied ( ) == Some ( b'/' ) {
40+ return Ok ( Ok ( path. into ( ) ) ) ;
41+ } ;
42+ // Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`.
43+ let magic_filenames = & [
44+ "CON" , "PRN" , "AUX" , "NUL" , "COM1" , "COM2" , "COM3" , "COM4" , "COM5" , "COM6" , "COM7" , "COM8" ,
45+ "COM9" , "LPT1" , "LPT2" , "LPT3" , "LPT4" , "LPT5" , "LPT6" , "LPT7" , "LPT8" , "LPT9" ,
46+ ] ;
47+ if magic_filenames. iter ( ) . any ( |m| m. as_bytes ( ) == bytes) {
48+ let mut result: Vec < u8 > = br"//./" . into ( ) ;
49+ result. extend ( bytes) ;
50+ return Ok ( Ok ( bytes_to_os_str ( & result) ?. into ( ) ) ) ;
51+ }
52+ // Otherwise we try to do something kind of close to what Windows does, but this is probably not
53+ // right in all cases. We iterate over the components between `/`, and remove trailing `.`,
54+ // except that trailing `..` remain unchanged.
55+ let mut result = vec ! [ ] ;
56+ let mut bytes = bytes; // the remaining bytes to process
57+ loop {
58+ let len = bytes. iter ( ) . position ( |& b| b == b'/' ) . unwrap_or ( bytes. len ( ) ) ;
59+ let mut component = & bytes[ ..len] ;
60+ if len >= 2 && component[ len - 1 ] == b'.' && component[ len - 2 ] != b'.' {
61+ // Strip trailing `.`
62+ component = & component[ ..len - 1 ] ;
63+ }
64+ // Add this component to output.
65+ result. extend ( component) ;
66+ // Prepare next iteration.
67+ if len < bytes. len ( ) {
68+ // There's a component after this; add `/` and process remaining bytes.
69+ result. push ( b'/' ) ;
70+ bytes = & bytes[ len + 1 ..] ;
71+ continue ;
72+ } else {
73+ // This was the last component and it did not have a trailing `/`.
74+ break ;
75+ }
76+ }
77+ // Let the host `absolute` function do working-dir handling
78+ Ok ( path:: absolute ( bytes_to_os_str ( & result) ?) )
79+ }
80+
2481impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
2582pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
2683 fn emulate_foreign_item_inner (
@@ -112,7 +169,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
112169
113170 let written = if handle == -11 || handle == -12 {
114171 // stdout/stderr
115- use std :: io:: { self , Write } ;
172+ use io:: Write ;
116173
117174 let buf_cont =
118175 this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( u64:: from ( n) ) ) ?;
@@ -146,6 +203,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
146203 dest,
147204 ) ?;
148205 }
206+ "GetFullPathNameW" => {
207+ let [ filename, size, buffer, filepart] =
208+ this. check_shim ( abi, Abi :: System { unwind : false } , link_name, args) ?;
209+ this. check_no_isolation ( "`GetFullPathNameW`" ) ?;
210+
211+ let filename = this. read_pointer ( filename) ?;
212+ let size = this. read_scalar ( size) ?. to_u32 ( ) ?;
213+ let buffer = this. read_pointer ( buffer) ?;
214+ let filepart = this. read_pointer ( filepart) ?;
215+
216+ if !this. ptr_is_null ( filepart) ? {
217+ throw_unsup_format ! ( "GetFullPathNameW: non-null `lpFilePart` is not supported" ) ;
218+ }
219+
220+ let filename = this. read_path_from_wide_str ( filename) ?;
221+ let result = match win_absolute ( & filename) ? {
222+ Err ( err) => {
223+ this. set_last_error_from_io_error ( err. kind ( ) ) ?;
224+ Scalar :: from_u32 ( 0 ) // return zero upon failure
225+ }
226+ Ok ( abs_filename) => {
227+ this. set_last_error ( Scalar :: from_u32 ( 0 ) ) ?; // make sure this is unambiguously not an error
228+ Scalar :: from_u32 ( helpers:: windows_check_buffer_size (
229+ this. write_path_to_wide_str (
230+ & abs_filename,
231+ buffer,
232+ size. into ( ) ,
233+ /*truncate*/ false ,
234+ ) ?,
235+ ) )
236+ }
237+ } ;
238+ this. write_scalar ( result, dest) ?;
239+ }
149240
150241 // Allocation
151242 "HeapAlloc" => {
0 commit comments