diff --git a/libc/Android.mk b/libc/Android.mk index a78196700..45dadd26b 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -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 \ @@ -297,6 +296,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 \ diff --git a/libc/stdio/tmpfile.c b/libc/bionic/tmpfile.cpp similarity index 50% rename from libc/stdio/tmpfile.c rename to libc/bionic/tmpfile.cpp index 39f7e929a..ad58a9275 100644 --- a/libc/stdio/tmpfile.c +++ b/libc/bionic/tmpfile.cpp @@ -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 -#include -#include -#include #include +#include #include #include -#include -#include +#include +#include +#include -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; } diff --git a/tests/Android.mk b/tests/Android.mk index be47585ab..38f8ceb1d 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -22,6 +22,7 @@ test_src_files = \ getcwd_test.cpp \ pthread_test.cpp \ regex_test.cpp \ + stdio_test.cpp \ string_test.cpp \ stubs_test.cpp \ diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp new file mode 100644 index 000000000..72af7962e --- /dev/null +++ b/tests/stdio_test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +TEST(stdio, tmpfile_fileno_fprintf_rewind_fgets) { + FILE* fp = tmpfile(); + ASSERT_TRUE(fp != NULL); + + int fd = fileno(fp); + ASSERT_NE(fd, -1); + + struct stat sb; + int rc = fstat(fd, &sb); + ASSERT_NE(rc, -1); + ASSERT_EQ(sb.st_mode & 0777, 0600U); + + rc = fprintf(fp, "hello\n"); + ASSERT_EQ(rc, 6); + + rewind(fp); + + char buf[16]; + char* s = fgets(buf, sizeof(buf), fp); + ASSERT_TRUE(s != NULL); + ASSERT_STREQ("hello\n", s); + + fclose(fp); +}