root/src/dps8/utfile.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hash32s
  2. utfile_mkstemps

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 485f7d3b-f630-11ec-953b-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2021-2024 The DPS8M Development Team
   9  *
  10  * This software is made available under the terms of the ICU License.
  11  * See the LICENSE.md file at the top-level directory of this distribution.
  12  *
  13  * ---------------------------------------------------------------------------
  14  */
  15 
  16 #include <ctype.h>
  17 #include <signal.h>
  18 #include <errno.h>
  19 #include <fcntl.h>
  20 #include <stdio.h>
  21 #include <stdlib.h>
  22 #include <string.h>
  23 #if defined(__sunos) && defined(SYSV)
  24 # include <sys/param.h>
  25 #endif /* if defined(__sunos) && defined(SYSV) */
  26 #include <sys/stat.h>
  27 #include <sys/types.h>
  28 #include <time.h>
  29 #include <sys/time.h>
  30 #include <stdint.h>
  31 #include <unistd.h>
  32 
  33 #define MAX_MKSTEMPS_TRIES 10000
  34 
  35 #if defined(__MINGW64__) || defined(__MINGW32__)
  36 # include "bsd_random.h"
  37 # define random  bsd_random
  38 # define srandom bsd_srandom
  39 #endif /* if defined(__MINGW64__) || defined(__MINGW32__) */
  40 
  41 #if defined(FREE)
  42 # undef FREE
  43 #endif /* if defined(FREE) */
  44 #define FREE(p) do  \
  45   {                 \
  46     free((p));      \
  47     (p) = NULL;     \
  48   } while(0)
  49 
  50 static char valid_file_name_chars[]
  51   = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  52 
  53 static inline uint32_t
  54 hash32s(const void *buf, size_t len, uint32_t h)
     /* [previous][next][first][last][top][bottom][index][help] */
  55 {
  56   const unsigned char *p = buf;
  57 
  58   for (size_t i = 0; i < len; i++)
  59     h = h * 31 + p[i];
  60 
  61   h ^= h >> 17;
  62   h *= UINT32_C(0xed5ad4bb);
  63   h ^= h >> 11;
  64   h *= UINT32_C(0xac4c1b51);
  65   h ^= h >> 15;
  66   h *= UINT32_C(0x31848bab);
  67   h ^= h >> 14;
  68 
  69   return h;
  70 }
  71 
  72  /*
  73   * This is a minimal portable replacement for the mkstemps
  74   * function, which is not available on all platforms.
  75   */
  76 
  77 int
  78 utfile_mkstemps(char *request_pattern, int suffix_length)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80   long pattern_length;
  81   int st1ret;
  82   char *mask_pointer;
  83   struct timespec st1;
  84 
  85   char *pattern = strdup(request_pattern);
  86   if (!pattern)
  87     {
  88       (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
  89                      __func__, __FILE__, __LINE__);
  90 #if defined(USE_BACKTRACE)
  91 # if defined(SIGUSR2)
  92       (void)raise(SIGUSR2);
  93       /*NOTREACHED*/ /* unreachable */
  94 # endif /* if defined(SIGUSR2) */
  95 #endif /* if defined(USE_BACKTRACE) */
  96       abort();
  97     }
  98 
  99   pattern_length = (long) strlen(pattern);
 100 
 101 #if defined(USE_MONOTONIC)
 102   st1ret = clock_gettime(CLOCK_MONOTONIC, &st1);
 103 #else
 104   st1ret = clock_gettime(CLOCK_REALTIME, &st1);
 105 #endif /* if defined(USE_MONOTONIC) */
 106   if (st1ret != 0)
 107     {
 108       (void)fprintf (stderr, "\rFATAL: clock_gettime failure! Aborting at %s[%s:%d]\r\n",
 109                      __func__, __FILE__, __LINE__);
 110 #if defined(USE_BACKTRACE)
 111 # if defined(SIGUSR2)
 112       (void)raise(SIGUSR2);
 113       /*NOTREACHED*/ /* unreachable */
 114 # endif /* if defined(SIGUSR2) */
 115 #endif /* if defined(USE_BACKTRACE) */
 116       abort();
 117     }
 118 
 119   uint32_t h = 0;  /* initial hash value */
 120 #if __STDC_VERSION__ < 201112L
 121   /* LINTED E_OLD_STYLE_FUNC_DECL */
 122   void *(*mallocptr)() = malloc;
 123   h = hash32s(&mallocptr, sizeof(mallocptr), h);
 124 #endif /* if __STDC_VERSION__ < 201112L */
 125   void *small = malloc(1);
 126   h = hash32s(&small, sizeof(small), h);
 127   FREE(small);
 128   void *big = malloc(1UL << 20);
 129   h = hash32s(&big, sizeof(big), h);
 130   FREE(big);
 131   void *ptr = &ptr;
 132   h = hash32s(&ptr, sizeof(ptr), h);
 133   time_t t = time(0);
 134   h = hash32s(&t, sizeof(t), h);
 135 #if !defined(_AIX)
 136   for (int i = 0; i < 1000; i++)
 137     {
 138       unsigned long counter = 0;
 139       clock_t start = clock();
 140       while (clock() == start)
 141         {
 142           counter++;
 143         }
 144       h = hash32s(&start, sizeof(start), h);
 145       h = hash32s(&counter, sizeof(counter), h);
 146     }
 147 #endif /* if !defined(_AIX) */
 148   int mypid = (int)getpid();
 149   h = hash32s(&mypid, sizeof(mypid), h);
 150   char rnd[4];
 151   FILE *f = fopen("/dev/urandom", "rb");
 152   if (f)
 153     {
 154       if (fread(rnd, sizeof(rnd), 1, f))
 155         {
 156           h = hash32s(rnd, sizeof(rnd), h);
 157         }
 158       fclose(f);
 159     }
 160   srandom(h);
 161 
 162   if (( (long) pattern_length - 6 ) < (long) suffix_length)
 163   {
 164     FREE(pattern);
 165     return ( -1 );
 166   }
 167 
 168   long mask_offset = (long) pattern_length - ( 6 + (long) suffix_length );
 169 
 170   if (strncmp(&pattern[mask_offset], "XXXXXX", 6))
 171   {
 172     FREE(pattern);
 173     return ( -1 );
 174   }
 175 
 176   mask_pointer = &pattern[mask_offset];
 177 
 178   long valid_char_count = (long) strlen(valid_file_name_chars);
 179 
 180   if (valid_char_count < 1)
 181     {
 182       FREE(pattern);
 183       return ( -1 );
 184     }
 185 
 186   for (int count = 0; count < MAX_MKSTEMPS_TRIES; count++)
 187   {
 188     for (int mask_index = 0; mask_index < 6; mask_index++)
 189     {
 190       mask_pointer[mask_index]
 191         = valid_file_name_chars[random() % valid_char_count];
 192     }
 193 
 194     int fd = open(pattern, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
 195 
 196     if (fd >= 0)
 197     {
 198       FREE(pattern);
 199       return ( fd );
 200     }
 201 
 202     /*
 203      * If the error is not "file already exists",
 204      * or is a directory, then we just bail out.
 205      */
 206 
 207     if (( errno != EEXIST ) && ( errno != EISDIR ))
 208     {
 209       break;
 210     }
 211   }
 212 
 213   /*
 214    * If we get here, we were unable to create
 215    * a unique file name, despite many tries.
 216    */
 217 
 218   FREE(pattern);
 219   return ( -1 );
 220 }

/* [previous][next][first][last][top][bottom][index][help] */