Skip to content

Commit f6f0979

Browse files
committed
FIX: supporting close needs RwLocks, atomics are racy
1 parent e44fc88 commit f6f0979

File tree

4 files changed

+60
-66
lines changed

4 files changed

+60
-66
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ statx = []
3232

3333
[dependencies]
3434
libc = "0.2"
35+
parking_lot = "0.12"
3536

3637
[build-dependencies]
37-
libc = "0.2"
3838
conf_test = "0.2"
3939

4040
[dev-dependencies]

src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl<'a> DirMethodFlags<'a> {
140140
/// Creates a new 'Normal' independently owned handle to the underlying directory.
141141
pub fn clone_upgrade(&self) -> io::Result<Dir> {
142142
Ok(Dir::new(clone_dirfd_upgrade(
143-
self.object.rawfd()?,
143+
*self.object.rawfd()?,
144144
self.flags,
145145
)?))
146146
}

src/dir.rs

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::mem;
55
use std::os::unix::ffi::OsStringExt;
66
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
77
use std::path::{Path, PathBuf};
8-
use std::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, Ordering};
8+
9+
use parking_lot::{RwLock, RwLockReadGuard};
910

1011
use crate::list::{open_dirfd, DirIter};
1112
use crate::metadata::{self, Metadata};
@@ -36,18 +37,18 @@ pub const O_SEARCH: libc::c_int = 0;
3637
///
3738
/// Construct it either with ``Dir::cwd()`` or ``Dir::open(path)``
3839
#[derive(Debug)]
39-
pub struct Dir(AtomicRawFd);
40+
pub struct Dir(RwLock<RawFd>);
4041

