to avoid file descriptor leaks to the child process.
- Pass O_CLOEXEC to open(2) system call if it is defined.
- Use fcntl(2) and F_DUPFD_CLOEXEC instead of dup(2)
if F_DUPFD_CLOEXEC is defined.
- Set FD_CLOEXEC with fcntl(2) if a file descriptor does not have.
CHECK_FUNCTION_EXISTS_GLIBC(chown HAVE_CHOWN)
CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT)
CHECK_FUNCTION_EXISTS_GLIBC(ctime_r HAVE_CTIME_R)
+CHECK_FUNCTION_EXISTS_GLIBC(dirfd HAVE_DIRFD)
CHECK_FUNCTION_EXISTS_GLIBC(fchdir HAVE_FCHDIR)
CHECK_FUNCTION_EXISTS_GLIBC(fchflags HAVE_FCHFLAGS)
CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD)
*/
#cmakedefine HAVE_DIRENT_H 1
+/* Define to 1 if you have the `dirfd' function. */
+#cmakedefine HAVE_DIRFD 1
+
/* Define to 1 if you have the <dlfcn.h> header file. */
#cmakedefine HAVE_DLFCN_H 1
# To avoid necessity for including windows.h or special forward declaration
# workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *'
AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *])
-AC_CHECK_FUNCS([chflags chown chroot ctime_r])
+AC_CHECK_FUNCS([chflags chown chroot ctime_r dirfd])
AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fork])
AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate])
AC_CHECK_FUNCS([futimens futimes futimesat])
void __archive_errx(int retvalue, const char *msg) __LA_DEAD;
+void __archive_ensure_cloexec_flag(int fd);
int __archive_mktemp(const char *tmpdir);
int __archive_clean(struct archive *);
#include "archive_private.h"
#include "archive_read_disk_private.h"
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
/*
* Linux and FreeBSD plug this obvious hole in POSIX.1e in
* different ways.
if (fd < 0) {
if (a->tree != NULL)
fd = a->open_on_current_dir(a->tree, path,
- O_RDONLY | O_NONBLOCK);
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
else
- fd = open(path, O_RDONLY | O_NONBLOCK);
+ fd = open(path, O_RDONLY | O_NONBLOCK |
+ O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
}
if (fd >= 0) {
int stflags;
ret = ARCHIVE_WARN;
goto cleanup;
}
- tempfd = open(tempfile, O_RDONLY);
+ tempfd = open(tempfile, O_RDONLY | O_CLOEXEC);
if (tempfd < 0) {
archive_set_error(&a->archive, errno,
"Could not open extended attribute file");
ret = ARCHIVE_WARN;
goto cleanup;
}
+ __archive_ensure_cloexec_flag(tempfd);
if (fstat(tempfd, ©file_stat)) {
archive_set_error(&a->archive, errno,
"Could not check size of extended attributes");
path = archive_entry_pathname(entry);
if (a->tree != NULL)
*fd = a->open_on_current_dir(a->tree, path,
- O_RDONLY | O_NONBLOCK);
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
else
- *fd = open(path, O_RDONLY | O_NONBLOCK);
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (*fd < 0) {
archive_set_error(&a->archive, errno,
"Can't open `%s'", path);
return (ARCHIVE_FAILED);
}
+ __archive_ensure_cloexec_flag(*fd);
}
/* Initialize buffer to avoid the error valgrind complains about. */
if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
return (ARCHIVE_OK);
- *fd = open(path, O_RDONLY | O_NONBLOCK);
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (*fd < 0) {
archive_set_error(&a->archive, errno,
"Can't open `%s'", path);
return (ARCHIVE_FAILED);
}
+ __archive_ensure_cloexec_flag(*fd);
initial_off = 0;
}
#ifndef O_BINARY
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
/*-
* This is a new directory-walking system that addresses a number
static int close_and_restore_time(int fd, struct tree *,
struct restore_time *);
static int open_on_current_dir(struct tree *, const char *, int);
+static int tree_dup(int);
static struct archive_vtable *
* Open the current file.
*/
if (t->entry_fd < 0) {
- int flags = O_RDONLY | O_BINARY;
+ int flags = O_RDONLY | O_BINARY | O_CLOEXEC;
/*
* Eliminate or reduce cache effects if we can.
#endif
t->entry_fd = open_on_current_dir(t,
tree_current_access_path(t), flags);
+ __archive_ensure_cloexec_flag(t->entry_fd);
#if defined(O_NOATIME)
/*
* When we did open the file with O_NOATIME flag,
int stflags;
t->entry_fd = open_on_current_dir(t,
- tree_current_access_path(t), O_RDONLY | O_NONBLOCK);
+ tree_current_access_path(t),
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(t->entry_fd);
if (t->entry_fd >= 0) {
r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS,
&stflags);
* where current is.
*/
int fd = openat(tree_current_dir_fd(t),
- tree_current_access_path(t), O_RDONLY);
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
if (fd < 0) {
archive_set_error(&a->archive, errno,
"openat failed");
* where current is.
*/
int fd = openat(tree_current_dir_fd(t),
- tree_current_access_path(t), O_RDONLY);
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
if (fd < 0) {
archive_set_error(&a->archive, errno,
"openat failed");
* where current is.
*/
int fd = openat(tree_current_dir_fd(t),
- tree_current_access_path(t), O_RDONLY);
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
if (fd < 0) {
archive_set_error(&a->archive, errno,
"openat failed");
#endif
}
+static int
+tree_dup(int fd)
+{
+ int new_fd;
+#ifdef F_DUPFD_CLOEXEC
+ static volatile int can_dupfd_cloexec = 1;
+
+ if (can_dupfd_cloexec) {
+ new_fd = fcntl(fd, F_DUPFD_CLOEXEC);
+ if (new_fd != -1)
+ return (new_fd);
+ /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC,
+ * but it cannot be used. So we have to try dup(). */
+ /* We won't try F_DUPFD_CLOEXEC. */
+ can_dupfd_cloexec = 0;
+ }
+#endif /* F_DUPFD_CLOEXEC */
+ new_fd = dup(fd);
+ __archive_ensure_cloexec_flag(new_fd);
+ return (new_fd);
+}
+
/*
* Add a directory path to the current stack.
*/
tree_push(t, path, 0, 0, 0, NULL);
t->stack->flags = needsFirstVisit;
t->maxOpenCount = t->openCount = 1;
- t->initial_dir_fd = open(".", O_RDONLY);
- t->working_dir_fd = dup(t->initial_dir_fd);
+ t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(t->initial_dir_fd);
+ t->working_dir_fd = tree_dup(t->initial_dir_fd);
return (t);
}
int flag, new_fd, r = 0;
t->dirname_length = archive_strlen(&t->path);
- flag = O_RDONLY;
+ flag = O_RDONLY | O_CLOEXEC;
#if defined(O_DIRECTORY)
flag |= O_DIRECTORY;
#endif
new_fd = open_on_current_dir(t, t->stack->name.s, flag);
+ __archive_ensure_cloexec_flag(new_fd);
if (new_fd < 0) {
t->tree_errno = errno;
r = TREE_ERROR_DIR;
prev_dir_fd = t->working_dir_fd;
if (te->flags & isDirLink)
new_fd = te->symlink_parent_fd;
- else
- new_fd = open_on_current_dir(t, "..", O_RDONLY);
+ else {
+ new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(new_fd);
+ }
if (new_fd < 0) {
t->tree_errno = errno;
r = TREE_ERROR_FATAL;
#endif
#if defined(HAVE_FDOPENDIR)
- if ((t->d = fdopendir(dup(t->working_dir_fd))) == NULL) {
-#else
- if (tree_enter_working_dir(t) != 0 ||
- (t->d = opendir(".")) == NULL) {
+ t->d = fdopendir(tree_dup(t->working_dir_fd));
+#else /* HAVE_FDOPENDIR */
+ if (tree_enter_working_dir(t) == 0) {
+ t->d = opendir(".");
+#if HAVE_DIRFD || defined(dirfd)
+ __archive_ensure_cloexec_flag(dirfd(t->d));
#endif
+ }
+#endif /* HAVE_FDOPENDIR */
+ if (t->d == NULL) {
r = tree_ascend(t); /* Undo "chdir" */
tree_pop(t);
t->tree_errno = errno;
#endif
#include "archive.h"
+#include "archive_private.h"
#include "archive_string.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
struct read_file_data {
int fd;
filename = "";
} else if (mine->filename_type == FNT_MBS) {
filename = mine->filename.m;
- fd = open(filename, O_RDONLY | O_BINARY);
+ fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
if (fd < 0) {
archive_set_error(a, errno,
"Failed to open '%s'", filename);
#ifndef O_BINARY
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
#define MTREE_HAS_DEVICE 0x0001
#define MTREE_HAS_FFLAGS 0x0002
if (archive_entry_filetype(entry) == AE_IFREG ||
archive_entry_filetype(entry) == AE_IFDIR) {
- mtree->fd = open(path, O_RDONLY | O_BINARY);
+ mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(mtree->fd);
if (mtree->fd == -1 &&
(errno != ENOENT ||
archive_strlen(&mtree->contents_name) > 0)) {
/*-
- * Copyright (c) 2009,2010 Michihiro NAKAJIMA
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
#include "archive_private.h"
#include "archive_string.h"
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
/* Generic initialization of 'struct archive' objects. */
int
__archive_clean(struct archive *a)
fd = mkstemp(temp_name.s);
if (fd < 0)
goto exit_tmpfile;
+ __archive_ensure_cloexec_flag(fd);
unlink(temp_name.s);
exit_tmpfile:
archive_string_free(&temp_name);
archive_strcat(&temp_name, "XXXXXXXXXX");
ep = temp_name.s + archive_strlen(&temp_name);
- fd = open("/dev/random", O_RDONLY);
+ fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
if (fd < 0)
seed = time(NULL);
else {
p = tp;
while (p < ep)
*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
- fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600);
+ fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
+ 0600);
} while (fd < 0 && errno == EEXIST);
if (fd < 0)
goto exit_tmpfile;
+ __archive_ensure_cloexec_flag(fd);
unlink(temp_name.s);
exit_tmpfile:
archive_string_free(&temp_name);
#endif /* HAVE_MKSTEMP */
#endif /* !_WIN32 || __CYGWIN__ */
+
+/*
+ * Set FD_CLOEXEC flag to a file descriptor if it is not set.
+ * We have to set the flag if the platform does not provide O_CLOEXEC
+ * or F_DUPFD_CLOEXEC flags.
+ *
+ * Note: This function is absolutely called after creating a new file
+ * descriptor even if the platform seemingly provides O_CLOEXEC or
+ * F_DUPFD_CLOEXEC macros because it is possible that the platform
+ * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
+ */
+void
+__archive_ensure_cloexec_flag(int fd)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ (void)fd; /* UNSED */
+#else
+ int flags;
+
+ if (fd >= 0) {
+ flags = fcntl(fd, F_GETFD);
+ if (flags != -1 && (flags & FD_CLOEXEC) == 0)
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+#endif
+}
#ifndef O_BINARY
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
struct fixup_entry {
struct fixup_entry *next;
return;
/* Try to record our starting dir. */
- a->restore_pwd = open(".", O_RDONLY | O_BINARY);
+ a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(a->restore_pwd);
if (a->restore_pwd < 0)
return;
a->todo = 0;
a->deferred = 0;
} else if (r == 0 && a->filesize > 0) {
- a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY);
+ a->fd = open(a->name,
+ O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(a->fd);
if (a->fd < 0)
r = errno;
}
/* FALLTHROUGH */
case AE_IFREG:
a->fd = open(a->name,
- O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode);
+ O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);
+ __archive_ensure_cloexec_flag(a->fd);
r = (a->fd < 0);
break;
case AE_IFCHR:
return (ARCHIVE_OK);
/* If we weren't given an fd, open it ourselves. */
- if (myfd < 0)
- myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY);
+ if (myfd < 0) {
+ myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(myfd);
+ }
if (myfd < 0)
return (ARCHIVE_OK);
#endif
#include "archive.h"
+#include "archive_private.h"
#include "archive_string.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
struct write_file_data {
int fd;
const char *mbs;
mine = (struct write_file_data *)client_data;
- flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;
/*
* Open the file.
return (ARCHIVE_FATAL);
}
mine->fd = open(mbs, flags, 0666);
+ __archive_ensure_cloexec_flag(mine->fd);
#endif
if (mine->fd < 0) {
if (mbs != NULL)