From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-3.9 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id 55E761F55B for ; Mon, 1 Jun 2020 05:17:29 +0000 (UTC) Received: from localhost ([::1]:47922 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jfcpE-0002Yq-2u for normalperson@yhbt.net; Mon, 01 Jun 2020 01:17:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33582) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jfcpA-0002Wl-Oi for bug-gnulib@gnu.org; Mon, 01 Jun 2020 01:17:24 -0400 Received: from zimbra.cs.ucla.edu ([131.179.128.68]:42126) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jfcp8-0005Gb-HR for bug-gnulib@gnu.org; Mon, 01 Jun 2020 01:17:24 -0400 Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id B17C81600C3 for ; Sun, 31 May 2020 22:17:19 -0700 (PDT) Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id v-mqh3R_P1Kx; Sun, 31 May 2020 22:17:17 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id BC6FD160059; Sun, 31 May 2020 22:17:17 -0700 (PDT) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id XEujamH9Wnel; Sun, 31 May 2020 22:17:17 -0700 (PDT) Received: from day.example.com (cpe-23-242-74-103.socal.res.rr.com [23.242.74.103]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id 83CE116004F; Sun, 31 May 2020 22:17:17 -0700 (PDT) From: Paul Eggert To: bug-gnulib@gnu.org Subject: [PATCH] tempname: merge from glibc and coreutils Date: Sun, 31 May 2020 22:17:13 -0700 Message-Id: <20200601051713.12911-1-eggert@cs.ucla.edu> X-Mailer: git-send-email 2.17.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Received-SPF: pass client-ip=131.179.128.68; envelope-from=eggert@cs.ucla.edu; helo=zimbra.cs.ucla.edu X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/01 01:17:19 X-ACL-Warn: Detected OS = Linux 3.1-3.10 X-Spam_score_int: -41 X-Spam_score: -4.2 X-Spam_bar: ---- X-Spam_report: (-4.2 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: bug-gnulib@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Gnulib discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paul Eggert Errors-To: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org Sender: "bug-gnulib" Also, merge in Gnulib=E2=80=99s more-recent methods of making it easier to share between Gnulib and glibc, and fix a few randomness glitches. * lib/tempname.c: Include libc-config.h, not config.h, if !_LIBC. (__set_errno): Remove; libc-config.h does that for us. Do not include . (__secure_getenv) [_LIBC]: New macro. (__try_tempname, __getpid, __gettimeofday) [!_LIBC]: Remove macros. (RANDOM_BITS): Rewrite. (RANDOM_VALUE_MAX, BASE_62_DIGITS, BASE_62_POWER): New macros. (random_value): New typedef. (try_file, try_dir, try_nocreate): Move up. (gen_tempname_len, try_tempname_len): New functions. (gen_tempname_len): Use a constant array rather than a switch. (try_tempname_len): Don=E2=80=99t assume string length fits in int. Generalize use of RANDOM_BITS. If _LIBC, don=E2=80=99t assume RANDOM_BIT= S has enough entropy (it=E2=80=99s a bit short). (__gen_tempname): Rewrite in terms of gen_tempname_len. (__try_tempname): Rewrite in terms of try_tempname_len. * lib/tempname.h (gen_tempname_len, try_tempname_len): New decls. * modules/tempname (Depends-on): Remove gettimeofday, sys_time. Add getentropy, libc-config. --- ChangeLog | 26 +++++ lib/tempname.c | 277 +++++++++++++++++++++++------------------------ lib/tempname.h | 7 ++ modules/tempname | 4 +- 4 files changed, 173 insertions(+), 141 deletions(-) diff --git a/ChangeLog b/ChangeLog index e6ba65b08..b7b39272f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2020-05-31 Paul Eggert + + tempname: merge from glibc and coreutils + Also, merge in Gnulib=E2=80=99s more-recent methods of making it easier + to share between Gnulib and glibc, and fix a few randomness + glitches. + * lib/tempname.c: Include libc-config.h, not config.h, if !_LIBC. + (__set_errno): Remove; libc-config.h does that for us. + Do not include . + (__secure_getenv) [_LIBC]: New macro. + (__try_tempname, __getpid, __gettimeofday) [!_LIBC]: Remove macros. + (RANDOM_BITS): Rewrite. + (RANDOM_VALUE_MAX, BASE_62_DIGITS, BASE_62_POWER): New macros. + (random_value): New typedef. + (try_file, try_dir, try_nocreate): Move up. + (gen_tempname_len, try_tempname_len): New functions. + (gen_tempname_len): Use a constant array rather than a switch. + (try_tempname_len): Don=E2=80=99t assume string length fits in int. + Generalize use of RANDOM_BITS. If _LIBC, don=E2=80=99t assume RANDOM_B= ITS + has enough entropy (it=E2=80=99s a bit short). + (__gen_tempname): Rewrite in terms of gen_tempname_len. + (__try_tempname): Rewrite in terms of try_tempname_len. + * lib/tempname.h (gen_tempname_len, try_tempname_len): New decls. + * modules/tempname (Depends-on): Remove gettimeofday, sys_time. + Add getentropy, libc-config. + 2020-05-31 Bruno Haible =20 getrandom, getentropy: Mention the crypto/gc-random module. diff --git a/lib/tempname.c b/lib/tempname.c index 0aad0616c..ca7e8b1e7 100644 --- a/lib/tempname.c +++ b/lib/tempname.c @@ -1,24 +1,22 @@ -/* tempname.c - generate the name of a temporary file. +/* Copyright (C) 1991-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. =20 - Copyright (C) 1991-2003, 2005-2007, 2009-2020 Free Software Foundatio= n, Inc. + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. =20 - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, + The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see = . */ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. =20 -/* Extracted from glibc sysdeps/posix/tempname.c. See also tmpdir.c. *= / + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ =20 #if !_LIBC -# include +# include # include "tempname.h" #endif =20 @@ -26,9 +24,6 @@ #include =20 #include -#ifndef __set_errno -# define __set_errno(Val) errno =3D (Val) -#endif =20 #include #ifndef P_tmpdir @@ -52,7 +47,6 @@ #include =20 #include -#include #include #include =20 @@ -60,43 +54,33 @@ =20 #if _LIBC # define struct_stat64 struct stat64 +# define __secure_getenv __libc_secure_getenv #else # define struct_stat64 struct stat -# define __try_tempname try_tempname # define __gen_tempname gen_tempname -# define __getpid getpid -# define __gettimeofday gettimeofday # define __mkdir mkdir # define __open open # define __lxstat64(version, file, buf) lstat (file, buf) #endif =20 #ifdef _LIBC -# include -# if HP_TIMING_AVAIL -# define RANDOM_BITS(Var) \ - if (__builtin_expect (value =3D=3D UINT64_C (0), 0)) = \ - { = \ - /* If this is the first time this function is used initialize = \ - the variable we accumulate the value in to some somewhat = \ - random value. If we'd not do this programs at startup time = \ - might have a reduced set of possible names, at least on slow = \ - machines. */ = \ - struct timeval tv; = \ - __gettimeofday (&tv, NULL); = \ - value =3D ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; = \ - } = \ - HP_TIMING_NOW (Var) -# endif -#endif - -/* Use the widest available unsigned type if uint64_t is not - available. The algorithm below extracts a number less than 62**6 - (approximately 2**35.725) from uint64_t, so ancient hosts where - uintmax_t is only 32 bits lose about 3.725 bits of randomness, - which is better than not having mkstemp at all. */ -#if !defined UINT64_MAX && !defined uint64_t -# define uint64_t uintmax_t +# include +# define RANDOM_BITS(Var) ((Var) =3D random_bits ()) +typedef uint32_t random_value; +# define RANDOM_VALUE_MAX UINT32_MAX +# define BASE_62_DIGITS 5 /* 62**5 < UINT32_MAX */ +# define BASE_62_POWER (62 * 62 * 62 * 62 * 62) /* 2**BASE_62_DIGITS */ +#else +/* Use getentropy if it works, falling back on a 64-bit linear + congruential generator that starts with whatever Var's value + happens to be. */ +# define RANDOM_BITS(Var) \ + ((void) (getentropy (&(Var), sizeof (Var)) =3D=3D 0 \ + || ((Var) =3D 2862933555777941757 * (Var) + 3037000493))) +typedef uint_fast64_t random_value; +# define RANDOM_VALUE_MAX UINT_FAST64_MAX +# define BASE_62_DIGITS 10 /* 62**10 < UINT_FAST64_MAX */ +# define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 6= 2) #endif =20 #if _LIBC @@ -172,18 +156,80 @@ __path_search (char *tmpl, size_t tmpl_len, const c= har *dir, const char *pfx, } #endif /* _LIBC */ =20 +#if _LIBC +static int try_tempname_len (char *, int, void *, int (*) (char *, void = *), + size_t); +#endif + +static int +try_file (char *tmpl, void *flags) +{ + int *openflags =3D 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 _GL_UNUSED) +{ + return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); +} + +static int +try_nocreate (char *tmpl, void *flags _GL_UNUSED) +{ + struct_stat64 st; + + if (__lxstat64 (_STAT_VER, tmpl, &st) =3D=3D 0 || errno =3D=3D EOVERFL= OW) + __set_errno (EEXIST); + return errno =3D=3D ENOENT ? 0 : -1; +} + /* These are the characters used in temporary file names. */ static const char letters[] =3D "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; =20 +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e., end in at least X_SUFFIX_LEN "X"s, + possibly with a suffix). + The name constructed does not exist at the time of the call to + this function. 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 06= 00. + __GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +#ifdef _LIBC +static +#endif +int +gen_tempname_len (char *tmpl, int suffixlen, int flags, int kind, + size_t x_suffix_len) +{ + static int (*const tryfunc[]) (char *, void *) =3D + { + [__GT_FILE] =3D try_file, + [__GT_DIR] =3D try_dir, + [__GT_NOCREATE] =3D try_nocreate + }; + return try_tempname_len (tmpl, suffixlen, &flags, tryfunc[kind], + x_suffix_len); +} + +#ifdef _LIBC +static +#endif int -__try_tempname (char *tmpl, int suffixlen, void *args, - int (*tryfunc) (char *, void *)) +try_tempname_len (char *tmpl, int suffixlen, void *args, + int (*tryfunc) (char *, void *), size_t x_suffix_len) { - int len; + size_t len; char *XXXXXX; - static uint64_t value; - uint64_t random_time_bits; unsigned int count; int fd =3D -1; int save_errno =3D errno; @@ -193,7 +239,8 @@ __try_tempname (char *tmpl, int suffixlen, void *args= , can exist for a given template is 62**6. It should never be necessary to try all of these combinations. Instead if a reasonabl= e number of names is tried (we define reasonable as 62**3) fail to - give the system administrator the chance to remove the problems. *= / + give the system administrator the chance to remove the problems. + This value requires that X_SUFFIX_LEN be at least 3. */ #define ATTEMPTS_MIN (62 * 62 * 62) =20 /* The number of times to attempt to generate a temporary file. To @@ -204,44 +251,45 @@ __try_tempname (char *tmpl, int suffixlen, void *ar= gs, unsigned int attempts =3D ATTEMPTS_MIN; #endif =20 + /* A random variable. */ + random_value v; + + /* How many random base-62 digits can currently be extracted from V. = */ + int vdigits =3D 0; + + /* Least unfair value for V. If V is less than this, V can generate + BASE_62_DIGITS digits fairly. Otherwise it might be biased. */ + random_value const unfair_min + =3D RANDOM_VALUE_MAX - RANDOM_VALUE_MAX % BASE_62_POWER; + len =3D strlen (tmpl); - if (len < 6 + suffixlen || memcmp (&tmpl[len - 6 - suffixlen], "XXXXXX= ", 6)) + if (len < x_suffix_len + suffixlen + || strspn (&tmpl[len - x_suffix_len - suffixlen], "X") < x_suffix_= len) { __set_errno (EINVAL); return -1; } =20 /* This is where the Xs start. */ - XXXXXX =3D &tmpl[len - 6 - suffixlen]; - - /* Get some more or less random data. */ -#ifdef RANDOM_BITS - RANDOM_BITS (random_time_bits); -#else - { - struct timeval tv; - __gettimeofday (&tv, NULL); - random_time_bits =3D ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; - } -#endif - value +=3D random_time_bits ^ __getpid (); + XXXXXX =3D &tmpl[len - x_suffix_len - suffixlen]; =20 - for (count =3D 0; count < attempts; value +=3D 7777, ++count) + for (count =3D 0; count < attempts; ++count) { - uint64_t v =3D value; - - /* Fill in the random bits. */ - XXXXXX[0] =3D letters[v % 62]; - v /=3D 62; - XXXXXX[1] =3D letters[v % 62]; - v /=3D 62; - XXXXXX[2] =3D letters[v % 62]; - v /=3D 62; - XXXXXX[3] =3D letters[v % 62]; - v /=3D 62; - XXXXXX[4] =3D letters[v % 62]; - v /=3D 62; - XXXXXX[5] =3D letters[v % 62]; + for (size_t i =3D 0; i < x_suffix_len; i++) + { + if (vdigits =3D=3D 0) + { + do + RANDOM_BITS (v); + while (unfair_min <=3D v); + + vdigits =3D BASE_62_DIGITS; + } + + XXXXXX[i] =3D letters[v % 62]; + v /=3D 62; + vdigits--; + } =20 fd =3D tryfunc (tmpl, args); if (fd >=3D 0) @@ -258,66 +306,17 @@ __try_tempname (char *tmpl, int suffixlen, void *ar= gs, return -1; } =20 -static int -try_file (char *tmpl, void *flags) -{ - int *openflags =3D 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 _GL_UNUSED) -{ - return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); -} - -static int -try_nocreate (char *tmpl, void *flags _GL_UNUSED) +int +__gen_tempname (char *tmpl, int suffixlen, int flags, int kind) { - struct_stat64 st; - - if (__lxstat64 (_STAT_VER, tmpl, &st) =3D=3D 0 || errno =3D=3D EOVERFL= OW) - __set_errno (EEXIST); - return errno =3D=3D ENOENT ? 0 : -1; + return gen_tempname_len (tmpl, suffixlen, flags, kind, 6); } =20 -/* 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 06= 00. - __GT_DIR: create a directory, which will be mode 0700. - - We use a clever algorithm to get hard-to-predict names. */ +#if !_LIBC int -__gen_tempname (char *tmpl, int suffixlen, int flags, int kind) +try_tempname (char *tmpl, int suffixlen, void *args, + int (*tryfunc) (char *, void *)) { - int (*tryfunc) (char *, void *); - - switch (kind) - { - case __GT_FILE: - tryfunc =3D try_file; - break; - - case __GT_DIR: - tryfunc =3D try_dir; - break; - - case __GT_NOCREATE: - tryfunc =3D try_nocreate; - break; - - default: - assert (! "invalid KIND in __gen_tempname"); - abort (); - } - return __try_tempname (tmpl, suffixlen, &flags, tryfunc); + return try_tempname_len (tmpl, suffixlen, args, tryfunc, 6); } +#endif diff --git a/lib/tempname.h b/lib/tempname.h index abb926508..00dcbe4c9 100644 --- a/lib/tempname.h +++ b/lib/tempname.h @@ -50,6 +50,9 @@ extern "C" { =20 We use a clever algorithm to get hard-to-predict names. */ extern int gen_tempname (char *tmpl, int suffixlen, int flags, int kind)= ; +/* Similar, except X_SUFFIX_LEN gives the number of Xs. */ +extern int gen_tempname_len (char *tmpl, int suffixlen, int flags, int k= ind, + size_t x_suffix_len); =20 /* Similar to gen_tempname, but TRYFUNC is called for each temporary name to try. If TRYFUNC returns a non-negative number, TRY_GEN_TEMPN= AME @@ -57,6 +60,10 @@ extern int gen_tempname (char *tmpl, int suffixlen, in= t flags, int kind); name is tried, or else TRY_GEN_TEMPNAME returns -1. */ extern int try_tempname (char *tmpl, int suffixlen, void *args, int (*tryfunc) (char *, void *)); +/* Similar, except X_SUFFIX_LEN gives the number of Xs. */ +extern int try_tempname_len (char *tmpl, int suffixlen, void *args, + int (*tryfunc) (char *, void *), + size_t x_suffix_len); =20 #ifdef __cplusplus } diff --git a/modules/tempname b/modules/tempname index fa91ed397..6d6a034eb 100644 --- a/modules/tempname +++ b/modules/tempname @@ -10,12 +10,12 @@ m4/tempname.m4 Depends-on: extensions fcntl-h -gettimeofday +getentropy +libc-config lstat mkdir stdint sys_stat -sys_time =20 configure.ac: gl_FUNC_GEN_TEMPNAME --=20 2.17.1