Skip to content

Commit 304ccc5

Browse files
committed
Auto merge of #3591 - RalfJung:win-symlink-trouble, r=RalfJung
do not run symlink tests on Windows hosts Fixes #3587
2 parents e1473e6 + b7f37ba commit 304ccc5

File tree

6 files changed

+144
-89
lines changed

6 files changed

+144
-89
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Symlink tests are separate since they don't in general work on a Windows host.
2+
//@ignore-host-windows: creating symlinks requires admin permissions on Windows
3+
//@ignore-target-windows: File handling is not implemented yet
4+
//@compile-flags: -Zmiri-disable-isolation
5+
6+
use std::ffi::CString;
7+
use std::fs;
8+
use std::io::{Error, ErrorKind};
9+
use std::os::unix::ffi::OsStrExt;
10+
use std::path::PathBuf;
11+
12+
#[path = "../../utils/mod.rs"]
13+
mod utils;
14+
15+
/// Prepare: compute filename and make sure the file does not exist.
16+
fn prepare(filename: &str) -> PathBuf {
17+
let path = utils::tmp().join(filename);
18+
// Clean the paths for robustness.
19+
fs::remove_file(&path).ok();
20+
path
21+
}
22+
23+
/// Prepare like above, and also write some initial content to the file.
24+
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
25+
let path = prepare(filename);
26+
fs::write(&path, content).unwrap();
27+
path
28+
}
29+
30+
fn main() {
31+
let bytes = b"Hello, World!\n";
32+
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
33+
let expected_path = path.as_os_str().as_bytes();
34+
35+
let symlink_path = prepare("miri_test_fs_symlink.txt");
36+
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
37+
38+
// Test that the expected string gets written to a buffer of proper
39+
// length, and that a trailing null byte is not written.
40+
let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
41+
let symlink_c_ptr = symlink_c_str.as_ptr();
42+
43+
// Make the buf one byte larger than it needs to be,
44+
// and check that the last byte is not overwritten.
45+
let mut large_buf = vec![0xFF; expected_path.len() + 1];
46+
let res =
47+
unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
48+
// Check that the resolved path was properly written into the buf.
49+
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
50+
assert_eq!(large_buf.last(), Some(&0xFF));
51+
assert_eq!(res, large_buf.len() as isize - 1);
52+
53+
// Test that the resolved path is truncated if the provided buffer
54+
// is too small.
55+
let mut small_buf = [0u8; 2];
56+
let res =
57+
unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
58+
assert_eq!(small_buf, &expected_path[..small_buf.len()]);
59+
assert_eq!(res, small_buf.len() as isize);
60+
61+
// Test that we report a proper error for a missing path.
62+
let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
63+
let res = unsafe {
64+
libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
65+
};
66+
assert_eq!(res, -1);
67+
assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
68+
}

tests/pass-dep/libc/libc-fs-with-isolation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ignore-target-windows: no libc on Windows
1+
//@ignore-target-windows: File handling is not implemented yet
22
//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
33
//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"
44

tests/pass-dep/libc/libc-fs.rs

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
//@ignore-target-windows: no libc on Windows
1+
//@ignore-target-windows: File handling is not implemented yet
22
//@compile-flags: -Zmiri-disable-isolation
33

44
#![feature(io_error_more)]
55
#![feature(io_error_uncategorized)]
66

