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 "ialloc.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
101 static bool
102 check_extension (char *file, idx_t filelen, char e,
103 int dir_fd, idx_t *base_max)
104 {
105 char *base = last_component (file);
106 idx_t baselen = base_len (base);
107 idx_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM;
108
109 if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen)
110 {
111
112 if (*base_max == 0)
113 {
114 long name_max;
115 if (dir_fd < 0)
116 {
117
118
119
120 char tmp[sizeof "."];
121 memcpy (tmp, base, sizeof ".");
122 strcpy (base, ".");
123 errno = 0;
124 name_max = pathconf (file, _PC_NAME_MAX);
125 name_max -= !errno;
126 memcpy (base, tmp, sizeof ".");
127 }
128 else
129 {
130 errno = 0;
131 name_max = fpathconf (dir_fd, _PC_NAME_MAX);
132 name_max -= !errno;
133 }
134
135 *base_max = (0 <= name_max && name_max <= SIZE_MAX ? name_max
136 : name_max < -1 ? NAME_MAX_MINIMUM : SIZE_MAX);
137 }
138
139 baselen_max = *base_max;
140 }
141
142 if (HAVE_DOS_FILE_NAMES && baselen_max <= 12)
143 {
144
145 char *dot = strchr (base, '.');
146 if (!dot)
147 baselen_max = 8;
148 else
149 {
150 char const *second_dot = strchr (dot + 1, '.');
151 baselen_max = (second_dot
152 ? second_dot - base
153 : dot + 1 - base + 3);
154 }
155 }
156
157 if (baselen <= baselen_max)
158 return true;
159 else
160 {
161 baselen = file + filelen - base;
162 if (baselen_max <= baselen)
163 baselen = baselen_max - 1;
164 base[baselen] = e;
165 base[baselen + 1] = '\0';
166 return false;
167 }
168 }
169
170
171
172 enum numbered_backup_result
173 {
174
175
176 BACKUP_IS_SAME_LENGTH,
177
178
179
180 BACKUP_IS_LONGER,
181
182
183
184 BACKUP_IS_NEW,
185
186
187 BACKUP_NOMEM
188 };
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 static enum numbered_backup_result
209 numbered_backup (int dir_fd, char **buffer, idx_t buffer_size, idx_t filelen,
210 idx_t base_offset, DIR **dirpp, int *pnew_fd)
211 {
212 enum numbered_backup_result result = BACKUP_IS_NEW;
213 DIR *dirp = *dirpp;
214 char *buf = *buffer;
215 idx_t versionlenmax = 1;
216 idx_t baselen = filelen - base_offset;
217
218 if (dirp)
219 rewinddir (dirp);
220 else
221 {
222
223
224 char tmp[sizeof "."];
225 char *base = buf + base_offset;
226 memcpy (tmp, base, sizeof ".");
227 strcpy (base, ".");
228 dirp = opendirat (dir_fd, buf, 0, pnew_fd);
229 if (!dirp && errno == ENOMEM)
230 result = BACKUP_NOMEM;
231 memcpy (base, tmp, sizeof ".");
232 strcpy (base + baselen, ".~1~");
233 if (!dirp)
234 return result;
235 *dirpp = dirp;
236 }
237
238 for (struct dirent *dp; (dp = readdir (dirp)) != NULL; )
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 char const *p = dp->d_name + baselen + 2;
247
248
249
250
251
252 if (! ('1' <= *p && *p <= '9'))
253 continue;
254 bool all_9s = (*p == '9');
255 idx_t versionlen;
256 for (versionlen = 1; ISDIGIT (p[versionlen]); versionlen++)
257 all_9s &= (p[versionlen] == '9');
258
259 if (! (p[versionlen] == '~' && !p[versionlen + 1]
260 && (versionlenmax < versionlen
261 || (versionlenmax == versionlen
262 && memcmp (buf + filelen + 2, p, versionlen) <= 0))))
263 continue;
264
265
266
267
268
269 versionlenmax = all_9s + versionlen;
270 result = (all_9s ? BACKUP_IS_LONGER : BACKUP_IS_SAME_LENGTH);
271 idx_t new_buffer_size = filelen + 2 + versionlenmax + 2;
272 if (buffer_size < new_buffer_size)
273 {
274 idx_t grown;
275 if (! INT_ADD_WRAPV (new_buffer_size, new_buffer_size >> 1, &grown))
276 new_buffer_size = grown;
277 char *new_buf = irealloc (buf, new_buffer_size);
278 if (!new_buf)
279 {
280 *buffer = buf;
281 return BACKUP_NOMEM;
282 }
283 buf = new_buf;
284 buffer_size = new_buffer_size;
285 }
286 char *q = buf + filelen;
287 *q++ = '.';
288 *q++ = '~';
289 *q = '0';
290 q += all_9s;
291 memcpy (q, p, versionlen + 2);
292
293
294
295 q += versionlen;
296 while (*--q == '9')
297 *q = '0';
298 ++*q;
299 }
300
301 *buffer = buf;
302 return result;
303 }
304
305
306
307
308
309
310
311 char *
312 backupfile_internal (int dir_fd, char const *file,
313 enum backup_type backup_type, bool rename)
314 {
315 idx_t base_offset = last_component (file) - file;
316 idx_t filelen = base_offset + base_len (file + base_offset);
317
318 if (! simple_backup_suffix)
319 set_simple_backup_suffix (NULL);
320
321
322
323 idx_t simple_backup_suffix_size = strlen (simple_backup_suffix) + 1;
324 idx_t backup_suffix_size_guess = simple_backup_suffix_size;
325 enum { GUESS = sizeof ".~12345~" };
326 if (backup_suffix_size_guess < GUESS)
327 backup_suffix_size_guess = GUESS;
328
329 idx_t ssize = filelen + backup_suffix_size_guess + 1;
330 char *s = imalloc (ssize);
331 if (!s)
332 return s;
333
334 DIR *dirp = NULL;
335 int sdir = -1;
336 idx_t base_max = 0;
337 while (true)
338 {
339 bool extended = true;
340 memcpy (s, file, filelen);
341
342 if (backup_type == simple_backups)
343 memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size);
344 else
345 switch (numbered_backup (dir_fd, &s, ssize, filelen, base_offset,
346 &dirp, &sdir))
347 {
348 case BACKUP_IS_SAME_LENGTH:
349 break;
350
351 case BACKUP_IS_NEW:
352 if (backup_type == numbered_existing_backups)
353 {
354 backup_type = simple_backups;
355 memcpy (s + filelen, simple_backup_suffix,
356 simple_backup_suffix_size);
357 }
358 FALLTHROUGH;
359 case BACKUP_IS_LONGER:
360 extended = check_extension (s, filelen, '~', sdir, &base_max);
361 break;
362
363 case BACKUP_NOMEM:
364 if (dirp)
365 closedir (dirp);
366 free (s);
367 errno = ENOMEM;
368 return NULL;
369 }
370
371 if (! rename)
372 break;
373
374 if (sdir < 0)
375 {
376 sdir = AT_FDCWD;
377 base_offset = 0;
378 }
379 unsigned flags = backup_type == simple_backups ? 0 : RENAME_NOREPLACE;
380 if (renameatu (AT_FDCWD, file, sdir, s + base_offset, flags) == 0)
381 break;
382 int e = errno;
383 if (! (e == EEXIST && extended))
384 {
385 if (dirp)
386 closedir (dirp);
387 free (s);
388 errno = e;
389 return NULL;
390 }
391 }
392
393 if (dirp)
394 closedir (dirp);
395 return s;
396 }