libs/libc/stdio: correct the prototype of mktemp(3)

From:   int       mktemp(FAR char *path_template);
To:     FAR char *mktemp(FAR char *path_template);

See the reference here:
https://pubs.opengroup.org/onlinepubs/009695399/functions/mktemp.html

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2020-09-17 16:03:38 +08:00 committed by Xiang Xiao
parent 7816b5ec8c
commit 4067a9f057
5 changed files with 226 additions and 227 deletions

View file

@ -235,7 +235,7 @@ lldiv_t lldiv(long long number, long long denom);
/* Temporary files */ /* Temporary files */
int mktemp(FAR char *path_template); FAR char *mktemp(FAR char *path_template);
int mkstemp(FAR char *path_template); int mkstemp(FAR char *path_template);
/* Sorting */ /* Sorting */

View file

@ -87,21 +87,24 @@
FAR char *tempnam(FAR const char *dir, FAR const char *pfx) FAR char *tempnam(FAR const char *dir, FAR const char *pfx)
{ {
FAR char *template;
FAR char *path; FAR char *path;
int ret;
asprintf(&path, "%s/%s-XXXXXX.tmp", dir, pfx); asprintf(&template, "%s/%s-XXXXXX", dir, pfx);
if (path) if (template)
{ {
ret = mktemp(path); path = mktemp(template);
if (ret == OK) if (path != NULL)
{ {
return path; return path;
} }
lib_free(path); lib_free(template);
}
else
{
set_errno(ENOMEM);
} }
set_errno(ENOMEM);
return NULL; return NULL;
} }

View file

@ -82,14 +82,12 @@
FAR char *tmpnam(FAR char *s) FAR char *tmpnam(FAR char *s)
{ {
static char path[L_tmpnam]; static char path[L_tmpnam];
int ret;
if (s == NULL) if (s == NULL)
{ {
s = path; s = path;
} }
snprintf(s, L_tmpnam, "%s/XXXXXX.tmp", P_tmpdir); snprintf(s, L_tmpnam, "%s/XXXXXX", P_tmpdir);
ret = mktemp(s); return mktemp(s);
return (ret == OK) ? s : NULL;
} }

View file

@ -25,143 +25,9 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <nuttx/compiler.h> #include <nuttx/compiler.h>
#include <stdint.h> #include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <fcntl.h>
#include <errno.h>
#include <nuttx/semaphore.h>
/****************************************************************************
* Pre-processor definitions
****************************************************************************/
#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 = MAX_XS - 1; i >= 0; 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(_SEM_ERRNO(ret) == EINTR || _SEM_ERRNO(ret) == ECANCELED);
}
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 const uint8_t *src, FAR char *dest, int len)
{
if (len < MAX_XS)
{
src += MAX_XS - len;
}
for (; len > 0; len--)
{
*dest++ = base62_to_char(*src++);
}
}
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
@ -195,78 +61,13 @@ static void copy_base62(FAR const uint8_t *src, FAR char *dest, int len)
int mkstemp(FAR char *path_template) int mkstemp(FAR char *path_template)
{ {
uint8_t base62[MAX_XS]; FAR char *path = mktemp(path_template);
uint32_t retries; int ret = ERROR;
FAR char *xptr;
int xlen;
int fd;
int i;
/* Count the number of X's at the end of the template */ if (path)
xptr = &path_template[strlen(path_template)];
for (xlen = 0; xlen < MAX_XS && path_template < xptr && *(xptr - 1) == 'X';
xlen++, xptr--);
if (xlen == 0)
{ {
/* No Xs? There should always really be 6 */ ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0666);
return open(path_template, O_RDWR | O_CREAT | O_EXCL, 0666);
} }
/* Ignore any X's after the sixth */ return ret;
if (xlen > MAX_XS)
{
xptr += 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(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(path_template, O_RDWR | O_CREAT | O_EXCL, 0666);
if (fd >= 0)
{
/* We have it... return the file descriptor */
return fd;
}
retries--;
}
/* We could not find an unique filename */
return ERROR;
} }

View file

@ -25,17 +25,146 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <nuttx/compiler.h> #include <nuttx/compiler.h>
#include <unistd.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <nuttx/semaphore.h>
/**************************************************************************** /****************************************************************************
* Pre-processor definitions * Pre-processor definitions
****************************************************************************/ ****************************************************************************/
#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 * 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 = MAX_XS - 1; i >= 0; 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(_SEM_ERRNO(ret) == EINTR || _SEM_ERRNO(ret) == ECANCELED);
}
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 const uint8_t *src, FAR char *dest, int len)
{
if (len < MAX_XS)
{
src += MAX_XS - len;
}
for (; len > 0; len--)
{
*dest++ = base62_to_char(*src++);
}
}
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@ -44,19 +173,87 @@
* Name: mktemp * Name: mktemp
* *
* Description: * Description:
* The use of mktemp is dangerous; use mkstemp instead. * The mktemp() function generates a unique temporary filename from
* template. The last six characters of template must be XXXXXX and these
* are replaced with a string that makes the filename unique. Since it
* will be modified, template must not be a string constant, but should be
* declared as a character array.
* *
****************************************************************************/ ****************************************************************************/
int mktemp(FAR char *path_template) FAR char *mktemp(FAR char *path_template)
{ {
int fd = mkstemp(path_template); uint8_t base62[MAX_XS];
if (fd < 0) uint32_t retries;
struct stat buf;
FAR char *xptr;
int xlen;
int ret;
int i;
/* Count the number of X's at the end of the template */
xptr = &path_template[strlen(path_template)];
for (xlen = 0; xlen < MAX_XS && path_template < xptr && *(xptr - 1) == 'X';
xlen++, xptr--);
if (xlen == 0)
{ {
return ERROR; /* No Xs? There should always really be 6 */
return path_template;
} }
close(fd); /* Ignore any X's after the sixth */
return OK;
}
if (xlen > MAX_XS)
{
xptr += 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(base62, xptr, xlen);
/* Attempt to stat the candidate file */
ret = stat(path_template, &buf);
if (ret < 0 && errno == ENOENT)
{
/* We have it... Clear the errno and return the template */
set_errno(0);
return path_template;
}
retries--;
}
/* We could not find an unique filename */
set_errno(EINVAL);
return NULL;
}