From 8abda1c9752d07c9450057acd1aa9ec3220f9a28 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Sat, 31 Jan 2015 23:49:13 +0100 Subject: [PATCH] tempname: new try_tempname function The way how gen_tempname() creates files is not always sufficient. For example, it may make sense to create directories when creating the temporary file or directory fails with errno set to ENOENT. Add a try_tempname() variant of gen_tempname() that allows that. Implement gen_tempname() on top of it. * lib/tempname.c (try_tempname): New function and backend of gen_tempname(). (try_file, try_dir, try_nocreate): Callbacks to use for the different kinds that gen_tempname supports (GT_FILE, GT_DIR, GT_NOCREATE). * lib/tempname.h (try_tempname): Declare here. * modules/tempname: Mention try_tempname. --- lib/tempname.c | 122 ++++++++++++++++++++++++++++++++----------------------- lib/tempname.h | 7 ++++ modules/tempname | 2 +- 3 files changed, 80 insertions(+), 51 deletions(-) diff --git a/lib/tempname.c b/lib/tempname.c index 088b224..4d91281 100644 --- a/lib/tempname.c +++ b/lib/tempname.c @@ -62,6 +62,7 @@ # define struct_stat64 struct stat64 #else # define struct_stat64 struct stat +# define __try_tempname try_tempname # define __gen_tempname gen_tempname # define __getpid getpid # define __gettimeofday gettimeofday @@ -176,21 +177,8 @@ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; -/* Generate a temporary file name based on TMPL. TMPL must match the - rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix). - The name constructed does not exist at the time of the call to - __gen_tempname. TMPL is overwritten with the result. - - KIND may be one of: - __GT_NOCREATE: simply verify that the name does not exist - at the time of the call. - __GT_FILE: create the file using open(O_CREAT|O_EXCL) - and return a read-write fd. The file is mode 0600. - __GT_DIR: create a directory, which will be mode 0700. - - We use a clever algorithm to get hard-to-predict names. */ int -__gen_tempname (char *tmpl, int suffixlen, int flags, int kind) +__try_tempname (char *tmpl, int suffixlen, void *args, int (*try) (char *, void *)) { int len; char *XXXXXX; @@ -199,7 +187,6 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind) unsigned int count; int fd = -1; int save_errno = errno; - struct_stat64 st; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that @@ -256,41 +243,7 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind) v /= 62; XXXXXX[5] = letters[v % 62]; - switch (kind) - { - case __GT_FILE: - fd = __open (tmpl, - (flags & ~O_ACCMODE) - | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - break; - - case __GT_DIR: - fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); - break; - - case __GT_NOCREATE: - /* This case is backward from the other three. __gen_tempname - succeeds if __xstat fails because the name does not exist. - Note the continue to bypass the common logic at the bottom - of the loop. */ - if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) - { - if (errno == ENOENT) - { - __set_errno (save_errno); - return 0; - } - else - /* Give up now. */ - return -1; - } - continue; - - default: - assert (! "invalid KIND in __gen_tempname"); - abort (); - } - + fd = try (tmpl, args); if (fd >= 0) { __set_errno (save_errno); @@ -304,3 +257,72 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind) __set_errno (EEXIST); return -1; } + +static int +try_file (char *tmpl, void *flags) +{ + int *openflags = flags; + return __open (tmpl, + (*openflags & ~O_ACCMODE) + | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); +} + +static int +try_dir (char *tmpl, void *flags) +{ + return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); +} + +static int +try_nocreate (char *tmpl, void *flags) +{ + struct_stat64 st; + + if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) + { + if (errno == ENOENT) + return 0; + } + else + __set_errno (EEXIST); + return -1; +} + +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix). + The name constructed does not exist at the time of the call to + __gen_tempname. TMPL is overwritten with the result. + + KIND may be one of: + __GT_NOCREATE: simply verify that the name does not exist + at the time of the call. + __GT_FILE: create the file using open(O_CREAT|O_EXCL) + and return a read-write fd. The file is mode 0600. + __GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +int +__gen_tempname (char *tmpl, int suffixlen, int flags, int kind) +{ + int (*try) (char *, void *); + + switch (kind) + { + case __GT_FILE: + try = try_file; + break; + + case __GT_DIR: + try = try_dir; + break; + + case __GT_NOCREATE: + try = try_nocreate; + break; + + default: + assert (! "invalid KIND in __gen_tempname"); + abort (); + } + return __try_tempname (tmpl, suffixlen, &flags, try); +} diff --git a/lib/tempname.h b/lib/tempname.h index b560ee5..f7c98d0 100644 --- a/lib/tempname.h +++ b/lib/tempname.h @@ -47,4 +47,11 @@ We use a clever algorithm to get hard-to-predict names. */ extern int gen_tempname (char *tmpl, int suffixlen, int flags, int kind); +/* Similar to gen_tempname, but TRY is called for each temporary + name to try. If TRY returns a non-negative number, TRY_GEN_TEMPNAME + returns with this value. Otherwise, if errno is set to EEXIST, another + name is tried, or else TRY_GEN_TEMPNAME returns -1. */ +extern int try_tempname(char *tmpl, int suffixlen, void *args, + int (*try) (char *, void *)); + #endif /* GL_TEMPNAME_H */ diff --git a/modules/tempname b/modules/tempname index 7fafd72..c589d70 100644 --- a/modules/tempname +++ b/modules/tempname @@ -1,5 +1,5 @@ Description: -gen_tempname() function: create a private temporary file or directory. +gen_tempname() and try_tempname() functions: create a private temporary file or directory. Files: lib/tempname.c -- 2.1.0