Skip to content

Commit b4c54b4

Browse files
committed
Auto merge of rust-lang#1143 - christianpoveda:symlink-shim, r=RalfJung
Add shim for symbolic link creation r? @RalfJung
2 parents a91f379 + 5e71f2d commit b4c54b4

File tree

3 files changed

+84
-5
lines changed

3 files changed

+84
-5
lines changed

src/shims/foreign_items.rs

+10
Original file line numberDiff line numberDiff line change
@@ -494,11 +494,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
494494
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
495495
}
496496

497+
"symlink" => {
498+
let result = this.symlink(args[0], args[1])?;
499+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
500+
}
501+
497502
"stat$INODE64" => {
498503
let result = this.stat(args[0], args[1])?;
499504
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
500505
}
501506

507+
"lstat$INODE64" => {
508+
let result = this.lstat(args[0], args[1])?;
509+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
510+
}
511+
502512
"clock_gettime" => {
503513
let result = this.clock_gettime(args[0], args[1])?;
504514
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;

src/shims/fs.rs

+55-4
Original file line numberDiff line numberDiff line change
@@ -276,24 +276,76 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
276276
this.try_unwrap_io_result(result)
277277
}
278278

279+
fn symlink(
280+
&mut self,
281+
target_op: OpTy<'tcx, Tag>,
282+
linkpath_op: OpTy<'tcx, Tag>
283+
) -> InterpResult<'tcx, i32> {
284+
#[cfg(target_family = "unix")]
285+
fn create_link(src: PathBuf, dst: PathBuf) -> std::io::Result<()> {
286+
std::os::unix::fs::symlink(src, dst)
287+
}
288+
289+
#[cfg(target_family = "windows")]
290+
fn create_link(src: PathBuf, dst: PathBuf) -> std::io::Result<()> {
291+
use std::os::windows::fs;
292+
if src.is_dir() {
293+
fs::symlink_dir(src, dst)
294+
} else {
295+
fs::symlink_file(src, dst)
296+
}
297+
}
298+
299+
let this = self.eval_context_mut();
300+
301+
this.check_no_isolation("symlink")?;
302+
303+
let target = this.read_os_str_from_c_str(this.read_scalar(target_op)?.not_undef()?)?.into();
304+
let linkpath = this.read_os_str_from_c_str(this.read_scalar(linkpath_op)?.not_undef()?)?.into();
305+
306+
this.try_unwrap_io_result(create_link(target, linkpath).map(|_| 0))
307+
}
308+
279309
fn stat(
280310
&mut self,
281311
path_op: OpTy<'tcx, Tag>,
282312
buf_op: OpTy<'tcx, Tag>,
283313
) -> InterpResult<'tcx, i32> {
284314
let this = self.eval_context_mut();
315+
this.check_no_isolation("stat")?;
316+
// `stat` always follows symlinks.
317+
this.stat_or_lstat(true, path_op, buf_op)
318+
}
319+
320+
// `lstat` is used to get symlink metadata.
321+
fn lstat(
322+
&mut self,
323+
path_op: OpTy<'tcx, Tag>,
324+
buf_op: OpTy<'tcx, Tag>,
325+
) -> InterpResult<'tcx, i32> {
326+
let this = self.eval_context_mut();
327+
this.check_no_isolation("lstat")?;
328+
this.stat_or_lstat(false, path_op, buf_op)
329+
}
330+
331+
fn stat_or_lstat(
332+
&mut self,
333+
follow_symlink: bool,
334+
path_op: OpTy<'tcx, Tag>,
335+
buf_op: OpTy<'tcx, Tag>,
336+
) -> InterpResult<'tcx, i32> {
337+
let this = self.eval_context_mut();
285338

286339
if this.tcx.sess.target.target.target_os.to_lowercase() != "macos" {
287-
throw_unsup_format!("The `stat` shim is only available for `macos` targets.")
340+
throw_unsup_format!("The `stat` and `lstat` shims are only available for `macos` targets.")
288341
}
289342

290343
let path_scalar = this.read_scalar(path_op)?.not_undef()?;
291344
let path: PathBuf = this.read_os_str_from_c_str(path_scalar)?.into();
292345

293346
let buf = this.deref_operand(buf_op)?;
294347

295-
// `stat` always follows symlinks. `lstat` is used to get symlink metadata.
296-
let metadata = match FileMetadata::new(this, path, true)? {
348+
let metadata = match FileMetadata::new(this, path, follow_symlink)? {
297349
Some(metadata) => metadata,
298350
None => return Ok(-1),
299351
};
@@ -545,7 +597,6 @@ impl FileMetadata {
545597
let metadata = if follow_symlink {
546598
std::fs::metadata(path)
547599
} else {
548-
// FIXME: metadata for symlinks need testing.
549600
std::fs::symlink_metadata(path)
550601
};
551602

tests/run-pass/fs.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ fn main() {
1919
let tmp = std::env::temp_dir();
2020
let filename = PathBuf::from("miri_test_fs.txt");
2121
let path = tmp.join(&filename);
22+
let symlink_path = tmp.join("miri_test_fs_symlink.txt");
2223
let bytes = b"Hello, World!\n";
24+
// Clean the paths for robustness.
25+
remove_file(&path).ok();
26+
remove_file(&symlink_path).ok();
2327

2428
// Test creating, writing and closing a file (closing is tested when `file` is dropped).
2529
let mut file = File::create(&path).unwrap();
@@ -39,9 +43,23 @@ fn main() {
3943
// Test that metadata of an absolute path is correct.
4044
test_metadata(bytes, &path).unwrap();
4145
// Test that metadata of a relative path is correct.
42-
std::env::set_current_dir(tmp).unwrap();
46+
std::env::set_current_dir(&tmp).unwrap();
4347
test_metadata(bytes, &filename).unwrap();
4448

49+
// Creating a symbolic link should succeed.
50+
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
51+
// Test that the symbolic link has the same contents as the file.
52+
let mut symlink_file = File::open(&symlink_path).unwrap();
53+
let mut contents = Vec::new();
54+
symlink_file.read_to_end(&mut contents).unwrap();
55+
assert_eq!(bytes, contents.as_slice());
56+
// Test that metadata of a symbolic link is correct.
57+
test_metadata(bytes, &symlink_path).unwrap();
58+
// Test that the metadata of a symbolic link is correct when not following it.
59+
assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
60+
// Removing symbolic link should succeed.
61+
remove_file(&symlink_path).unwrap();
62+
4563
// Removing file should succeed.
4664
remove_file(&path).unwrap();
4765

0 commit comments

Comments
 (0)