Merge branch 'fdopendir' into 'master'
Implement fdopendir and fdclosedir See merge request redox-os/relibc!712
This commit is contained in:
@@ -16,7 +16,10 @@ use crate::{
|
||||
platform::{self, types::*, Pal, Sys},
|
||||
};
|
||||
|
||||
use super::errno::{EINVAL, EIO, ENOMEM};
|
||||
use super::{
|
||||
errno::{self, EINVAL, EIO, ENOMEM, ENOTDIR},
|
||||
sys_stat,
|
||||
};
|
||||
|
||||
const INITIAL_BUFSIZE: usize = 512;
|
||||
|
||||
@@ -43,6 +46,26 @@ impl DIR {
|
||||
opaque_offset: 0,
|
||||
}))
|
||||
}
|
||||
pub fn from_fd(fd: c_int) -> Result<Box<Self>, Errno> {
|
||||
let mut stat = sys_stat::stat::default();
|
||||
unsafe {
|
||||
Sys::fstat(fd, &mut stat)?;
|
||||
}
|
||||
if (stat.st_mode & sys_stat::S_IFMT) != sys_stat::S_IFDIR {
|
||||
return Err(Errno(ENOTDIR));
|
||||
}
|
||||
Sys::fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as _)?;
|
||||
|
||||
// Take ownership now but not earlier so we don't close the fd on error.
|
||||
let file = File::new(fd);
|
||||
Ok(Self {
|
||||
file,
|
||||
buf: Vec::with_capacity(INITIAL_BUFSIZE),
|
||||
buf_offset: 0,
|
||||
opaque_offset: 0,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
fn next_dirent(&mut self) -> Result<*mut dirent, Errno> {
|
||||
let mut this_dent = self.buf.get(self.buf_offset..).ok_or(Errno(EIO))?;
|
||||
if this_dent.is_empty() {
|
||||
@@ -161,6 +184,19 @@ pub extern "C" fn closedir(dir: Box<DIR>) -> c_int {
|
||||
dir.close().map(|()| 0).or_minus_one_errno()
|
||||
}
|
||||
|
||||
/// See <https://man.freebsd.org/cgi/man.cgi?query=fdopendir&sektion=3>
|
||||
///
|
||||
/// FreeBSD extension that transfers ownership of the directory file descriptor to the user.
|
||||
///
|
||||
/// It doesn't matter if DIR was opened with [`opendir`] or [`fdopendir`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn fdclosedir(dir: Box<DIR>) -> c_int {
|
||||
let mut file = dir.file;
|
||||
file.reference = true;
|
||||
|
||||
*file
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html>.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn dirfd(dir: &mut DIR) -> c_int {
|
||||
@@ -175,6 +211,12 @@ pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
|
||||
DIR::new(path).or_errno_null_mut()
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html>.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn fdopendir(fd: c_int) -> *mut DIR {
|
||||
DIR::from_fd(fd).or_errno_null_mut()
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_getdents.html>.
|
||||
// #[no_mangle]
|
||||
pub extern "C" fn posix_getdents(
|
||||
|
||||
@@ -27,6 +27,7 @@ EXPECT_NAMES=\
|
||||
crypt/sha256 \
|
||||
crypt/sha512 \
|
||||
destructor \
|
||||
dirent/fdopendir \
|
||||
dirent/scandir \
|
||||
endian \
|
||||
err \
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
__attribute__((nonnull))
|
||||
static bool check_dot(struct dirent* dirent) {
|
||||
const char* dots[2] = {
|
||||
".",
|
||||
".."
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (strcmp(dots[i], dirent->d_name) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
char template[] = "/tmp/fdotest.XXXXXX";
|
||||
if (!mkdtemp(template)) {
|
||||
perror("mkdtemp");
|
||||
goto bye;
|
||||
}
|
||||
|
||||
const char* movies[] = {
|
||||
"big_lebowski",
|
||||
"blade_runner",
|
||||
"grand_budapest_hotel",
|
||||
"taxi_driver"
|
||||
};
|
||||
const size_t movies_len = sizeof(movies)/sizeof(char*);
|
||||
char paths[sizeof(movies)/sizeof(char*)][PATH_MAX] = {0};
|
||||
for (size_t i = 0; i < movies_len; ++i) {
|
||||
// Concat the path
|
||||
const size_t len = sizeof(template) - 1;
|
||||
char buf[PATH_MAX] = {0};
|
||||
memcpy(buf, template, len);
|
||||
buf[len] = '/';
|
||||
|
||||
memcpy(&buf[len + 1], movies[i], strlen(movies[i]));
|
||||
memcpy(paths[i], buf, PATH_MAX);
|
||||
|
||||
// And now create the file
|
||||
int fd = open(buf, O_CREAT);
|
||||
if (fd == -1) {
|
||||
perror("open");
|
||||
goto rmfiles;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// FIXME: Redox requires read perms for the dir while Linux/BSD don't.
|
||||
int dir = open(template, O_DIRECTORY | O_RDONLY);
|
||||
if (dir == -1) {
|
||||
perror("open");
|
||||
goto rmfiles;
|
||||
}
|
||||
|
||||
DIR* iter = fdopendir(dir);
|
||||
if (!iter) {
|
||||
perror("fdopendir");
|
||||
goto closedirfd;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < movies_len; ++i) {
|
||||
errno = 0;
|
||||
|
||||
struct dirent* dirent = readdir(iter);
|
||||
if (!dirent) {
|
||||
if (errno) {
|
||||
perror("readdir");
|
||||
}
|
||||
fprintf(
|
||||
stderr,
|
||||
"Expected entry #%lu but directory stream is complete\n",
|
||||
i
|
||||
);
|
||||
|
||||
goto closediriter;
|
||||
}
|
||||
|
||||
// Skip . and ..
|
||||
if (check_dot(dirent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that the entry matches one of the names.
|
||||
// readdir's order is indeterministic and looping over the names
|
||||
// is simpler than qsort for a test.
|
||||
for (size_t j = 0; j < movies_len; ++j) {
|
||||
if (strcmp(movies[j], dirent->d_name) == 0) {
|
||||
goto continue_outer;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unexpected entry: %s\n",
|
||||
dirent->d_name
|
||||
);
|
||||
goto closediriter;
|
||||
|
||||
continue_outer:
|
||||
continue;
|
||||
}
|
||||
|
||||
// fdclosedir returns ownership of the original fd.
|
||||
int returned_fd = fdclosedir(iter);
|
||||
// Internally, both closedir and fdclosedir consume the boxed DIR.
|
||||
iter = NULL;
|
||||
if (returned_fd != dir) {
|
||||
fputs("fdclosedir returned the wrong descriptor\n", stderr);
|
||||
goto closedirfd;
|
||||
}
|
||||
|
||||
// Check that the file descriptor is still valid.
|
||||
struct stat stat = {0};
|
||||
if (fstatat(returned_fd, "", &stat, AT_EMPTY_PATH) == -1) {
|
||||
perror("fstatat");
|
||||
fputs("fdclosedir shouldn't have closed the fd\n", stderr);
|
||||
goto closedirfd;
|
||||
}
|
||||
|
||||
status = EXIT_SUCCESS;
|
||||
closediriter:
|
||||
if (iter) {
|
||||
closedir(iter);
|
||||
}
|
||||
closedirfd:
|
||||
close(dir);
|
||||
rmfiles:
|
||||
for (size_t i = 0; i < movies_len; ++i) {
|
||||
if (strnlen(paths[i], PATH_MAX) > 4) {
|
||||
unlink(paths[i]);
|
||||
}
|
||||
}
|
||||
rmdir(template);
|
||||
bye:
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user