1 /* backupfile.c -- make Emacs style backup file names
2 
3    Copyright (C) 1990-2006, 2009-2021 Free Software Foundation, Inc.
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17 
18 /* Written by Paul Eggert and David MacKenzie.
19    Some algorithms adapted from GNU Emacs.  */
20 
21 #include <config.h>
22 
23 #include "backup-internal.h"
24 
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "attribute.h"
34 #include "basename-lgpl.h"
35 #include "idx.h"
36 #include "opendirat.h"
37 #include "renameatu.h"
38 #include "xalloc-oversized.h"
39 
40 #ifndef _D_EXACT_NAMLEN
41 # define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
42 #endif
43 
44 #if ! (HAVE_PATHCONF && defined _PC_NAME_MAX)
45 # define pathconf(file, option) (errno = -1)
46 # define fpathconf(fd, option) (errno = -1)
47 #endif
48 
49 #ifndef _POSIX_NAME_MAX
50 # define _POSIX_NAME_MAX 14
51 #endif
52 
53 #if defined _XOPEN_NAME_MAX
54 # define NAME_MAX_MINIMUM _XOPEN_NAME_MAX
55 #else
56 # define NAME_MAX_MINIMUM _POSIX_NAME_MAX
57 #endif
58 
59 #ifndef HAVE_DOS_FILE_NAMES
60 # define HAVE_DOS_FILE_NAMES 0
61 #endif
62 #ifndef HAVE_LONG_FILE_NAMES
63 # define HAVE_LONG_FILE_NAMES 0
64 #endif
65 
66 /* ISDIGIT differs from isdigit, as follows:
67    - Its arg may be any int or unsigned int; it need not be an unsigned char
68      or EOF.
69    - It's typically faster.
70    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
71    ISDIGIT unless it's important to use the locale's definition
72    of "digit" even when the host does not conform to POSIX.  */
73 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
74 
75 /* The extension added to file names to produce a simple (as opposed
76    to numbered) backup file name. */
77 char const *simple_backup_suffix = NULL;
78 
79 /* Set SIMPLE_BACKUP_SUFFIX to S, or to a default specified by the
80    environment if S is null.  If S or the environment does not specify
81    a valid backup suffix, use "~".  */
82 void
set_simple_backup_suffix(char const * s)83 set_simple_backup_suffix (char const *s)
84 {
85   if (!s)
86     s = getenv ("SIMPLE_BACKUP_SUFFIX");
87   simple_backup_suffix = s && *s && s == last_component (s) ? s : "~";
88 }
89 
90 /* If FILE (which was of length FILELEN before an extension was
91    appended to it) is too long, replace the extension with the single
92    char E.  If the result is still too long, remove the char just
93    before E.
94 
95    If DIR_FD is nonnegative, it is a file descriptor for FILE's parent.
96    *NAME_MAX is either 0, or the cached result of a previous call for
97    FILE's parent's _PC_NAME_MAX.  */
98 
99 static void
check_extension(char * file,size_t filelen,char e,int dir_fd,size_t * base_max)100 check_extension (char *file, size_t filelen, char e,
101                  int dir_fd, size_t *base_max)
102 {
103   char *base = last_component (file);
104   size_t baselen = base_len (base);
105   size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM;
106 
107   if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen)
108     {
109       /* The new base name is long enough to require a pathconf check.  */
110       if (*base_max == 0)
111         {
112           long name_max;
113           if (dir_fd < 0)
114             {
115               /* Temporarily modify the buffer into its parent
116                  directory name, invoke pathconf on the directory, and
117                  then restore the buffer.  */
118               char tmp[sizeof "."];
119               memcpy (tmp, base, sizeof ".");
120               strcpy (base, ".");
121               errno = 0;
122               name_max = pathconf (file, _PC_NAME_MAX);
123               name_max -= !errno;
124               memcpy (base, tmp, sizeof ".");
125             }
126           else
127             {
128               errno = 0;
129               name_max = fpathconf (dir_fd, _PC_NAME_MAX);
130               name_max -= !errno;
131             }
132 
133           *base_max = (0 <= name_max && name_max <= SIZE_MAX ? name_max
134                        : name_max < -1 ? NAME_MAX_MINIMUM : SIZE_MAX);
135         }
136 
137       baselen_max = *base_max;
138     }
139 
140   if (HAVE_DOS_FILE_NAMES && baselen_max <= 12)
141     {
142       /* Live within DOS's 8.3 limit.  */
143       char *dot = strchr (base, '.');
144       if (!dot)
145         baselen_max = 8;
146       else
147         {
148           char const *second_dot = strchr (dot + 1, '.');
149           baselen_max = (second_dot
150                          ? second_dot - base
151                          : dot + 1 - base + 3);
152         }
153     }
154 
155   if (baselen_max < baselen)
156     {
157       baselen = file + filelen - base;
158       if (baselen_max <= baselen)
159         baselen = baselen_max - 1;
160       base[baselen] = e;
161       base[baselen + 1] = '\0';
162     }
163 }
164 
165 /* Returned values for NUMBERED_BACKUP.  */
166 
167 enum numbered_backup_result
168   {
169     /* The new backup name is the same length as an existing backup
170        name, so it's valid for that directory.  */
171     BACKUP_IS_SAME_LENGTH,
172 
173     /* Some backup names already exist, but the returned name is longer
174        than any of them, and its length should be checked.  */
175     BACKUP_IS_LONGER,
176 
177     /* There are no existing backup names.  The new name's length
178        should be checked.  */
179     BACKUP_IS_NEW,
180 
181     /* Memory allocation failure.  */
182     BACKUP_NOMEM
183   };
184 
185 /* Relative to DIR_FD, *BUFFER contains a file name.
186    Store into *BUFFER the next backup name for the named file,
187    with a version number greater than all the
188    existing numbered backups.  Reallocate *BUFFER as necessary; its
189    initial allocated size is BUFFER_SIZE, which must be at least 4
190    bytes longer than the file name to make room for the initially
191    appended ".~1".  FILELEN is the length of the original file name.
192    BASE_OFFSET is the offset of the basename in *BUFFER.
193    The returned value indicates what kind of backup was found.  If an
194    I/O or other read error occurs, use the highest backup number that
195    was found.
196 
197    *DIRPP is the destination directory.  If *DIRPP is null, open the
198    destination directory and store the resulting stream into *DIRPP
199    and its file descriptor into *PNEW_FD without closing the stream.  */
200 
201 static enum numbered_backup_result
numbered_backup(int dir_fd,char ** buffer,size_t buffer_size,size_t filelen,idx_t base_offset,DIR ** dirpp,int * pnew_fd)202 numbered_backup (int dir_fd, char **buffer, size_t buffer_size, size_t filelen,
203                  idx_t base_offset, DIR **dirpp, int *pnew_fd)
204 {
205   enum numbered_backup_result result = BACKUP_IS_NEW;
206   DIR *dirp = *dirpp;
207   struct dirent *dp;
208   char *buf = *buffer;
209   size_t versionlenmax = 1;
210   char *base = buf + base_offset;
211   size_t baselen = base_len (base);
212 
213   if (dirp)
214     rewinddir (dirp);
215   else
216     {
217       /* Temporarily modify the buffer into its parent directory name,
218          open the directory, and then restore the buffer.  */
219       char tmp[sizeof "."];
220       memcpy (tmp, base, sizeof ".");
221       strcpy (base, ".");
222       dirp = opendirat (dir_fd, buf, 0, pnew_fd);
223       if (!dirp && errno == ENOMEM)
224         result = BACKUP_NOMEM;
225       memcpy (base, tmp, sizeof ".");
226       strcpy (base + baselen, ".~1~");
227       if (!dirp)
228         return result;
229       *dirpp = dirp;
230     }
231 
232   while ((dp = readdir (dirp)) != NULL)
233     {
234       char const *p;
235       char *q;
236       bool all_9s;
237       size_t versionlen;
238 
239       if (_D_EXACT_NAMLEN (dp) < baselen + 4)
240         continue;
241 
242       if (memcmp (buf + base_offset, dp->d_name, baselen + 2) != 0)
243         continue;
244 
245       p = dp->d_name + baselen + 2;
246 
247       /* Check whether this file has a version number and if so,
248          whether it is larger.  Use string operations rather than
249          integer arithmetic, to avoid problems with integer overflow.  */
250 
251       if (! ('1' <= *p && *p <= '9'))
252         continue;
253       all_9s = (*p == '9');
254       for (versionlen = 1; ISDIGIT (p[versionlen]); versionlen++)
255         all_9s &= (p[versionlen] == '9');
256 
257       if (! (p[versionlen] == '~' && !p[versionlen + 1]
258              && (versionlenmax < versionlen
259                  || (versionlenmax == versionlen
260                      && memcmp (buf + filelen + 2, p, versionlen) <= 0))))
261         continue;
262 
263       /* This entry has the largest version number seen so far.
264          Append this highest numbered extension to the file name,
265          prepending '0' to the number if it is all 9s.  */
266 
267       versionlenmax = all_9s + versionlen;
268       result = (all_9s ? BACKUP_IS_LONGER : BACKUP_IS_SAME_LENGTH);
269       size_t new_buffer_size = filelen + 2 + versionlenmax + 2;
270       if (buffer_size < new_buffer_size)
271         {
272           if (! xalloc_oversized (new_buffer_size, 2))
273             new_buffer_size *= 2;
274           char *new_buf = realloc (buf, new_buffer_size);
275           if (!new_buf)
276             {
277               *buffer = buf;
278               return BACKUP_NOMEM;
279             }
280           buf = new_buf;
281           buffer_size = new_buffer_size;
282         }
283       q = buf + filelen;
284       *q++ = '.';
285       *q++ = '~';
286       *q = '0';
287       q += all_9s;
288       memcpy (q, p, versionlen + 2);
289 
290       /* Add 1 to the version number.  */
291 
292       q += versionlen;
293       while (*--q == '9')
294         *q = '0';
295       ++*q;
296     }
297 
298   *buffer = buf;
299   return result;
300 }
301 
302 /* Relative to DIR_FD, return the name of the new backup file for the
303    existing file FILE, allocated with malloc.
304    If RENAME, also rename FILE to the new name.
305    On failure, return NULL and set errno.
306    Do not call this function if backup_type == no_backups.  */
307 
308 char *
backupfile_internal(int dir_fd,char const * file,enum backup_type backup_type,bool rename)309 backupfile_internal (int dir_fd, char const *file,
310                      enum backup_type backup_type, bool rename)
311 {
312   idx_t base_offset = last_component (file) - file;
313   size_t filelen = base_offset + strlen (file + base_offset);
314 
315   if (! simple_backup_suffix)
316     set_simple_backup_suffix (NULL);
317 
318   /* Allow room for simple or ".~N~" backups.  The guess must be at
319      least sizeof ".~1~", but otherwise will be adjusted as needed.  */
320   size_t simple_backup_suffix_size = strlen (simple_backup_suffix) + 1;
321   size_t backup_suffix_size_guess = simple_backup_suffix_size;
322   enum { GUESS = sizeof ".~12345~" };
323   if (backup_suffix_size_guess < GUESS)
324     backup_suffix_size_guess = GUESS;
325 
326   ssize_t ssize = filelen + backup_suffix_size_guess + 1;
327   char *s = malloc (ssize);
328   if (!s)
329     return s;
330 
331   DIR *dirp = NULL;
332   int sdir = -1;
333   size_t base_max = 0;
334   while (true)
335     {
336       memcpy (s, file, filelen + 1);
337 
338       if (backup_type == simple_backups)
339         memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size);
340       else
341         switch (numbered_backup (dir_fd, &s, ssize, filelen, base_offset,
342                                  &dirp, &sdir))
343           {
344           case BACKUP_IS_SAME_LENGTH:
345             break;
346 
347           case BACKUP_IS_NEW:
348             if (backup_type == numbered_existing_backups)
349               {
350                 backup_type = simple_backups;
351                 memcpy (s + filelen, simple_backup_suffix,
352                         simple_backup_suffix_size);
353               }
354             FALLTHROUGH;
355           case BACKUP_IS_LONGER:
356             check_extension (s, filelen, '~', sdir, &base_max);
357             break;
358 
359           case BACKUP_NOMEM:
360             if (dirp)
361               closedir (dirp);
362             free (s);
363             errno = ENOMEM;
364             return NULL;
365           }
366 
367       if (! rename)
368         break;
369 
370       if (sdir < 0)
371         {
372           sdir = AT_FDCWD;
373           base_offset = 0;
374         }
375       unsigned flags = backup_type == simple_backups ? 0 : RENAME_NOREPLACE;
376       if (renameatu (AT_FDCWD, file, sdir, s + base_offset, flags) == 0)
377         break;
378       int e = errno;
379       if (e != EEXIST)
380         {
381           if (dirp)
382             closedir (dirp);
383           free (s);
384           errno = e;
385           return NULL;
386         }
387     }
388 
389   if (dirp)
390     closedir (dirp);
391   return s;
392 }
393