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