Skip to content

Commit 1141b21

Browse files
committed
Windows shims for GetCurrentDirectoryW/SetCurrentDirectoryW
1 parent a481b8f commit 1141b21

File tree

4 files changed

+127
-61
lines changed

4 files changed

+127
-61
lines changed

src/shims/env.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
138138
}
139139
}
140140
None => {
141-
let envvar_not_found = this.eval_path_scalar(&["std", "sys", "windows", "c", "ERROR_ENVVAR_NOT_FOUND"])?;
142-
this.set_last_error(envvar_not_found.not_undef()?)?;
141+
let envvar_not_found = this.eval_windows("ERROR_ENVVAR_NOT_FOUND")?;
142+
this.set_last_error(envvar_not_found)?;
143143
0 // return zero upon failure
144144
}
145145
})
@@ -289,6 +289,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
289289
size_op: OpTy<'tcx, Tag>,
290290
) -> InterpResult<'tcx, Scalar<Tag>> {
291291
let this = self.eval_context_mut();
292+
let target_os = &this.tcx.sess.target.target.target_os;
293+
assert!(target_os == "linux" || target_os == "macos", "`getcwd` is only available for the UNIX target family");
292294

293295
this.check_no_isolation("getcwd")?;
294296

@@ -308,8 +310,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
308310
Ok(Scalar::null_ptr(&*this.tcx))
309311
}
310312

313+
#[allow(non_snake_case)]
314+
fn GetCurrentDirectoryW(
315+
&mut self,
316+
size_op: OpTy<'tcx, Tag>, // DWORD
317+
buf_op: OpTy<'tcx, Tag>, // LPTSTR
318+
) -> InterpResult<'tcx, u32> {
319+
let this = self.eval_context_mut();
320+
this.assert_target_os("windows", "GetCurrentDirectoryW");
321+
322+
this.check_no_isolation("GetCurrentDirectoryW")?;
323+
324+
let size = u64::from(this.read_scalar(size_op)?.to_u32()?);
325+
let buf = this.read_scalar(buf_op)?.not_undef()?;
326+
327+
// If we cannot get the current directory, we return 0
328+
match env::current_dir() {
329+
Ok(cwd) => {
330+
let len = this.write_path_to_wide_str(&cwd, buf, size)?.1;
331+
return Ok(u32::try_from(len).unwrap())
332+
}
333+
Err(e) => this.set_last_error_from_io_error(e)?,
334+
}
335+
Ok(0)
336+
}
337+
311338
fn chdir(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
312339
let this = self.eval_context_mut();
340+
let target_os = &this.tcx.sess.target.target.target_os;
341+
assert!(target_os == "linux" || target_os == "macos", "`getcwd` is only available for the UNIX target family");
313342

314343
this.check_no_isolation("chdir")?;
315344

@@ -324,6 +353,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
324353
}
325354
}
326355

356+
#[allow(non_snake_case)]
357+
fn SetCurrentDirectoryW (
358+
&mut self,
359+
path_op: OpTy<'tcx, Tag> // LPCTSTR
360+
) -> InterpResult<'tcx, i32> { // Returns BOOL(i32 in Windows)
361+
let this = self.eval_context_mut();
362+
this.assert_target_os("windows", "SetCurrentDirectoryW");
363+
364+
this.check_no_isolation("SetCurrentDirectoryW")?;
365+
366+
let path = this.read_path_from_wide_str(this.read_scalar(path_op)?.not_undef()?)?;
367+
368+
match env::set_current_dir(path) {
369+
Ok(()) => Ok(1),
370+
Err(e) => {
371+
this.set_last_error_from_io_error(e)?;
372+
Ok(0)
373+
}
374+
}
375+
}
376+
327377
/// Updates the `environ` static.
328378
/// The first time it gets called, also initializes `extra.environ`.
329379
fn update_environ(&mut self) -> InterpResult<'tcx> {

src/shims/foreign_items/windows.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4040
this.write_scalar(Scalar::from_i32(result), dest)?;
4141
}
4242

43+
"GetCurrentDirectoryW" => {
44+
let result = this.GetCurrentDirectoryW(args[0], args[1])?;
45+
this.write_scalar(Scalar::from_u32(result), dest)?;
46+
}
47+
48+
"SetCurrentDirectoryW" => {
49+
let result = this.SetCurrentDirectoryW(args[0])?;
50+
this.write_scalar(Scalar::from_i32(result), dest)?;
51+
}
52+
4353
// File related shims
4454
"GetStdHandle" => {
4555
let which = this.read_scalar(args[0])?.to_i32()?;

src/shims/os_str.rs

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -177,72 +177,77 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
177177
'mir: 'a,
178178
{
179179
let this = self.eval_context_ref();
180-
let os_str = this.read_os_str_from_c_str(scalar)?;
180+
let os_str: &'a OsStr = this.read_os_str_from_c_str(scalar)?;
181181

182-
#[cfg(windows)]
183-
return Ok(if this.tcx.sess.target.target.target_os == "windows" {
184-
// Windows-on-Windows, all fine.
185-
Cow::Borrowed(Path::new(os_str))
186-
} else {
187-
// Unix target, Windows host. Need to convert target '/' to host '\'.
188-
let converted = os_str
189-
.encode_wide()
190-
.map(|wchar| if wchar == '/' as u16 { '\\' as u16 } else { wchar })
191-
.collect::<Vec<_>>();
192-
Cow::Owned(PathBuf::from(OsString::from_wide(&converted)))
193-
});
194-
#[cfg(unix)]
195-
return Ok(if this.tcx.sess.target.target.target_os == "windows" {
196-
// Windows target, Unix host. Need to convert target '\' to host '/'.
197-
let converted = os_str
198-
.as_bytes()
199-
.iter()
200-
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
201-
.collect::<Vec<_>>();
202-
Cow::Owned(PathBuf::from(OsString::from_vec(converted)))
203-
} else {
204-
// Unix-on-Unix, all is fine.
205-
Cow::Borrowed(Path::new(os_str))
206-
});
182+
Ok(match convert_path_separator(os_str, &this.tcx.sess.target.target.target_os) {
183+
Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)),
184+
Cow::Owned(y) => Cow::Owned(PathBuf::from(y)),
185+
})
186+
}
187+
188+
/// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed.
189+
fn read_path_from_wide_str(&self, scalar: Scalar<Tag>) -> InterpResult<'tcx, PathBuf> {
190+
let this = self.eval_context_ref();
191+
let os_str: OsString = this.read_os_str_from_wide_str(scalar)?;
192+
193+
Ok(PathBuf::from(&convert_path_separator(&os_str, &this.tcx.sess.target.target.target_os)))
207194
}
208195

