This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: add mkstemps, mkdtemp


-----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) */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]