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 }