77
use std::ffi::{CStr, CString, OsString};
8-
use std::fs::{canonicalize, remove_dir_all, remove_file, File};
8+
use std::fs::{self, canonicalize, remove_file, File};
99
use std::io::{Error, ErrorKind, Write};
1010
use std::os::unix::ffi::OsStrExt;
1111
use std::os::unix::io::AsRawFd;
@@ -21,7 +21,6 @@ fn main() {
2121
test_ftruncate::<libc::off_t>(libc::ftruncate);
2222
#[cfg(target_os = "linux")]
2323
test_ftruncate::<libc::off64_t>(libc::ftruncate64);
24-
test_readlink();
2524
test_file_open_unix_allow_two_args();
2625
test_file_open_unix_needs_three_args();
2726
test_file_open_unix_extra_third_arg();
@@ -42,24 +41,22 @@ fn main() {
4241
fn prepare(filename: &str) -> PathBuf {
4342
let path = utils::tmp().join(filename);
4443
// Clean the paths for robustness.
45-
remove_file(&path).ok();
44+
fs::remove_file(&path).ok();
4645
path
4746
}
4847

4948
/// Prepare directory: compute directory name and make sure it does not exist.
50-
#[allow(unused)]
5149
fn prepare_dir(dirname: &str) -> PathBuf {
5250
let path = utils::tmp().join(&dirname);
5351
// Clean the directory for robustness.
54-
remove_dir_all(&path).ok();
52+
fs::remove_dir_all(&path).ok();
5553
path
5654
}
5755

5856
/// Prepare like above, and also write some initial content to the file.
5957
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
6058
let path = prepare(filename);
61-
let mut file = File::create(&path).unwrap();
62-
file.write(content).unwrap();
59+
fs::write(&path, content).unwrap();
6360
path
6461
}
6562

@@ -106,46 +103,6 @@ fn test_canonicalize_too_long() {
106103
assert!(canonicalize(too_long).is_err());
107104
}
108105

109-
fn test_readlink() {
110-
let bytes = b"Hello, World!\n";
111-
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
112-
let expected_path = path.as_os_str().as_bytes();
113-
114-
let symlink_path = prepare("miri_test_fs_symlink.txt");
115-
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
116-
117-
// Test that the expected string gets written to a buffer of proper
118-
// length, and that a trailing null byte is not written.
119-
let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
120-
let symlink_c_ptr = symlink_c_str.as_ptr();
121-
122-
// Make the buf one byte larger than it needs to be,
123-
// and check that the last byte is not overwritten.
124-
let mut large_buf = vec![0xFF; expected_path.len() + 1];
125-
let res =
126-
unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
127-
// Check that the resolved path was properly written into the buf.
128-
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
129-
assert_eq!(large_buf.last(), Some(&0xFF));
130-
assert_eq!(res, large_buf.len() as isize - 1);
131-
132-
// Test that the resolved path is truncated if the provided buffer
133-
// is too small.
134-
let mut small_buf = [0u8; 2];
135-
let res =
136-
unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
137-
assert_eq!(small_buf, &expected_path[..small_buf.len()]);
138-
assert_eq!(res, small_buf.len() as isize);
139-
140-
// Test that we report a proper error for a missing path.
141-
let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
142-
let res = unsafe {
143-
libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
144-
};
145-
assert_eq!(res, -1);
146-
assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
147-
}
148-
149106
fn test_rename() {
150107
let path1 = prepare("miri_test_libc_fs_source.txt");
151108
let path2 = prepare("miri_test_libc_fs_rename_destination.txt");

tests/pass-dep/libc/libc-time.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ignore-target-windows: no libc on Windows
1+
//@ignore-target-windows: no libc time APIs on Windows
22
//@compile-flags: -Zmiri-disable-isolation
33
use std::ffi::CStr;
44
use std::{env, mem, ptr};

tests/pass/shims/fs-symlink.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Symlink tests are separate since they don't in general work on a Windows host.
2+
//@ignore-host-windows: creating symlinks requires admin permissions on Windows
3+
//@ignore-target-windows: File handling is not implemented yet
4+
//@compile-flags: -Zmiri-disable-isolation
5+
6+
use std::fs::{self, read_link, remove_file, File};
7+
use std::io::{Read, Result};
8+
use std::path::{Path, PathBuf};
9+
10+
#[path = "../../utils/mod.rs"]
11+
mod utils;
12+
13+
/// Prepare: compute filename and make sure the file does not exist.
14+
fn prepare(filename: &str) -> PathBuf {
15+
let path = utils::tmp().join(filename);
16+
// Clean the paths for robustness.
17+
fs::remove_file(&path).ok();
18+
path
19+
}
20+
21+
/// Prepare like above, and also write some initial content to the file.
22+
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
23+
let path = prepare(filename);
24+
fs::write(&path, content).unwrap();
25+
path
26+
}
27+
28+
fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
29+
// Test that the file metadata is correct.
30+
let metadata = path.metadata()?;
31+
// `path` should point to a file.
32+
assert!(metadata.is_file());
33+
// The size of the file must be equal to the number of written bytes.
34+
assert_eq!(bytes.len() as u64, metadata.len());
35+
Ok(())
36+
}
37+
38+
fn main() {
39+
let bytes = b"Hello, World!\n";
40+
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
41+
let symlink_path = prepare("miri_test_fs_symlink.txt");
42+
43+
// Creating a symbolic link should succeed.
44+
#[cfg(unix)]
45+
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
46+
#[cfg(windows)]
47+
std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap();
48+
// Test that the symbolic link has the same contents as the file.
49+
let mut symlink_file = File::open(&symlink_path).unwrap();
50+
let mut contents = Vec::new();
51+
symlink_file.read_to_end(&mut contents).unwrap();
52+
assert_eq!(bytes, contents.as_slice());
53+
54+
// Test that metadata of a symbolic link (i.e., the file it points to) is correct.
55+
check_metadata(bytes, &symlink_path).unwrap();
56+
// Test that the metadata of a symbolic link is correct when not following it.
57+
assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
58+
// Check that we can follow the link.
59+
assert_eq!(read_link(&symlink_path).unwrap(), path);
60+
// Removing symbolic link should succeed.
61+
remove_file(&symlink_path).unwrap();
62+
63+
// Removing file should succeed.
64+
remove_file(&path).unwrap();
65+
}

