Skip to content

Commit f844eb2

Browse files
committed
Auto merge of #1353 - divergentdave:file-set-len, r=RalfJung
Implement ftruncate64/ftruncate for File::set_len This adds a shim for `ftruncate64` (on Linux) or `ftruncate` (on macOS) to enable support for `File::set_len`, and includes a test.
2 parents 8d9db57 + f49839a commit f844eb2

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

src/shims/foreign_items/posix/linux.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
3434
let result = this.linux_readdir64_r(args[0], args[1], args[2])?;
3535
this.write_scalar(Scalar::from_i32(result), dest)?;
3636
}
37+
"ftruncate64" => {
38+
let result = this.ftruncate64(args[0], args[1])?;
39+
this.write_scalar(Scalar::from_i32(result), dest)?;
40+
}
3741
// Linux-only
3842
"posix_fadvise" => {
3943
let _fd = this.read_scalar(args[0])?.to_i32()?;

src/shims/foreign_items/posix/macos.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4444
let result = this.macos_readdir_r(args[0], args[1], args[2])?;
4545
this.write_scalar(Scalar::from_i32(result), dest)?;
4646
}
47+
"ftruncate" => {
48+
let result = this.ftruncate64(args[0], args[1])?;
49+
this.write_scalar(Scalar::from_i32(result), dest)?;
50+
}
4751

4852
// Environment related shims
4953
"_NSGetEnviron" => {

src/shims/fs.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
10621062
this.handle_not_found()
10631063
}
10641064
}
1065+
1066+
fn ftruncate64(
1067+
&mut self, fd_op: OpTy<'tcx, Tag>,
1068+
length_op: OpTy<'tcx, Tag>,
1069+
) -> InterpResult<'tcx, i32> {
1070+
let this = self.eval_context_mut();
1071+
1072+
this.check_no_isolation("ftruncate64")?;
1073+
1074+
let fd = this.read_scalar(fd_op)?.to_i32()?;
1075+
let length = this.read_scalar(length_op)?.to_i64()?;
1076+
if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.get_mut(&fd) {
1077+
if *writable {
1078+
if let Ok(length) = length.try_into() {
1079+
let result = file.set_len(length);
1080+
this.try_unwrap_io_result(result.map(|_| 0i32))
1081+
} else {
1082+
let einval = this.eval_libc("EINVAL")?;
1083+
this.set_last_error(einval)?;
1084+
Ok(-1)
1085+
}
1086+
} else {
1087+
// The file is not writable
1088+
let einval = this.eval_libc("EINVAL")?;
1089+
this.set_last_error(einval)?;
1090+
Ok(-1)
1091+
}
1092+
} else {
1093+
this.handle_not_found()
1094+
}
1095+
}
10651096
}
10661097

10671098
/// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when

tests/run-pass/fs.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ fn main() {
1313
test_file_create_new();
1414
test_seek();
1515
test_metadata();
16+
test_file_set_len();
1617
test_symlink();
1718
test_errors();
1819
test_rename();
@@ -155,6 +156,32 @@ fn test_metadata() {
155156
remove_file(&path).unwrap();
156157
}
157158

159+
fn test_file_set_len() {
160+
let bytes = b"Hello, World!\n";
161+
let path = prepare_with_content("miri_test_fs_set_len.txt", bytes);
162+
163+
// Test extending the file
164+
let mut file = OpenOptions::new().read(true).write(true).open(&path).unwrap();
165+
let bytes_extended = b"Hello, World!\n\x00\x00\x00\x00\x00\x00";
166+
file.set_len(20).unwrap();
167+
let mut contents = Vec::new();
168+
file.read_to_end(&mut contents).unwrap();
169+
assert_eq!(bytes_extended, contents.as_slice());
170+
171+
// Test truncating the file
172+
file.seek(SeekFrom::Start(0)).unwrap();
173+
file.set_len(10).unwrap();
174+
let mut contents = Vec::new();
175+
file.read_to_end(&mut contents).unwrap();
176+
assert_eq!(&bytes[..10], contents.as_slice());
177+
178+
// Can't use set_len on a file not opened for writing
179+
let file = OpenOptions::new().read(true).open(&path).unwrap();
180+
assert_eq!(ErrorKind::InvalidInput, file.set_len(14).unwrap_err().kind());
181+
182+
remove_file(&path).unwrap();
183+
}
184+
158185
fn test_symlink() {
159186
let bytes = b"Hello, World!\n";
160187
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);

0 commit comments

Comments
 (0)