From d13731711f0af54c26b739c4220c558430d01011 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 5 Nov 2014 09:39:18 -0600 Subject: [PATCH] Add mktemp() and mkstemp() --- include/stdlib.h | 4 +- libc/stdlib/Make.defs | 4 + libc/stdlib/lib_mkstemp.c | 297 ++++++++++++++++++++++++++++++++++++++ libc/stdlib/lib_mktemp.c | 81 +++++++++++ 4 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 libc/stdlib/lib_mkstemp.c create mode 100644 libc/stdlib/lib_mktemp.c diff --git a/include/stdlib.h b/include/stdlib.h index 127b35163d..c149e0ac11 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -1,7 +1,7 @@ /**************************************************************************** * include/stdlib.h * - * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -182,6 +182,8 @@ long int labs(long int j); #ifdef CONFIG_HAVE_LONG_LONG long long int llabs(long long int j); #endif +int mktemp(FAR char *template); +int mkstemp(FAR char *template); /* Sorting */ diff --git a/libc/stdlib/Make.defs b/libc/stdlib/Make.defs index 5501ef0c16..6a0d8ad7d8 100644 --- a/libc/stdlib/Make.defs +++ b/libc/stdlib/Make.defs @@ -40,6 +40,10 @@ CSRCS += lib_llabs.c lib_rand.c lib_qsort.c CSRCS += lib_strtol.c lib_strtoll.c lib_strtoul.c lib_strtoull.c CSRCS += lib_strtod.c lib_checkbase.c +ifeq ($(CONFIG_FS_WRITABLE),y) +CSRCS += lib_mktemp.c lib_mkstemp.c +endif + # Add the stdlib directory to the build DEPPATH += --dep-path stdlib diff --git a/libc/stdlib/lib_mkstemp.c b/libc/stdlib/lib_mkstemp.c new file mode 100644 index 0000000000..3269463f5f --- /dev/null +++ b/libc/stdlib/lib_mkstemp.c @@ -0,0 +1,297 @@ +/**************************************************************************** + * libc/stdlib/lib_mkstemp.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_FS_WRITABLE + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + + #ifndef CONFIG_LIBC_TMPDIR +# define CONFIG_LIBC_TMPDIR "/tmp" +#endif + +#define MAX_XS 6 +#define MIN_NUMERIC 0 /* 0-9: Numeric */ +#define MAX_NUMERIC 9 +#define MIN_UPPERCASE 10 /* 10-35: Upper case */ +#define MAX_UPPERCASE 35 +#define MIN_LOWERCASE 36 /* 36-61: Lower case */ +#define MAX_LOWERCASE 61 +#define MAX_BASE62 MAX_LOWERCASE + +/* 62**1 = 62 + * 62**2 = 3844 + * 62**3 = 238328 + * 62**4 = 14776336 + * 62**5 = 916132832 + * 62**6 = 56800235584 > UINT32_MAX + */ + +#define BIG_XS 5 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint8_t g_base62[MAX_XS]; +static sem_t g_b62sem = SEM_INITIALIZER(1); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: base62_to_char + * + * Description: + * Convert a base62 value to a printable character. + * + ****************************************************************************/ + +static char base62_to_char(uint8_t base62) +{ + if (base62 <= MAX_NUMERIC) + { + return '0' + base62; + } + else if (base62 <= MAX_UPPERCASE) + { + return 'A' + base62 - MIN_UPPERCASE; + } + else /* if (base62 <= MAX_LOWERCASE) */ + { + DEBUGASSERT(base62 <= MAX_LOWERCASE); + return 'a' + base62 - MIN_LOWERCASE; + } +} + +/**************************************************************************** + * Name: incr_base62 + * + * Description: + * increment the base62 value array. + * + ****************************************************************************/ + +static void incr_base62(void) +{ + int i; + + for (i = 0; i < MAX_XS; i++) + { + if (g_base62[i] < MAX_LOWERCASE) + { + g_base62[i]++; + return; + } + else + { + g_base62[i] = 0; + } + } +} + +/**************************************************************************** + * Name: get_base62 + * + * Description: + * Atomically copy and increment the base62 array. + * + ****************************************************************************/ + +static void get_base62(FAR uint8_t *ptr) +{ + int ret; + + while ((ret = sem_wait(&g_b62sem)) < 0) + { + DEBUGASSERT(errno == EINTR); + } + + memcpy(ptr, g_base62, MAX_XS); + incr_base62(); + sem_post(&g_b62sem); +} + +/**************************************************************************** + * Name: copy_base62 + * + * Description: + * Copy the base62 array into the template filename, converting each + * base62 value to a printable character. + * + ****************************************************************************/ + +static void copy_base62(FAR char *dest, int len) +{ + FAR const uint8_t *src; + + src = g_base62; + if (len < MAX_XS) + { + src += MAX_XS - len; + } + + for (; len > 0; len--) + { + *dest++ = base62_to_char(*src++); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mkstemp + * + * Description: + * The mkstemp() function replaces the contents of the string pointed to + * by template by a unique filename, and returns a file descriptor for the + * file open for reading and writing. The function thus prevents any + * possible race condition between testing whether the file exists and + * opening it for use. The string in template should look like a filename + * with six trailing 'X' s; mkstemp() replaces each 'X' with a character + * from the portable filename character set. The characters are chosen + * such that the resulting name does not duplicate the name of an existing + * file at the time of a call to mkstemp(). + * + * Input Parameters: + * template - The base file name that will be modified to produce the + * unique file name. This must be a full path beginning with /tmp. + * This function will modify only the first XXXXXX characters within + * that full path. + * + * Returned Value: + * + * Upon successful completion, mkstemp() returns an open file descriptor. + * Otherwise, -1 is returned if no suitable file could be created. + * + ****************************************************************************/ + +int mkstemp(FAR char *template) +{ + uint8_t base62[MAX_XS]; + uint32_t retries; + FAR char *xptr; + FAR char *ptr; + int xlen; + int fd; + int i; + + /* Count the number of X's at the end of the template */ + + xptr = strchr(template, 'X'); + if (!xptr) + { + /* No Xs? There should always really be 6 */ + + return open(template, O_RDWR | O_CREAT | O_EXCL, 0666); + } + + /* There is at least one.. count all of them */ + + for (xlen = 0, ptr = xptr; xlen < MAX_XS && *ptr == 'X'; xlen++, ptr++); + + /* Ignore any X's after the sixth */ + + if (xlen > MAX_XS) + { + xlen = MAX_XS; + } + + /* If xlen is small, then we need to determine the maximum number of + * retries before the values will repeat. + */ + + if (xlen >= BIG_XS) + { + retries = UINT32_MAX; + } + else + { + for (i = 1, retries = 62; i < xlen; i++, retries *= 62); + } + + /* Then loop until we find a unique file name */ + + while (retries > 0) + { + /* Sample and increment the base62 counter */ + + get_base62(base62); + + /* Form the candidate file name */ + + copy_base62(xptr, xlen); + + /* Attempt to open the candidate file -- creating it exclusively + * + * REVISIT: This prohibits the use of this function to create unique + * directories + */ + + fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0666); + if (fd >= 0) + { + /* We have it... return the file descriptor */ + + return fd; + } + } + + /* We could not find an unique filename */ + + return ERROR; +} + +#endif /* CONFIG_FS_WRITABLE */ + diff --git a/libc/stdlib/lib_mktemp.c b/libc/stdlib/lib_mktemp.c new file mode 100644 index 0000000000..705c5cb9e3 --- /dev/null +++ b/libc/stdlib/lib_mktemp.c @@ -0,0 +1,81 @@ +/**************************************************************************** + * libc/stdlib/lib_mktemp.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#ifdef CONFIG_FS_WRITABLE + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mktemp + * + * Description: + * The use of mktemp is dangerous; use mkstemp instead. + * + ****************************************************************************/ + +int mktemp(FAR char *template) +{ + int fd = mkstemp(template); + if (fd < 0) + { + return ERROR; + } + + close(fd); + return OK; +} + +#endif /* CONFIG_FS_WRITABLE */ +