Skip to content

Commit 6eac0e4

Browse files
authored
extend of lseek on dir (#245)
1 parent 2096149 commit 6eac0e4

File tree

2 files changed

+163
-25
lines changed

2 files changed

+163
-25
lines changed

project/libfuse-fs/src/overlayfs/async_io.rs

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::io::ErrorKind;
1313
use std::num::NonZeroU32;
1414
use std::sync::Arc;
1515
use std::sync::atomic::Ordering;
16-
use tracing::error;
1716
use tracing::info;
1817
use tracing::trace;
1918

@@ -908,8 +907,6 @@ impl Filesystem for OverlayFs {
908907
offset: u64,
909908
whence: u32,
910909
) -> Result<ReplyLSeek> {
911-
// can this be on dir? FIXME: assume file for now
912-
// we need special process if it can be called on dir
913910
let node = self.lookup_node(req, inode, "").await?;
914911

915912
if node.whiteout.load(Ordering::Relaxed) {
@@ -918,14 +915,87 @@ impl Filesystem for OverlayFs {
918915

919916
let st = node.stat64(req).await?;
920917
if utils::is_dir(&st.attr.kind) {
921-
error!("lseek on directory");
922-
return Err(Error::from_raw_os_error(libc::EINVAL).into());
923-
}
918+
// Special handling and security restrictions for directory operations.
919+
// Use the common API to obtain the underlying layer and handle info.
920+
let (layer, real_inode, real_handle) = self.find_real_info_from_handle(fh).await?;
921+
922+
// Verify that the underlying handle refers to a directory.
923+
let handle_stat = match layer.getattr(req, real_inode, Some(real_handle), 0).await {
924+
Ok(s) => s,
925+
Err(_) => return Err(Error::from_raw_os_error(libc::EBADF).into()),
926+
};
924927

925-
let (layer, real_inode, real_handle) = self.find_real_info_from_handle(fh).await?;
926-
layer
927-
.lseek(req, real_inode, real_handle, offset, whence)
928-
.await
928+
if !utils::is_dir(&handle_stat.attr.kind) {
929+
return Err(Error::from_raw_os_error(libc::ENOTDIR).into());
930+
}
931+
932+
// Handle directory lseek operations according to POSIX standard
933+
// This enables seekdir/telldir functionality on directories
934+
match whence {
935+
// SEEK_SET: Set the directory position to an absolute value
936+
x if x == libc::SEEK_SET as u32 => {
937+
// Validate offset bounds to prevent overflow
938+
// Directory offsets should not exceed i64::MAX
939+
if offset > i64::MAX as u64 {
940+
return Err(Error::from_raw_os_error(libc::EINVAL).into());
941+
}
942+
943+
// Perform the seek operation on the underlying layer
944+
// Delegate to the lower layer implementation
945+
layer
946+
.lseek(req, real_inode, real_handle, offset, whence)
947+
.await
948+
}
949+
// SEEK_CUR: Move relative to the current directory position
950+
x if x == libc::SEEK_CUR as u32 => {
951+
// Get current position from underlying layer
952+
// This is needed to calculate the new position
953+
let current = match layer
954+
.lseek(req, real_inode, real_handle, 0, libc::SEEK_CUR as u32)
955+
.await
956+
{
957+
Ok(r) => r.offset,
958+
Err(_) => return Err(Error::from_raw_os_error(libc::EINVAL).into()),
959+
};
960+
961+
// Check for potential overflow when adding the provided offset
962+
// This prevents invalid position calculations
963+
if let Some(new_offset) = current.checked_add(offset) {
964+
// Ensure the new offset is within valid bounds
965+
if new_offset > i64::MAX as u64 {
966+
return Err(Error::from_raw_os_error(libc::EINVAL).into());
967+
}
968+
969+
// Actually set the underlying offset to the new value so behavior
970+
// matches passthrough which uses libc::lseek64 to set the fd offset.
971+
match layer
972+
.lseek(
973+
req,
974+
real_inode,
975+
real_handle,
976+
new_offset,
977+
libc::SEEK_SET as u32,
978+
)
979+
.await
980+
{
981+
Ok(_) => Ok(ReplyLSeek { offset: new_offset }),
982+
Err(_) => Err(Error::from_raw_os_error(libc::EINVAL).into()),
983+
}
984+
} else {
985+
Err(Error::from_raw_os_error(libc::EINVAL).into())
986+
}
987+
}
988+
// Any other whence value is invalid for directories
989+
_ => Err(Error::from_raw_os_error(libc::EINVAL).into()),
990+
}
991+
} else {
992+
// Keep the original lseek behavior for regular files
993+
// Delegate directly to the underlying layer
994+
let (layer, real_inode, real_handle) = self.find_real_info_from_handle(fh).await?;
995+
layer
996+
.lseek(req, real_inode, real_handle, offset, whence)
997+
.await
998+
}
929999
}
9301000

9311001
async fn interrupt(&self, _req: Request, _unique: u64) -> Result<()> {

project/libfuse-fs/src/passthrough/async_io.rs

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
8080
// changes the kernel offset while we are using it.
8181
let (_guard, dir) = data.get_file_mut().await;
8282

83-
// alloc buff ,pay attention to alian.
83+
// Allocate buffer; pay attention to alignment.
8484
let mut buffer = vec![0u8; BUFFER_SIZE];
8585

8686
// Safe because this doesn't modify any memory and we check the return value.
@@ -180,7 +180,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
180180
// changes the kernel offset while we are using it.
181181
let (_guard, dir) = data.get_file_mut().await;
182182

183-
// alloc buff ,pay attention to alian.
183+
// Allocate buffer; pay attention to alignment.
184184
let mut buffer = vec![0u8; BUFFER_SIZE];
185185

186186
// Safe because this doesn't modify any memory and we check the return value.
@@ -1794,21 +1794,89 @@ impl Filesystem for PassthroughFs {
17941794
// Let the Arc<HandleData> in scope, otherwise fd may get invalid.
17951795
let data = self.handle_map.get(fh, inode).await?;
17961796

1797-
// Acquire the lock to get exclusive access, otherwise it may break do_readdir().
1798-
let (_guard, file) = data.get_file_mut().await;
1797+
// Check file type to determine appropriate lseek handling
1798+
let st = stat_fd(data.get_file(), None)?;
1799+
let is_dir = (st.st_mode & libc::S_IFMT) == libc::S_IFDIR;
1800+
1801+
if is_dir {
1802+
// Directory special handling: support SEEK_SET and SEEK_CUR with bounds checks.
1803+
// Acquire the lock to get exclusive access
1804+
let (_guard, file) = data.get_file_mut().await;
1805+
1806+
// Handle directory lseek operations according to POSIX standard
1807+
// This enables seekdir/telldir functionality on directories
1808+
match whence {
1809+
// SEEK_SET: set directory offset to an absolute value
1810+
x if x == libc::SEEK_SET as u32 => {
1811+
// Validate offset bounds to prevent overflow
1812+
// Directory offsets should not exceed i64::MAX
1813+
if offset > i64::MAX as u64 {
1814+
return Err(io::Error::from_raw_os_error(libc::EINVAL).into());
1815+
}
17991816

1800-
// Safe because this doesn't modify any memory and we check the return value.
1801-
let res = unsafe {
1802-
libc::lseek(
1803-
file.as_raw_fd(),
1804-
offset as libc::off64_t,
1805-
whence as libc::c_int,
1806-
)
1807-
};
1808-
if res < 0 {
1809-
Err(io::Error::last_os_error().into())
1817+
// Perform the seek operation using libc::lseek64
1818+
// This directly manipulates the file descriptor's position
1819+
let res = unsafe {
1820+
libc::lseek64(file.as_raw_fd(), offset as libc::off64_t, libc::SEEK_SET)
1821+
};
1822+
if res < 0 {
1823+
return Err(io::Error::last_os_error().into());
1824+
}
1825+
Ok(ReplyLSeek { offset: res as u64 })
1826+
}
1827+
// SEEK_CUR: move relative to current directory offset
1828+
x if x == libc::SEEK_CUR as u32 => {
1829+
// Get current position using libc::lseek64 with offset 0
1830+
let cur = unsafe { libc::lseek64(file.as_raw_fd(), 0, libc::SEEK_CUR) };
1831+
if cur < 0 {
1832+
return Err(io::Error::last_os_error().into());
1833+
}
1834+
let current = cur as u64;
1835+
1836+
// Compute new offset safely to prevent arithmetic overflow
1837+
if let Some(new_offset) = current.checked_add(offset) {
1838+
// Ensure the new offset is within valid bounds
1839+
if new_offset > i64::MAX as u64 {
1840+
return Err(io::Error::from_raw_os_error(libc::EINVAL).into());
1841+
}
1842+
// Set the new offset using libc::lseek64
1843+
let res = unsafe {
1844+
libc::lseek64(
1845+
file.as_raw_fd(),
1846+
new_offset as libc::off64_t,
1847+
libc::SEEK_SET,
1848+
)
1849+
};
1850+
if res < 0 {
1851+
return Err(io::Error::last_os_error().into());
1852+
}
1853+
Ok(ReplyLSeek { offset: new_offset })
1854+
} else {
1855+
Err(io::Error::from_raw_os_error(libc::EINVAL).into())
1856+
}
1857+
}
1858+
// Other whence values are invalid for directories (e.g., SEEK_END)
1859+
_ => Err(io::Error::from_raw_os_error(libc::EINVAL).into()),
1860+
}
18101861
} else {
1811-
Ok(ReplyLSeek { offset: res as u64 })
1862+
// File seek handling for non-directory files
1863+
// Acquire the lock to get exclusive access, otherwise it may break do_readdir().
1864+
let (_guard, file) = data.get_file_mut().await;
1865+
1866+
// Safe because this doesn't modify any memory and we check the return value.
1867+
// Use 64-bit seek for regular files to match kernel offsets
1868+
let res = unsafe {
1869+
libc::lseek64(
1870+
file.as_raw_fd(),
1871+
offset as libc::off64_t,
1872+
whence as libc::c_int,
1873+
)
1874+
};
1875+
if res < 0 {
1876+
Err(io::Error::last_os_error().into())
1877+
} else {
1878+
Ok(ReplyLSeek { offset: res as u64 })
1879+
}
18121880
}
18131881
}
18141882
}

0 commit comments

Comments
 (0)