]>
Commit | Line | Data |
---|---|---|
8f776fd9 TK |
1 | /*- |
2 | * Copyright (c) 2003-2007 Tim Kientzle | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * | |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR | |
15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
17 | * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, | |
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #include "bsdtar_platform.h" | |
8f776fd9 TK |
27 | |
28 | #ifdef HAVE_SYS_STAT_H | |
29 | #include <sys/stat.h> | |
30 | #endif | |
31 | #ifdef HAVE_SYS_TYPES_H | |
32 | #include <sys/types.h> /* Linux doesn't define mode_t, etc. in sys/stat.h. */ | |
33 | #endif | |
34 | #include <ctype.h> | |
35 | #ifdef HAVE_ERRNO_H | |
36 | #include <errno.h> | |
37 | #endif | |
b6314161 TK |
38 | #ifdef HAVE_IO_H |
39 | #include <io.h> | |
40 | #endif | |
8f776fd9 TK |
41 | #ifdef HAVE_STDARG_H |
42 | #include <stdarg.h> | |
43 | #endif | |
d494e132 MN |
44 | #ifdef HAVE_STDINT_H |
45 | #include <stdint.h> | |
46 | #endif | |
8f776fd9 TK |
47 | #include <stdio.h> |
48 | #ifdef HAVE_STDLIB_H | |
49 | #include <stdlib.h> | |
50 | #endif | |
51 | #ifdef HAVE_STRING_H | |
52 | #include <string.h> | |
53 | #endif | |
2fa0c563 TK |
54 | #ifdef HAVE_WCTYPE_H |
55 | #include <wctype.h> | |
56 | #else | |
57 | /* If we don't have wctype, we need to hack up some version of iswprint(). */ | |
ce450a48 | 58 | #define iswprint isprint |
2fa0c563 | 59 | #endif |
8f776fd9 TK |
60 | |
61 | #include "bsdtar.h" | |
71d1bfe0 | 62 | #include "err.h" |
9769e6f7 | 63 | #include "passphrase.h" |
8f776fd9 | 64 | |
8ddc25de | 65 | static size_t bsdtar_expand_char(char *, size_t, size_t, char); |
9abc999f | 66 | static const char *strip_components(const char *path, int elements); |
8f776fd9 | 67 | |
48d48ddc | 68 | #if defined(_WIN32) && !defined(__CYGWIN__) |
ce450a48 | 69 | #define read _read |
48d48ddc TK |
70 | #endif |
71 | ||
2fa0c563 TK |
72 | /* TODO: Hack up a version of mbtowc for platforms with no wide |
73 | * character support at all. I think the following might suffice, | |
74 | * but it needs careful testing. | |
75 | * #if !HAVE_MBTOWC | |
ce450a48 | 76 | * #define mbtowc(wcp, p, n) ((*wcp = *p), 1) |
2fa0c563 TK |
77 | * #endif |
78 | */ | |
79 | ||
8f776fd9 TK |
80 | /* |
81 | * Print a string, taking care with any non-printable characters. | |
2fa0c563 TK |
82 | * |
83 | * Note that we use a stack-allocated buffer to receive the formatted | |
84 | * string if we can. This is partly performance (avoiding a call to | |
85 | * malloc()), partly out of expedience (we have to call vsnprintf() | |
86 | * before malloc() anyway to find out how big a buffer we need; we may | |
87 | * as well point that first call at a small local buffer in case it | |
88 | * works), but mostly for safety (so we can use this to print messages | |
89 | * about out-of-memory conditions). | |
8f776fd9 TK |
90 | */ |
91 | ||
92 | void | |
93 | safe_fprintf(FILE *f, const char *fmt, ...) | |
94 | { | |
2fa0c563 TK |
95 | char fmtbuff_stack[256]; /* Place to format the printf() string. */ |
96 | char outbuff[256]; /* Buffer for outgoing characters. */ | |
97 | char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ | |
98 | char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ | |
99 | int fmtbuff_length; | |
b7f3d08e | 100 | int length, n; |
8f776fd9 | 101 | va_list ap; |
2fa0c563 | 102 | const char *p; |
8f776fd9 | 103 | unsigned i; |
2fa0c563 TK |
104 | wchar_t wc; |
105 | char try_wc; | |
8f776fd9 TK |
106 | |
107 | /* Use a stack-allocated buffer if we can, for speed and safety. */ | |
2fa0c563 TK |
108 | fmtbuff_heap = NULL; |
109 | fmtbuff_length = sizeof(fmtbuff_stack); | |
110 | fmtbuff = fmtbuff_stack; | |
8f776fd9 | 111 | |
2fa0c563 | 112 | /* Try formatting into the stack buffer. */ |
8f776fd9 | 113 | va_start(ap, fmt); |
2fa0c563 | 114 | length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); |
8f776fd9 | 115 | va_end(ap); |
2fa0c563 TK |
116 | |
117 | /* If the result was too large, allocate a buffer on the heap. */ | |
b4a4200f MN |
118 | while (length < 0 || length >= fmtbuff_length) { |
119 | if (length >= fmtbuff_length) | |
120 | fmtbuff_length = length+1; | |
121 | else if (fmtbuff_length < 8192) | |
122 | fmtbuff_length *= 2; | |
ce39cb40 TK |
123 | else if (fmtbuff_length < 1000000) |
124 | fmtbuff_length += fmtbuff_length / 4; | |
b4a4200f | 125 | else { |
3a9718de | 126 | length = fmtbuff_length; |
ce39cb40 TK |
127 | fmtbuff_heap[length-1] = '\0'; |
128 | break; | |
b4a4200f MN |
129 | } |
130 | free(fmtbuff_heap); | |
2fa0c563 TK |
131 | fmtbuff_heap = malloc(fmtbuff_length); |
132 | ||
133 | /* Reformat the result into the heap buffer if we can. */ | |
134 | if (fmtbuff_heap != NULL) { | |
135 | fmtbuff = fmtbuff_heap; | |
8f776fd9 | 136 | va_start(ap, fmt); |
2fa0c563 | 137 | length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); |
8f776fd9 | 138 | va_end(ap); |
2fa0c563 TK |
139 | } else { |
140 | /* Leave fmtbuff pointing to the truncated | |
141 | * string in fmtbuff_stack. */ | |
b7b12fad | 142 | fmtbuff = fmtbuff_stack; |
2fa0c563 | 143 | length = sizeof(fmtbuff_stack) - 1; |
b4a4200f | 144 | break; |
8f776fd9 TK |
145 | } |
146 | } | |
147 | ||
2fa0c563 TK |
148 | /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit |
149 | * more portable, so we use that here instead. */ | |
e5f6f42a | 150 | if (mbtowc(NULL, NULL, 1) == -1) { /* Reset the shift state. */ |
e00007f8 TK |
151 | /* mbtowc() should never fail in practice, but |
152 | * handle the theoretical error anyway. */ | |
ce39cb40 | 153 | free(fmtbuff_heap); |
e5f6f42a MN |
154 | return; |
155 | } | |
2fa0c563 | 156 | |
8f776fd9 | 157 | /* Write data, expanding unprintable characters. */ |
2fa0c563 | 158 | p = fmtbuff; |
8f776fd9 | 159 | i = 0; |
2fa0c563 | 160 | try_wc = 1; |
8f776fd9 | 161 | while (*p != '\0') { |
2fa0c563 TK |
162 | |
163 | /* Convert to wide char, test if the wide | |
164 | * char is printable in the current locale. */ | |
165 | if (try_wc && (n = mbtowc(&wc, p, length)) != -1) { | |
166 | length -= n; | |
167 | if (iswprint(wc) && wc != L'\\') { | |
168 | /* Printable, copy the bytes through. */ | |
169 | while (n-- > 0) | |
170 | outbuff[i++] = *p++; | |
171 | } else { | |
172 | /* Not printable, format the bytes. */ | |
173 | while (n-- > 0) | |
d0a87926 | 174 | i += (unsigned)bsdtar_expand_char( |
8ddc25de | 175 | outbuff, sizeof(outbuff), i, *p++); |
8f776fd9 | 176 | } |
2fa0c563 TK |
177 | } else { |
178 | /* After any conversion failure, don't bother | |
179 | * trying to convert the rest. */ | |
8ddc25de | 180 | i += (unsigned)bsdtar_expand_char(outbuff, sizeof(outbuff), i, *p++); |
2fa0c563 | 181 | try_wc = 0; |
8f776fd9 TK |
182 | } |
183 | ||
2fa0c563 | 184 | /* If our output buffer is full, dump it and keep going. */ |
e37b620f | 185 | if (i > (sizeof(outbuff) - 128)) { |
71145f2f | 186 | outbuff[i] = '\0'; |
2fa0c563 | 187 | fprintf(f, "%s", outbuff); |
8f776fd9 TK |
188 | i = 0; |
189 | } | |
190 | } | |
71145f2f | 191 | outbuff[i] = '\0'; |
2fa0c563 TK |
192 | fprintf(f, "%s", outbuff); |
193 | ||
194 | /* If we allocated a heap-based formatting buffer, free it now. */ | |
3a9718de | 195 | free(fmtbuff_heap); |
2fa0c563 TK |
196 | } |
197 | ||
198 | /* | |
199 | * Render an arbitrary sequence of bytes into printable ASCII characters. | |
200 | */ | |
201 | static size_t | |
8ddc25de | 202 | bsdtar_expand_char(char *buff, size_t buffsize, size_t offset, char c) |
2fa0c563 TK |
203 | { |
204 | size_t i = offset; | |
205 | ||
411e8f16 | 206 | if (isprint((unsigned char)c) && c != '\\') |
2fa0c563 TK |
207 | buff[i++] = c; |
208 | else { | |
209 | buff[i++] = '\\'; | |
210 | switch (c) { | |
211 | case '\a': buff[i++] = 'a'; break; | |
212 | case '\b': buff[i++] = 'b'; break; | |
213 | case '\f': buff[i++] = 'f'; break; | |
214 | case '\n': buff[i++] = 'n'; break; | |
215 | #if '\r' != '\n' | |
216 | /* On some platforms, \n and \r are the same. */ | |
217 | case '\r': buff[i++] = 'r'; break; | |
218 | #endif | |
219 | case '\t': buff[i++] = 't'; break; | |
220 | case '\v': buff[i++] = 'v'; break; | |
221 | case '\\': buff[i++] = '\\'; break; | |
222 | default: | |
8ddc25de | 223 | snprintf(buff + i, buffsize - i, "%03o", 0xFF & (int)c); |
2fa0c563 TK |
224 | i += 3; |
225 | } | |
226 | } | |
8f776fd9 | 227 | |
2fa0c563 | 228 | return (i - offset); |
8f776fd9 TK |
229 | } |
230 | ||
8f776fd9 TK |
231 | int |
232 | yes(const char *fmt, ...) | |
233 | { | |
234 | char buff[32]; | |
235 | char *p; | |
236 | ssize_t l; | |
237 | ||
238 | va_list ap; | |
239 | va_start(ap, fmt); | |
240 | vfprintf(stderr, fmt, ap); | |
241 | va_end(ap); | |
242 | fprintf(stderr, " (y/N)? "); | |
243 | fflush(stderr); | |
244 | ||
1cfd6ea0 | 245 | l = read(2, buff, sizeof(buff) - 1); |
ce1dd93f TK |
246 | if (l < 0) { |
247 | fprintf(stderr, "Keyboard read failed\n"); | |
248 | exit(1); | |
249 | } | |
250 | if (l == 0) | |
8f776fd9 TK |
251 | return (0); |
252 | buff[l] = 0; | |
253 | ||
254 | for (p = buff; *p != '\0'; p++) { | |
411e8f16 | 255 | if (isspace((unsigned char)*p)) |
8f776fd9 TK |
256 | continue; |
257 | switch(*p) { | |
258 | case 'y': case 'Y': | |
259 | return (1); | |
260 | case 'n': case 'N': | |
261 | return (0); | |
262 | default: | |
263 | return (0); | |
264 | } | |
265 | } | |
266 | ||
267 | return (0); | |
268 | } | |
269 | ||
8f776fd9 TK |
270 | /*- |
271 | * The logic here for -C <dir> attempts to avoid | |
272 | * chdir() as long as possible. For example: | |
273 | * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") | |
274 | * "-C /foo -C bar file" needs chdir("/foo/bar") | |
275 | * "-C /foo -C bar /file1" does not need chdir() | |
276 | * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 | |
277 | * | |
278 | * The only correct way to handle this is to record a "pending" chdir | |
279 | * request and combine multiple requests intelligently until we | |
280 | * need to process a non-absolute file. set_chdir() adds the new dir | |
281 | * to the pending list; do_chdir() actually executes any pending chdir. | |
282 | * | |
283 | * This way, programs that build tar command lines don't have to worry | |
284 | * about -C with non-existent directories; such requests will only | |
285 | * fail if the directory must be accessed. | |
c27ff43c | 286 | * |
8f776fd9 TK |
287 | */ |
288 | void | |
289 | set_chdir(struct bsdtar *bsdtar, const char *newdir) | |
290 | { | |
96725b79 MN |
291 | #if defined(_WIN32) && !defined(__CYGWIN__) |
292 | if (newdir[0] == '/' || newdir[0] == '\\' || | |
293 | /* Detect this type, for example, "C:\" or "C:/" */ | |
294 | (((newdir[0] >= 'a' && newdir[0] <= 'z') || | |
295 | (newdir[0] >= 'A' && newdir[0] <= 'Z')) && | |
296 | newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { | |
297 | #else | |
8f776fd9 | 298 | if (newdir[0] == '/') { |
96725b79 | 299 | #endif |
8f776fd9 TK |
300 | /* The -C /foo -C /bar case; dump first one. */ |
301 | free(bsdtar->pending_chdir); | |
302 | bsdtar->pending_chdir = NULL; | |
303 | } | |
304 | if (bsdtar->pending_chdir == NULL) | |
305 | /* Easy case: no previously-saved dir. */ | |
306 | bsdtar->pending_chdir = strdup(newdir); | |
307 | else { | |
308 | /* The -C /foo -C bar case; concatenate */ | |
309 | char *old_pending = bsdtar->pending_chdir; | |
310 | size_t old_len = strlen(old_pending); | |
8ddc25de SM |
311 | size_t new_len = old_len + strlen(newdir) + 2; |
312 | bsdtar->pending_chdir = malloc(new_len); | |
8f776fd9 TK |
313 | if (old_pending[old_len - 1] == '/') |
314 | old_pending[old_len - 1] = '\0'; | |
315 | if (bsdtar->pending_chdir != NULL) | |
8ddc25de | 316 | snprintf(bsdtar->pending_chdir, new_len, "%s/%s", |
8f776fd9 TK |
317 | old_pending, newdir); |
318 | free(old_pending); | |
319 | } | |
320 | if (bsdtar->pending_chdir == NULL) | |
71d1bfe0 | 321 | lafe_errc(1, errno, "No memory"); |
8f776fd9 TK |
322 | } |
323 | ||
324 | void | |
325 | do_chdir(struct bsdtar *bsdtar) | |
326 | { | |
327 | if (bsdtar->pending_chdir == NULL) | |
328 | return; | |
329 | ||
330 | if (chdir(bsdtar->pending_chdir) != 0) { | |
71d1bfe0 | 331 | lafe_errc(1, 0, "could not chdir to '%s'\n", |
8f776fd9 TK |
332 | bsdtar->pending_chdir); |
333 | } | |
334 | free(bsdtar->pending_chdir); | |
335 | bsdtar->pending_chdir = NULL; | |
336 | } | |
337 | ||
87599157 | 338 | static const char * |
a954d2fd | 339 | strip_components(const char *p, int elements) |
9abc999f | 340 | { |
a954d2fd | 341 | /* Skip as many elements as necessary. */ |
9abc999f TK |
342 | while (elements > 0) { |
343 | switch (*p++) { | |
344 | case '/': | |
a954d2fd TK |
345 | #if defined(_WIN32) && !defined(__CYGWIN__) |
346 | case '\\': /* Support \ path sep on Windows ONLY. */ | |
347 | #endif | |
9abc999f | 348 | elements--; |
9abc999f TK |
349 | break; |
350 | case '\0': | |
351 | /* Path is too short, skip it. */ | |
352 | return (NULL); | |
353 | } | |
354 | } | |
355 | ||
a954d2fd TK |
356 | /* Skip any / characters. This handles short paths that have |
357 | * additional / termination. This also handles the case where | |
358 | * the logic above stops in the middle of a duplicate // | |
359 | * sequence (which would otherwise get converted to an | |
360 | * absolute path). */ | |
361 | for (;;) { | |
362 | switch (*p) { | |
363 | case '/': | |
364 | #if defined(_WIN32) && !defined(__CYGWIN__) | |
365 | case '\\': /* Support \ path sep on Windows ONLY. */ | |
366 | #endif | |
367 | ++p; | |
368 | break; | |
369 | case '\0': | |
370 | return (NULL); | |
371 | default: | |
372 | return (p); | |
373 | } | |
374 | } | |
9abc999f TK |
375 | } |
376 | ||
cf8e67ff TK |
377 | static void |
378 | warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) | |
379 | { | |
380 | if (!bsdtar->warned_lead_slash) { | |
381 | lafe_warnc(0, | |
382 | "Removing leading '%c' from member names", | |
383 | c[0]); | |
384 | bsdtar->warned_lead_slash = 1; | |
385 | } | |
386 | } | |
387 | ||
388 | static void | |
389 | warn_strip_drive_letter(struct bsdtar *bsdtar) | |
390 | { | |
391 | if (!bsdtar->warned_lead_slash) { | |
392 | lafe_warnc(0, | |
393 | "Removing leading drive letter from " | |
394 | "member names"); | |
395 | bsdtar->warned_lead_slash = 1; | |
396 | } | |
397 | } | |
398 | ||
399 | /* | |
400 | * Convert absolute path to non-absolute path by skipping leading | |
401 | * absolute path prefixes. | |
402 | */ | |
403 | static const char* | |
404 | strip_absolute_path(struct bsdtar *bsdtar, const char *p) | |
405 | { | |
406 | const char *rp; | |
407 | ||
408 | /* Remove leading "//./" or "//?/" or "//?/UNC/" | |
409 | * (absolute path prefixes used by Windows API) */ | |
410 | if ((p[0] == '/' || p[0] == '\\') && | |
411 | (p[1] == '/' || p[1] == '\\') && | |
412 | (p[2] == '.' || p[2] == '?') && | |
413 | (p[3] == '/' || p[3] == '\\')) | |
414 | { | |
415 | if (p[2] == '?' && | |
416 | (p[4] == 'U' || p[4] == 'u') && | |
417 | (p[5] == 'N' || p[5] == 'n') && | |
418 | (p[6] == 'C' || p[6] == 'c') && | |
419 | (p[7] == '/' || p[7] == '\\')) | |
420 | p += 8; | |
421 | else | |
422 | p += 4; | |
423 | warn_strip_drive_letter(bsdtar); | |
424 | } | |
425 | ||
426 | /* Remove multiple leading slashes and Windows drive letters. */ | |
427 | do { | |
428 | rp = p; | |
429 | if (((p[0] >= 'a' && p[0] <= 'z') || | |
430 | (p[0] >= 'A' && p[0] <= 'Z')) && | |
431 | p[1] == ':') { | |
432 | p += 2; | |
433 | warn_strip_drive_letter(bsdtar); | |
434 | } | |
435 | ||
436 | /* Remove leading "/../", "/./", "//", etc. */ | |
437 | while (p[0] == '/' || p[0] == '\\') { | |
438 | if (p[1] == '.' && | |
439 | p[2] == '.' && | |
440 | (p[3] == '/' || p[3] == '\\')) { | |
441 | p += 3; /* Remove "/..", leave "/" for next pass. */ | |
442 | } else if (p[1] == '.' && | |
443 | (p[2] == '/' || p[2] == '\\')) { | |
444 | p += 2; /* Remove "/.", leave "/" for next pass. */ | |
445 | } else | |
446 | p += 1; /* Remove "/". */ | |
447 | warn_strip_leading_char(bsdtar, rp); | |
448 | } | |
449 | } while (rp != p); | |
450 | ||
451 | return (p); | |
452 | } | |
453 | ||
8f776fd9 TK |
454 | /* |
455 | * Handle --strip-components and any future path-rewriting options. | |
456 | * Returns non-zero if the pathname should not be extracted. | |
457 | * | |
cf8e67ff TK |
458 | * Note: The rewrites are applied uniformly to pathnames and hardlink |
459 | * names but not to symlink bodies. This is deliberate: Symlink | |
460 | * bodies are not necessarily filenames. Even when they are, they | |
461 | * need to be interpreted relative to the directory containing them, | |
462 | * so simple rewrites like this are rarely appropriate. | |
463 | * | |
8f776fd9 TK |
464 | * TODO: Support pax-style regex path rewrites. |
465 | */ | |
466 | int | |
467 | edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) | |
468 | { | |
469 | const char *name = archive_entry_pathname(entry); | |
cf8e67ff TK |
470 | const char *original_name = name; |
471 | const char *hardlinkname = archive_entry_hardlink(entry); | |
472 | const char *original_hardlinkname = hardlinkname; | |
b708276a | 473 | #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) |
bf9f5fbc | 474 | char *subst_name; |
bf9f5fbc JS |
475 | int r; |
476 | ||
cf8e67ff | 477 | /* Apply user-specified substitution to pathname. */ |
3147f660 | 478 | r = apply_substitution(bsdtar, name, &subst_name, 0, 0); |
bf9f5fbc | 479 | if (r == -1) { |
71d1bfe0 | 480 | lafe_warnc(0, "Invalid substitution, skipping entry"); |
bf9f5fbc JS |
481 | return 1; |
482 | } | |
483 | if (r == 1) { | |
4bef229c | 484 | archive_entry_copy_pathname(entry, subst_name); |
a7d5cd7a TK |
485 | if (*subst_name == '\0') { |
486 | free(subst_name); | |
bf9f5fbc | 487 | return -1; |
a7d5cd7a TK |
488 | } else |
489 | free(subst_name); | |
bf9f5fbc | 490 | name = archive_entry_pathname(entry); |
cf8e67ff | 491 | original_name = name; |
bf9f5fbc JS |
492 | } |
493 | ||
cf8e67ff TK |
494 | /* Apply user-specified substitution to hardlink target. */ |
495 | if (hardlinkname != NULL) { | |
496 | r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); | |
bf9f5fbc | 497 | if (r == -1) { |
71d1bfe0 | 498 | lafe_warnc(0, "Invalid substitution, skipping entry"); |
bf9f5fbc JS |
499 | return 1; |
500 | } | |
501 | if (r == 1) { | |
4bef229c | 502 | archive_entry_copy_hardlink(entry, subst_name); |
bf9f5fbc JS |
503 | free(subst_name); |
504 | } | |
cf8e67ff TK |
505 | hardlinkname = archive_entry_hardlink(entry); |
506 | original_hardlinkname = hardlinkname; | |
bf9f5fbc | 507 | } |
cf8e67ff TK |
508 | |
509 | /* Apply user-specified substitution to symlink body. */ | |
bf9f5fbc | 510 | if (archive_entry_symlink(entry) != NULL) { |
3147f660 | 511 | r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); |
bf9f5fbc | 512 | if (r == -1) { |
71d1bfe0 | 513 | lafe_warnc(0, "Invalid substitution, skipping entry"); |
bf9f5fbc JS |
514 | return 1; |
515 | } | |
516 | if (r == 1) { | |
4bef229c | 517 | archive_entry_copy_symlink(entry, subst_name); |
bf9f5fbc JS |
518 | free(subst_name); |
519 | } | |
520 | } | |
521 | #endif | |
8f776fd9 TK |
522 | |
523 | /* Strip leading dir names as per --strip-components option. */ | |
9abc999f | 524 | if (bsdtar->strip_components > 0) { |
9abc999f TK |
525 | name = strip_components(name, bsdtar->strip_components); |
526 | if (name == NULL) | |
527 | return (1); | |
528 | ||
cf8e67ff TK |
529 | if (hardlinkname != NULL) { |
530 | hardlinkname = strip_components(hardlinkname, | |
9abc999f | 531 | bsdtar->strip_components); |
cf8e67ff | 532 | if (hardlinkname == NULL) |
8f776fd9 | 533 | return (1); |
8f776fd9 TK |
534 | } |
535 | } | |
536 | ||
6a9dcf9f | 537 | if ((bsdtar->flags & OPTFLAG_ABSOLUTE_PATHS) == 0) { |
cf8e67ff TK |
538 | /* By default, don't write or restore absolute pathnames. */ |
539 | name = strip_absolute_path(bsdtar, name); | |
540 | if (*name == '\0') | |
80b77b32 | 541 | name = "."; |
cf8e67ff TK |
542 | |
543 | if (hardlinkname != NULL) { | |
544 | hardlinkname = strip_absolute_path(bsdtar, hardlinkname); | |
545 | if (*hardlinkname == '\0') | |
546 | return (1); | |
547 | } | |
80b77b32 MN |
548 | } else { |
549 | /* Strip redundant leading '/' characters. */ | |
550 | while (name[0] == '/' && name[1] == '/') | |
551 | name++; | |
8f776fd9 TK |
552 | } |
553 | ||
cf8e67ff TK |
554 | /* Replace name in archive_entry. */ |
555 | if (name != original_name) { | |
556 | archive_entry_copy_pathname(entry, name); | |
557 | } | |
558 | if (hardlinkname != original_hardlinkname) { | |
559 | archive_entry_copy_hardlink(entry, hardlinkname); | |
8f776fd9 TK |
560 | } |
561 | return (0); | |
562 | } | |
563 | ||
7206f0a8 TK |
564 | /* |
565 | * It would be nice to just use printf() for formatting large numbers, | |
566 | * but the compatibility problems are quite a headache. Hence the | |
567 | * following simple utility function. | |
568 | */ | |
569 | const char * | |
570 | tar_i64toa(int64_t n0) | |
571 | { | |
572 | static char buff[24]; | |
a7b900bd | 573 | uint64_t n = n0 < 0 ? -n0 : n0; |
7206f0a8 TK |
574 | char *p = buff + sizeof(buff); |
575 | ||
576 | *--p = '\0'; | |
577 | do { | |
996f201f | 578 | *--p = '0' + (int)(n % 10); |
a7b900bd | 579 | } while (n /= 10); |
7206f0a8 TK |
580 | if (n0 < 0) |
581 | *--p = '-'; | |
582 | return p; | |
583 | } | |
584 | ||
8f776fd9 TK |
585 | /* |
586 | * Like strcmp(), but try to be a little more aware of the fact that | |
587 | * we're comparing two paths. Right now, it just handles leading | |
588 | * "./" and trailing '/' specially, so that "a/b/" == "./a/b" | |
589 | * | |
590 | * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" | |
591 | * TODO: After this works, push it down into libarchive. | |
592 | * TODO: Publish the path normalization routines in libarchive so | |
593 | * that bsdtar can normalize paths and use fast strcmp() instead | |
594 | * of this. | |
c27ff43c TK |
595 | * |
596 | * Note: This is currently only used within write.c, so should | |
597 | * not handle \ path separators. | |
8f776fd9 TK |
598 | */ |
599 | ||
600 | int | |
601 | pathcmp(const char *a, const char *b) | |
602 | { | |
603 | /* Skip leading './' */ | |
604 | if (a[0] == '.' && a[1] == '/' && a[2] != '\0') | |
605 | a += 2; | |
606 | if (b[0] == '.' && b[1] == '/' && b[2] != '\0') | |
607 | b += 2; | |
608 | /* Find the first difference, or return (0) if none. */ | |
609 | while (*a == *b) { | |
610 | if (*a == '\0') | |
611 | return (0); | |
612 | a++; | |
613 | b++; | |
614 | } | |
615 | /* | |
616 | * If one ends in '/' and the other one doesn't, | |
617 | * they're the same. | |
618 | */ | |
619 | if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') | |
620 | return (0); | |
621 | if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') | |
622 | return (0); | |
623 | /* They're really different, return the correct sign. */ | |
624 | return (*(const unsigned char *)a - *(const unsigned char *)b); | |
625 | } | |
9769e6f7 MN |
626 | |
627 | #define PPBUFF_SIZE 1024 | |
628 | const char * | |
629 | passphrase_callback(struct archive *a, void *_client_data) | |
630 | { | |
631 | struct bsdtar *bsdtar = (struct bsdtar *)_client_data; | |
632 | (void)a; /* UNUSED */ | |
633 | ||
634 | if (bsdtar->ppbuff == NULL) { | |
635 | bsdtar->ppbuff = malloc(PPBUFF_SIZE); | |
636 | if (bsdtar->ppbuff == NULL) | |
637 | lafe_errc(1, errno, "Out of memory"); | |
638 | } | |
639 | return lafe_readpassphrase("Enter passphrase:", | |
640 | bsdtar->ppbuff, PPBUFF_SIZE); | |
641 | } | |
642 | ||
643 | void | |
644 | passphrase_free(char *ppbuff) | |
645 | { | |
646 | if (ppbuff != NULL) { | |
647 | memset(ppbuff, 0, PPBUFF_SIZE); | |
648 | free(ppbuff); | |
649 | } | |
650 | } | |
b1be9dd8 BJ |
651 | |
652 | /* | |
653 | * Display information about the current file. | |
654 | * | |
655 | * The format here roughly duplicates the output of 'ls -l'. | |
656 | * This is based on SUSv2, where 'tar tv' is documented as | |
657 | * listing additional information in an "unspecified format," | |
658 | * and 'pax -l' is documented as using the same format as 'ls -l'. | |
659 | */ | |
660 | void | |
661 | list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) | |
662 | { | |
663 | char tmp[100]; | |
664 | size_t w; | |
665 | const char *p; | |
666 | const char *fmt; | |
667 | time_t tim; | |
668 | static time_t now; | |
5be1a96f | 669 | struct tm *ltime; |
0348e24b | 670 | #if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) |
5be1a96f MM |
671 | struct tm tmbuf; |
672 | #endif | |
b1be9dd8 BJ |
673 | |
674 | /* | |
675 | * We avoid collecting the entire list in memory at once by | |
676 | * listing things as we see them. However, that also means we can't | |
677 | * just pre-compute the field widths. Instead, we start with guesses | |
678 | * and just widen them as necessary. These numbers are completely | |
679 | * arbitrary. | |
680 | */ | |
681 | if (!bsdtar->u_width) { | |
682 | bsdtar->u_width = 6; | |
683 | bsdtar->gs_width = 13; | |
684 | } | |
685 | if (!now) | |
686 | time(&now); | |
687 | fprintf(out, "%s %d ", | |
688 | archive_entry_strmode(entry), | |
689 | archive_entry_nlink(entry)); | |
690 | ||
691 | /* Use uname if it's present, else uid. */ | |
692 | p = archive_entry_uname(entry); | |
693 | if ((p == NULL) || (*p == '\0')) { | |
8ddc25de | 694 | snprintf(tmp, sizeof(tmp), "%lu ", |
b1be9dd8 BJ |
695 | (unsigned long)archive_entry_uid(entry)); |
696 | p = tmp; | |
697 | } | |
698 | w = strlen(p); | |
699 | if (w > bsdtar->u_width) | |
700 | bsdtar->u_width = w; | |
701 | fprintf(out, "%-*s ", (int)bsdtar->u_width, p); | |
702 | ||
703 | /* Use gname if it's present, else gid. */ | |
704 | p = archive_entry_gname(entry); | |
705 | if (p != NULL && p[0] != '\0') { | |
706 | fprintf(out, "%s", p); | |
707 | w = strlen(p); | |
708 | } else { | |
8ddc25de | 709 | snprintf(tmp, sizeof(tmp), "%lu", |
b1be9dd8 BJ |
710 | (unsigned long)archive_entry_gid(entry)); |
711 | w = strlen(tmp); | |
712 | fprintf(out, "%s", tmp); | |
713 | } | |
714 | ||
715 | /* | |
716 | * Print device number or file size, right-aligned so as to make | |
717 | * total width of group and devnum/filesize fields be gs_width. | |
718 | * If gs_width is too small, grow it. | |
719 | */ | |
720 | if (archive_entry_filetype(entry) == AE_IFCHR | |
721 | || archive_entry_filetype(entry) == AE_IFBLK) { | |
8ddc25de | 722 | snprintf(tmp, sizeof(tmp), "%lu,%lu", |
b1be9dd8 BJ |
723 | (unsigned long)archive_entry_rdevmajor(entry), |
724 | (unsigned long)archive_entry_rdevminor(entry)); | |
725 | } else { | |
726 | strcpy(tmp, tar_i64toa(archive_entry_size(entry))); | |
727 | } | |
728 | if (w + strlen(tmp) >= bsdtar->gs_width) | |
729 | bsdtar->gs_width = w+strlen(tmp)+1; | |
730 | fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); | |
731 | ||
732 | /* Format the time using 'ls -l' conventions. */ | |
733 | tim = archive_entry_mtime(entry); | |
734 | #define HALF_YEAR (time_t)365 * 86400 / 2 | |
735 | #if defined(_WIN32) && !defined(__CYGWIN__) | |
736 | #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ | |
737 | #else | |
738 | #define DAY_FMT "%e" /* Day number without leading zeros */ | |
739 | #endif | |
740 | if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) | |
741 | fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; | |
742 | else | |
743 | fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; | |
0348e24b RP |
744 | #if defined(HAVE_LOCALTIME_S) |
745 | ltime = localtime_s(&tmbuf, &tim) ? NULL : &tmbuf; | |
2d329073 RP |
746 | #elif defined(HAVE_LOCALTIME_R) |
747 | ltime = localtime_r(&tim, &tmbuf); | |
5be1a96f MM |
748 | #else |
749 | ltime = localtime(&tim); | |
750 | #endif | |
751 | strftime(tmp, sizeof(tmp), fmt, ltime); | |
b1be9dd8 BJ |
752 | fprintf(out, " %s ", tmp); |
753 | safe_fprintf(out, "%s", archive_entry_pathname(entry)); | |
754 | ||
755 | /* Extra information for links. */ | |
756 | if (archive_entry_hardlink(entry)) /* Hard link */ | |
757 | safe_fprintf(out, " link to %s", | |
758 | archive_entry_hardlink(entry)); | |
759 | else if (archive_entry_symlink(entry)) /* Symbolic link */ | |
760 | safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); | |
761 | } |