diff --git a/include/stdlib.h b/include/stdlib.h index 1902950ced0b..93cf122b92e5 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -282,6 +282,8 @@ const char * int heapsort(void *, size_t, size_t, int (*)(const void *, const void *)); int l64a_r(long, char *, int); int mergesort(void *, size_t, size_t, int (*)(const void *, const void *)); +int mkostemp(char *, int); +int mkostemps(char *, int, int); void qsort_r(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *)); int radixsort(const unsigned char **, int, const unsigned char *, diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index 0062d3dd8dd8..419d03194b76 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -60,7 +60,8 @@ MLINKS+=getc.3 fgetc.3 getc.3 getc_unlocked.3 getc.3 getchar.3 \ getc.3 getchar_unlocked.3 getc.3 getw.3 MLINKS+=getline.3 getdelim.3 MLINKS+=getwc.3 fgetwc.3 getwc.3 getwchar.3 -MLINKS+=mktemp.3 mkdtemp.3 mktemp.3 mkstemp.3 mktemp.3 mkstemps.3 +MLINKS+=mktemp.3 mkdtemp.3 mktemp.3 mkstemp.3 mktemp.3 mkstemps.3 \ + mktemp.3 mkostemp.3 mktemp.3 mkostemps.3 MLINKS+=open_memstream.3 open_wmemstream.3 MLINKS+=printf.3 asprintf.3 printf.3 dprintf.3 printf.3 fprintf.3 \ printf.3 snprintf.3 printf.3 sprintf.3 \ diff --git a/lib/libc/stdio/Symbol.map b/lib/libc/stdio/Symbol.map index 538b29aac4cb..d2a8c92b2411 100644 --- a/lib/libc/stdio/Symbol.map +++ b/lib/libc/stdio/Symbol.map @@ -158,6 +158,8 @@ FBSD_1.3 { fmemopen; open_memstream; open_wmemstream; + mkostemp; + mkostemps; }; FBSDprivate_1.0 { diff --git a/lib/libc/stdio/mktemp.3 b/lib/libc/stdio/mktemp.3 index 6a46cf0f6e17..816a6b6fcd08 100644 --- a/lib/libc/stdio/mktemp.3 +++ b/lib/libc/stdio/mktemp.3 @@ -28,7 +28,7 @@ .\" @(#)mktemp.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd July 5, 2013 +.Dd August 8, 2013 .Dt MKTEMP 3 .Os .Sh NAME @@ -42,6 +42,10 @@ .Fn mktemp "char *template" .Ft int .Fn mkstemp "char *template" +.Ft int +.Fn mkostemp "char *template" "int oflags" +.Ft int +.Fn mkostemps "char *template" "int suffixlen" "int oflags" .Ft char * .Fn mkdtemp "char *template" .In unistd.h @@ -85,16 +89,41 @@ This avoids the race between testing for a file's existence and opening it for use. .Pp The +.Fn mkostemp +function +is like +.Fn mkstemp +but allows specifying additional +.Xr open 2 +flags (defined in +.In fcntl.h ) . +The permitted flags are +.Dv O_APPEND , +.Dv O_DIRECT , +.Dv O_SHLOCK , +.Dv O_EXLOCK , +.Dv O_SYNC +and +.Dv O_CLOEXEC . +.Pp +The .Fn mkstemps -function acts the same as -.Fn mkstemp , -except it permits a suffix to exist in the template. +and +.Fn mkostemps +functions act the same as +.Fn mkstemp +and +.Fn mkostemp +respectively, +except they permit a suffix to exist in the template. The template should be of the form .Pa /tmp/tmpXXXXXXsuffix . The .Fn mkstemps +and +.Fn mkostemps function -is told the length of the suffix string. +are told the length of the suffix string. .Pp The .Fn mkdtemp @@ -110,9 +139,11 @@ functions return a pointer to the template on success and .Dv NULL on failure. The -.Fn mkstemp -and +.Fn mkstemp , +.Fn mkostemp .Fn mkstemps +and +.Fn mkostemps functions return \-1 if no suitable file could be created. If either call fails an error code is placed in the global variable @@ -120,7 +151,9 @@ If either call fails an error code is placed in the global variable .Sh ERRORS The .Fn mkstemp , -.Fn mkstemps +.Fn mkostemp , +.Fn mkstemps , +.Fn mkostemps and .Fn mkdtemp functions @@ -133,8 +166,25 @@ The pathname portion of the template is not an existing directory. .El .Pp The +.Fn mkostemp +and +.Fn mkostemps +functions +may also set +.Va errno +to the following value: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa oflags +argument is invalid. +.El +.Pp +The .Fn mkstemp , -.Fn mkstemps +.Fn mkostemp , +.Fn mkstemps , +.Fn mkostemps and .Fn mkdtemp functions @@ -145,9 +195,11 @@ to any value specified by the function. .Pp The -.Fn mkstemp -and +.Fn mkstemp , +.Fn mkostemp , .Fn mkstemps +and +.Fn mkostemps functions may also set .Va errno @@ -209,8 +261,11 @@ function is expected to conform to and is not specified by .St -p1003.1-2008 . The +.Fn mkostemp , .Fn mkstemps -function does not conform to any standard. +and +.Fn mkostemps +functions do not conform to any standard. .Sh HISTORY A .Fn mktemp @@ -232,6 +287,12 @@ function first appeared in .Ox 2.4 , and later in .Fx 3.4 . +The +.Fn mkostemp +and +.Fn mkostemps +functions appeared in +.Fx 10.0 . .Sh BUGS This family of functions produces filenames which can be guessed, though the risk is minimized when large numbers of @@ -248,6 +309,8 @@ and opening it for use particularly dangerous from a security perspective. Whenever it is possible, .Fn mkstemp +or +.Fn mkostemp should be used instead, since it does not have the race condition. If .Fn mkstemp diff --git a/lib/libc/stdio/mktemp.c b/lib/libc/stdio/mktemp.c index 58783ddefa85..6fc552a18b2f 100644 --- a/lib/libc/stdio/mktemp.c +++ b/lib/libc/stdio/mktemp.c @@ -47,17 +47,33 @@ __FBSDID("$FreeBSD$"); char *_mktemp(char *); -static int _gettemp(char *, int *, int, int); +static int _gettemp(char *, int *, int, int, int); static const unsigned char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +int +mkostemps(char *path, int slen, int oflags) +{ + int fd; + + return (_gettemp(path, &fd, 0, slen, oflags) ? fd : -1); +} + int mkstemps(char *path, int slen) { int fd; - return (_gettemp(path, &fd, 0, slen) ? fd : -1); + return (_gettemp(path, &fd, 0, slen, 0) ? fd : -1); +} + +int +mkostemp(char *path, int oflags) +{ + int fd; + + return (_gettemp(path, &fd, 0, 0, oflags) ? fd : -1); } int @@ -65,19 +81,19 @@ mkstemp(char *path) { int fd; - return (_gettemp(path, &fd, 0, 0) ? fd : -1); + return (_gettemp(path, &fd, 0, 0, 0) ? fd : -1); } char * mkdtemp(char *path) { - return (_gettemp(path, (int *)NULL, 1, 0) ? path : (char *)NULL); + return (_gettemp(path, (int *)NULL, 1, 0, 0) ? path : (char *)NULL); } char * _mktemp(char *path) { - return (_gettemp(path, (int *)NULL, 0, 0) ? path : (char *)NULL); + return (_gettemp(path, (int *)NULL, 0, 0, 0) ? path : (char *)NULL); } __warn_references(mktemp, @@ -90,7 +106,7 @@ mktemp(char *path) } static int -_gettemp(char *path, int *doopen, int domkdir, int slen) +_gettemp(char *path, int *doopen, int domkdir, int slen, int oflags) { char *start, *trv, *suffp, *carryp; char *pad; @@ -99,7 +115,9 @@ _gettemp(char *path, int *doopen, int domkdir, int slen) uint32_t rand; char carrybuf[MAXPATHLEN]; - if ((doopen != NULL && domkdir) || slen < 0) { + if ((doopen != NULL && domkdir) || slen < 0 || + (oflags & ~(O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC | + O_CLOEXEC)) != 0) { errno = EINVAL; return (0); } @@ -151,7 +169,8 @@ _gettemp(char *path, int *doopen, int domkdir, int slen) for (;;) { if (doopen) { if ((*doopen = - _open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) + _open(path, O_CREAT|O_EXCL|O_RDWR|oflags, 0600)) >= + 0) return (1); if (errno != EEXIST) return (0); diff --git a/tools/regression/lib/libc/stdio/Makefile b/tools/regression/lib/libc/stdio/Makefile index f496f73f86e6..4b252b91c57d 100644 --- a/tools/regression/lib/libc/stdio/Makefile +++ b/tools/regression/lib/libc/stdio/Makefile @@ -1,8 +1,8 @@ # $FreeBSD$ -TESTS= test-fmemopen test-getdelim test-open_memstream test-open_wmemstream \ - test-perror test-print-positional test-printbasic test-printfloat \ - test-scanfloat +TESTS= test-fmemopen test-getdelim test-mkostemp test-open_memstream \ + test-open_wmemstream test-perror test-print-positional test-printbasic \ + test-printfloat test-scanfloat CFLAGS+= -lm .PHONY: tests diff --git a/tools/regression/lib/libc/stdio/test-mkostemp.c b/tools/regression/lib/libc/stdio/test-mkostemp.c new file mode 100644 index 000000000000..5f67c72be4a0 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-mkostemp.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Test program for mkostemp(). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char template[] = _PATH_TMP "mkostemp.XXXXXXXX"; +static int testnum; + +#define MISCFLAGS (O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC) + +static void +test_one(int oflags) +{ + char tmpf[sizeof(template)]; + struct stat st1, st2; + int fd; + + memcpy(tmpf, template, sizeof(tmpf)); + fd = mkostemp(tmpf, oflags); + if (fd < 0) { + printf("not ok %d - oflags=%#x " + "mkostemp() reported failure: %s\n", + testnum++, oflags, strerror(errno)); + return; + } + if (memcmp(tmpf, template, sizeof(tmpf) - 8 - 1) != 0) { + printf("not ok %d - oflags=%#x " + "returned pathname does not match template: %s\n", + testnum++, oflags, tmpf); + return; + } + do { + if (fcntl(fd, F_GETFD) != + (oflags & O_CLOEXEC ? FD_CLOEXEC : 0)) { + printf("not ok %d - oflags=%#x " + "close-on-exec flag incorrect\n", + testnum++, oflags); + break; + } + if ((fcntl(fd, F_GETFL) & MISCFLAGS) != (oflags & MISCFLAGS)) { + printf("not ok %d - oflags=%#x " + "open flags incorrect\n", + testnum++, oflags); + break; + } + if (stat(tmpf, &st1) == -1) { + printf("not ok %d - oflags=%#x " + "cannot stat returned pathname %s: %s\n", + testnum++, oflags, tmpf, strerror(errno)); + break; + } + if (fstat(fd, &st2) == -1) { + printf("not ok %d - oflags=%#x " + "cannot fstat returned fd %d: %s\n", + testnum++, oflags, fd, strerror(errno)); + break; + } + if (!S_ISREG(st1.st_mode) || (st1.st_mode & 0777) != 0600 || + st1.st_nlink != 1 || st1.st_size != 0) { + printf("not ok %d - oflags=%#x " + "named file attributes incorrect\n", + testnum++, oflags); + break; + } + if (!S_ISREG(st2.st_mode) || (st2.st_mode & 0777) != 0600 || + st2.st_nlink != 1 || st2.st_size != 0) { + printf("not ok %d - oflags=%#x " + "opened file attributes incorrect\n", + testnum++, oflags); + break; + } + if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) { + printf("not ok %d - oflags=%#x " + "named and opened file do not match\n", + testnum++, oflags); + break; + } + (void)unlink(tmpf); + if (fstat(fd, &st2) == -1) + printf("not ok %d - oflags=%#x " + "cannot fstat returned fd %d again: %s\n", + testnum++, oflags, fd, strerror(errno)); + else if (st2.st_nlink != 0) + printf("not ok %d - oflags=%#x " + "st_nlink is not 0 after unlink\n", + testnum++, oflags); + else + printf("ok %d - oflags=%#x\n", testnum++, oflags); + (void)close(fd); + return; + } while (0); + (void)close(fd); + (void)unlink(tmpf); +} + +static void +test_badflags(void) +{ + char tmpf[sizeof(template)]; + + memcpy(tmpf, template, sizeof(tmpf)); + if (mkostemp(tmpf, O_CREAT) == -1) + printf("ok %d - mkostemp(O_CREAT) correctly failed\n", + testnum++); + else + printf("not ok %d - mkostemp(O_CREAT) wrongly succeeded\n", + testnum++); +} + +int +main(int argc, char *argv[]) +{ + int i; + const char *e; + + printf("1..5\n"); + testnum = 1; + + test_one(0); + test_one(O_CLOEXEC); + test_one(O_APPEND); + test_one(O_APPEND | O_CLOEXEC); + test_badflags(); + + return (0); +}