Fix tmpfile(3).

This could be better, but at least now it works.

Change-Id: I88b7cf3f7ce8e5fa0b3fe678b7d1679a68ffffc9
This commit is contained in:
Elliott Hughes
2012-09-24 17:55:15 -07:00
parent 6e0e03c38b
commit 91875dcd6e
4 changed files with 119 additions and 38 deletions

View File

@@ -123,7 +123,6 @@ libc_common_src_files := \
stdio/sscanf.c \
stdio/stdio.c \
stdio/tempnam.c \
stdio/tmpfile.c \
stdio/tmpnam.c \
stdio/ungetc.c \
stdio/vasprintf.c \
@@ -296,6 +295,7 @@ libc_common_src_files := \
bionic/tdestroy.c \
bionic/time64.c \
bionic/thread_atexit.c \
bionic/tmpfile.cpp \
bionic/utime.c \
bionic/utmp.c \
netbsd/gethnamaddr.c \

View File

@@ -1,4 +1,3 @@
/* $OpenBSD: tmpfile.c,v 1.10 2005/10/10 12:00:52 espie Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -31,51 +30,84 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
FILE *
tmpfile(void)
{
sigset_t set, oset;
FILE *fp;
int fd, sverrno;
#define TRAILER "tmp.XXXXXXXXXX"
char buf[sizeof(_PATH_TMP) + sizeof(TRAILER)];
class ScopedSignalBlocker {
public:
ScopedSignalBlocker() {
sigset_t set;
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &old_set_);
}
(void)memcpy(buf, _PATH_TMP, sizeof(_PATH_TMP) - 1);
(void)memcpy(buf + sizeof(_PATH_TMP) - 1, TRAILER, sizeof(TRAILER));
~ScopedSignalBlocker() {
sigprocmask(SIG_SETMASK, &old_set_, NULL);
}
sigfillset(&set);
(void)sigprocmask(SIG_BLOCK, &set, &oset);
private:
sigset_t old_set_;
};
fd = mkstemp(buf);
if (fd != -1) {
mode_t u;
static FILE* __tmpfile_dir(const char* tmp_dir) {
char buf[PATH_MAX];
int path_length = snprintf(buf, sizeof(buf), "%s/tmp.XXXXXXXXXX", tmp_dir);
if (path_length >= sizeof(buf)) {
return NULL;
}
(void)unlink(buf);
u = umask(0);
(void)umask(u);
(void)fchmod(fd, 0666 & ~u);
}
int fd;
{
ScopedSignalBlocker ssb;
fd = mkstemp(buf);
if (fd == -1) {
return NULL;
}
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
// Unlink the file now so that it's removed when closed.
unlink(buf);
if (fd == -1)
return (NULL);
// Can we still use the file now it's unlinked?
// File systems without hard link support won't have the usual Unix semantics.
struct stat sb;
int rc = fstat(fd, &sb);
if (rc == -1) {
int old_errno = errno;
close(fd);
errno = old_errno;
return NULL;
}
}
if ((fp = fdopen(fd, "w+")) == NULL) {
sverrno = errno;
(void)close(fd);
errno = sverrno;
return (NULL);
}
return (fp);
// Turn the file descriptor into a FILE*.
FILE* fp = fdopen(fd, "w+");
if (fp != NULL) {
return fp;
}
// Failure. Clean up. We already unlinked, so we just need to close.
int old_errno = errno;
close(fd);
errno = old_errno;
return NULL;
}
FILE* tmpfile() {
// TODO: get this app's temporary directory from the framework ("/data/data/app/cache").
// $EXTERNAL_STORAGE turns out not to be very useful because it doesn't support hard links.
// This means we can't do the usual trick of calling unlink before handing the file back.
FILE* fp = __tmpfile_dir("/data/local/tmp");
if (fp == NULL) {
// P_tmpdir is "/tmp/", but POSIX explicitly says that tmpdir(3) should try P_tmpdir before
// giving up. This is potentially useful for bionic on the host anyway.
fp = __tmpfile_dir(P_tmpdir);
}
return fp;
}