tests/pass/shims/fs.rs

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
//@ignore-target-windows: File handling is not implemented yet
22
//@compile-flags: -Zmiri-disable-isolation
33

4-
// If this test is failing for you locally, you can try
5-
// 1. Deleting the files `/tmp/miri_*`
6-
// 2. Setting `MIRI_TEMP` or `TMPDIR` to a different directory, without the `miri_*` files
7-
84
#![feature(io_error_more)]
95
#![feature(io_error_uncategorized)]
106

117
use std::collections::HashMap;
128
use std::ffi::OsString;
139
use std::fs::{
14-
canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename,
10+
self, canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename,
1511
File, OpenOptions,
1612
};
1713
use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write};
@@ -29,7 +25,6 @@ fn main() {
2925
test_metadata();
3026
test_file_set_len();
3127
test_file_sync();
32-
test_symlink();
3328
test_errors();
3429
test_rename();
3530
test_directory();
@@ -41,23 +36,22 @@ fn main() {
4136
fn prepare(filename: &str) -> PathBuf {
4237
let path = utils::tmp().join(filename);
4338
// Clean the paths for robustness.
44-
remove_file(&path).ok();
39+
fs::remove_file(&path).ok();
4540
path
4641
}
4742

4843
/// Prepare directory: compute directory name and make sure it does not exist.
4944
fn prepare_dir(dirname: &str) -> PathBuf {
5045
let path = utils::tmp().join(&dirname);
5146
// Clean the directory for robustness.
52-
remove_dir_all(&path).ok();
47+
fs::remove_dir_all(&path).ok();
5348
path
5449
}
5550

5651
/// Prepare like above, and also write some initial content to the file.
5752
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
5853
let path = prepare(filename);
59-
let mut file = File::create(&path).unwrap();
60-
file.write(content).unwrap();
54+
fs::write(&path, content).unwrap();
6155
path
6256
}
6357

@@ -223,35 +217,6 @@ fn test_file_sync() {
223217
remove_file(&path).unwrap();
224218
}
225219

226-
fn test_symlink() {
227-
let bytes = b"Hello, World!\n";
228-
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
229-
let symlink_path = prepare("miri_test_fs_symlink.txt");
230-
231-
// Creating a symbolic link should succeed.
232-
#[cfg(unix)]
233-
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
234-
#[cfg(windows)]
235-
std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap();
236-
// Test that the symbolic link has the same contents as the file.
237-
let mut symlink_file = File::open(&symlink_path).unwrap();
238-
let mut contents = Vec::new();
239-
symlink_file.read_to_end(&mut contents).unwrap();
240-
assert_eq!(bytes, contents.as_slice());
241-
242-
// Test that metadata of a symbolic link (i.e., the file it points to) is correct.
243-
check_metadata(bytes, &symlink_path).unwrap();
244-
// Test that the metadata of a symbolic link is correct when not following it.
245-
assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
246-
// Check that we can follow the link.
247-
assert_eq!(read_link(&symlink_path).unwrap(), path);
248-
// Removing symbolic link should succeed.
249-
remove_file(&symlink_path).unwrap();
250-
251-
// Removing file should succeed.
252-
remove_file(&path).unwrap();
253-
}
254-
255220
fn test_errors() {
256221
let bytes = b"Hello, World!\n";
257222
let path = prepare("miri_test_fs_errors.txt");

0 commit comments

Comments
 (0)