Commit | Line | Data |
---|---|---|
c4f36ac6 WJ |
1 | /* Extract files from a tar archive. |
2 | Copyright (C) 1988 Free Software Foundation | |
3 | ||
4 | This file is part of GNU Tar. | |
5 | ||
6 | GNU Tar is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 1, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GNU Tar is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU Tar; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
19 | ||
20 | /* | |
21 | * Extract files from a tar archive. | |
22 | * | |
23 | * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu. | |
24 | * | |
25 | * @(#) extract.c 1.32 87/11/11 - gnu | |
26 | */ | |
27 | ||
28 | #include <stdio.h> | |
29 | #include <errno.h> | |
30 | #include <sys/types.h> | |
31 | #include <sys/stat.h> | |
32 | ||
33 | #ifdef BSD42 | |
34 | #include <sys/file.h> | |
35 | #endif | |
36 | ||
37 | #ifdef USG | |
38 | #include <fcntl.h> | |
39 | #endif | |
40 | ||
41 | #ifdef MSDOS | |
42 | #include <fcntl.h> | |
43 | #endif /* MSDOS */ | |
44 | ||
45 | /* | |
46 | * Some people don't have a #define for these. | |
47 | */ | |
48 | #ifndef O_BINARY | |
49 | #define O_BINARY 0 | |
50 | #endif | |
51 | #ifndef O_NDELAY | |
52 | #define O_NDELAY 0 | |
53 | #endif | |
54 | ||
55 | #ifdef NO_OPEN3 | |
56 | /* We need the #define's even though we don't use them. */ | |
57 | #include "open3.h" | |
58 | #endif | |
59 | ||
60 | #ifdef EMUL_OPEN3 | |
61 | /* Simulated 3-argument open for systems that don't have it */ | |
62 | #include "open3.h" | |
63 | #endif | |
64 | ||
65 | extern int errno; /* From libc.a */ | |
66 | extern time_t time(); /* From libc.a */ | |
67 | extern char *index(); /* From libc.a or port.c */ | |
68 | ||
69 | #include "tar.h" | |
70 | #include "port.h" | |
71 | ||
72 | extern FILE *msg_file; | |
73 | ||
74 | extern union record *head; /* Points to current tape header */ | |
75 | extern struct stat hstat; /* Stat struct corresponding */ | |
76 | extern int head_standard; /* Tape header is in ANSI format */ | |
77 | ||
78 | extern char *save_name; | |
79 | extern long save_totsize; | |
80 | extern long save_sizeleft; | |
81 | ||
82 | extern void print_header(); | |
83 | extern void skip_file(); | |
84 | extern void skip_extended_headers(); | |
85 | extern void pr_mkdir(); | |
86 | ||
87 | int make_dirs(); /* Makes required directories */ | |
88 | ||
89 | static time_t now = 0; /* Current time */ | |
90 | static we_are_root = 0; /* True if our effective uid == 0 */ | |
91 | static int notumask = ~0; /* Masks out bits user doesn't want */ | |
92 | ||
93 | /* | |
94 | * "Scratch" space to store the information about a sparse file before | |
95 | * writing the info into the header or extended header | |
96 | */ | |
97 | /*struct sp_array *sparsearray;*/ | |
98 | ||
99 | /* number of elts storable in the sparsearray */ | |
100 | /*int sp_array_size = 10;*/ | |
101 | ||
102 | /* | |
103 | * Set up to extract files. | |
104 | */ | |
105 | extr_init() | |
106 | { | |
107 | int ourmask; | |
108 | ||
109 | now = time((time_t *)0); | |
110 | if (geteuid() == 0) | |
111 | we_are_root = 1; | |
112 | ||
113 | /* | |
114 | * We need to know our umask. But if f_use_protection is set, | |
115 | * leave our kernel umask at 0, and our "notumask" at ~0. | |
116 | */ | |
117 | ourmask = umask(0); /* Read it */ | |
118 | if (!f_use_protection) { | |
119 | (void) umask (ourmask); /* Set it back how it was */ | |
120 | notumask = ~ourmask; /* Make umask override permissions */ | |
121 | } | |
122 | } | |
123 | ||
124 | ||
125 | /* | |
126 | * Extract a file from the archive. | |
127 | */ | |
128 | void | |
129 | extract_archive() | |
130 | { | |
131 | register char *data; | |
132 | int fd, check, namelen, written, openflag; | |
133 | long size; | |
134 | time_t acc_upd_times[2]; | |
135 | register int skipcrud; | |
136 | register int i; | |
137 | int sparse_ind = 0; | |
138 | union record *exhdr; | |
139 | int end_nulls; | |
140 | ||
141 | saverec(&head); /* Make sure it sticks around */ | |
142 | userec(head); /* And go past it in the archive */ | |
143 | decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */ | |
144 | ||
145 | if(f_confirm && !confirm("extract",head->header.name)) { | |
146 | if (head->header.isextended) | |
147 | skip_extended_headers(); | |
148 | skip_file((long)hstat.st_size); | |
149 | saverec((union record **)0); | |
150 | return; | |
151 | } | |
152 | ||
153 | /* Print the record from 'head' and 'hstat' */ | |
154 | if (f_verbose) | |
155 | print_header(); | |
156 | ||
157 | /* | |
158 | * Check for fully specified pathnames and other atrocities. | |
159 | * | |
160 | * Note, we can't just make a pointer to the new file name, | |
161 | * since saverec() might move the header and adjust "head". | |
162 | * We have to start from "head" every time we want to touch | |
163 | * the header record. | |
164 | */ | |
165 | skipcrud = 0; | |
166 | while (!f_absolute_paths && '/' == head->header.name[skipcrud]) { | |
167 | static int warned_once = 0; | |
168 | ||
169 | skipcrud++; /* Force relative path */ | |
170 | if (!warned_once++) { | |
171 | msg("Removing leading / from absolute path names in the archive."); | |
172 | } | |
173 | } | |
174 | ||
175 | switch (head->header.linkflag) { | |
176 | ||
177 | default: | |
178 | msg("Unknown file type '%c' for %s, extracted as normal file", | |
179 | head->header.linkflag, skipcrud+head->header.name); | |
180 | /* FALL THRU */ | |
181 | ||
182 | /* | |
183 | * JK - What we want to do if the file is sparse is loop through | |
184 | * the array of sparse structures in the header and read in | |
185 | * and translate the character strings representing 1) the offset | |
186 | * at which to write and 2) how many bytes to write into numbers, | |
187 | * which we store into the scratch array, "sparsearray". This | |
188 | * array makes our life easier the same way it did in creating | |
189 | * the tar file that had to deal with a sparse file. | |
190 | * | |
191 | * After we read in the first five (at most) sparse structures, | |
192 | * we check to see if the file has an extended header, i.e., | |
193 | * if more sparse structures are needed to describe the contents | |
194 | * of the new file. If so, we read in the extended headers | |
195 | * and continue to store their contents into the sparsearray. | |
196 | */ | |
197 | case LF_SPARSE: | |
198 | sp_array_size = 10; | |
199 | sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array)); | |
200 | for (i = 0; i < SPARSE_IN_HDR; i++) { | |
201 | sparsearray[i].offset = | |
202 | from_oct(1+12, head->header.sp[i].offset); | |
203 | sparsearray[i].numbytes = | |
204 | from_oct(1+12, head->header.sp[i].numbytes); | |
205 | if (!sparsearray[i].numbytes) | |
206 | break; | |
207 | } | |
208 | ||
209 | /* end_nulls = from_oct(1+12, head->header.ending_blanks);*/ | |
210 | ||
211 | if (head->header.isextended) { | |
212 | /* read in the list of extended headers | |
213 | and translate them into the sparsearray | |
214 | as before */ | |
215 | ||
216 | /* static */ int ind = SPARSE_IN_HDR; | |
217 | ||
218 | for (;;) { | |
219 | ||
220 | exhdr = findrec(); | |
221 | for (i = 0; i < SPARSE_EXT_HDR; i++) { | |
222 | ||
223 | if (i+ind > sp_array_size-1) { | |
224 | /* | |
225 | * realloc the scratch area | |
226 | * since we've run out of room -- | |
227 | */ | |
228 | sparsearray = (struct sp_array *) | |
229 | realloc(sparsearray, | |
230 | 2 * sp_array_size * (sizeof(struct sp_array))); | |
231 | sp_array_size *= 2; | |
232 | } | |
233 | if (!exhdr->ext_hdr.sp[i].numbytes) | |
234 | break; | |
235 | sparsearray[i+ind].offset = | |
236 | from_oct(1+12, exhdr->ext_hdr.sp[i].offset); | |
237 | sparsearray[i+ind].numbytes = | |
238 | from_oct(1+12, exhdr->ext_hdr.sp[i].numbytes); | |
239 | } | |
240 | if (!exhdr->ext_hdr.isextended) | |
241 | break; | |
242 | else { | |
243 | ind += SPARSE_EXT_HDR; | |
244 | userec(exhdr); | |
245 | } | |
246 | } | |
247 | userec(exhdr); | |
248 | } | |
249 | ||
250 | /* FALL THRU */ | |
251 | case LF_OLDNORMAL: | |
252 | case LF_NORMAL: | |
253 | case LF_CONTIG: | |
254 | /* | |
255 | * Appears to be a file. | |
256 | * See if it's really a directory. | |
257 | */ | |
258 | namelen = strlen(skipcrud+head->header.name)-1; | |
259 | if (head->header.name[skipcrud+namelen] == '/') | |
260 | goto really_dir; | |
261 | ||
262 | /* FIXME, deal with protection issues */ | |
263 | again_file: | |
264 | openflag = (f_keep? | |
265 | O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_EXCL: | |
266 | O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_TRUNC) | |
267 | | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND); | |
268 | /* | |
269 | * JK - The last | is a kludge to solve the problem | |
270 | * the O_APPEND flag causes with files we are | |
271 | * trying to make sparse: when a file is opened | |
272 | * with O_APPEND, it writes to the last place | |
273 | * that something was written, thereby ignoring | |
274 | * any lseeks that we have done. We add this | |
275 | * extra condition to make it able to lseek when | |
276 | * a file is sparse, i.e., we don't open the new | |
277 | * file with this flag. (Grump -- this bug caused | |
278 | * me to waste a good deal of time, I might add) | |
279 | */ | |
280 | ||
281 | if(f_exstdout) { | |
282 | fd = 1; | |
283 | goto extract_file; | |
284 | } | |
285 | #ifdef O_CTG | |
286 | /* | |
287 | * Contiguous files (on the Masscomp) have to specify | |
288 | * the size in the open call that creates them. | |
289 | */ | |
290 | if (head->header.linkflag == LF_CONTIG) | |
291 | fd = open(skipcrud+head->header.name, openflag | O_CTG, | |
292 | hstat.st_mode, hstat.st_size); | |
293 | else | |
294 | #endif | |
295 | { | |
296 | #ifdef NO_OPEN3 | |
297 | /* | |
298 | * On raw V7 we won't let them specify -k (f_keep), but | |
299 | * we just bull ahead and create the files. | |
300 | */ | |
301 | fd = creat(skipcrud+head->header.name, | |
302 | hstat.st_mode); | |
303 | #else | |
304 | /* | |
305 | * With 3-arg open(), we can do this up right. | |
306 | */ | |
307 | fd = open(skipcrud+head->header.name, openflag, | |
308 | hstat.st_mode); | |
309 | #endif | |
310 | } | |
311 | ||
312 | if (fd < 0) { | |
313 | if (make_dirs(skipcrud+head->header.name)) | |
314 | goto again_file; | |
315 | msg_perror("Could not create file %s",skipcrud+head->header.name); | |
316 | if (head->header.isextended) | |
317 | skip_extended_headers(); | |
318 | skip_file((long)hstat.st_size); | |
319 | goto quit; | |
320 | } | |
321 | ||
322 | extract_file: | |
323 | if (head->header.linkflag == LF_SPARSE) { | |
324 | char *name; | |
325 | int namelen; | |
326 | ||
327 | /* | |
328 | * Kludge alert. NAME is assigned to header.name | |
329 | * because during the extraction, the space that | |
330 | * contains the header will get scribbled on, and | |
331 | * the name will get munged, so any error messages | |
332 | * that happen to contain the filename will look | |
333 | * REAL interesting unless we do this. | |
334 | */ | |
335 | namelen = strlen(skipcrud+head->header.name); | |
336 | name = (char *) malloc((sizeof(char)) * namelen); | |
337 | bcopy(skipcrud+head->header.name, name, namelen); | |
338 | size = hstat.st_size; | |
339 | extract_sparse_file(fd, &size, hstat.st_size, | |
340 | name); | |
341 | } | |
342 | else | |
343 | for (size = hstat.st_size; | |
344 | size > 0; | |
345 | size -= written) { | |
346 | ||
347 | long offset, | |
348 | numbytes; | |
349 | ||
350 | if(f_multivol) { | |
351 | save_name=head->header.name; | |
352 | save_totsize=hstat.st_size; | |
353 | save_sizeleft=size; | |
354 | } | |
355 | ||
356 | /* | |
357 | * Locate data, determine max length | |
358 | * writeable, write it, record that | |
359 | * we have used the data, then check | |
360 | * if the write worked. | |
361 | */ | |
362 | data = findrec()->charptr; | |
363 | if (data == NULL) { /* Check it... */ | |
364 | msg("Unexpected EOF on archive file"); | |
365 | break; | |
366 | } | |
367 | /* | |
368 | * JK - If the file is sparse, use the sparsearray | |
369 | * that we created before to lseek into the new | |
370 | * file the proper amount, and to see how many | |
371 | * bytes we want to write at that position. | |
372 | */ | |
373 | /* if (head->header.linkflag == LF_SPARSE) { | |
374 | off_t pos; | |
375 | ||
376 | pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0); | |
377 | printf("%d at %d\n", (int) pos, sparse_ind); | |
378 | written = sparsearray[sparse_ind++].numbytes; | |
379 | } else*/ | |
380 | written = endofrecs()->charptr - data; | |
381 | if (written > size) | |
382 | written = size; | |
383 | errno = 0; | |
384 | check = write(fd, data, written); | |
385 | /* | |
386 | * The following is in violation of strict | |
387 | * typing, since the arg to userec | |
388 | * should be a struct rec *. FIXME. | |
389 | */ | |
390 | userec((union record *)(data + written - 1)); | |
391 | if (check == written) continue; | |
392 | /* | |
393 | * Error in writing to file. | |
394 | * Print it, skip to next file in archive. | |
395 | */ | |
396 | if(check<0) | |
397 | msg_perror("couldn't write to file %s",skipcrud+head->header.name); | |
398 | else | |
399 | msg("could only write %d of %d bytes to file %s",written,check,skipcrud+head->header.name); | |
400 | skip_file((long)(size - written)); | |
401 | break; /* Still do the close, mod time, chmod, etc */ | |
402 | } | |
403 | ||
404 | if(f_multivol) | |
405 | save_name = 0; | |
406 | ||
407 | /* If writing to stdout, don't try to do anything | |
408 | to the filename; it doesn't exist, or we don't | |
409 | want to touch it anyway */ | |
410 | if(f_exstdout) | |
411 | break; | |
412 | ||
413 | /* if (head->header.isextended) { | |
414 | register union record *exhdr; | |
415 | register int i; | |
416 | ||
417 | for (i = 0; i < 21; i++) { | |
418 | long offset; | |
419 | ||
420 | if (!exhdr->ext_hdr.sp[i].numbytes) | |
421 | break; | |
422 | offset = from_oct(1+12, | |
423 | exhdr->ext_hdr.sp[i].offset); | |
424 | written = from_oct(1+12, | |
425 | exhdr->ext_hdr.sp[i].numbytes); | |
426 | lseek(fd, offset, 0); | |
427 | check = write(fd, data, written); | |
428 | if (check == written) continue; | |
429 | ||
430 | } | |
431 | ||
432 | ||
433 | }*/ | |
434 | check = close(fd); | |
435 | if (check < 0) { | |
436 | msg_perror("Error while closing %s",skipcrud+head->header.name); | |
437 | } | |
438 | ||
439 | ||
440 | set_filestat: | |
441 | ||
442 | /* | |
443 | * If we are root, set the owner and group of the extracted | |
444 | * file. This does what is wanted both on real Unix and on | |
445 | * System V. If we are running as a user, we extract as that | |
446 | * user; if running as root, we extract as the original owner. | |
447 | */ | |
448 | if (we_are_root || f_do_chown) { | |
449 | if (chown(skipcrud+head->header.name, hstat.st_uid, | |
450 | hstat.st_gid) < 0) { | |
451 | msg_perror("cannot chown file %s to uid %d gid %d",skipcrud+head->header.name,hstat.st_uid,hstat.st_gid); | |
452 | } | |
453 | } | |
454 | ||
455 | /* | |
456 | * Set the modified time of the file. | |
457 | * | |
458 | * Note that we set the accessed time to "now", which | |
459 | * is really "the time we started extracting files". | |
460 | * unless f_gnudump is used, in which case .st_atime is used | |
461 | */ | |
462 | if (!f_modified) { | |
463 | /* fixme if f_gnudump should set ctime too, but how? */ | |
464 | if(f_gnudump) | |
465 | acc_upd_times[0]=hstat.st_atime; | |
466 | else acc_upd_times[0] = now; /* Accessed now */ | |
467 | acc_upd_times[1] = hstat.st_mtime; /* Mod'd */ | |
468 | if (utime(skipcrud+head->header.name, | |
469 | acc_upd_times) < 0) { | |
470 | msg_perror("couldn't change access and modification times of %s",skipcrud+head->header.name); | |
471 | } | |
472 | } | |
473 | /* We do the utime before the chmod because some versions of | |
474 | utime are broken and trash the modes of the file. Since | |
475 | we then change the mode anyway, we don't care. . . */ | |
476 | ||
477 | /* | |
478 | * If '-k' is not set, open() or creat() could have saved | |
479 | * the permission bits from a previously created file, | |
480 | * ignoring the ones we specified. | |
481 | * Even if -k is set, if the file has abnormal | |
482 | * mode bits, we must chmod since writing or chown() has | |
483 | * probably reset them. | |
484 | * | |
485 | * If -k is set, we know *we* created this file, so the mode | |
486 | * bits were set by our open(). If the file is "normal", we | |
487 | * skip the chmod. This works because we did umask(0) if -p | |
488 | * is set, so umask will have left the specified mode alone. | |
489 | */ | |
490 | if ((!f_keep) | |
491 | || (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) { | |
492 | if (chmod(skipcrud+head->header.name, | |
493 | notumask & (int)hstat.st_mode) < 0) { | |
494 | msg_perror("cannot change mode of file %s to %ld",skipcrud+head->header.name,notumask & (int)hstat.st_mode); | |
495 | } | |
496 | } | |
497 | ||
498 | quit: | |
499 | break; | |
500 | ||
501 | case LF_LINK: | |
502 | again_link: | |
503 | { | |
504 | struct stat st1,st2; | |
505 | ||
506 | check = link (head->header.linkname, | |
507 | skipcrud+head->header.name); | |
508 | if (check == 0) | |
509 | break; | |
510 | if (make_dirs(skipcrud+head->header.name)) | |
511 | goto again_link; | |
512 | if(f_gnudump && errno==EEXIST) | |
513 | break; | |
514 | if( stat(head->header.linkname, &st1) == 0 | |
515 | && stat(skipcrud+head->header.name, &st2)==0 | |
516 | && st1.st_dev==st2.st_dev | |
517 | && st1.st_ino==st2.st_ino) | |
518 | break; | |
519 | msg_perror("Could not link %s to %s", | |
520 | skipcrud+head->header.name,head->header.linkname); | |
521 | } | |
522 | break; | |
523 | ||
524 | #ifdef S_IFLNK | |
525 | case LF_SYMLINK: | |
526 | again_symlink: | |
527 | check = symlink(head->header.linkname, | |
528 | skipcrud+head->header.name); | |
529 | /* FIXME, don't worry uid, gid, etc... */ | |
530 | if (check == 0) | |
531 | break; | |
532 | if (make_dirs(skipcrud+head->header.name)) | |
533 | goto again_symlink; | |
534 | msg_perror("Could not create symlink to %s",head->header.linkname); | |
535 | break; | |
536 | #endif | |
537 | ||
538 | #ifdef S_IFCHR | |
539 | case LF_CHR: | |
540 | hstat.st_mode |= S_IFCHR; | |
541 | goto make_node; | |
542 | #endif | |
543 | ||
544 | #ifdef S_IFBLK | |
545 | case LF_BLK: | |
546 | hstat.st_mode |= S_IFBLK; | |
547 | goto make_node; | |
548 | #endif | |
549 | ||
550 | #ifdef S_IFIFO | |
551 | /* If local system doesn't support FIFOs, use default case */ | |
552 | case LF_FIFO: | |
553 | hstat.st_mode |= S_IFIFO; | |
554 | hstat.st_rdev = 0; /* FIXME, do we need this? */ | |
555 | goto make_node; | |
556 | #endif | |
557 | ||
558 | make_node: | |
559 | check = mknod(skipcrud+head->header.name, | |
560 | (int) hstat.st_mode, (int) hstat.st_rdev); | |
561 | if (check != 0) { | |
562 | if (make_dirs(skipcrud+head->header.name)) | |
563 | goto make_node; | |
564 | msg_perror("Could not make %s",skipcrud+head->header.name); | |
565 | break; | |
566 | }; | |
567 | goto set_filestat; | |
568 | ||
569 | case LF_DIR: | |
570 | case LF_DUMPDIR: | |
571 | namelen = strlen(skipcrud+head->header.name)-1; | |
572 | really_dir: | |
573 | /* Check for trailing /, and zap as many as we find. */ | |
574 | while (namelen && head->header.name[skipcrud+namelen] == '/') | |
575 | head->header.name[skipcrud+namelen--] = '\0'; | |
576 | if(f_gnudump) { /* Read the entry and delete files | |
577 | that aren't listed in the archive */ | |
578 | gnu_restore(skipcrud); | |
579 | ||
580 | } else if(head->header.linkflag==LF_DUMPDIR) | |
581 | skip_file((long)(hstat.st_size)); | |
582 | ||
583 | ||
584 | again_dir: | |
585 | check = mkdir(skipcrud+head->header.name, | |
586 | (we_are_root ? 0 : 0300) | (int)hstat.st_mode); | |
587 | if (check != 0) { | |
588 | struct stat st1; | |
589 | ||
590 | if (make_dirs(skipcrud+head->header.name)) | |
591 | goto again_dir; | |
592 | /* If we're trying to create '.', let it be. */ | |
593 | if (head->header.name[skipcrud+namelen] == '.' && | |
594 | (namelen==0 || | |
595 | head->header.name[skipcrud+namelen-1]=='/')) | |
596 | goto check_perms; | |
597 | if( errno==EEXIST | |
598 | && stat(skipcrud+head->header.name,&st1)==0 | |
599 | && (st1.st_mode&S_IFMT)==S_IFDIR) | |
600 | break; | |
601 | msg_perror("Could not create directory %s",skipcrud+head->header.name); | |
602 | break; | |
603 | } | |
604 | ||
605 | check_perms: | |
606 | if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode)) { | |
607 | hstat.st_mode |= 0300; | |
608 | msg("Added write and execute permission to directory %s", | |
609 | skipcrud+head->header.name); | |
610 | } | |
611 | ||
612 | goto set_filestat; | |
613 | /* FIXME, Remember timestamps for after files created? */ | |
614 | /* FIXME, change mode after files created (if was R/O dir) */ | |
615 | case LF_VOLHDR: | |
616 | if(f_verbose) { | |
617 | printf("Reading %s\n",head->header.name); | |
618 | } | |
619 | break; | |
620 | ||
621 | case LF_NAMES: | |
622 | extract_mangle(head); | |
623 | break; | |
624 | ||
625 | case LF_MULTIVOL: | |
626 | msg("Can't extract '%s'--file is continued from another volume\n",head->header.name); | |
627 | skip_file((long)hstat.st_size); | |
628 | break; | |
629 | ||
630 | } | |
631 | ||
632 | /* We don't need to save it any longer. */ | |
633 | saverec((union record **) 0); /* Unsave it */ | |
634 | } | |
635 | ||
636 | /* | |
637 | * After a file/link/symlink/dir creation has failed, see if | |
638 | * it's because some required directory was not present, and if | |
639 | * so, create all required dirs. | |
640 | */ | |
641 | int | |
642 | make_dirs(pathname) | |
643 | char *pathname; | |
644 | { | |
645 | char *p; /* Points into path */ | |
646 | int madeone = 0; /* Did we do anything yet? */ | |
647 | int save_errno = errno; /* Remember caller's errno */ | |
648 | int check; | |
649 | ||
650 | if (errno != ENOENT) | |
651 | return 0; /* Not our problem */ | |
652 | ||
653 | for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) { | |
654 | /* Avoid mkdir of empty string, if leading or double '/' */ | |
655 | if (p == pathname || p[-1] == '/') | |
656 | continue; | |
657 | /* Avoid mkdir where last part of path is '.' */ | |
658 | if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/')) | |
659 | continue; | |
660 | *p = 0; /* Truncate the path there */ | |
661 | check = mkdir (pathname, 0777); /* Try to create it as a dir */ | |
662 | if (check == 0) { | |
663 | /* Fix ownership */ | |
664 | if (we_are_root) { | |
665 | if (chown(pathname, hstat.st_uid, | |
666 | hstat.st_gid) < 0) { | |
667 | msg_perror("cannot change owner of %s to uid %d gid %d",pathname,hstat.st_uid,hstat.st_gid); | |
668 | } | |
669 | } | |
670 | pr_mkdir(pathname, p-pathname, notumask&0777); | |
671 | madeone++; /* Remember if we made one */ | |
672 | *p = '/'; | |
673 | continue; | |
674 | } | |
675 | *p = '/'; | |
676 | if (errno == EEXIST) /* Directory already exists */ | |
677 | continue; | |
678 | /* | |
679 | * Some other error in the mkdir. We return to the caller. | |
680 | */ | |
681 | break; | |
682 | } | |
683 | ||
684 | errno = save_errno; /* Restore caller's errno */ | |
685 | return madeone; /* Tell them to retry if we made one */ | |
686 | } | |
687 | ||
688 | extract_sparse_file(fd, sizeleft, totalsize, name) | |
689 | int fd; | |
690 | long *sizeleft, | |
691 | totalsize; | |
692 | char *name; | |
693 | { | |
694 | register char *data; | |
695 | union record *datarec; | |
696 | int sparse_ind = 0; | |
697 | int written, | |
698 | count; | |
699 | ||
700 | /* assuming sizeleft is initially totalsize */ | |
701 | ||
702 | ||
703 | while (*sizeleft > 0) { | |
704 | datarec = findrec(); | |
705 | if (datarec == NULL) { | |
706 | msg("Unexpected EOF on archive file"); | |
707 | return; | |
708 | } | |
709 | lseek(fd, sparsearray[sparse_ind].offset, 0); | |
710 | written = sparsearray[sparse_ind++].numbytes; | |
711 | while (written > RECORDSIZE) { | |
712 | count = write(fd, datarec->charptr, RECORDSIZE); | |
713 | if (count < 0) | |
714 | msg_perror("couldn't write to file %s", name); | |
715 | written -= count; | |
716 | *sizeleft -= count; | |
717 | userec(datarec); | |
718 | datarec = findrec(); | |
719 | } | |
720 | ||
721 | count = write(fd, datarec->charptr, written); | |
722 | ||
723 | if (count < 0) { | |
724 | msg_perror("couldn't write to file %s", name); | |
725 | } else if (count != written) { | |
726 | msg("could only write %d of %d bytes to file %s", totalsize - *sizeleft, totalsize, name); | |
727 | skip_file((long) (*sizeleft)); | |
728 | } | |
729 | ||
730 | written -= count; | |
731 | *sizeleft -= count; | |
732 | userec(datarec); | |
733 | } | |
734 | free(sparsearray); | |
735 | /* if (end_nulls) { | |
736 | register int i; | |
737 | ||
738 | printf("%d\n", (int) end_nulls); | |
739 | for (i = 0; i < end_nulls; i++) | |
740 | write(fd, "\000", 1); | |
741 | }*/ | |
742 | userec(datarec); | |
743 | } |