diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index c772ee6b558d..5b40f3c69063 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -489,6 +489,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } + | "lseek64" + | "lseek" + => { + let result = this.lseek64(args[0], args[1], args[2])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + "unlink" => { let result = this.unlink(args[0])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; diff --git a/src/shims/fs.rs b/src/shims/fs.rs index 2871dcbdcb8f..f0efc073f75c 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::fs::{remove_file, File, OpenOptions}; -use std::io::{Read, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; use std::time::SystemTime; @@ -264,6 +264,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } + fn lseek64( + &mut self, + fd_op: OpTy<'tcx, Tag>, + offset_op: OpTy<'tcx, Tag>, + whence_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i64> { + let this = self.eval_context_mut(); + + this.check_no_isolation("lseek64")?; + + let fd = this.read_scalar(fd_op)?.to_i32()?; + let offset = this.read_scalar(offset_op)?.to_i64()?; + let whence = this.read_scalar(whence_op)?.to_i32()?; + + let seek_from = if whence == this.eval_libc_i32("SEEK_SET")? { + SeekFrom::Start(offset as u64) + } else if whence == this.eval_libc_i32("SEEK_CUR")? { + SeekFrom::Current(offset) + } else if whence == this.eval_libc_i32("SEEK_END")? { + SeekFrom::End(offset) + } else { + let einval = this.eval_libc("EINVAL")?; + this.set_last_error(einval)?; + return Ok(-1); + }; + + if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) { + let result = handle.file.seek(seek_from).map(|offset| offset as i64); + this.try_unwrap_io_result(result) + } else { + this.handle_not_found() + } + } + fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); diff --git a/tests/run-pass/fs.rs b/tests/run-pass/fs.rs index cc8f6e01f323..7483bf3ec8b8 100644 --- a/tests/run-pass/fs.rs +++ b/tests/run-pass/fs.rs @@ -2,7 +2,7 @@ // compile-flags: -Zmiri-disable-isolation use std::fs::{File, remove_file}; -use std::io::{Read, Write, ErrorKind, Result}; +use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom}; use std::path::{PathBuf, Path}; fn test_metadata(bytes: &[u8], path: &Path) -> Result<()> { @@ -42,6 +42,23 @@ fn main() { file.read_to_end(&mut contents).unwrap(); assert_eq!(bytes, contents.as_slice()); + // Test that seeking to the beginning and reading until EOF gets the text again. + file.seek(SeekFrom::Start(0)).unwrap(); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(bytes, contents.as_slice()); + // Test seeking relative to the end of the file. + file.seek(SeekFrom::End(-1)).unwrap(); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice()); + // Test seeking relative to the current position. + file.seek(SeekFrom::Start(5)).unwrap(); + file.seek(SeekFrom::Current(-3)).unwrap(); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(&bytes[2..], contents.as_slice()); + // Test that metadata of an absolute path is correct. test_metadata(bytes, &path).unwrap(); // Test that metadata of a relative path is correct.