+2014-12-01 Keith Marshall <keithmarshall@users.sourceforge.net>
+
+ Add implementation of mkstemp() and mkdtemp() functions.
+
+ * mingwex/cryptnam.c: New file; it implements...
+ (__mingw_crypto_tmpname): ...this helper function; it generates a
+ cryptographically secure sequence of characters, used as the random
+ component in temporary file and directory names.
+
+ * mingwex/mkstemp.c: New file; it implements...
+ (__mingw_mkstemp): ...this provider of mkstemp() functionality.
+
+ * mingwex/mkdtemp.c: New file; it implements...
+ (__mingw_mkdtemp): ...this provider of mkdtemp() functionality.
+
+ * include/stdlib.h (mkstemp): Declare protototye; provide an inline
+ function implementation, with LIBIMPL extern semantics, in terms of...
+ (__mingw_mkstemp): ...this; also declare prototype, and define...
+ (_MKSTEMP_INVOKE, _MKSTEMP_DEFAULT): ...these supporting constants.
+ (_MKSTEMP_SETMODE): New macro; define it, also providing...
+ (MKSTEMP_SETMODE) [!_NO_OLDNAMES]: ...this alias.
+ (mkdtemp): Declare prototype; provide inline implementation, with
+ JMPSTUB extern semantics, in terms of...
+ (__mingw_mkdtemp): ...this; declare prototype.
+
+ * Makefile.in (libmingwex.a): Add dependency rule, to include...
+ (mkstemp.$OBJEXT, mkdtemp.$OBJEXT, cryptname.$OBJEXT): ...these.
+
2014-11-30 Keith Marshall <keithmarshall@users.sourceforge.net>
More JMPSTUB rationalization of inline functions.
#libmingwex.a: $(addsuffix .$(OBJEXT), glob membarrier)
libmingwex.a: $(addsuffix .$(OBJEXT), mingw-aligned-malloc mingw-fseek glob)
libmingwex.a: $(addsuffix .$(OBJEXT), getopt basename dirname ftruncate usleep)
+libmingwex.a: $(addsuffix .$(OBJEXT), mkstemp mkdtemp cryptnam)
libmingwex.a: $(addsuffix .$(OBJEXT), tdelete tfind tsearch twalk)
#endif /* __MSVCRT__ */
#endif /* !__NO_ISOCEXT */
+#if !defined __STRICT_ANSI__
+/*
+ * POSIX/BSD extensions in libmingwex.a; maybe these should be exposed only on
+ * the basis of some POSIX or BSD specific feature tests, but for convenience,
+ * we require only !__STRICT_ANSI__
+ *
+ *
+ * mkstemp(3) function support; added per feature request #2003.
+ * POSIX wants _XOPEN_SOURCE >= 500, (implying _POSIX_C_SOURCE >= 200112L),
+ * but, as noted above, we will settle for !__STRICT_ANSI__.
+ */
+int __cdecl __MINGW_NOTHROW mkstemp( char * );
+int __cdecl __MINGW_NOTHROW __mingw_mkstemp( int, char * );
+
+/* The following macros are a MinGW specific extension, to facilite
+ * use of _O_TEMPORARY, (in addition to the POSIX required attributes),
+ * when creating the temporary file. Note that they require fcntl.h,
+ * which stdlib.h should NOT automatically include; we leave the onus
+ * on the user to explicitly include it, if using MKSTEMP_SETMODE.
+ */
+#define _MKSTEMP_INVOKE 0
+#define _MKSTEMP_DEFAULT _O_CREAT | _O_EXCL | _O_RDWR
+#define _MKSTEMP_SETMODE(M) __mingw_mkstemp( _MKSTEMP_DEFAULT | (M), NULL )
+#ifndef _NO_OLDNAMES
+#define MKSTEMP_SETMODE(M) __mingw_mkstemp( _MKSTEMP_DEFAULT | (M), NULL )
#endif
+__CRT_ALIAS __LIBIMPL__(( FUNCTION = mkstemp ))
+int __cdecl __MINGW_NOTHROW mkstemp( char *__filename_template )
+{ return __mingw_mkstemp( _MKSTEMP_INVOKE, __filename_template ); }
+
+/* mkdtemp(3) function support: added as adjunct to feature request #2003.
+ * POSIX wants _XOPEN_SOURCE >= 700, (implying _POSIX_C_SOURCE >= 200809L),
+ * but once again, we will settle for !__STRICT_ANSI__.
+ */
+char * __cdecl __MINGW_NOTHROW mkdtemp( char * );
+char * __cdecl __MINGW_NOTHROW __mingw_mkdtemp( char * );
+
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = mkdtemp ))
+char * __cdecl __MINGW_NOTHROW mkdtemp( char *__dirname_template )
+{ return __mingw_mkdtemp( __dirname_template ); }
+
+#endif /* (!__STRICT_ANSI__) */
+
_END_C_DECLS
#endif /* Not RC_INVOKED */
--- /dev/null
+/*
+ * cryptnam.c
+ *
+ * Implementation of a cryptographically secure random character sequence
+ * generator; this is specifically tailored to satisfy the requirement for
+ * replacement of the sequence of six 'XXXXXX's, within the templates for
+ * the file name, or the directory name, in MinGW.org implementations of
+ * the mkstemp(3) and mkdtemp(3) functions, respectively.
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2013, 2014, MinGW.org Project.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the following
+ * disclaimer shall be included in all copies or substantial portions of
+ * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+#define WIN32_LEAN_AND_MEAN
+
+#include <limits.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <string.h>
+
+#define CRYPTO_INLINE static __inline__ __attribute__((__always_inline__))
+
+CRYPTO_INLINE HCRYPTPROV crypto_provider( void )
+#define RSA_MODE( OPT, FLAG ) PROV_RSA_##OPT, CRYPT_##FLAG
+{
+ /* Helper to establish a cryptographic provider context for the
+ * cryptographically secure random number generator.
+ *
+ * At the outset, this provider requires initialization.
+ */
+ static HCRYPTPROV id = (HCRYPTPROV)(0);
+
+ /* On second, and subsequent calls, it should already have been
+ * initialized...
+ */
+ if( id != (HCRYPTPROV)(0) )
+ /*
+ * ...so, simply return the saved context handle...
+ */
+ return id;
+
+ /* If we're still here, this must be the first call, (or any
+ * preceding call failed to initialize the context); initialize
+ * it now, and if successful...
+ */
+ if( CryptAcquireContext( &id, NULL, NULL, RSA_MODE( FULL, VERIFYCONTEXT ) ) )
+ /*
+ * ...return the now-initialized context handle.
+ */
+ return id;
+
+ /* And finally, if we ever get to here, the context remains
+ * uninitialized; ensure that it remains marked as such, and
+ * return the uninitialized context handle.
+ */
+ return id = (HCRYPTPROV)(0);
+}
+
+CRYPTO_INLINE void *crypto_randomize( void *buf, size_t buflen )
+{
+ /* Helper to fill a specified buffer, of specified length,
+ * with cryptographically secure random bytes...
+ */
+ if( CryptGenRandom( crypto_provider(), buflen, buf ) )
+ /*
+ * ...returning a pointer to the buffer, when successful...
+ */
+ return buf;
+
+ /* ...or nothing, otherwise.
+ */
+ return NULL;
+}
+
+CRYPTO_INLINE unsigned char *crypto_random_filename_char( unsigned char *caret )
+{
+ /* Helper to generate a random sequence of characters, suitable for
+ * use in file names; although there are other valid possibilities, we
+ * restrict this to the set of lower case ASCII alpha-numerics, giving
+ * us 36 degrees of freedom for each character; (note that we cannot
+ * gain additional degrees of freedom by using mixed case, because
+ * the MS-Windows file system is case-insensitive).
+ */
+ const unsigned char span = 'z' - 'a' + 1 + '9' - '0' + 1;
+
+ /* We also wish to ensure that each of the possible 36 characters has
+ * an equal probability of selection; thus, of the UCHAR_MAX possible
+ * raw byte selections, we want to consider at most the largest even
+ * multiple of the 36 character span, which lies below the UCHAR_MAX
+ * limit, (which, since zero is a valid choice, is one less than the
+ * result of discounting the remainder from modulo division).
+ */
+ const unsigned char max = UCHAR_MAX - (UCHAR_MAX % span) - 1;
+
+ /* Deposit randomly selected characters at the "caret" location...
+ */
+ do { if( crypto_randomize( caret, sizeof( unsigned char ) ) == NULL )
+ /*
+ * ...bailing out, on any failure of the sequence generator...
+ */
+ return NULL;
+
+ /* ...until we get one which is within the largest possible
+ * subset which yields equal probabilty to each outcome, when
+ * reduced modulo the 36 available degrees of freedom.
+ */
+ } while( *caret > max );
+
+ /* Perform the modulo 36 reduction, and offset the result into the
+ * alpha-numeric character range...
+ */
+ *caret = '0' + (*caret % span);
+ /*
+ * ...while discounting those unsuitable characters which lie within
+ * the range, between '9' and 'a' exclusively.
+ */
+ if( *caret > '9' ) *caret += 'a' - '9' - 1;
+
+ /* Finally, return the "caret" location, indicating the successful
+ * transformation of the character in that position.
+ */
+ return caret;
+}
+
+char *__mingw_crypto_tmpname( char *template )
+{
+ /* Helper function, based on Microsoft's wincrypt API, to construct
+ * the candidate names for temporary files, both in a less predictable
+ * manner than Microsoft's _mktemp() function, and without suffering
+ * its inherent limitation of allowing no more than 26 file names
+ * per template per process thread.
+ *
+ * We begin by locating the position, within the given template,
+ * where the string of six replaceable 'XXXXXX's should begin.
+ */
+ char *tail = template + strlen( template ) - 6;
+
+ /* Provided this appears sane -- i.e. it at least doesn't place the
+ * six character "tail" before the start of the template itself...
+ */
+ if( tail >= template )
+ {
+ /* ...then, walk over each of the six bytes of the "tail", until
+ * we reach the NUL terminator...
+ */
+ while( *tail )
+ {
+ /* ...checking that each byte is initially ASCII 'X', as POSIX
+ * requires them to be; (note that we don't consider that these
+ * may be MBCS trail bytes, since the required 'X' is a single
+ * byte in an MBCS representation anyway)...
+ */
+ if( (*tail != 'X') || (crypto_random_filename_char( tail++ ) == NULL) )
+ /*
+ * ...bailing out, and returning nothing, if not.
+ */
+ return NULL;
+ }
+ }
+ /* Finally, when we have successfully replaced all six 'XXXXXX's,
+ * we return the modified template, in place.
+ */
+ return template;
+}
+
+/* $RCSfile$: end of file */
--- /dev/null
+/*
+ * mkdtemp.c
+ *
+ * Implementation of an (approximately) POSIX conforming mkdtemp(3).
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2013, 2014, MinGW.org Project.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the following
+ * disclaimer shall be included in all copies or substantial portions of
+ * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <direct.h>
+#include <errno.h>
+
+/* Candidate names for the temporary directory are generated, based on
+ * a cryptographically secure random character sequence; this externally
+ * implemented character sequence generator is shared by mkstemp(3) and
+ * mkdtemp(3).
+ */
+extern char *__mingw_crypto_tmpname( char * );
+
+char *__mingw_mkdtemp( char *template )
+{
+ /* Formal MinGW implementation of the mkdtemp(3) function; to begin,
+ * check that the caller gave us a viable template...
+ */
+ if( template == NULL )
+ {
+ /* ...bailing out, if nothing at all...
+ */
+ errno = EINVAL;
+ }
+ else
+ { /* ...but assume that anything at all is potentially viable;
+ * set up a retry limit, and estimate the storage requirement
+ * for a working scratch buffer.
+ */
+ int retry = TMP_MAX;
+ size_t bufsiz = 1 + strlen( template );
+
+ /* Until we either successfully create a directory, or we have
+ * exhausted the retry limit while attempting to do so...
+ */
+ while( retry-- > 0 )
+ {
+ /* ...set up the scratch buffer, copy the template into it,
+ * then transform to get a cryptographically secure candidate
+ * name for the temporary directory; (each retry cycle will
+ * generate a randomly differing candidate name)...
+ */
+ char dirname[bufsiz];
+ if( __mingw_crypto_tmpname( strcpy( dirname, template ) ) == NULL )
+ {
+ /* ...bailing out, on any unsuccessful attempt to generate
+ * the candidate name; (this is most likely to occur during
+ * the first cycle, due to a malformed template; if we can
+ * successfully generate the first candidate, successive
+ * attempts are unlikely to fail).
+ */
+ errno = EINVAL;
+ retry = 0;
+ }
+ else
+ { /* We got a usable candidate directory name; try to create
+ * the named directory...
+ */
+ if( mkdir( dirname ) == 0 )
+ /*
+ * ...and, on success, update the template to reflect the
+ * name of the directory we've created, and we are done...
+ */
+ return strcpy( template, dirname );
+
+ /* ...but, if we failed for any reason other than that a file
+ * or a directory with the candidate name already exists...
+ */
+ else if( errno != EEXIST )
+ /*
+ * ...then, any retry will most likely also fail, so we may
+ * as well just give up now.
+ */
+ retry = 0;
+ }
+ }
+ }
+ /* If we get to here, then all of our attempts to create a directory
+ * were unsuccessful; return NULL, to indicate failure.
+ */
+ return NULL;
+}
+
+/* $RCSfile$: end of file */
--- /dev/null
+/*
+ * mkstemp.c
+ *
+ * Implementation of an (approximately) POSIX conforming mkstemp(3)
+ * function; invocation is via an inline wrapper, defined in stdlib.h,
+ * which delegates to the library routine defined herein.
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2013, 2014, MinGW.org Project.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the following
+ * disclaimer shall be included in all copies or substantial portions of
+ * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Candidate names for the temporary file are generated, based on a
+ * cryptographically secure random character sequence; this externally
+ * implemented character sequence generator is shared by mkstemp(3)
+ * and mkdtemp(3).
+ */
+extern char *__mingw_crypto_tmpname( char * );
+
+int __mingw_mkstemp( int setmode, char *template )
+{
+ /* Implementation of the low-level functional support for mkstemp(3);
+ * this provides the formal function implementation, including support
+ * for adjustment of its behaviour w.r.t. temporary file persistence.
+ *
+ * By default, temporary files will persist until explicitly deleted;
+ * POSIX prescribes that temporary files are to be created with the
+ * following attributes, (with O_BINARY added, to ensure there is
+ * no undue influence from "helpful" text mode transformations):
+ */
+ static int omode = _O_CREAT | _O_EXCL | _O_RDWR | _O_BINARY;
+
+ if( setmode )
+ /* On POSIX platforms, programmers may adopt an idiom such as:
+ *
+ * if( mkstemp( template ) >= 0 )
+ * { unlink( template );
+ * . . .
+ * }
+ *
+ * to ensure that a temporary file does NOT persist after it is
+ * closed; MS-Windows does not allow such use of unlink(2), while
+ * the file remains open. Thus, MS-Windows programmers must take
+ * extra care, to close and unlink temporary files AFTER use, if
+ * similar behaviour is desired.
+ *
+ * To mitigate this MS-Windows limitation, we provide support for
+ * an alternative, MinGW specific idiom:
+ *
+ * #include <fcntl.h>
+ *
+ * _MKSTEMP_SETMODE( _O_TEMPORARY );
+ * if( mkstemp( template ) >= 0 )
+ * {
+ * . . .
+ * }
+ *
+ * to achieve a similar effect to that of the above POSIX idiom.
+ */
+ return omode = (omode & ~_O_TEMPORARY) | (setmode & _O_TEMPORARY);
+
+ else
+ { /* Formal MinGW implementation of the mkstemp(3) function; to begin,
+ * we assume that it may fail, and record an invalid file descriptor
+ * for return in such eventuality.
+ */
+ int fd = -1;
+
+ /* Check that the caller gave us a viable template...
+ */
+ if( template == NULL )
+ {
+ /* ...bailing out, if nothing at all...
+ */
+ errno = EINVAL;
+ }
+ else
+ { /* ...but assume that anything at all is potentially viable;
+ * set up a retry limit, and estimate the storage requirement
+ * for a working scratch buffer.
+ */
+ int retry = TMP_MAX;
+ size_t bufsiz = 1 + strlen( template );
+
+ /* Until we either get a valid file descriptor, or we exhaust
+ * the retry limit while attempting to get one...
+ */
+ while( (fd < 0) && (retry-- > 0) )
+ {
+ /* ...set up the scratch buffer, copy the template into it,
+ * then transform to get a cryptographically secure candidate
+ * file name for the temporary file; (each retry cycle will
+ * generate a randomly differing candidate file name)...
+ */
+ char filename[bufsiz];
+ if( __mingw_crypto_tmpname( strcpy( filename, template ) ) == NULL )
+ {
+ /* ...bailing out, on any unsuccessful attempt to generate
+ * the candidate name; (this is most likely to occur during
+ * the first cycle, due to a malformed template; if we can
+ * successfully generate the first candidate, successive
+ * attempts are unlikely to fail).
+ */
+ errno = EINVAL;
+ retry = 0;
+ }
+ else
+ { /* We got a usable candidate file name; attempt to open it
+ * as a new file...
+ */
+ if( (fd = open( filename, omode, _S_IREAD | _S_IWRITE )) >= 0 )
+ /*
+ * ...and, on success, update the template to reflect the
+ * name of the file we've opened, and we are done...
+ */
+ strcpy( template, filename );
+
+ /* ...but, if we failed for any reason other than that a file
+ * with the candidate name already exists...
+ */
+ else if( errno != EEXIST )
+ /*
+ * ...then, any retry will most likely also fail, so we may
+ * as well just give up now.
+ */
+ retry = 0;
+ }
+ }
+ }
+ /* Finally, whether we succeeded in opening any temporary file, or we
+ * ultimately gave up in disgust, we return the prevailing state of the
+ * file descriptor we attempted to assign.
+ */
+ return fd;
+ }
+}
+
+/* $RCSfile$: end of file */