4142
impl Dir {
4243
/// low level ctor, only used by builder and tests
4344
pub(crate) fn new(fd: RawFd) -> Self {
44-
Dir(AtomicRawFd::new(fd))
45+
Dir(RwLock::new(fd))
4546
}
4647

4748
/// Gets the underlying RawFd from a Dir handle.
48-
pub(crate) fn rawfd(&self) -> io::Result<RawFd> {
49-
let rawfd = self.0.load(Ordering::Acquire);
50-
if rawfd != -1 {
49+
pub(crate) fn rawfd(&self) -> io::Result<RwLockReadGuard<RawFd>> {
50+
let rawfd = self.0.read();
51+
if *rawfd != -1 {
5152
Ok(rawfd)
5253
} else {
5354
Err(io::Error::from_raw_os_error(libc::EBADF))
@@ -92,7 +93,7 @@ impl Dir {
9293
/// explicitly. Thats what 'is_dir()' is for. Returns 'true' when the underlying handles
9394
/// represents a directory and false otherwise.
9495
pub fn is_dir(&self) -> bool {
95-
match fd_type(self.0.load(Ordering::Acquire)).unwrap_or(FdType::Other) {
96+
match fd_type(*self.0.read()).unwrap_or(FdType::Other) {
9697
FdType::NormalDir | FdType::OPathDir => true,
9798
FdType::Other => false,
9899
}
@@ -113,7 +114,7 @@ impl Dir {
113114
/// Create a DirIter from a Dir
114115
/// Dir must not be a handle opened with O_PATH.
115116
pub fn list(self) -> io::Result<DirIter> {
116-
let fd = self.rawfd()?;
117+
let fd = *self.rawfd()?;
117118
std::mem::forget(self);
118119
open_dirfd(fd)
119120
}
@@ -159,7 +160,7 @@ impl Dir {
159160
pub(crate) fn _sub_dir(&self, path: &CStr, flags: libc::c_int) -> io::Result<Dir> {
160161
Ok(Dir::new(unsafe {
161162
libc_ok(libc::openat(
162-
self.rawfd()?,
163+
*self.rawfd()?,
163164
path.as_ptr(),
164165
flags | O_DIRECTORY,
165166
))?
@@ -175,7 +176,7 @@ impl Dir {
175176
let mut buf = vec![0u8; 4096];
176177
let res = unsafe {
177178
libc::readlinkat(
178-
self.rawfd()?,
179+
*self.rawfd()?,
179180
path.as_ptr(),
180181
buf.as_mut_ptr() as *mut libc::c_char,
181182
buf.len(),
@@ -371,7 +372,7 @@ impl Dir {
371372
// promoted as they are in C this would break on Freebsd where
372373
// *mode_t* is an alias for `uint16_t`.
373374
let res = libc_ok(libc::openat(
374-
self.rawfd()?,
375+
*self.rawfd()?,
375376
path.as_ptr(),
376377
flags | libc::O_CLOEXEC | libc::O_NOFOLLOW,
377378
mode as libc::c_uint,
@@ -389,7 +390,7 @@ impl Dir {
389390

390391
fn _symlink(&self, path: &CStr, link: &CStr) -> io::Result<()> {
391392
unsafe {
392-
let res = libc::symlinkat(link.as_ptr(), self.rawfd()?, path.as_ptr());
393+
let res = libc::symlinkat(link.as_ptr(), *self.rawfd()?, path.as_ptr());
393394
if res < 0 {
394395
Err(io::Error::last_os_error())
395396
} else {
@@ -405,7 +406,7 @@ impl Dir {
405406

406407
fn _create_dir(&self, path: &CStr, mode: libc::mode_t) -> io::Result<()> {
407408
unsafe {
408-
libc_ok(libc::mkdirat(self.rawfd()?, path.as_ptr(), mode))?;
409+
libc_ok(libc::mkdirat(*self.rawfd()?, path.as_ptr(), mode))?;
409410
}
410411
Ok(())
411412
}
@@ -448,7 +449,7 @@ impl Dir {
448449

449450
fn _unlink(&self, path: &CStr, flags: libc::c_int) -> io::Result<()> {
450451
unsafe {
451-
libc_ok(libc::unlinkat(self.rawfd()?, path.as_ptr(), flags))?;
452+
libc_ok(libc::unlinkat(*self.rawfd()?, path.as_ptr(), flags))?;
452453
}
453454
Ok(())
454455
}
@@ -507,7 +508,7 @@ impl Dir {
507508
#[cfg(feature = "proc_self_fd")]
508509
pub fn recover_path(&self) -> io::Result<PathBuf> {
509510
let fd = self.rawfd()?;
510-
if fd != libc::AT_FDCWD {
511+
if *fd != libc::AT_FDCWD {
511512
read_link(format!("/proc/self/fd/{}", fd))
512513
} else {
513514
read_link("/proc/self/cwd")
@@ -529,7 +530,7 @@ impl Dir {
529530
unsafe {
530531
let mut stat = mem::zeroed(); // TODO(cehteh): uninit
531532
libc_ok(libc::fstatat(
532-
self.rawfd()?,
533+
*self.rawfd()?,
533534
path.as_ptr(),
534535
&mut stat,
535536
flags,
@@ -542,7 +543,7 @@ impl Dir {
542543
pub fn self_metadata(&self) -> io::Result<Metadata> {
543544
unsafe {
544545
let mut stat = mem::zeroed(); // TODO(cehteh): uninit
545-
libc_ok(libc::fstat(self.rawfd()?, &mut stat))?;
546+
libc_ok(libc::fstat(*self.rawfd()?, &mut stat))?;
546547
Ok(metadata::new(stat))
547548
}
548549
}
@@ -563,25 +564,26 @@ impl Dir {
563564
/// Creates a new independently owned handle to the underlying directory.
564565
/// The new handle has the same (Normal/O_PATH) semantics as the original handle.
565566
pub fn try_clone(&self) -> io::Result<Self> {
566-
Ok(Dir::new(clone_dirfd(self.rawfd()?)?))
567+
Ok(Dir::new(clone_dirfd(*self.rawfd()?)?))
567568
}
568569

569570
/// Creates a new 'Normal' independently owned handle to the underlying directory.
570571
pub fn clone_upgrade(&self) -> io::Result<Self> {
571-
Ok(Dir::new(clone_dirfd_upgrade(self.rawfd()?, 0)?))
572+
Ok(Dir::new(clone_dirfd_upgrade(*self.rawfd()?, 0)?))
572573
}
573574

574575
/// Creates a new 'O_PATH' restricted independently owned handle to the underlying directory.
575576
pub fn clone_downgrade(&self) -> io::Result<Self> {
576-
Ok(Dir::new(clone_dirfd_downgrade(self.rawfd()?)?))
577+
Ok(Dir::new(clone_dirfd_downgrade(*self.rawfd()?)?))
577578
}
578579

579580
/// Explicit closing of a file handle. The object stays alive but all operations on it will fail.
580581
pub fn close(&self) {
581-
let fd = self.0.swap(-1, Ordering::AcqRel);
582-
if fd != libc::AT_FDCWD && fd != -1 {
582+
let mut fd = self.0.write();
583+
if *fd != libc::AT_FDCWD && *fd != -1 {
583584
unsafe {
584-
libc::close(fd);
585+
libc::close(*fd);
586+
*fd = -1;
585587
}
586588
}
587589
}
@@ -726,9 +728,9 @@ where
726728
fn _rename(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr) -> io::Result<()> {
727729
unsafe {
728730
libc_ok(libc::renameat(
729-
old_dir.rawfd()?,
731+
*old_dir.rawfd()?,
730732
old.as_ptr(),
731-
new_dir.rawfd()?,
733+
*new_dir.rawfd()?,
732734
new.as_ptr(),
733735
))?;
734736
}
@@ -766,9 +768,9 @@ fn _hardlink(
766768
) -> io::Result<()> {
767769
unsafe {
768770
libc_ok(libc::linkat(
769-
old_dir.rawfd()?,
771+
*old_dir.rawfd()?,
770772
old.as_ptr(),
771-
new_dir.rawfd()?,
773+
*new_dir.rawfd()?,
772774
new.as_ptr(),
773775
flags,
774776
))?;
@@ -831,7 +833,7 @@ fn _rename_flags(
831833
impl AsRawFd for Dir {
832834
#[inline]
833835
fn as_raw_fd(&self) -> RawFd {
834-
self.0.load(Ordering::Acquire)
836+
*self.0.read()
835837
}
836838
}
837839

@@ -847,7 +849,7 @@ impl FromRawFd for Dir {
847849
impl IntoRawFd for Dir {
848850
#[inline]
849851
fn into_raw_fd(self) -> RawFd {
850-
let result = self.0.load(Ordering::Acquire);
852+
let result = *self.0.read();
851853
mem::forget(self);
852854
result
853855
}
@@ -864,26 +866,6 @@ pub(crate) fn to_cstr<P: AsPath>(path: P) -> io::Result<P::Buffer> {
864866
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "nul byte in file name"))
865867
}
866868

867-
// For the esoteric platforms where c_int is not i32,
868-
// make the RawFd/c_int atomic in a portable way
869-
trait Atomic {
870-
type T;
871-
}
872-
873-
impl Atomic for i16 {
874-
type T = AtomicI16;
875-
}
876-
877-
impl Atomic for i32 {
878-
type T = AtomicI32;
879-
}
880-
881-
impl Atomic for i64 {
882-
type T = AtomicI64;
883-
}
884-
885-
type AtomicRawFd = <RawFd as Atomic>::T;
886-
887869
#[cfg(test)]
888870
mod test {
889871
use std::io::Read;

src/list.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::ffi::{CStr, CString, OsStr};
22
use std::io;
3+
use std::fmt;
34
use std::mem;
45
use std::os::unix::ffi::OsStrExt;
5-
use std::sync::atomic::{AtomicPtr, Ordering};
66
use std::sync::Arc;
77

8+
use parking_lot::{RwLock, RwLockReadGuard};
9+
810
use crate::{dir::libc_ok, metadata, Metadata, SimpleType};
911

1012
// We have such weird constants because C types are ugly
@@ -14,7 +16,6 @@ const DOTDOT: [libc::c_char; 3] = [b'.' as libc::c_char, b'.' as libc::c_char, 0
1416
/// Iterator over directory entries
1517
///
1618
/// Created using `Dir::list_dir()`
17-
#[derive(Debug)]
1819
pub struct DirIter {
1920
// Needs Arc here to be shared with Entries, for metdata()
2021
dir: Arc<DirHandle>,
@@ -40,7 +41,6 @@ pub struct DirPosition {
4041
}
4142

4243
/// Entry returned by iterating over `DirIter` iterator
43-
#[derive(Debug)]
4444
pub struct Entry {
4545
dir: Arc<DirHandle>,
4646
pub(crate) name: CString,
@@ -69,7 +69,7 @@ impl Entry {
6969
unsafe {
7070
let mut stat = mem::zeroed(); // TODO(cehteh): uninit
7171
libc_ok(libc::fstatat(
72-
libc::dirfd(self.dir.raw()?),
72+
libc::dirfd(*self.dir.raw()?),
7373
self.name.as_ptr(),
7474
&mut stat,
7575
libc::AT_SYMLINK_NOFOLLOW,
@@ -84,6 +84,17 @@ impl Entry {
8484
}
8585
}
8686

87+
impl fmt::Debug for Entry {
88+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89+
f.debug_struct("Entry")
90+
.field("dir", &Arc::as_ptr(&self.dir))
91+
.field("name", &self.name)
92+
.field("file_type", &self.file_type)
93+
.field("ino", &self.ino)
94+
.finish()
95+
}
96+
}
97+
8798
#[cfg(any(target_os = "linux", target_os = "fuchsia"))]
8899
unsafe fn errno_location() -> *mut libc::c_int {
89100
libc::__errno_location()
@@ -110,7 +121,7 @@ impl DirIter {
110121
// Reset errno to detect if error occurred
111122
*errno_location() = 0;
112123

113-
let entry = libc::readdir(self.dir.raw()?);
124+
let entry = libc::readdir(*self.dir.raw()?);
114125
if entry.is_null() {
115126
if *errno_location() == 0 {
116127
return Ok(None);
@@ -123,7 +134,7 @@ impl DirIter {
123134

124135
/// Returns the current directory iterator position. The result should be handled as opaque value
125136
pub fn current_position(&self) -> io::Result<DirPosition> {
126-
let pos = unsafe { libc::telldir(self.dir.raw()?) };
137+
let pos = unsafe { libc::telldir(*self.dir.raw()?) };
127138

128139
if pos == -1 {
129140
Err(io::Error::last_os_error())
@@ -136,14 +147,14 @@ impl DirIter {
136147
/// Sets the current directory iterator position to some location queried by 'current_position()'
137148
pub fn seek(&self, position: DirPosition) {
138149
if let Ok(dir) = self.dir.raw() {
139-
unsafe { libc::seekdir(dir, position.pos) };
150+
unsafe { libc::seekdir(*dir, position.pos) };
140151
}
141152
}
142153

143154
/// Resets the current directory iterator position to the beginning
144155
pub fn rewind(&self) {
145156
if let Ok(dir) = self.dir.raw() {
146-
unsafe { libc::rewinddir(dir) };
157+
unsafe { libc::rewinddir(*dir) };
147158
}
148159
}
149160

@@ -196,16 +207,16 @@ impl Iterator for DirIter {
196207
}
197208
}
198209

199-
#[derive(Debug)]
200-
struct DirHandle(AtomicPtr<libc::DIR>);
210+
//#[derive(Debug)]
211+
struct DirHandle(RwLock<*mut libc::DIR>);
201212

202213
impl DirHandle {
203214
fn new(dir: *mut libc::DIR) -> Self {
204-
DirHandle(AtomicPtr::new(dir))
215+
DirHandle(RwLock::new(dir))
205216
}
206217

207-
fn raw(&self) -> io::Result<*mut libc::DIR> {
208-
let dir = self.0.load(Ordering::Acquire);
218+
fn raw(&self) -> io::Result<RwLockReadGuard<*mut libc::DIR>> {
219+
let dir = self.0.read();
209220
if !dir.is_null() {
210221
Ok(dir)
211222
} else {
@@ -214,10 +225,11 @@ impl DirHandle {
214225
}
215226

216227
fn close(&self) {
217-
let dir = self.0.swap(std::ptr::null_mut(), Ordering::AcqRel);
228+
let mut dir = self.0.write();
218229
if !dir.is_null() {
219230
unsafe {
220-
libc::closedir(dir);
231+
libc::closedir(*dir);
232+
*dir = std::ptr::null_mut();
221233
}
222234
}
223235
}

0 commit comments

Comments
 (0)