209-
/// Write a Path to the machine memory, adjusting path separators if needed.
196+
/// Write a Path to the machine memory (as a null-terminated sequence of bytes),
197+
/// adjusting path separators if needed.
210198
fn write_path_to_c_str(
211199
&mut self,
212200
path: &Path,
213201
scalar: Scalar<Tag>,
214202
size: u64,
215203
) -> InterpResult<'tcx, (bool, u64)> {
216204
let this = self.eval_context_mut();
217-
218-
#[cfg(windows)]
219-
let os_str = if this.tcx.sess.target.target.target_os == "windows" {
220-
// Windows-on-Windows, all fine.
221-
Cow::Borrowed(path.as_os_str())
222-
} else {
223-
// Unix target, Windows host. Need to convert host '\\' to target '/'.
224-
let converted = path
225-
.as_os_str()
226-
.encode_wide()
227-
.map(|wchar| if wchar == '\\' as u16 { '/' as u16 } else { wchar })
228-
.collect::<Vec<_>>();
229-
Cow::Owned(OsString::from_wide(&converted))
230-
};
231-
#[cfg(unix)]
232-
let os_str = if this.tcx.sess.target.target.target_os == "windows" {
233-
// Windows target, Unix host. Need to convert host '/' to target '\'.
234-
let converted = path
235-
.as_os_str()
236-
.as_bytes()
237-
.iter()
238-
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
239-
.collect::<Vec<_>>();
240-
Cow::Owned(OsString::from_vec(converted))
241-
} else {
242-
// Unix-on-Unix, all is fine.
243-
Cow::Borrowed(path.as_os_str())
244-
};
245-
205+
let os_str = convert_path_separator(path.as_os_str(), &this.tcx.sess.target.target.target_os);
246206
this.write_os_str_to_c_str(&os_str, scalar, size)
247207
}
208+
209+
/// Write a Path to the machine memory (as a null-terminated sequence of `u16`s),
210+
/// adjusting path separators if needed.
211+
fn write_path_to_wide_str(
212+
&mut self,
213+
path: &Path,
214+
scalar: Scalar<Tag>,
215+
size: u64,
216+
) -> InterpResult<'tcx, (bool, u64)> {
217+
let this = self.eval_context_mut();
218+
let os_str = convert_path_separator(path.as_os_str(), &this.tcx.sess.target.target.target_os);
219+
this.write_os_str_to_wide_str(&os_str, scalar, size)
220+
}
221+
}
222+
223+
/// Perform path separator conversion if needed.
224+
fn convert_path_separator<'a>(
225+
os_str: &'a OsStr,
226+
target_os: &str,
227+
) -> Cow<'a, OsStr> {
228+
#[cfg(windows)]
229+
return if target_os == "windows" {
230+
// Windows-on-Windows, all fine.
231+
Cow::Borrowed(os_str)
232+
} else {
233+
// Unix target, Windows host. Need to convert host '\\' to target '/'.
234+
let converted = os_str
235+
.encode_wide()
236+
.map(|wchar| if wchar == '\\' as u16 { '/' as u16 } else { wchar })
237+
.collect::<Vec<_>>();
238+
Cow::Owned(OsString::from_wide(&converted))
239+
};
240+
#[cfg(unix)]
241+
return if target_os == "windows" {
242+
// Windows target, Unix host. Need to convert host '/' to target '\'.
243+
let converted = os_str
244+
.as_bytes()
245+
.iter()
246+
.map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
247+
.collect::<Vec<_>>();
248+
Cow::Owned(OsString::from_vec(converted))
249+
} else {
250+
// Unix-on-Unix, all is fine.
251+
Cow::Borrowed(os_str)
252+
};
248253
}

tests/run-pass/current_dir.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
// ignore-windows: TODO the windows hook is not done yet
21
// compile-flags: -Zmiri-disable-isolation
32
use std::env;
4-
use std::path::Path;
3+
use std::io::ErrorKind;
54

65
fn main() {
76
// Test that `getcwd` is available
@@ -11,7 +10,9 @@ fn main() {
1110
// keep the current directory equal to `cwd`.
1211
let parent = cwd.parent().unwrap_or(&cwd);
1312
// Test that `chdir` is available
14-
assert!(env::set_current_dir(&Path::new("..")).is_ok());
13+
assert!(env::set_current_dir("..").is_ok());
1514
// Test that `..` goes to the parent directory
1615
assert_eq!(env::current_dir().unwrap(), parent);
16+
// Test that `chdir` to a non-existing directory returns a proper error
17+
assert_eq!(env::set_current_dir("thisdoesnotexist").unwrap_err().kind(), ErrorKind::NotFound);
1718
}

0 commit comments

Comments
 (0)