Skip to content

Commit

Permalink
Fix fdopendir so dirfd stays open until closedir (for fstatat etc)
Browse files Browse the repository at this point in the history
  • Loading branch information
raforg authored and kencu committed Jul 22, 2023
1 parent b570b7f commit 8889f27
Show file tree
Hide file tree
Showing 9 changed files with 566 additions and 46 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ FORCE_ARCH ?=
ARCHFLAGS ?=
LIPO ?= lipo
CC ?= cc $(ARCHFLAGS)
CFLAGS ?= -Os -Wall
CFLAGS ?= -Os -Wall -Wno-deprecated-declarations
DLIBCFLAGS ?= -fPIC
SLIBCFLAGS ?=
CXX ?= c++ $(ARCHFLAGS)
Expand Down Expand Up @@ -84,7 +84,7 @@ FIND_LIBHEADERS := find $(SRCINCDIR) -type f \( -name '*.h' -o \
LIBHEADERS := $(shell $(FIND_LIBHEADERS))
ALLHEADERS := $(LIBHEADERS) $(wildcard $(SRCDIR)/*.h)

MULTISRCS := $(SRCDIR)/fdopendir.c
MULTISRCS := # Used to have $(SRCDIR)/fdopendir.c because it used struct stat
ADDSRCS := $(SRCDIR)/add_symbols.c
LIBSRCS := $(filter-out $(MULTISRCS) $(ADDSRCS),$(wildcard $(SRCDIR)/*.c))

Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ Wrapped headers and replaced functions are:
</tr>
<tr>
<td><code>dirent.h</code></td>
<td>Adds <code>fdopendir</code> function</td>
<td>Adds <code>fdopendir</code> function, and wraps <code>opendir</code>,
<code>readdir</code>, <code>readdir_r</code>, <code>rewinddir</code>,
<code>seekdir</code>, <code>telldir</code>, <code>dirfd</code>, and
<code>closedir</code>, to support <code>fdopendir</code></td>
<td>OSX10.9</td>
</tr>
<tr>
Expand Down
51 changes: 50 additions & 1 deletion include/dirent.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

/*
* Copyright (c) 2019
* Copyright (c) 2023 raf <raf@raf.org>, Tavian Barnes <tavianator@tavianator.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
Expand All @@ -27,6 +28,14 @@
/* fdopendir */
#if __MP_LEGACY_SUPPORT_FDOPENDIR__

/* Wrapper struct for DIR */
typedef struct __MP_LEGACY_SUPPORT_DIR __MP_LEGACY_SUPPORT_DIR;
struct __MP_LEGACY_SUPPORT_DIR {
DIR *__mpls_dir;
int __mpls_dirfd;
};
#define DIR __MP_LEGACY_SUPPORT_DIR

__MP__BEGIN_DECLS

#ifndef __DARWIN_ALIAS_I
Expand All @@ -35,7 +44,47 @@ extern DIR *fdopendir(int fd) __DARWIN_ALIAS(fdopendir);
extern DIR *fdopendir(int fd) __DARWIN_ALIAS_I(fdopendir);
#endif

__MP__END_DECLS
/* Wrapper functions/macros to support fdopendir */
extern DIR *__mpls_opendir(const char *name);
extern int __mpls_closedir(DIR *dir);
extern int __mpls_dirfd(DIR *dir);

#define opendir(name) __mpls_opendir(name)
#define closedir(dir) __mpls_closedir(dir)

#ifndef __MP_LEGACY_SUPPORT_NO_DIRFD_MACRO
#undef dirfd
#define dirfd(dir) __mpls_dirfd(dir)
#endif

static inline struct dirent *__mpls_readdir(DIR *dir) {
return readdir(dir->__mpls_dir);
}

static inline int __mpls_readdir_r(DIR *dir, struct dirent *entry, struct dirent **result) {
return readdir_r(dir->__mpls_dir, entry, result);
}

static inline void __mpls_rewinddir(DIR *dir) {
rewinddir(dir->__mpls_dir);
}

static inline void __mpls_seekdir(DIR *dir, long loc) {
seekdir(dir->__mpls_dir, loc);
}

static inline long __mpls_telldir(DIR *dir) {
return telldir(dir->__mpls_dir);
}

#define readdir __mpls_readdir
#define readdir_r __mpls_readdir_r
#define rewinddir __mpls_rewinddir
#define seekdir __mpls_seekdir
#define telldir __mpls_telldir

__MP__END_DECLS

#endif /* __MP_LEGACY_SUPPORT_FDOPENDIR__ */

#endif /* _MACPORTS_DIRENT_H_ */
6 changes: 5 additions & 1 deletion src/best_fchdir.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2019
* Copyright (c) 2023 raf <raf@raf.org>
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
Expand Down Expand Up @@ -30,7 +31,10 @@ int best_fchdir(int dirfd)
return syscall(SYS___pthread_fchdir, dirfd);
#else
/* Tiger does not have kernel support for __pthread_fchdir, so we have to fall back to fchdir */
/* unless we can come up with a per-thread compatible implementation that works on Tiger */
/* unless we can come up with a per-thread compatible implementation that works on Tiger. */
/* Accept dirfd == -1 (which is meaningful for __pthread_fchdir) but do nothing with it. */
if (dirfd == -1)
return 0;
return syscall(SYS_fchdir, dirfd);
#endif
}
Expand Down
128 changes: 89 additions & 39 deletions src/fdopendir.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

/*-
/*
* Copyright (c) 2019
* Copyright (c) 2023 raf <raf@raf.org>, Tavian Barnes <tavianator@tavianator.com>
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
Expand All @@ -22,74 +23,123 @@

#include "common-priv.h"

#define __MP_LEGACY_SUPPORT_NO_DIRFD_MACRO

#include <stdlib.h>
#include <dirent.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#undef DIR
#undef opendir
#undef closedir


/*
* Implementation behavior largely follows these man page descriptions:
*
* https://www.freebsd.org/cgi/man.cgi?query=fdopendir&sektion=3
* https://linux.die.net/man/3/fdopendir
*
* On success, this function returns allocated memory that must be
* deallocated by __mpls_closedir() (see closedir() macro in <dirent.h>).
*/

DIR *fdopendir(int dirfd) {
DIR *dir;
struct stat st;
int oldCWD = -1;
__MP_LEGACY_SUPPORT_DIR *fdopendir(int dirfd) {

if (fstat(dirfd, &st) < 0)
return 0;
/* Check dirfd here (for macos-10.4, see _ATCALL() and best_fchdir()) */

if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
if (dirfd != AT_FDCWD && dirfd < 0) {
errno = EBADF;
return 0;
}

if (dirfd == AT_FDCWD) {
dir = opendir (".");
/* dirfd can be closed only upon success */
if (dir) PROTECT_ERRNO(close(dirfd));
return dir;
}
/* Open the supplied directory safely */

oldCWD = open(".", O_RDONLY);
if (oldCWD == -1)
DIR *dir = _ATCALL(dirfd, ".", NULL, opendir("."));
if (!dir)
return 0;

if(best_fchdir(dirfd) < 0) {
if (oldCWD != -1) PROTECT_ERRNO(close(oldCWD));
/* Wrap it and return it (with the supplied directory file descriptor) */

__MP_LEGACY_SUPPORT_DIR *mplsdir = malloc(sizeof(*mplsdir));
if (!mplsdir) {
(void)closedir(dir);
errno = ENOMEM;
return 0;
}

dir = opendir (".");
mplsdir->__mpls_dir = dir;
mplsdir->__mpls_dirfd = dirfd;

return mplsdir;
}

if (best_fchdir(oldCWD) < 0) {
if (dir) PROTECT_ERRNO(closedir(dir));
if (oldCWD != -1) PROTECT_ERRNO(close(oldCWD));
/*
* Wrapped version of opendir() for fdopendir() compatibility
*
* On success, this function returns allocated memory that must be
* deallocated by __mpls_closedir() (see closedir() macro in <dirent.h>).
*/

__MP_LEGACY_SUPPORT_DIR *__mpls_opendir(const char *name) {

DIR *dir = opendir(name);
if (!dir)
return 0;

__MP_LEGACY_SUPPORT_DIR *mplsdir = malloc(sizeof(*mplsdir));
if (!mplsdir) {
(void)closedir(dir);
errno = ENOMEM;
return 0;
}

if (oldCWD != -1)
PROTECT_ERRNO(close(oldCWD));
mplsdir->__mpls_dir = dir;
mplsdir->__mpls_dirfd = -1;

return mplsdir;
}

/*
* Wrapped version of closedir() for fdopendir() compatibility (see
* closedir() macro in <dirent.h>).
*
* This function deallocates memory that was allocated by fdopendir() or
* __mpls_opendir().
*/

int __mpls_closedir(__MP_LEGACY_SUPPORT_DIR *mplsdir) {

if (!mplsdir) {
errno = EBADF;
return -1;
}

int rc = closedir(mplsdir->__mpls_dir);

if (mplsdir->__mpls_dirfd != AT_FDCWD && mplsdir->__mpls_dirfd != -1)
PROTECT_ERRNO(close(mplsdir->__mpls_dirfd));

free(mplsdir);

return rc;
}

/*
* Wrapped version of dirfd() for fdopendir() compatibility because dirfd()
* itself is already a macro (see dirfd() macro in <dirent.h>).
*/

int __mpls_dirfd(__MP_LEGACY_SUPPORT_DIR *mplsdir) {

/* Return the supplied directory file descriptor if there was one */

/*
* FIXME -- this recently added bit makes the fdopendir tests fail on Tiger.
* check the whole commit where it was added to make sure it is
* doing the proper thing on all systems. Probably need more extensive tests
* to execise the whole system more aggressively.
*/
if (mplsdir->__mpls_dirfd != AT_FDCWD && mplsdir->__mpls_dirfd != -1)
return mplsdir->__mpls_dirfd;

#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
/* dirfd can be closed only upon success */
if (dir && dirfd != -1) PROTECT_ERRNO(close(dirfd));
#endif
/* Otherwise call the underlying dirfd() macro */

return dir;
return dirfd(mplsdir->__mpls_dir);
}

#endif /* __MP_LEGACY_SUPPORT_FDOPENDIR__ */
26 changes: 26 additions & 0 deletions test/test_fdopendir.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

/*
* Copyright (c) 2019
* Copyright (c) 2023 raf <raf@raf.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
Expand All @@ -15,9 +16,11 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
Expand Down Expand Up @@ -55,6 +58,29 @@ int main() {
}

close(dfd);

/* Try to use fdopendir with stdin - Should fail with ENOTDIR */

if ((dir = fdopendir(STDIN_FILENO))) {
fprintf(stderr, "fdopendir(stdin) should have failed\n");
(void)closedir(dir);
return 1;
} else if (errno != ENOTDIR) {
perror("fdopendir(stdin) should have failed with ENOTDIR");
return 1;
}

/* Try to use fdopendir with -1 - Should fail with EBADF */

if ((dir = fdopendir(-1))) {
fprintf(stderr, "fdopendir(-1) should have failed\n");
(void)closedir(dir);
return 1;
} else if (errno != EBADF) {
perror("fdopendir(-1) should have failed with EBADF");
return 1;
}

return 0;
}

4 changes: 2 additions & 2 deletions test/test_fmemopen.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ test_autoalloc()
* Let fmemopen allocate the buffer.
*/

char str[] = "A quick test";
/* char str[] = "A quick test"; */
FILE *fp;
long pos;
size_t nofw, nofr, i;
size_t nofw, i;
int rc;

/* Open a FILE * using fmemopen. */
Expand Down

1 comment on commit 8889f27

@barracuda156
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raforg @kencu @macportsraf We got some issue with seekdir: vectorgraphics/asymptote#386 (comment)
asymptote uses legacy-support, and this failure only happens with the latest legacy-support-devel. See the comment from upstream there.

Please sign in to comment.