This source file includes following definitions.
- set_simple_backup_suffix
 
- check_extension
 
- numbered_backup
 
- backupfile_internal
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 #include <config.h>
  22 
  23 #include "backup-internal.h"
  24 
  25 #include <dirent.h>
  26 #include <errno.h>
  27 #include <fcntl.h>
  28 #include <stdbool.h>
  29 #include <stdint.h>
  30 #include <stdlib.h>
  31 #include <string.h>
  32 #include <unistd.h>
  33 
  34 #include "attribute.h"
  35 #include "basename-lgpl.h"
  36 #include "idx.h"
  37 #include "intprops.h"
  38 #include "opendirat.h"
  39 #include "renameatu.h"
  40 
  41 #ifndef _D_EXACT_NAMLEN
  42 # define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
  43 #endif
  44 
  45 #if ! (HAVE_PATHCONF && defined _PC_NAME_MAX)
  46 # define pathconf(file, option) (errno = -1)
  47 # define fpathconf(fd, option) (errno = -1)
  48 #endif
  49 
  50 #ifndef _POSIX_NAME_MAX
  51 # define _POSIX_NAME_MAX 14
  52 #endif
  53 
  54 #if defined _XOPEN_NAME_MAX
  55 # define NAME_MAX_MINIMUM _XOPEN_NAME_MAX
  56 #else
  57 # define NAME_MAX_MINIMUM _POSIX_NAME_MAX
  58 #endif
  59 
  60 #ifndef HAVE_DOS_FILE_NAMES
  61 # define HAVE_DOS_FILE_NAMES 0
  62 #endif
  63 #ifndef HAVE_LONG_FILE_NAMES
  64 # define HAVE_LONG_FILE_NAMES 0
  65 #endif
  66 
  67 
  68 
  69 
  70 
  71 
  72 
  73 
  74 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
  75 
  76 
  77 
  78 char const *simple_backup_suffix = NULL;
  79 
  80 
  81 
  82 
  83 void
  84 set_simple_backup_suffix (char const *s)
     
  85 {
  86   if (!s)
  87     s = getenv ("SIMPLE_BACKUP_SUFFIX");
  88   simple_backup_suffix = s && *s && s == last_component (s) ? s : "~";
  89 }
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 
  98 
  99 
 100 static void
 101 check_extension (char *file, size_t filelen, char e,
     
 102                  int dir_fd, size_t *base_max)
 103 {
 104   char *base = last_component (file);
 105   size_t baselen = base_len (base);
 106   size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM;
 107 
 108   if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen)
 109     {
 110       
 111       if (*base_max == 0)
 112         {
 113           long name_max;
 114           if (dir_fd < 0)
 115             {
 116               
 117 
 118 
 119               char tmp[sizeof "."];
 120               memcpy (tmp, base, sizeof ".");
 121               strcpy (base, ".");
 122               errno = 0;
 123               name_max = pathconf (file, _PC_NAME_MAX);
 124               name_max -= !errno;
 125               memcpy (base, tmp, sizeof ".");
 126             }
 127           else
 128             {
 129               errno = 0;
 130               name_max = fpathconf (dir_fd, _PC_NAME_MAX);
 131               name_max -= !errno;
 132             }
 133 
 134           *base_max = (0 <= name_max && name_max <= SIZE_MAX ? name_max
 135                        : name_max < -1 ? NAME_MAX_MINIMUM : SIZE_MAX);
 136         }
 137 
 138       baselen_max = *base_max;
 139     }
 140 
 141   if (HAVE_DOS_FILE_NAMES && baselen_max <= 12)
 142     {
 143       
 144       char *dot = strchr (base, '.');
 145       if (!dot)
 146         baselen_max = 8;
 147       else
 148         {
 149           char const *second_dot = strchr (dot + 1, '.');
 150           baselen_max = (second_dot
 151                          ? second_dot - base
 152                          : dot + 1 - base + 3);
 153         }
 154     }
 155 
 156   if (baselen_max < baselen)
 157     {
 158       baselen = file + filelen - base;
 159       if (baselen_max <= baselen)
 160         baselen = baselen_max - 1;
 161       base[baselen] = e;
 162       base[baselen + 1] = '\0';
 163     }
 164 }
 165 
 166 
 167 
 168 enum numbered_backup_result
 169   {
 170     
 171 
 172     BACKUP_IS_SAME_LENGTH,
 173 
 174     
 175 
 176     BACKUP_IS_LONGER,
 177 
 178     
 179 
 180     BACKUP_IS_NEW,
 181 
 182     
 183     BACKUP_NOMEM
 184   };
 185 
 186 
 187 
 188 
 189 
 190 
 191 
 192 
 193 
 194 
 195 
 196 
 197 
 198 
 199 
 200 
 201 
 202 static enum numbered_backup_result
 203 numbered_backup (int dir_fd, char **buffer, size_t buffer_size, size_t filelen,
     
 204                  idx_t base_offset, DIR **dirpp, int *pnew_fd)
 205 {
 206   enum numbered_backup_result result = BACKUP_IS_NEW;
 207   DIR *dirp = *dirpp;
 208   struct dirent *dp;
 209   char *buf = *buffer;
 210   size_t versionlenmax = 1;
 211   char *base = buf + base_offset;
 212   size_t baselen = base_len (base);
 213 
 214   if (dirp)
 215     rewinddir (dirp);
 216   else
 217     {
 218       
 219 
 220       char tmp[sizeof "."];
 221       memcpy (tmp, base, sizeof ".");
 222       strcpy (base, ".");
 223       dirp = opendirat (dir_fd, buf, 0, pnew_fd);
 224       if (!dirp && errno == ENOMEM)
 225         result = BACKUP_NOMEM;
 226       memcpy (base, tmp, sizeof ".");
 227       strcpy (base + baselen, ".~1~");
 228       if (!dirp)
 229         return result;
 230       *dirpp = dirp;
 231     }
 232 
 233   while ((dp = readdir (dirp)) != NULL)
 234     {
 235       char const *p;
 236       char *q;
 237       bool all_9s;
 238       size_t versionlen;
 239 
 240       if (_D_EXACT_NAMLEN (dp) < baselen + 4)
 241         continue;
 242 
 243       if (memcmp (buf + base_offset, dp->d_name, baselen + 2) != 0)
 244         continue;
 245 
 246       p = dp->d_name + baselen + 2;
 247 
 248       
 249 
 250 
 251 
 252       if (! ('1' <= *p && *p <= '9'))
 253         continue;
 254       all_9s = (*p == '9');
 255       for (versionlen = 1; ISDIGIT (p[versionlen]); versionlen++)
 256         all_9s &= (p[versionlen] == '9');
 257 
 258       if (! (p[versionlen] == '~' && !p[versionlen + 1]
 259              && (versionlenmax < versionlen
 260                  || (versionlenmax == versionlen
 261                      && memcmp (buf + filelen + 2, p, versionlen) <= 0))))
 262         continue;
 263 
 264       
 265 
 266 
 267 
 268       versionlenmax = all_9s + versionlen;
 269       result = (all_9s ? BACKUP_IS_LONGER : BACKUP_IS_SAME_LENGTH);
 270       size_t new_buffer_size = filelen + 2 + versionlenmax + 2;
 271       if (buffer_size < new_buffer_size)
 272         {
 273           size_t grown;
 274           if (! INT_ADD_WRAPV (new_buffer_size, new_buffer_size >> 1, &grown))
 275             new_buffer_size = grown;
 276           char *new_buf = realloc (buf, new_buffer_size);
 277           if (!new_buf)
 278             {
 279               *buffer = buf;
 280               return BACKUP_NOMEM;
 281             }
 282           buf = new_buf;
 283           buffer_size = new_buffer_size;
 284         }
 285       q = buf + filelen;
 286       *q++ = '.';
 287       *q++ = '~';
 288       *q = '0';
 289       q += all_9s;
 290       memcpy (q, p, versionlen + 2);
 291 
 292       
 293 
 294       q += versionlen;
 295       while (*--q == '9')
 296         *q = '0';
 297       ++*q;
 298     }
 299 
 300   *buffer = buf;
 301   return result;
 302 }
 303 
 304 
 305 
 306 
 307 
 308 
 309 
 310 char *
 311 backupfile_internal (int dir_fd, char const *file,
     
 312                      enum backup_type backup_type, bool rename)
 313 {
 314   idx_t base_offset = last_component (file) - file;
 315   size_t filelen = base_offset + strlen (file + base_offset);
 316 
 317   if (! simple_backup_suffix)
 318     set_simple_backup_suffix (NULL);
 319 
 320   
 321 
 322   size_t simple_backup_suffix_size = strlen (simple_backup_suffix) + 1;
 323   size_t backup_suffix_size_guess = simple_backup_suffix_size;
 324   enum { GUESS = sizeof ".~12345~" };
 325   if (backup_suffix_size_guess < GUESS)
 326     backup_suffix_size_guess = GUESS;
 327 
 328   ssize_t ssize = filelen + backup_suffix_size_guess + 1;
 329   char *s = malloc (ssize);
 330   if (!s)
 331     return s;
 332 
 333   DIR *dirp = NULL;
 334   int sdir = -1;
 335   size_t base_max = 0;
 336   while (true)
 337     {
 338       memcpy (s, file, filelen + 1);
 339 
 340       if (backup_type == simple_backups)
 341         memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size);
 342       else
 343         switch (numbered_backup (dir_fd, &s, ssize, filelen, base_offset,
 344                                  &dirp, &sdir))
 345           {
 346           case BACKUP_IS_SAME_LENGTH:
 347             break;
 348 
 349           case BACKUP_IS_NEW:
 350             if (backup_type == numbered_existing_backups)
 351               {
 352                 backup_type = simple_backups;
 353                 memcpy (s + filelen, simple_backup_suffix,
 354                         simple_backup_suffix_size);
 355               }
 356             FALLTHROUGH;
 357           case BACKUP_IS_LONGER:
 358             check_extension (s, filelen, '~', sdir, &base_max);
 359             break;
 360 
 361           case BACKUP_NOMEM:
 362             if (dirp)
 363               closedir (dirp);
 364             free (s);
 365             errno = ENOMEM;
 366             return NULL;
 367           }
 368 
 369       if (! rename)
 370         break;
 371 
 372       if (sdir < 0)
 373         {
 374           sdir = AT_FDCWD;
 375           base_offset = 0;
 376         }
 377       unsigned flags = backup_type == simple_backups ? 0 : RENAME_NOREPLACE;
 378       if (renameatu (AT_FDCWD, file, sdir, s + base_offset, flags) == 0)
 379         break;
 380       int e = errno;
 381       if (e != EEXIST)
 382         {
 383           if (dirp)
 384             closedir (dirp);
 385           free (s);
 386           errno = e;
 387           return NULL;
 388         }
 389     }
 390 
 391   if (dirp)
 392     closedir (dirp);
 393   return s;
 394 }