Add mkostemp() and mkostemps().

These are like mkstemp() and mkstemps() but allow passing open(2) flags like
O_CLOEXEC.
This commit is contained in:
Jilles Tjoelker 2013-08-09 17:24:23 +00:00
parent 5d82a21469
commit 65ba8dff5f
7 changed files with 275 additions and 24 deletions

View File

@ -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 *,

View File

@ -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 \

View File

@ -158,6 +158,8 @@ FBSD_1.3 {
fmemopen;
open_memstream;
open_wmemstream;
mkostemp;
mkostemps;
};
FBSDprivate_1.0 {

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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);
}