Skip to content

Commit bb6391b

Browse files
committed
“extend the lseek on dir”
1 parent 557efb9 commit bb6391b

File tree

2 files changed

+104
-67
lines changed

2 files changed

+104
-67
lines changed

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

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -916,68 +916,77 @@ impl Filesystem for OverlayFs {
916916

917917
let st = node.stat64(req).await?;
918918
if utils::is_dir(&st.attr.kind) {
919-
// Special handling and security restrictions for directory operations
920-
let handles = self.handles.lock().await;
921-
let handle_data = handles.get(&fh).ok_or_else(|| {
922-
Error::new(ErrorKind::InvalidInput, "Invalid directory handle")
923-
})?;
924-
925-
// Verify handle type and permissions
926-
if let Some(ref rh) = handle_data.real_handle {
927-
// Verify that this handle is indeed a directory handle
928-
let handle_stat = if let Ok(stat) = rh.layer
929-
.getattr(req, rh.inode, Some(rh.handle.load(Ordering::Relaxed)), 0)
930-
.await
931-
{
932-
stat
933-
} else {
934-
return Err(Error::from_raw_os_error(libc::EBADF).into());
935-
};
919+
// Special handling and security restrictions for directory operations.
920+
// Use the common API to obtain the underlying layer and handle info.
921+
let (layer, real_inode, real_handle) = self.find_real_info_from_handle(fh).await?;
936922

937-
if !utils::is_dir(&handle_stat.attr.kind) {
938-
return Err(Error::from_raw_os_error(libc::ENOTDIR).into());
939-
}
923+
// Verify that the underlying handle refers to a directory.
924+
let handle_stat = match layer
925+
.getattr(req, real_inode, Some(real_handle), 0)
926+
.await
927+
{
928+
Ok(s) => s,
929+
Err(_) => return Err(Error::from_raw_os_error(libc::EBADF).into()),
930+
};
931+
932+
if !utils::is_dir(&handle_stat.attr.kind) {
933+
return Err(Error::from_raw_os_error(libc::ENOTDIR).into());
934+
}
940935

941-
match whence {
942-
libc::SEEK_SET => {
943-
// Validate offset bounds
944-
if offset > i64::MAX as u64 {
936+
// Handle directory lseek operations according to POSIX standard
937+
// This enables seekdir/telldir functionality on directories
938+
match whence {
939+
// SEEK_SET: Set the directory position to an absolute value
940+
libc::SEEK_SET => {
941+
// Validate offset bounds to prevent overflow
942+
// Directory offsets should not exceed i64::MAX
943+
if offset > i64::MAX as u64 {
944+
return Err(Error::from_raw_os_error(libc::EINVAL).into());
945+
}
946+
947+
// Perform the seek operation on the underlying layer
948+
// Delegate to the lower layer implementation
949+
layer.lseek(req, real_inode, real_handle, offset, whence).await
950+
}
951+
// SEEK_CUR: Move relative to the current directory position
952+
libc::SEEK_CUR => {
953+
// Get current position from underlying layer
954+
// This is needed to calculate the new position
955+
let current = match layer
956+
.lseek(req, real_inode, real_handle, 0, libc::SEEK_CUR)
957+
.await
958+
{
959+
Ok(r) => r.offset,
960+
Err(_) => return Err(Error::from_raw_os_error(libc::EINVAL).into()),
961+
};
962+
963+
// Check for potential overflow when adding the provided offset
964+
// This prevents invalid position calculations
965+
if let Some(new_offset) = current.checked_add(offset) {
966+
// Ensure the new offset is within valid bounds
967+
if new_offset > i64::MAX as u64 {
945968
return Err(Error::from_raw_os_error(libc::EINVAL).into());
946969
}
947970

948-
// Perform the seek operation
949-
rh.layer
950-
.lseek(req, rh.inode, rh.handle.load(Ordering::Relaxed), offset, whence)
951-
.await
952-
},
953-
libc::SEEK_CUR => {
954-
// Get current position
955-
let current = if let Ok(reply) = rh.layer
956-
.lseek(req, rh.inode, rh.handle.load(Ordering::Relaxed), 0, libc::SEEK_CUR)
971+
// Actually set the underlying offset to the new value so behavior
972+
// matches passthrough which uses libc::lseek64 to set the fd offset.
973+
match layer
974+
.lseek(req, real_inode, real_handle, new_offset, libc::SEEK_SET)
957975
.await
958976
{
959-
reply.offset
960-
} else {
961-
return Err(Error::from_raw_os_error(libc::EINVAL).into());
962-
};
963-
964-
// Check for potential overflow
965-
if let Some(new_offset) = current.checked_add(offset) {
966-
if new_offset > i64::MAX as u64 {
967-
return Err(Error::from_raw_os_error(libc::EINVAL).into());
968-
}
969-
Ok(ReplyLSeek { offset: new_offset })
970-
} else {
971-
Err(Error::from_raw_os_error(libc::EINVAL).into())
977+
Ok(_) => Ok(ReplyLSeek { offset: new_offset }),
978+
Err(_) => Err(Error::from_raw_os_error(libc::EINVAL).into()),
972979
}
973-
},
974-
_ => Err(Error::from_raw_os_error(libc::EINVAL).into()),
980+
} else {
981+
Err(Error::from_raw_os_error(libc::EINVAL).into())
982+
}
975983
}
976-
} else {
977-
Err(Error::new(ErrorKind::InvalidInput, "Invalid directory handle").into())
984+
// Any other whence value is invalid for directories
985+
_ => Err(Error::from_raw_os_error(libc::EINVAL).into()),
978986
}
979987
} else {
980988
// Keep the original lseek behavior for regular files
989+
// Delegate directly to the underlying layer
981990
let (layer, real_inode, real_handle) = self.find_real_info_from_handle(fh).await?;
982991
layer
983992
.lseek(req, real_inode, real_handle, offset, whence)

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

Lines changed: 45 additions & 17 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,45 +1794,73 @@ 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-
// 检查文件类型
1797+
// Check file type to determine appropriate lseek handling
17981798
let st = stat_fd(data.get_file(), None)?;
17991799
let is_dir = (st.st_mode & libc::S_IFMT) == libc::S_IFDIR;
18001800

18011801
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
18021808
match whence {
1803-
// 目录只支持 SEEK_SET
1809+
// SEEK_SET: set directory offset to an absolute value
18041810
libc::SEEK_SET => {
1805-
// 检查目录offset的有效性
1811+
// Validate offset bounds to prevent overflow
1812+
// Directory offsets should not exceed i64::MAX
18061813
if offset > i64::MAX as u64 {
18071814
return Err(io::Error::from_raw_os_error(libc::EINVAL).into());
18081815
}
18091816

1810-
// Acquire the lock to get exclusive access
1811-
let (_guard, file) = data.get_file_mut().await;
1812-
1813-
// 对目录的seek操作,特别注意安全边界
1817+
// Perform the seek operation using libc::lseek64
1818+
// This directly manipulates the file descriptor's position
18141819
let res = unsafe {
1815-
libc::lseek64(
1816-
file.as_raw_fd(),
1817-
offset as libc::off64_t,
1818-
libc::SEEK_SET,
1819-
)
1820+
libc::lseek64(file.as_raw_fd(), offset as libc::off64_t, libc::SEEK_SET)
18201821
};
1821-
18221822
if res < 0 {
18231823
return Err(io::Error::last_os_error().into());
18241824
}
18251825
Ok(ReplyLSeek { offset: res as u64 })
18261826
}
1827-
// 目录不支持其他seek类型
1827+
// SEEK_CUR: move relative to current directory offset
1828+
libc::SEEK_CUR => {
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(file.as_raw_fd(), new_offset as libc::off64_t, libc::SEEK_SET)
1845+
};
1846+
if res < 0 {
1847+
return Err(io::Error::last_os_error().into());
1848+
}
1849+
Ok(ReplyLSeek { offset: new_offset })
1850+
} else {
1851+
Err(io::Error::from_raw_os_error(libc::EINVAL).into())
1852+
}
1853+
}
1854+
// Other whence values are invalid for directories (e.g., SEEK_END)
18281855
_ => Err(io::Error::from_raw_os_error(libc::EINVAL).into()),
18291856
}
18301857
} else {
1831-
// 文件的seek操作
1858+
// File seek handling for non-directory files
18321859
// Acquire the lock to get exclusive access, otherwise it may break do_readdir().
18331860
let (_guard, file) = data.get_file_mut().await;
18341861

18351862
// Safe because this doesn't modify any memory and we check the return value.
1863+
// Use standard libc::lseek for regular files
18361864
let res = unsafe {
18371865
libc::lseek(
18381866
file.as_raw_fd(),

0 commit comments

Comments
 (0)