Commit | Line | Data |
---|---|---|
8c4ebc23 JH |
1 | /* Create a tar archive. |
2 | Copyright (C) 1985, 1992, 1993 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 2, 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 | * Create a tar archive. | |
22 | * | |
23 | * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu. | |
24 | */ | |
25 | ||
26 | #ifdef _AIX | |
27 | #pragma alloca | |
28 | #endif | |
29 | #include <sys/types.h> | |
30 | #include <stdio.h> | |
31 | #include <errno.h> | |
32 | #ifndef STDC_HEADERS | |
33 | extern int errno; | |
34 | #endif | |
35 | ||
36 | #ifdef BSD42 | |
37 | #include <sys/file.h> | |
38 | #else | |
39 | #ifndef V7 | |
40 | #include <fcntl.h> | |
41 | #endif | |
42 | #endif | |
43 | ||
44 | #include "tar.h" | |
45 | #include "port.h" | |
46 | ||
47 | #ifndef __MSDOS__ | |
48 | #include <pwd.h> | |
49 | #include <grp.h> | |
50 | #endif | |
51 | ||
52 | #if defined (_POSIX_VERSION) | |
53 | #include <utime.h> | |
54 | #else | |
55 | struct utimbuf | |
56 | { | |
57 | long actime; | |
58 | long modtime; | |
59 | }; | |
60 | ||
61 | #endif | |
62 | ||
63 | extern struct stat hstat; /* Stat struct corresponding */ | |
64 | ||
65 | #ifndef __MSDOS__ | |
66 | extern dev_t ar_dev; | |
67 | extern ino_t ar_ino; | |
68 | #endif | |
69 | ||
70 | /* JF */ | |
71 | extern struct name *gnu_list_name; | |
72 | ||
73 | /* | |
74 | * If there are no symbolic links, there is no lstat(). Use stat(). | |
75 | */ | |
76 | #ifndef S_ISLNK | |
77 | #define lstat stat | |
78 | #endif | |
79 | ||
80 | extern void print_header (); | |
81 | ||
82 | union record *start_header (); | |
83 | void blank_name_list (); | |
84 | int check_exclude (); | |
85 | PTR ck_malloc (); | |
86 | PTR ck_realloc (); | |
87 | void clear_buffer (); | |
88 | void close_archive (); | |
89 | void collect_and_sort_names (); | |
90 | int confirm (); | |
91 | int deal_with_sparse (); | |
92 | void find_new_file_size (); | |
93 | void finish_header (); | |
94 | int finish_sparse_file (); | |
95 | void finduname (); | |
96 | void findgname (); | |
97 | int is_dot_or_dotdot (); | |
98 | void open_archive (); | |
99 | char *name_next (); | |
100 | void name_close (); | |
101 | void to_oct (); | |
102 | void dump_file (); | |
103 | void write_dir_file (); | |
104 | void write_eot (); | |
105 | void write_long (); | |
106 | int zero_record (); | |
107 | ||
108 | /* This code moved from tar.h since create.c is the only file that cares | |
109 | about 'struct link's. This means that other files might not have to | |
110 | include sys/types.h any more. */ | |
111 | ||
112 | struct link | |
113 | { | |
114 | struct link *next; | |
115 | dev_t dev; | |
116 | ino_t ino; | |
117 | short linkcount; | |
118 | char name[1]; | |
119 | }; | |
120 | ||
121 | struct link *linklist; /* Points to first link in list */ | |
122 | ||
123 | static nolinks; /* Gets set if we run out of RAM */ | |
124 | ||
125 | /* | |
126 | * "Scratch" space to store the information about a sparse file before | |
127 | * writing the info into the header or extended header | |
128 | */ | |
129 | /* struct sp_array *sparsearray;*/ | |
130 | ||
131 | /* number of elts storable in the sparsearray */ | |
132 | /*int sparse_array_size = 10;*/ | |
133 | ||
134 | void | |
135 | create_archive () | |
136 | { | |
137 | register char *p; | |
138 | char *name_from_list (); | |
139 | ||
140 | open_archive (0); /* Open for writing */ | |
141 | ||
142 | if (f_gnudump) | |
143 | { | |
144 | char *buf = ck_malloc (PATH_MAX); | |
145 | char *q, *bufp; | |
146 | ||
147 | collect_and_sort_names (); | |
148 | ||
149 | while (p = name_from_list ()) | |
150 | dump_file (p, -1, 1); | |
151 | /* if(!f_dironly) { */ | |
152 | blank_name_list (); | |
153 | while (p = name_from_list ()) | |
154 | { | |
155 | strcpy (buf, p); | |
156 | if (p[strlen (p) - 1] != '/') | |
157 | strcat (buf, "/"); | |
158 | bufp = buf + strlen (buf); | |
159 | for (q = gnu_list_name->dir_contents; q && *q; q += strlen (q) + 1) | |
160 | { | |
161 | if (*q == 'Y') | |
162 | { | |
163 | strcpy (bufp, q + 1); | |
164 | dump_file (buf, -1, 1); | |
165 | } | |
166 | } | |
167 | } | |
168 | /* } */ | |
169 | free (buf); | |
170 | } | |
171 | else | |
172 | { | |
173 | while (p = name_next (1)) | |
174 | dump_file (p, -1, 1); | |
175 | } | |
176 | ||
177 | write_eot (); | |
178 | close_archive (); | |
179 | if (f_gnudump) | |
180 | write_dir_file (); | |
181 | name_close (); | |
182 | } | |
183 | ||
184 | /* | |
185 | * Dump a single file. If it's a directory, recurse. | |
186 | * Result is 1 for success, 0 for failure. | |
187 | * Sets global "hstat" to stat() output for this file. | |
188 | */ | |
189 | void | |
190 | dump_file (p, curdev, toplevel) | |
191 | char *p; /* File name to dump */ | |
192 | int curdev; /* Device our parent dir was on */ | |
193 | int toplevel; /* Whether we are a toplevel call */ | |
194 | { | |
195 | union record *header; | |
196 | char type; | |
197 | extern char *save_name; /* JF for multi-volume support */ | |
198 | extern long save_totsize; | |
199 | extern long save_sizeleft; | |
200 | union record *exhdr; | |
201 | char save_linkflag; | |
202 | extern time_t new_time; | |
203 | int critical_error = 0; | |
204 | struct utimbuf restore_times; | |
205 | /* int sparse_ind = 0;*/ | |
206 | ||
207 | ||
208 | if (f_confirm && !confirm ("add", p)) | |
209 | return; | |
210 | ||
211 | /* | |
212 | * Use stat if following (rather than dumping) 4.2BSD's | |
213 | * symbolic links. Otherwise, use lstat (which, on non-4.2 | |
214 | * systems, is #define'd to stat anyway. | |
215 | */ | |
216 | #ifdef STX_HIDDEN /* AIX */ | |
217 | if (0 != f_follow_links ? | |
218 | statx (p, &hstat, STATSIZE, STX_HIDDEN) : | |
219 | statx (p, &hstat, STATSIZE, STX_HIDDEN | STX_LINK)) | |
220 | #else | |
221 | if (0 != f_follow_links ? stat (p, &hstat) : lstat (p, &hstat)) | |
222 | #endif | |
223 | { | |
224 | badperror: | |
225 | msg_perror ("can't add file %s", p); | |
226 | badfile: | |
227 | if (!f_ignore_failed_read || critical_error) | |
228 | errors++; | |
229 | return; | |
230 | } | |
231 | ||
232 | restore_times.actime = hstat.st_atime; | |
233 | restore_times.modtime = hstat.st_mtime; | |
234 | ||
235 | #ifdef S_ISHIDDEN | |
236 | if (S_ISHIDDEN (hstat.st_mode)) | |
237 | { | |
238 | char *new = (char *) alloca (strlen (p) + 2); | |
239 | if (new) | |
240 | { | |
241 | strcpy (new, p); | |
242 | strcat (new, "@"); | |
243 | p = new; | |
244 | } | |
245 | } | |
246 | #endif | |
247 | ||
248 | /* See if we only want new files, and check if this one is too old to | |
249 | put in the archive. */ | |
250 | if (f_new_files | |
251 | && !f_gnudump | |
252 | && new_time > hstat.st_mtime | |
253 | && !S_ISDIR (hstat.st_mode) | |
254 | && (f_new_files > 1 || new_time > hstat.st_ctime)) | |
255 | { | |
256 | if (curdev == -1) | |
257 | { | |
258 | msg ("%s: is unchanged; not dumped", p); | |
259 | } | |
260 | return; | |
261 | } | |
262 | ||
263 | #ifndef __MSDOS__ | |
264 | /* See if we are trying to dump the archive */ | |
265 | if (ar_dev && hstat.st_dev == ar_dev && hstat.st_ino == ar_ino) | |
266 | { | |
267 | msg ("%s is the archive; not dumped", p); | |
268 | return; | |
269 | } | |
270 | #endif | |
271 | /* | |
272 | * Check for multiple links. | |
273 | * | |
274 | * We maintain a list of all such files that we've written so | |
275 | * far. Any time we see another, we check the list and | |
276 | * avoid dumping the data again if we've done it once already. | |
277 | */ | |
278 | if (hstat.st_nlink > 1 | |
279 | && (S_ISREG (hstat.st_mode) | |
280 | #ifdef S_ISCTG | |
281 | || S_ISCTG (hstat.st_mode) | |
282 | #endif | |
283 | #ifdef S_ISCHR | |
284 | || S_ISCHR (hstat.st_mode) | |
285 | #endif | |
286 | #ifdef S_ISBLK | |
287 | || S_ISBLK (hstat.st_mode) | |
288 | #endif | |
289 | #ifdef S_ISFIFO | |
290 | || S_ISFIFO (hstat.st_mode) | |
291 | #endif | |
292 | )) | |
293 | { | |
294 | register struct link *lp; | |
295 | ||
296 | /* First quick and dirty. Hashing, etc later FIXME */ | |
297 | for (lp = linklist; lp; lp = lp->next) | |
298 | { | |
299 | if (lp->ino == hstat.st_ino && | |
300 | lp->dev == hstat.st_dev) | |
301 | { | |
302 | char *link_name = lp->name; | |
303 | ||
304 | /* We found a link. */ | |
305 | while (!f_absolute_paths && *link_name == '/') | |
306 | { | |
307 | static int link_warn = 0; | |
308 | ||
309 | if (!link_warn) | |
310 | { | |
311 | msg ("Removing leading / from absolute links"); | |
312 | link_warn++; | |
313 | } | |
314 | link_name++; | |
315 | } | |
316 | if (link_name - lp->name >= NAMSIZ) | |
317 | write_long (link_name, LF_LONGLINK); | |
318 | current_link_name = link_name; | |
319 | ||
320 | hstat.st_size = 0; | |
321 | header = start_header (p, &hstat); | |
322 | if (header == NULL) | |
323 | { | |
324 | critical_error = 1; | |
325 | goto badfile; | |
326 | } | |
327 | strncpy (header->header.arch_linkname, | |
328 | link_name, NAMSIZ); | |
329 | ||
330 | /* Force null truncated */ | |
331 | header->header.arch_linkname[NAMSIZ - 1] = 0; | |
332 | ||
333 | header->header.linkflag = LF_LINK; | |
334 | finish_header (header); | |
335 | /* FIXME: Maybe remove from list after all links found? */ | |
336 | if (f_remove_files) | |
337 | { | |
338 | if (unlink (p) == -1) | |
339 | msg_perror ("cannot remove %s", p); | |
340 | } | |
341 | return; /* We dumped it */ | |
342 | } | |
343 | } | |
344 | ||
345 | /* Not found. Add it to the list of possible links. */ | |
346 | lp = (struct link *) ck_malloc ((unsigned) (sizeof (struct link) + strlen (p))); | |
347 | if (!lp) | |
348 | { | |
349 | if (!nolinks) | |
350 | { | |
351 | msg ( | |
352 | "no memory for links, they will be dumped as separate files"); | |
353 | nolinks++; | |
354 | } | |
355 | } | |
356 | lp->ino = hstat.st_ino; | |
357 | lp->dev = hstat.st_dev; | |
358 | strcpy (lp->name, p); | |
359 | lp->next = linklist; | |
360 | linklist = lp; | |
361 | } | |
362 | ||
363 | /* | |
364 | * This is not a link to a previously dumped file, so dump it. | |
365 | */ | |
366 | if (S_ISREG (hstat.st_mode) | |
367 | #ifdef S_ISCTG | |
368 | || S_ISCTG (hstat.st_mode) | |
369 | #endif | |
370 | ) | |
371 | { | |
372 | int f; /* File descriptor */ | |
373 | long bufsize, count; | |
374 | long sizeleft; | |
375 | register union record *start; | |
376 | int header_moved; | |
377 | char isextended = 0; | |
378 | int upperbound; | |
379 | /* int end_nulls = 0; */ | |
380 | ||
381 | header_moved = 0; | |
382 | ||
383 | #ifdef BSD42 | |
384 | if (f_sparse_files) | |
385 | { | |
386 | /* | |
387 | * JK - This is the test for sparseness: whether the | |
388 | * "size" of the file matches the number of blocks | |
389 | * allocated for it. If there is a smaller number | |
390 | * of blocks that would be necessary to accommodate | |
391 | * a file of this size, we have a sparse file, i.e., | |
392 | * at least one of those records in the file is just | |
393 | * a useless hole. | |
394 | */ | |
395 | #ifdef hpux /* Nice of HPUX to gratuitiously change it, huh? - mib */ | |
396 | if (hstat.st_size - (hstat.st_blocks * 1024) > 1024) | |
397 | #else | |
398 | if (hstat.st_size - (hstat.st_blocks * RECORDSIZE) > RECORDSIZE) | |
399 | #endif | |
400 | { | |
401 | int filesize = hstat.st_size; | |
402 | register int i; | |
403 | ||
404 | header = start_header (p, &hstat); | |
405 | if (header == NULL) | |
406 | { | |
407 | critical_error = 1; | |
408 | goto badfile; | |
409 | } | |
410 | header->header.linkflag = LF_SPARSE; | |
411 | header_moved++; | |
412 | ||
413 | /* | |
414 | * Call the routine that figures out the | |
415 | * layout of the sparse file in question. | |
416 | * UPPERBOUND is the index of the last | |
417 | * element of the "sparsearray," i.e., | |
418 | * the number of elements it needed to | |
419 | * describe the file. | |
420 | */ | |
421 | ||
422 | upperbound = deal_with_sparse (p, header); | |
423 | ||
424 | /* | |
425 | * See if we'll need an extended header | |
426 | * later | |
427 | */ | |
428 | if (upperbound > SPARSE_IN_HDR - 1) | |
429 | header->header.isextended++; | |
430 | /* | |
431 | * We store the "real" file size so | |
432 | * we can show that in case someone wants | |
433 | * to list the archive, i.e., tar tvf <file>. | |
434 | * It might be kind of disconcerting if the | |
435 | * shrunken file size was the one that showed | |
436 | * up. | |
437 | */ | |
438 | to_oct ((long) hstat.st_size, 1 + 12, | |
439 | header->header.realsize); | |
440 | ||
441 | /* | |
442 | * This will be the new "size" of the | |
443 | * file, i.e., the size of the file | |
444 | * minus the records of holes that we're | |
445 | * skipping over. | |
446 | */ | |
447 | ||
448 | find_new_file_size (&filesize, upperbound); | |
449 | hstat.st_size = filesize; | |
450 | to_oct ((long) filesize, 1 + 12, | |
451 | header->header.size); | |
452 | /* to_oct((long) end_nulls, 1+12, | |
453 | header->header.ending_blanks);*/ | |
454 | ||
455 | for (i = 0; i < SPARSE_IN_HDR; i++) | |
456 | { | |
457 | if (!sparsearray[i].numbytes) | |
458 | break; | |
459 | to_oct (sparsearray[i].offset, 1 + 12, | |
460 | header->header.sp[i].offset); | |
461 | to_oct (sparsearray[i].numbytes, 1 + 12, | |
462 | header->header.sp[i].numbytes); | |
463 | } | |
464 | ||
465 | } | |
466 | } | |
467 | #else | |
468 | upperbound = SPARSE_IN_HDR - 1; | |
469 | #endif | |
470 | ||
471 | sizeleft = hstat.st_size; | |
472 | /* Don't bother opening empty, world readable files. */ | |
473 | if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode)) | |
474 | { | |
475 | f = open (p, O_RDONLY | O_BINARY); | |
476 | if (f < 0) | |
477 | goto badperror; | |
478 | } | |
479 | else | |
480 | { | |
481 | f = -1; | |
482 | } | |
483 | ||
484 | /* If the file is sparse, we've already taken care of this */ | |
485 | if (!header_moved) | |
486 | { | |
487 | header = start_header (p, &hstat); | |
488 | if (header == NULL) | |
489 | { | |
490 | if (f >= 0) | |
491 | (void) close (f); | |
492 | critical_error = 1; | |
493 | goto badfile; | |
494 | } | |
495 | } | |
496 | #ifdef S_ISCTG | |
497 | /* Mark contiguous files, if we support them */ | |
498 | if (f_standard && S_ISCTG (hstat.st_mode)) | |
499 | { | |
500 | header->header.linkflag = LF_CONTIG; | |
501 | } | |
502 | #endif | |
503 | isextended = header->header.isextended; | |
504 | save_linkflag = header->header.linkflag; | |
505 | finish_header (header); | |
506 | if (isextended) | |
507 | { | |
508 | /* int sum = 0;*/ | |
509 | register int i; | |
510 | /* register union record *exhdr;*/ | |
511 | /* int arraybound = SPARSE_EXT_HDR;*/ | |
512 | /* static */ int index_offset = SPARSE_IN_HDR; | |
513 | ||
514 | extend:exhdr = findrec (); | |
515 | ||
516 | if (exhdr == NULL) | |
517 | { | |
518 | critical_error = 1; | |
519 | goto badfile; | |
520 | } | |
521 | bzero (exhdr->charptr, RECORDSIZE); | |
522 | for (i = 0; i < SPARSE_EXT_HDR; i++) | |
523 | { | |
524 | if (i + index_offset > upperbound) | |
525 | break; | |
526 | to_oct ((long) sparsearray[i + index_offset].numbytes, | |
527 | 1 + 12, | |
528 | exhdr->ext_hdr.sp[i].numbytes); | |
529 | to_oct ((long) sparsearray[i + index_offset].offset, | |
530 | 1 + 12, | |
531 | exhdr->ext_hdr.sp[i].offset); | |
532 | } | |
533 | userec (exhdr); | |
534 | /* sum += i; | |
535 | if (sum < upperbound) | |
536 | goto extend;*/ | |
537 | if (index_offset + i <= upperbound) | |
538 | { | |
539 | index_offset += i; | |
540 | exhdr->ext_hdr.isextended++; | |
541 | goto extend; | |
542 | } | |
543 | ||
544 | } | |
545 | if (save_linkflag == LF_SPARSE) | |
546 | { | |
547 | if (finish_sparse_file (f, &sizeleft, hstat.st_size, p)) | |
548 | goto padit; | |
549 | } | |
550 | else | |
551 | while (sizeleft > 0) | |
552 | { | |
553 | ||
554 | if (f_multivol) | |
555 | { | |
556 | save_name = p; | |
557 | save_sizeleft = sizeleft; | |
558 | save_totsize = hstat.st_size; | |
559 | } | |
560 | start = findrec (); | |
561 | ||
562 | bufsize = endofrecs ()->charptr - start->charptr; | |
563 | ||
564 | if (sizeleft < bufsize) | |
565 | { | |
566 | /* Last read -- zero out area beyond */ | |
567 | bufsize = (int) sizeleft; | |
568 | count = bufsize % RECORDSIZE; | |
569 | if (count) | |
570 | bzero (start->charptr + sizeleft, | |
571 | (int) (RECORDSIZE - count)); | |
572 | } | |
573 | count = read (f, start->charptr, bufsize); | |
574 | if (count < 0) | |
575 | { | |
576 | msg_perror ("read error at byte %ld, reading\ | |
577 | %d bytes, in file %s", hstat.st_size - sizeleft, bufsize, p); | |
578 | goto padit; | |
579 | } | |
580 | sizeleft -= count; | |
581 | ||
582 | /* This is nonportable (the type of userec's arg). */ | |
583 | userec (start + (count - 1) / RECORDSIZE); | |
584 | ||
585 | if (count == bufsize) | |
586 | continue; | |
587 | msg ("file %s shrunk by %d bytes, padding with zeros.", p, sizeleft); | |
588 | goto padit; /* Short read */ | |
589 | } | |
590 | ||
591 | if (f_multivol) | |
592 | save_name = 0; | |
593 | ||
594 | if (f >= 0) | |
595 | (void) close (f); | |
596 | ||
597 | if (f_remove_files) | |
598 | { | |
599 | if (unlink (p) == -1) | |
600 | msg_perror ("cannot remove %s", p); | |
601 | } | |
602 | if (f_atime_preserve) | |
603 | utime (p, &restore_times); | |
604 | return; | |
605 | ||
606 | /* | |
607 | * File shrunk or gave error, pad out tape to match | |
608 | * the size we specified in the header. | |
609 | */ | |
610 | padit: | |
611 | while (sizeleft > 0) | |
612 | { | |
613 | save_sizeleft = sizeleft; | |
614 | start = findrec (); | |
615 | bzero (start->charptr, RECORDSIZE); | |
616 | userec (start); | |
617 | sizeleft -= RECORDSIZE; | |
618 | } | |
619 | if (f_multivol) | |
620 | save_name = 0; | |
621 | if (f >= 0) | |
622 | (void) close (f); | |
623 | if (f_atime_preserve) | |
624 | utime (p, &restore_times); | |
625 | return; | |
626 | } | |
627 | ||
628 | #ifdef S_ISLNK | |
629 | else if (S_ISLNK (hstat.st_mode)) | |
630 | { | |
631 | int size; | |
632 | char *buf = alloca (PATH_MAX + 1); | |
633 | ||
634 | size = readlink (p, buf, PATH_MAX + 1); | |
635 | if (size < 0) | |
636 | goto badperror; | |
637 | buf[size] = '\0'; | |
638 | if (size >= NAMSIZ) | |
639 | write_long (buf, LF_LONGLINK); | |
640 | current_link_name = buf; | |
641 | ||
642 | hstat.st_size = 0; /* Force 0 size on symlink */ | |
643 | header = start_header (p, &hstat); | |
644 | if (header == NULL) | |
645 | { | |
646 | critical_error = 1; | |
647 | goto badfile; | |
648 | } | |
649 | strncpy (header->header.arch_linkname, buf, NAMSIZ); | |
650 | header->header.arch_linkname[NAMSIZ - 1] = '\0'; | |
651 | header->header.linkflag = LF_SYMLINK; | |
652 | finish_header (header); /* Nothing more to do to it */ | |
653 | if (f_remove_files) | |
654 | { | |
655 | if (unlink (p) == -1) | |
656 | msg_perror ("cannot remove %s", p); | |
657 | } | |
658 | return; | |
659 | } | |
660 | #endif | |
661 | ||
662 | else if (S_ISDIR (hstat.st_mode)) | |
663 | { | |
664 | register DIR *dirp; | |
665 | register struct dirent *d; | |
666 | char *namebuf; | |
667 | int buflen; | |
668 | register int len; | |
669 | int our_device = hstat.st_dev; | |
670 | ||
671 | /* Build new prototype name */ | |
672 | len = strlen (p); | |
673 | buflen = len + NAMSIZ; | |
674 | namebuf = ck_malloc (buflen + 1); | |
675 | strncpy (namebuf, p, buflen); | |
676 | while (len >= 1 && '/' == namebuf[len - 1]) | |
677 | len--; /* Delete trailing slashes */ | |
678 | namebuf[len++] = '/'; /* Now add exactly one back */ | |
679 | namebuf[len] = '\0'; /* Make sure null-terminated */ | |
680 | ||
681 | /* | |
682 | * Output directory header record with permissions | |
683 | * FIXME, do this AFTER files, to avoid R/O dir problems? | |
684 | * If old archive format, don't write record at all. | |
685 | */ | |
686 | if (!f_oldarch) | |
687 | { | |
688 | hstat.st_size = 0; /* Force 0 size on dir */ | |
689 | /* | |
690 | * If people could really read standard archives, | |
691 | * this should be: (FIXME) | |
692 | header = start_header(f_standard? p: namebuf, &hstat); | |
693 | * but since they'd interpret LF_DIR records as | |
694 | * regular files, we'd better put the / on the name. | |
695 | */ | |
696 | header = start_header (namebuf, &hstat); | |
697 | if (header == NULL) | |
698 | { | |
699 | critical_error = 1; | |
700 | goto badfile; /* eg name too long */ | |
701 | } | |
702 | ||
703 | if (f_gnudump) | |
704 | header->header.linkflag = LF_DUMPDIR; | |
705 | else if (f_standard) | |
706 | header->header.linkflag = LF_DIR; | |
707 | ||
708 | /* If we're gnudumping, we aren't done yet so don't close it. */ | |
709 | if (!f_gnudump) | |
710 | finish_header (header); /* Done with directory header */ | |
711 | } | |
712 | ||
713 | if (f_gnudump) | |
714 | { | |
715 | int sizeleft; | |
716 | int totsize; | |
717 | int bufsize; | |
718 | union record *start; | |
719 | int count; | |
720 | char *buf, *p_buf; | |
721 | ||
722 | buf = gnu_list_name->dir_contents; /* FOO */ | |
723 | totsize = 0; | |
724 | for (p_buf = buf; p_buf && *p_buf;) | |
725 | { | |
726 | int tmp; | |
727 | ||
728 | tmp = strlen (p_buf) + 1; | |
729 | totsize += tmp; | |
730 | p_buf += tmp; | |
731 | } | |
732 | totsize++; | |
733 | to_oct ((long) totsize, 1 + 12, header->header.size); | |
734 | finish_header (header); | |
735 | p_buf = buf; | |
736 | sizeleft = totsize; | |
737 | while (sizeleft > 0) | |
738 | { | |
739 | if (f_multivol) | |
740 | { | |
741 | save_name = p; | |
742 | save_sizeleft = sizeleft; | |
743 | save_totsize = totsize; | |
744 | } | |
745 | start = findrec (); | |
746 | bufsize = endofrecs ()->charptr - start->charptr; | |
747 | if (sizeleft < bufsize) | |
748 | { | |
749 | bufsize = sizeleft; | |
750 | count = bufsize % RECORDSIZE; | |
751 | if (count) | |
752 | bzero (start->charptr + sizeleft, RECORDSIZE - count); | |
753 | } | |
754 | bcopy (p_buf, start->charptr, bufsize); | |
755 | sizeleft -= bufsize; | |
756 | p_buf += bufsize; | |
757 | userec (start + (bufsize - 1) / RECORDSIZE); | |
758 | } | |
759 | if (f_multivol) | |
760 | save_name = 0; | |
761 | if (f_atime_preserve) | |
762 | utime (p, &restore_times); | |
763 | return; | |
764 | } | |
765 | ||
766 | /* Now output all the files in the directory */ | |
767 | #if 0 | |
768 | if (f_dironly) | |
769 | return; /* Unless the cmdline said not to */ | |
770 | #endif | |
771 | /* | |
772 | * See if we are crossing from one file system to another, | |
773 | * and avoid doing so if the user only wants to dump one file system. | |
774 | */ | |
775 | if (f_local_filesys && !toplevel && curdev != hstat.st_dev) | |
776 | { | |
777 | if (f_verbose) | |
778 | msg ("%s: is on a different filesystem; not dumped", p); | |
779 | return; | |
780 | } | |
781 | ||
782 | ||
783 | errno = 0; | |
784 | dirp = opendir (p); | |
785 | if (!dirp) | |
786 | { | |
787 | if (errno) | |
788 | { | |
789 | msg_perror ("can't open directory %s", p); | |
790 | } | |
791 | else | |
792 | { | |
793 | msg ("error opening directory %s", | |
794 | p); | |
795 | } | |
796 | return; | |
797 | } | |
798 | ||
799 | /* Hack to remove "./" from the front of all the file names */ | |
800 | if (len == 2 && namebuf[0] == '.' && namebuf[1] == '/') | |
801 | len = 0; | |
802 | ||
803 | /* Should speed this up by cd-ing into the dir, FIXME */ | |
804 | while (NULL != (d = readdir (dirp))) | |
805 | { | |
806 | /* Skip . and .. */ | |
807 | if (is_dot_or_dotdot (d->d_name)) | |
808 | continue; | |
809 | ||
810 | if (NLENGTH (d) + len >= buflen) | |
811 | { | |
812 | buflen = len + NLENGTH (d); | |
813 | namebuf = ck_realloc (namebuf, buflen + 1); | |
814 | /* namebuf[len]='\0'; | |
815 | msg("file name %s%s too long", | |
816 | namebuf, d->d_name); | |
817 | continue; */ | |
818 | } | |
819 | strcpy (namebuf + len, d->d_name); | |
820 | if (f_exclude && check_exclude (namebuf)) | |
821 | continue; | |
822 | dump_file (namebuf, our_device, 0); | |
823 | } | |
824 | ||
825 | closedir (dirp); | |
826 | free (namebuf); | |
827 | if (f_atime_preserve) | |
828 | utime (p, &restore_times); | |
829 | return; | |
830 | } | |
831 | ||
832 | #ifdef S_ISCHR | |
833 | else if (S_ISCHR (hstat.st_mode)) | |
834 | { | |
835 | type = LF_CHR; | |
836 | } | |
837 | #endif | |
838 | ||
839 | #ifdef S_ISBLK | |
840 | else if (S_ISBLK (hstat.st_mode)) | |
841 | { | |
842 | type = LF_BLK; | |
843 | } | |
844 | #endif | |
845 | ||
846 | /* Avoid screwy apollo lossage where S_IFIFO == S_IFSOCK */ | |
847 | #if (_ISP__M68K == 0) && (_ISP__A88K == 0) && defined(S_ISFIFO) | |
848 | else if (S_ISFIFO (hstat.st_mode)) | |
849 | { | |
850 | type = LF_FIFO; | |
851 | } | |
852 | #endif | |
853 | ||
854 | #ifdef S_ISSOCK | |
855 | else if (S_ISSOCK (hstat.st_mode)) | |
856 | { | |
857 | type = LF_FIFO; | |
858 | } | |
859 | #endif | |
860 | else | |
861 | goto unknown; | |
862 | ||
863 | if (!f_standard) | |
864 | goto unknown; | |
865 | ||
866 | hstat.st_size = 0; /* Force 0 size */ | |
867 | header = start_header (p, &hstat); | |
868 | if (header == NULL) | |
869 | { | |
870 | critical_error = 1; | |
871 | goto badfile; /* eg name too long */ | |
872 | } | |
873 | ||
874 | header->header.linkflag = type; | |
875 | #if defined(S_IFBLK) || defined(S_IFCHR) | |
876 | if (type != LF_FIFO) | |
877 | { | |
878 | to_oct ((long) major (hstat.st_rdev), 8, | |
879 | header->header.devmajor); | |
880 | to_oct ((long) minor (hstat.st_rdev), 8, | |
881 | header->header.devminor); | |
882 | } | |
883 | #endif | |
884 | ||
885 | finish_header (header); | |
886 | if (f_remove_files) | |
887 | { | |
888 | if (unlink (p) == -1) | |
889 | msg_perror ("cannot remove %s", p); | |
890 | } | |
891 | return; | |
892 | ||
893 | unknown: | |
894 | msg ("%s: Unknown file type; file ignored.", p); | |
895 | } | |
896 | ||
897 | int | |
898 | finish_sparse_file (fd, sizeleft, fullsize, name) | |
899 | int fd; | |
900 | long *sizeleft, fullsize; | |
901 | char *name; | |
902 | { | |
903 | union record *start; | |
904 | char tempbuf[RECORDSIZE]; | |
905 | int bufsize, sparse_ind = 0, count; | |
906 | long pos; | |
907 | long nwritten = 0; | |
908 | ||
909 | ||
910 | while (*sizeleft > 0) | |
911 | { | |
912 | start = findrec (); | |
913 | bzero (start->charptr, RECORDSIZE); | |
914 | bufsize = sparsearray[sparse_ind].numbytes; | |
915 | if (!bufsize) | |
916 | { /* we blew it, maybe */ | |
917 | msg ("Wrote %ld of %ld bytes to file %s", | |
918 | fullsize - *sizeleft, fullsize, name); | |
919 | break; | |
920 | } | |
921 | pos = lseek (fd, sparsearray[sparse_ind++].offset, 0); | |
922 | /* | |
923 | * If the number of bytes to be written here exceeds | |
924 | * the size of the temporary buffer, do it in steps. | |
925 | */ | |
926 | while (bufsize > RECORDSIZE) | |
927 | { | |
928 | /* if (amt_read) { | |
929 | count = read(fd, start->charptr+amt_read, RECORDSIZE-amt_read); | |
930 | bufsize -= RECORDSIZE - amt_read; | |
931 | amt_read = 0; | |
932 | userec(start); | |
933 | start = findrec(); | |
934 | bzero(start->charptr, RECORDSIZE); | |
935 | }*/ | |
936 | /* store the data */ | |
937 | count = read (fd, start->charptr, RECORDSIZE); | |
938 | if (count < 0) | |
939 | { | |
940 | msg_perror ("read error at byte %ld, reading %d bytes, in file %s", | |
941 | fullsize - *sizeleft, bufsize, name); | |
942 | return 1; | |
943 | } | |
944 | bufsize -= count; | |
945 | *sizeleft -= count; | |
946 | userec (start); | |
947 | nwritten += RECORDSIZE; /* XXX */ | |
948 | start = findrec (); | |
949 | bzero (start->charptr, RECORDSIZE); | |
950 | } | |
951 | ||
952 | ||
953 | clear_buffer (tempbuf); | |
954 | count = read (fd, tempbuf, bufsize); | |
955 | bcopy (tempbuf, start->charptr, RECORDSIZE); | |
956 | if (count < 0) | |
957 | { | |
958 | msg_perror ("read error at byte %ld, reading %d bytes, in file %s", | |
959 | fullsize - *sizeleft, bufsize, name); | |
960 | return 1; | |
961 | } | |
962 | /* if (amt_read >= RECORDSIZE) { | |
963 | amt_read = 0; | |
964 | userec(start+(count-1)/RECORDSIZE); | |
965 | if (count != bufsize) { | |
966 | msg("file %s shrunk by %d bytes, padding with zeros.", name, sizeleft); | |
967 | return 1; | |
968 | } | |
969 | start = findrec(); | |
970 | } else | |
971 | amt_read += bufsize;*/ | |
972 | nwritten += count; /* XXX */ | |
973 | *sizeleft -= count; | |
974 | userec (start); | |
975 | ||
976 | } | |
977 | free (sparsearray); | |
978 | /* printf ("Amount actually written is (I hope) %d.\n", nwritten); */ | |
979 | /* userec(start+(count-1)/RECORDSIZE);*/ | |
980 | return 0; | |
981 | ||
982 | } | |
983 | ||
984 | void | |
985 | init_sparsearray () | |
986 | { | |
987 | register int i; | |
988 | ||
989 | sp_array_size = 10; | |
990 | /* | |
991 | * Make room for our scratch space -- initially is 10 elts long | |
992 | */ | |
993 | sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array)); | |
994 | for (i = 0; i < sp_array_size; i++) | |
995 | { | |
996 | sparsearray[i].offset = 0; | |
997 | sparsearray[i].numbytes = 0; | |
998 | } | |
999 | } | |
1000 | ||
1001 | ||
1002 | ||
1003 | /* | |
1004 | * Okay, we've got a sparse file on our hands -- now, what we need to do is | |
1005 | * make a pass through the file and carefully note where any data is, i.e., | |
1006 | * we want to find how far into the file each instance of data is, and how | |
1007 | * many bytes are there. We store this information in the sparsearray, | |
1008 | * which will later be translated into header information. For now, we use | |
1009 | * the sparsearray as convenient storage. | |
1010 | * | |
1011 | * As a side note, this routine is a mess. If I could have found a cleaner | |
1012 | * way to do it, I would have. If anyone wants to find a nicer way to do | |
1013 | * this, feel free. | |
1014 | */ | |
1015 | ||
1016 | /* There is little point in trimming small amounts of null data at the */ | |
1017 | /* head and tail of blocks -- it's ok if we only avoid dumping blocks */ | |
1018 | /* of complete null data */ | |
1019 | int | |
1020 | deal_with_sparse (name, header, nulls_at_end) | |
1021 | char *name; | |
1022 | union record *header; | |
1023 | int nulls_at_end; | |
1024 | { | |
1025 | long numbytes = 0; | |
1026 | long offset = 0; | |
1027 | /* long save_offset;*/ | |
1028 | int fd; | |
1029 | /* int current_size = hstat.st_size;*/ | |
1030 | int sparse_ind = 0, cc; | |
1031 | char buf[RECORDSIZE]; | |
1032 | #if 0 | |
1033 | int read_last_data = 0; /* did we just read the last record? */ | |
1034 | #endif | |
1035 | int amidst_data = 0; | |
1036 | ||
1037 | header->header.isextended = 0; | |
1038 | /* | |
1039 | * Can't open the file -- this problem will be caught later on, | |
1040 | * so just return. | |
1041 | */ | |
1042 | if ((fd = open (name, O_RDONLY)) < 0) | |
1043 | return 0; | |
1044 | ||
1045 | init_sparsearray (); | |
1046 | clear_buffer (buf); | |
1047 | ||
1048 | while ((cc = read (fd, buf, sizeof buf)) != 0) | |
1049 | { | |
1050 | ||
1051 | if (sparse_ind > sp_array_size - 1) | |
1052 | { | |
1053 | ||
1054 | /* | |
1055 | * realloc the scratch area, since we've run out of room -- | |
1056 | */ | |
1057 | sparsearray = (struct sp_array *) | |
1058 | ck_realloc (sparsearray, | |
1059 | 2 * sp_array_size * (sizeof (struct sp_array))); | |
1060 | sp_array_size *= 2; | |
1061 | } | |
1062 | if (cc == sizeof buf) | |
1063 | { | |
1064 | if (zero_record (buf)) | |
1065 | { | |
1066 | if (amidst_data) | |
1067 | { | |
1068 | sparsearray[sparse_ind++].numbytes | |
1069 | = numbytes; | |
1070 | amidst_data = 0; | |
1071 | } | |
1072 | } | |
1073 | else | |
1074 | { /* !zero_record(buf) */ | |
1075 | if (amidst_data) | |
1076 | numbytes += cc; | |
1077 | else | |
1078 | { | |
1079 | amidst_data = 1; | |
1080 | numbytes = cc; | |
1081 | sparsearray[sparse_ind].offset | |
1082 | = offset; | |
1083 | } | |
1084 | } | |
1085 | } | |
1086 | else if (cc < sizeof buf) | |
1087 | { | |
1088 | /* This has to be the last bit of the file, so this */ | |
1089 | /* is somewhat shorter than the above. */ | |
1090 | if (!zero_record (buf)) | |
1091 | { | |
1092 | if (!amidst_data) | |
1093 | { | |
1094 | amidst_data = 1; | |
1095 | numbytes = cc; | |
1096 | sparsearray[sparse_ind].offset | |
1097 | = offset; | |
1098 | } | |
1099 | else | |
1100 | numbytes += cc; | |
1101 | } | |
1102 | } | |
1103 | offset += cc; | |
1104 | clear_buffer (buf); | |
1105 | } | |
1106 | if (amidst_data) | |
1107 | sparsearray[sparse_ind++].numbytes = numbytes; | |
1108 | else | |
1109 | { | |
1110 | sparsearray[sparse_ind].offset = offset-1; | |
1111 | sparsearray[sparse_ind++].numbytes = 1; | |
1112 | } | |
1113 | close (fd); | |
1114 | ||
1115 | return sparse_ind - 1; | |
1116 | } | |
1117 | ||
1118 | /* | |
1119 | * Just zeroes out the buffer so we don't confuse ourselves with leftover | |
1120 | * data. | |
1121 | */ | |
1122 | void | |
1123 | clear_buffer (buf) | |
1124 | char *buf; | |
1125 | { | |
1126 | register int i; | |
1127 | ||
1128 | for (i = 0; i < RECORDSIZE; i++) | |
1129 | buf[i] = '\0'; | |
1130 | } | |
1131 | ||
1132 | #if 0 /* I'm leaving this as a monument to Joy Kendall, who wrote it -mib */ | |
1133 | /* | |
1134 | * JK - | |
1135 | * This routine takes a character array, and tells where within that array | |
1136 | * the data can be found. It skips over any zeros, and sets the first | |
1137 | * non-zero point in the array to be the "start", and continues until it | |
1138 | * finds non-data again, which is marked as the "end." This routine is | |
1139 | * mainly for 1) seeing how far into a file we must lseek to data, given | |
1140 | * that we have a sparse file, and 2) determining the "real size" of the | |
1141 | * file, i.e., the number of bytes in the sparse file that are data, as | |
1142 | * opposed to the zeros we are trying to skip. | |
1143 | */ | |
1144 | where_is_data (from, to, buffer) | |
1145 | int *from, *to; | |
1146 | char *buffer; | |
1147 | { | |
1148 | register int i = 0; | |
1149 | register int save_to = *to; | |
1150 | int amidst_data = 0; | |
1151 | ||
1152 | ||
1153 | while (!buffer[i]) | |
1154 | i++; | |
1155 | *from = i; | |
1156 | ||
1157 | if (*from < 16) /* don't bother */ | |
1158 | *from = 0; | |
1159 | /* keep going to make sure there isn't more real | |
1160 | data in this record */ | |
1161 | while (i < RECORDSIZE) | |
1162 | { | |
1163 | if (!buffer[i]) | |
1164 | { | |
1165 | if (amidst_data) | |
1166 | { | |
1167 | save_to = i; | |
1168 | amidst_data = 0; | |
1169 | } | |
1170 | i++; | |
1171 | } | |
1172 | else if (buffer[i]) | |
1173 | { | |
1174 | if (!amidst_data) | |
1175 | amidst_data = 1; | |
1176 | i++; | |
1177 | } | |
1178 | } | |
1179 | if (i == RECORDSIZE) | |
1180 | *to = i; | |
1181 | else | |
1182 | *to = save_to; | |
1183 | ||
1184 | } | |
1185 | ||
1186 | #endif | |
1187 | ||
1188 | /* Note that this routine is only called if zero_record returned true */ | |
1189 | #if 0 /* But we actually don't need it at all. */ | |
1190 | where_is_data (from, to, buffer) | |
1191 | int *from, *to; | |
1192 | char *buffer; | |
1193 | { | |
1194 | char *fp, *tp; | |
1195 | ||
1196 | for (fp = buffer; !*fp; fp++) | |
1197 | ; | |
1198 | for (tp = buffer + RECORDSIZE - 1; !*tp; tp--) | |
1199 | ; | |
1200 | *from = fp - buffer; | |
1201 | *to = tp - buffer + 1; | |
1202 | } | |
1203 | ||
1204 | #endif | |
1205 | ||
1206 | ||
1207 | ||
1208 | /* | |
1209 | * Takes a recordful of data and basically cruises through it to see if | |
1210 | * it's made *entirely* of zeros, returning a 0 the instant it finds | |
1211 | * something that is a non-zero, i.e., useful data. | |
1212 | */ | |
1213 | int | |
1214 | zero_record (buffer) | |
1215 | char *buffer; | |
1216 | { | |
1217 | register int i; | |
1218 | ||
1219 | for (i = 0; i < RECORDSIZE; i++) | |
1220 | if (buffer[i] != '\000') | |
1221 | return 0; | |
1222 | return 1; | |
1223 | } | |
1224 | ||
1225 | void | |
1226 | find_new_file_size (filesize, highest_index) | |
1227 | int *filesize; | |
1228 | int highest_index; | |
1229 | { | |
1230 | register int i; | |
1231 | ||
1232 | *filesize = 0; | |
1233 | for (i = 0; sparsearray[i].numbytes && i <= highest_index; i++) | |
1234 | *filesize += sparsearray[i].numbytes; | |
1235 | } | |
1236 | ||
1237 | /* | |
1238 | * Make a header block for the file name whose stat info is st . | |
1239 | * Return header pointer for success, NULL if the name is too long. | |
1240 | */ | |
1241 | union record * | |
1242 | start_header (name, st) | |
1243 | char *name; | |
1244 | register struct stat *st; | |
1245 | { | |
1246 | register union record *header; | |
1247 | ||
1248 | if (strlen (name) >= NAMSIZ) | |
1249 | write_long (name, LF_LONGNAME); | |
1250 | ||
1251 | header = (union record *) findrec (); | |
1252 | bzero (header->charptr, sizeof (*header)); /* XXX speed up */ | |
1253 | ||
1254 | /* | |
1255 | * Check the file name and put it in the record. | |
1256 | */ | |
1257 | if (!f_absolute_paths) | |
1258 | { | |
1259 | static int warned_once = 0; | |
1260 | #ifdef __MSDOS__ | |
1261 | if (name[1] == ':') | |
1262 | { | |
1263 | name += 2; | |
1264 | if (!warned_once++) | |
1265 | msg ("Removing drive spec from names in the archive"); | |
1266 | } | |
1267 | #endif | |
1268 | while ('/' == *name) | |
1269 | { | |
1270 | name++; /* Force relative path */ | |
1271 | if (!warned_once++) | |
1272 | msg ("Removing leading / from absolute path names in the archive."); | |
1273 | } | |
1274 | } | |
1275 | current_file_name = name; | |
1276 | strncpy (header->header.arch_name, name, NAMSIZ); | |
1277 | header->header.arch_name[NAMSIZ - 1] = '\0'; | |
1278 | ||
1279 | to_oct ((long) (f_oldarch ? (st->st_mode & 07777) : st->st_mode), | |
1280 | 8, header->header.mode); | |
1281 | to_oct ((long) st->st_uid, 8, header->header.uid); | |
1282 | to_oct ((long) st->st_gid, 8, header->header.gid); | |
1283 | to_oct ((long) st->st_size, 1 + 12, header->header.size); | |
1284 | to_oct ((long) st->st_mtime, 1 + 12, header->header.mtime); | |
1285 | /* header->header.linkflag is left as null */ | |
1286 | if (f_gnudump) | |
1287 | { | |
1288 | to_oct ((long) st->st_atime, 1 + 12, header->header.atime); | |
1289 | to_oct ((long) st->st_ctime, 1 + 12, header->header.ctime); | |
1290 | } | |
1291 | ||
1292 | #ifndef NONAMES | |
1293 | /* Fill in new Unix Standard fields if desired. */ | |
1294 | if (f_standard) | |
1295 | { | |
1296 | header->header.linkflag = LF_NORMAL; /* New default */ | |
1297 | strcpy (header->header.magic, TMAGIC); /* Mark as Unix Std */ | |
1298 | finduname (header->header.uname, st->st_uid); | |
1299 | findgname (header->header.gname, st->st_gid); | |
1300 | } | |
1301 | #endif | |
1302 | return header; | |
1303 | } | |
1304 | ||
1305 | /* | |
1306 | * Finish off a filled-in header block and write it out. | |
1307 | * We also print the file name and/or full info if verbose is on. | |
1308 | */ | |
1309 | void | |
1310 | finish_header (header) | |
1311 | register union record *header; | |
1312 | { | |
1313 | register int i, sum; | |
1314 | register char *p; | |
1315 | ||
1316 | bcopy (CHKBLANKS, header->header.chksum, sizeof (header->header.chksum)); | |
1317 | ||
1318 | sum = 0; | |
1319 | p = header->charptr; | |
1320 | for (i = sizeof (*header); --i >= 0;) | |
1321 | { | |
1322 | /* | |
1323 | * We can't use unsigned char here because of old compilers, | |
1324 | * e.g. V7. | |
1325 | */ | |
1326 | sum += 0xFF & *p++; | |
1327 | } | |
1328 | ||
1329 | /* | |
1330 | * Fill in the checksum field. It's formatted differently | |
1331 | * from the other fields: it has [6] digits, a null, then a | |
1332 | * space -- rather than digits, a space, then a null. | |
1333 | * We use to_oct then write the null in over to_oct's space. | |
1334 | * The final space is already there, from checksumming, and | |
1335 | * to_oct doesn't modify it. | |
1336 | * | |
1337 | * This is a fast way to do: | |
1338 | * (void) sprintf(header->header.chksum, "%6o", sum); | |
1339 | */ | |
1340 | to_oct ((long) sum, 8, header->header.chksum); | |
1341 | header->header.chksum[6] = '\0'; /* Zap the space */ | |
1342 | ||
1343 | userec (header); | |
1344 | ||
1345 | if (f_verbose) | |
1346 | { | |
1347 | extern union record *head;/* Points to current tape header */ | |
1348 | extern int head_standard; /* Tape header is in ANSI format */ | |
1349 | ||
1350 | /* These globals are parameters to print_header, sigh */ | |
1351 | head = header; | |
1352 | /* hstat is already set up */ | |
1353 | head_standard = f_standard; | |
1354 | print_header (); | |
1355 | } | |
1356 | ||
1357 | return; | |
1358 | } | |
1359 | ||
1360 | ||
1361 | /* | |
1362 | * Quick and dirty octal conversion. | |
1363 | * Converts long "value" into a "digs"-digit field at "where", | |
1364 | * including a trailing space and room for a null. "digs"==3 means | |
1365 | * 1 digit, a space, and room for a null. | |
1366 | * | |
1367 | * We assume the trailing null is already there and don't fill it in. | |
1368 | * This fact is used by start_header and finish_header, so don't change it! | |
1369 | * | |
1370 | * This should be equivalent to: | |
1371 | * (void) sprintf(where, "%*lo ", digs-2, value); | |
1372 | * except that sprintf fills in the trailing null and we don't. | |
1373 | */ | |
1374 | void | |
1375 | to_oct (value, digs, where) | |
1376 | register long value; | |
1377 | register int digs; | |
1378 | register char *where; | |
1379 | { | |
1380 | ||
1381 | --digs; /* Trailing null slot is left alone */ | |
1382 | where[--digs] = ' '; /* Put in the space, though */ | |
1383 | ||
1384 | /* Produce the digits -- at least one */ | |
1385 | do | |
1386 | { | |
1387 | where[--digs] = '0' + (char) (value & 7); /* one octal digit */ | |
1388 | value >>= 3; | |
1389 | } | |
1390 | while (digs > 0 && value != 0); | |
1391 | ||
1392 | /* Leading spaces, if necessary */ | |
1393 | while (digs > 0) | |
1394 | where[--digs] = ' '; | |
1395 | ||
1396 | } | |
1397 | ||
1398 | ||
1399 | /* | |
1400 | * Write the EOT record(s). | |
1401 | * We actually zero at least one record, through the end of the block. | |
1402 | * Old tar writes garbage after two zeroed records -- and PDtar used to. | |
1403 | */ | |
1404 | void | |
1405 | write_eot () | |
1406 | { | |
1407 | union record *p; | |
1408 | int bufsize; | |
1409 | ||
1410 | p = findrec (); | |
1411 | if (p) | |
1412 | { | |
1413 | bufsize = endofrecs ()->charptr - p->charptr; | |
1414 | bzero (p->charptr, bufsize); | |
1415 | userec (p); | |
1416 | } | |
1417 | } | |
1418 | ||
1419 | /* Write a LF_LONGLINK or LF_LONGNAME record. */ | |
1420 | void | |
1421 | write_long (p, type) | |
1422 | char *p; | |
1423 | char type; | |
1424 | { | |
1425 | int size = strlen (p) + 1; | |
1426 | int bufsize; | |
1427 | union record *header; | |
1428 | struct stat foo; | |
1429 | ||
1430 | ||
1431 | bzero (&foo, sizeof foo); | |
1432 | foo.st_size = size; | |
1433 | ||
1434 | header = start_header ("././@LongLink", &foo); | |
1435 | header->header.linkflag = type; | |
1436 | finish_header (header); | |
1437 | ||
1438 | header = findrec (); | |
1439 | ||
1440 | bufsize = endofrecs ()->charptr - header->charptr; | |
1441 | ||
1442 | while (bufsize < size) | |
1443 | { | |
1444 | bcopy (p, header->charptr, bufsize); | |
1445 | p += bufsize; | |
1446 | size -= bufsize; | |
1447 | userec (header + (bufsize - 1) / RECORDSIZE); | |
1448 | header = findrec (); | |
1449 | bufsize = endofrecs ()->charptr - header->charptr; | |
1450 | } | |
1451 | bcopy (p, header->charptr, size); | |
1452 | bzero (header->charptr + size, bufsize - size); | |
1453 | userec (header + (size - 1) / RECORDSIZE); | |
1454 | } |