Implement tmpfile with O_TMPFILE (bug 21530)
Commit Message
If the kernel or the file system does not support O_TMPFILE fall back to
creating a temporary file based on tmpnam.
[BZ #21530]
* stdio-common/tmpfile.c (tmpfile) [O_TMPFILE]: Try opening an
unnamed file first.
---
stdio-common/tmpfile.c | 51 ++++++++++++++++++++++++++++++++++++--------------
1 file changed, 37 insertions(+), 14 deletions(-)
@@ -18,6 +18,8 @@
#include <fcntl.h>
#include <stdio.h>
+#include <string.h>
+#include <errno.h>
#include <unistd.h>
#include <iolibio.h>
@@ -27,30 +29,51 @@
#endif
-/* This returns a new stream opened on a temporary file (generated
- by tmpnam). The file is opened with mode "w+b" (binary read/write).
- If we couldn't generate a unique filename or the file couldn't
- be opened, NULL is returned. */
+/* This returns a new stream opened on an unnamed file (if supported) or a
+ temporary file (generated by tmpnam). The file is opened with mode
+ "w+b" (binary read/write). If we couldn't generate a unique filename
+ or the file couldn't be opened, NULL is returned. */
FILE *
tmpfile (void)
{
- char buf[FILENAME_MAX];
- int fd;
+ int fd = -1;
FILE *f;
-
- if (__path_search (buf, FILENAME_MAX, NULL, "tmpf", 0))
- return NULL;
int flags = 0;
#ifdef FLAGS
flags = FLAGS;
#endif
- fd = __gen_tempname (buf, 0, flags, __GT_FILE);
+
+#ifdef O_TMPFILE
+ fd = __open (P_tmpdir, O_RDWR | O_TMPFILE | O_EXCL | flags,
+ S_IRUSR | S_IWUSR);
+ if (fd < 0 && errno == ENOENT && strcmp (P_tmpdir, "/tmp") != 0)
+ fd = __open ("/tmp", O_RDWR | O_TMPFILE | O_EXCL | flags,
+ S_IRUSR | S_IWUSR);
if (fd < 0)
- return NULL;
+ {
+ if (errno != EISDIR && errno != EOPNOTSUPP)
+ return NULL;
- /* Note that this relies on the Unix semantics that
- a file is not really removed until it is closed. */
- (void) __unlink (buf);
+ /* Either O_TMPFILE is unknown to the kernel, or the file system
+ does not support it. */
+ }
+#endif
+ if (fd < 0)
+ {
+ char buf[FILENAME_MAX];
+
+ if (__path_search (buf, FILENAME_MAX, NULL, "tmpf", 0))
+ return NULL;
+ fd = __gen_tempname (buf, 0, flags, __GT_FILE);
+
+ if (fd >= 0)
+ /* Note that this relies on the Unix semantics that
+ a file is not really removed until it is closed. */
+ (void) __unlink (buf);
+ }
+
+ if (fd < 0)
+ return NULL;
if ((f = __fdopen (fd, "w+b")) == NULL)
__close (fd);