This is the mail archive of the
newlib@sourceware.org
mailing list for the newlib project.
Re: add mkstemps, mkdtemp
- From: Eric Blake <ebb9 at byu dot net>
- To: newlib at sources dot redhat dot com
- Date: Mon, 29 Jun 2009 06:51:17 -0600
- Subject: Re: add mkstemps, mkdtemp
- References: <4A46A005.7010408@byu.net>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
According to Eric Blake on 6/27/2009 4:41 PM:
> The ability to have an arbitrary file suffix on a temporary file is quite
> useful for programs that react to a file's extension; in fact, git wants
> to use mk[s]temps when available, even though no standards bodies have
> adopted it yet. According to google, mkstemps is available on several
> systems (sometimes by the more confusing name mktemps), but there is no
> consistency on whether it should be in <stdlib.h> or <unistd.h>. I went
> with <stdlib.h>, since that is where POSIX puts mkstemp. This also fixes
> the documentation (mktemp is NOT in <stdio.h>), but does not move the
> files to a more appropriate subdirectory.
Updated patch. Now that POSIX 2008 requires mkdtemp, we should also be
implementing that function at the same time. POSIX also requires mkdtemp
to fail with EINVAL if there are not six trailing 'X' (with mktemp, the
behavior with less than 6 'X' was implementation defined, but it is
easiest to define it to do the same as mkdtemp). mktemp is ELIX level 2,
but I'm guessing that mkdtemp and mkstemps should be ELIX level 4. Cygwin
doesn't use newlib's mktemp.c any more (the getpid() implementation is
puny, so cygwin replaced it with an arc4random() implementation). Also,
fixes a theoretical bug of moving into the suffix and calling isdigit on
an 8-bit signed char if you have all 36**6 file name candidates occupied
(in reality, I doubt anyone will ever have a file system that full).
2009-06-29 Eric Blake <ebb9@byu.net>
Add mkdtemp, mkstemps.
* libc/stdio/mktemp.c: Fix documentation.
(_gettemp): Add domkdir and length parameters. Check for
insufficient 'X' suffix. Drop cygwin-specific code.
(_mkstemp_r, _mktemp_r, mkstemp, mktemp): Adjust clients.
(_mkdtemp_r, _mkstemps_r, mkdtemp, mkstemps): New functions.
* libc/include/stdlib.h (_mkdtemp_r, _mkstemps_r, mkdtemp)
(mkstemps): Declare them.
- --
Don't work too hard, make some time for fun as well!
Eric Blake ebb9@byu.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iEYEARECAAYFAkpIuMUACgkQ84KuGfSFAYDecwCgmzKzJK5CxluB4SXpuXjyXw+Q
2ZAAn2AN+NwKMBTRPUzLS3O4AODaa8Wl
=TTxp
-----END PGP SIGNATURE-----
Index: libc/include/stdlib.h
===================================================================
RCS file: /cvs/src/src/newlib/libc/include/stdlib.h,v
retrieving revision 1.34
diff -u -p -r1.34 stdlib.h
--- libc/include/stdlib.h 24 Apr 2009 22:49:55 -0000 1.34
+++ libc/include/stdlib.h 29 Jun 2009 12:44:24 -0000
@@ -98,10 +98,14 @@ size_t _EXFUN(wcstombs,(char *, const wc
size_t _EXFUN(_wcstombs_r,(struct _reent *, char *, const wchar_t *, size_t, _mbstate_t *));
#ifndef __STRICT_ANSI__
#ifndef _REENT_ONLY
+char * _EXFUN(mkdtemp,(char *));
int _EXFUN(mkstemp,(char *));
+int _EXFUN(mkstemps,(char *, int));
char * _EXFUN(mktemp,(char *) _ATTRIBUTE ((warning ("the use of `mktemp' is dangerous; use `mkstemp' instead"))));
#endif
+char * _EXFUN(_mkdtemp_r, (struct _reent *, char *));
int _EXFUN(_mkstemp_r, (struct _reent *, char *));
+int _EXFUN(_mkstemps_r, (struct _reent *, char *, int));
char * _EXFUN(_mktemp_r, (struct _reent *, char *) _ATTRIBUTE ((warning ("the use of `mktemp' is dangerous; use `mkstemp' instead"))));
#endif
_VOID _EXFUN(qsort,(_PTR __base, size_t __nmemb, size_t __size, int(*_compar)(const _PTR, const _PTR)));
Index: libc/stdio/mktemp.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/stdio/mktemp.c,v
retrieving revision 1.10
diff -u -p -r1.10 mktemp.c
--- libc/stdio/mktemp.c 14 Mar 2009 12:14:08 -0000 1.10
+++ libc/stdio/mktemp.c 29 Jun 2009 12:44:24 -0000
@@ -23,27 +23,40 @@
/*
FUNCTION
-<<mktemp>>, <<mkstemp>>---generate unused file name
+<<mktemp>>, <<mkstemp>>, <<mkstemps>>---generate unused file name
+<<mkdtemp>>---generate unused directory
INDEX
mktemp
INDEX
+ mkdtemp
+INDEX
mkstemp
INDEX
+ mkstemps
+INDEX
_mktemp_r
INDEX
+ _mkdtemp_r
+INDEX
_mkstemp_r
+INDEX
+ _mkstemps_r
ANSI_SYNOPSIS
- #include <stdio.h>
+ #include <stdlib.h>
char *mktemp(char *<[path]>);
+ char *mkdtemp(char *<[path]>);
int mkstemp(char *<[path]>);
+ int mkstemps(char *<[path]>, int <[suffixlen]>);
char *_mktemp_r(struct _reent *<[reent]>, char *<[path]>);
+ char *_mkdtemp_r(struct _reent *<[reent]>, char *<[path]>);
int *_mkstemp_r(struct _reent *<[reent]>, char *<[path]>);
+ int *_mkstemps_r(struct _reent *<[reent]>, char *<[path]>, int <[len]>);
TRAD_SYNOPSIS
- #include <stdio.h>
+ #include <stdlib.h>
char *mktemp(<[path]>)
char *<[path]>;
@@ -59,20 +72,24 @@ TRAD_SYNOPSIS
char *<[path]>;
DESCRIPTION
-<<mktemp>> and <<mkstemp>> attempt to generate a file name that is not
-yet in use for any existing file. <<mkstemp>> creates the file and
-opens it for reading and writing; <<mktemp>> simply generates the file name.
+<<mktemp>>, <<mkstemp>>, and <<mkstemps>> attempt to generate a file name
+that is not yet in use for any existing file. <<mkstemp>> and <<mkstemps>>
+create the file and open it for reading and writing; <<mktemp>> simply
+generates the file name (making <<mktemp>> a security risk). <<mkdtemp>>
+attempts to create a directory instead of a file, with a permissions
+mask of 0700.
You supply a simple pattern for the generated file name, as the string
at <[path]>. The pattern should be a valid filename (including path
-information if you wish) ending with some number of `<<X>>'
+information if you wish) ending with at least six `<<X>>'
characters. The generated filename will match the leading part of the
name you supply, with the trailing `<<X>>' characters replaced by some
-combination of digits and letters.
+combination of digits and letters. With <<mkstemps>>, the `<<X>>'
+characters end <[suffixlen]> bytes before the end of the string.
-The alternate functions <<_mktemp_r>> and <<_mkstemp_r>> are reentrant
-versions. The extra argument <[reent]> is a pointer to a reentrancy
-structure.
+The alternate functions <<_mktemp_r>>, <<_mkdtemp_r>>, <<_mkstemp_r>>,
+and <<_mkstemps_r>> are reentrant versions. The extra argument <[reent]>
+is a pointer to a reentrancy structure.
RETURNS
<<mktemp>> returns the pointer <[path]> to the modified string
@@ -80,8 +97,11 @@ representing an unused filename, unless
the pattern you provided is not suitable for a filename; in that case,
it returns <<NULL>>.
-<<mkstemp>> returns a file descriptor to the newly created file,
-unless it could not generate an unused filename, or the pattern you
+<<mkdtemp>> returns the pointer <[path]> to the modified string if the
+directory was created, otherwise it returns <<NULL>>.
+
+<<mkstemp>> and <<mkstemps>> return a file descriptor to the newly created
+file, unless it could not generate an unused filename, or the pattern you
provided is not suitable for a filename; in that case, it returns
<<-1>>.
@@ -94,9 +114,11 @@ instead. It doesn't suffer the race con
PORTABILITY
ANSI C does not require either <<mktemp>> or <<mkstemp>>; the System
-V Interface Definition requires <<mktemp>> as of Issue 2.
+V Interface Definition requires <<mktemp>> as of Issue 2. POSIX 2001
+requires <<mkstemp>>, and POSIX 2008 requires <<mkdtemp>>, but
+<<mkstemps>> is not standardized.
-Supporting OS subroutines required: <<getpid>>, <<open>>, <<stat>>.
+Supporting OS subroutines required: <<getpid>>, <<mkdir>>, <<open>>, <<stat>>.
*/
#include <_ansi.h>
@@ -109,12 +131,15 @@ Supporting OS subroutines required: <<ge
#include <ctype.h>
static int
-_DEFUN(_gettemp, (ptr, path, doopen),
+_DEFUN(_gettemp, (ptr, path, doopen, domkdir, suffixlen),
struct _reent *ptr _AND
char *path _AND
- register int *doopen)
+ register int *doopen _AND
+ int domkdir _AND
+ int suffixlen)
{
register char *start, *trv;
+ char *end;
#ifdef __USE_INTERNAL_STAT64
struct stat64 sbuf;
#else
@@ -125,11 +150,18 @@ _DEFUN(_gettemp, (ptr, path, doopen),
pid = _getpid_r (ptr);
for (trv = path; *trv; ++trv) /* extra X's get set to 0's */
continue;
+ trv -= suffixlen;
+ end = trv;
while (*--trv == 'X')
{
*trv = (pid % 10) + '0';
pid /= 10;
}
+ if (end - trv < 6)
+ {
+ ptr->_errno = EINVAL;
+ return 0;
+ }
/*
* Check the target directory; if you have six X's and it
@@ -161,16 +193,22 @@ _DEFUN(_gettemp, (ptr, path, doopen),
for (;;)
{
+#if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 2
+ if (domkdir)
+ {
+ if (_mkdir_r (ptr, path, 0700) == 0)
+ return 1;
+ if (ptr->_errno != EEXIST)
+ return 0;
+ }
+ else
+#endif /* _ELIX_LEVEL */
if (doopen)
{
if ((*doopen = _open_r (ptr, path, O_CREAT | O_EXCL | O_RDWR, 0600))
>= 0)
return 1;
-#if defined(__CYGWIN__)
- if (ptr->_errno != EEXIST && ptr->_errno != EACCES)
-#else
if (ptr->_errno != EEXIST)
-#endif
return 0;
}
#ifdef __USE_INTERNAL_STAT64
@@ -183,12 +221,13 @@ _DEFUN(_gettemp, (ptr, path, doopen),
/* tricky little algorithm for backward compatibility */
for (trv = start;;)
{
- if (!*trv)
+ if (trv == end)
return 0;
if (*trv == 'z')
*trv++ = 'a';
else
{
+ /* Safe, since it only encounters 7-bit characters. */
if (isdigit (*trv))
*trv = 'a';
else
@@ -207,15 +246,36 @@ _DEFUN(_mkstemp_r, (ptr, path),
{
int fd;
- return (_gettemp (ptr, path, &fd) ? fd : -1);
+ return (_gettemp (ptr, path, &fd, 0, 0) ? fd : -1);
+}
+
+#if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 2
+char *
+_DEFUN(_mkdtemp_r, (ptr, path),
+ struct _reent *ptr _AND
+ char *path)
+{
+ return (_gettemp (ptr, path, (int *) NULL, 1, 0) ? path : NULL);
+}
+
+int
+_DEFUN(_mkstemps_r, (ptr, path, len),
+ struct _reent *ptr _AND
+ char *path _AND
+ int len)
+{
+ int fd;
+
+ return (_gettemp (ptr, path, &fd, 0, len) ? fd : -1);
}
+#endif /* _ELIX_LEVEL */
char *
_DEFUN(_mktemp_r, (ptr, path),
struct _reent *ptr _AND
char *path)
{
- return (_gettemp (ptr, path, (int *) NULL) ? path : (char *) NULL);
+ return (_gettemp (ptr, path, (int *) NULL, 0, 0) ? path : (char *) NULL);
}
#ifndef _REENT_ONLY
@@ -226,14 +286,33 @@ _DEFUN(mkstemp, (path),
{
int fd;
- return (_gettemp (_REENT, path, &fd) ? fd : -1);
+ return (_gettemp (_REENT, path, &fd, 0, 0) ? fd : -1);
+}
+
+# if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 2
+char *
+_DEFUN(mkdemp, (path),
+ char *path)
+{
+ return (_gettemp (_REENT, path, (int *) NULL, 1, 0) ? path : NULL);
+}
+
+int
+_DEFUN(mkstemps, (path, len),
+ char *path _AND
+ int len)
+{
+ int fd;
+
+ return (_gettemp (_REENT, path, &fd, 0, len) ? fd : -1);
}
+# endif /* _ELIX_LEVEL */
char *
_DEFUN(mktemp, (path),
char *path)
{
- return (_gettemp (_REENT, path, (int *) NULL) ? path : (char *) NULL);
+ return (_gettemp (_REENT, path, (int *) NULL, 0, 0) ? path : (char *) NULL);
}
#endif /* ! defined (_REENT_ONLY) */