diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 66f8fc76da99d7fe39d7e763377f03ee0fe096de..be548bdd31afc5b5046f3962f8318e8ebacb029c 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -2,8 +2,10 @@ pub use self::scheme::Scheme; pub use self::scheme_mut::SchemeMut; pub use self::scheme_block::SchemeBlock; pub use self::scheme_block_mut::SchemeBlockMut; +pub use self::seek::*; mod scheme; mod scheme_mut; mod scheme_block; mod scheme_block_mut; +mod seek; diff --git a/src/scheme/scheme.rs b/src/scheme/scheme.rs index 0c95a7415da3e931eaec4594c20c94e6a209844b..7cf77b930be9884f5c46c83c01b0770aba61c6bc 100644 --- a/src/scheme/scheme.rs +++ b/src/scheme/scheme.rs @@ -16,7 +16,7 @@ pub trait Scheme { SYS_DUP => self.dup(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }), SYS_READ => self.read(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }), SYS_WRITE => self.write(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }), - SYS_LSEEK => self.seek(packet.b, packet.c, packet.d), + SYS_LSEEK => self.seek(packet.b, packet.c as isize, packet.d).map(|o| o as usize), SYS_FCHMOD => self.fchmod(packet.b, packet.c as u16), SYS_FCHOWN => self.fchown(packet.b, packet.c as u32, packet.d as u32), SYS_FCNTL => self.fcntl(packet.b, packet.c, packet.d), @@ -92,7 +92,7 @@ pub trait Scheme { } #[allow(unused_variables)] - fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> { + fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> { Err(Error::new(EBADF)) } diff --git a/src/scheme/seek.rs b/src/scheme/seek.rs new file mode 100644 index 0000000000000000000000000000000000000000..09a45186ac6c828f3cb5f627a358e357c86e21f2 --- /dev/null +++ b/src/scheme/seek.rs @@ -0,0 +1,33 @@ +use core::cmp; +use core::convert::TryFrom; +use crate::error::*; +use crate::flag::*; + +/// Helper for seek calls +/// In most cases it's easier to use a usize to track the offset and buffer size internally, +/// but the seek interface uses isize. This wrapper ensures EOVERFLOW errors are returned +/// as appropriate if the value in the usize can't fit in the isize. +pub fn calc_seek_offset_usize(cur_offset: usize, pos: isize, whence: usize, buf_len: usize) -> Result<isize> { + let cur_offset = isize::try_from(cur_offset).or_else(|_| Err(Error::new(EOVERFLOW)))?; + let buf_len = isize::try_from(buf_len).or_else(|_| Err(Error::new(EOVERFLOW)))?; + calc_seek_offset_isize(cur_offset, pos, whence, buf_len) +} + +/// Helper for seek calls +/// Result is guaranteed to be positive. +/// EOVERFLOW returned if the arguments would cause an overflow. +/// EINVAL returned if the new offset is out of bounds. +pub fn calc_seek_offset_isize(cur_offset: isize, pos: isize, whence: usize, buf_len: isize) -> Result<isize> { + let new_offset = match whence { + SEEK_CUR => pos.checked_add(cur_offset), + SEEK_END => pos.checked_add(buf_len), + SEEK_SET => Some(pos), + _ => None, + }; + + match new_offset { + Some(new_offset) if new_offset < 0 => Err(Error::new(EINVAL)), + Some(new_offset) => Ok(cmp::min(new_offset, buf_len)), + None => Err(Error::new(EOVERFLOW)) + } +} \ No newline at end of file