251 |
&& S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC |
&& S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC |
252 |
&& S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC |
&& S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC |
253 |
&& archive_format != POSIX_FORMAT |
&& archive_format != POSIX_FORMAT |
254 |
|
&& archive_format != USTAR_FORMAT |
255 |
&& archive_format != GNU_FORMAT) |
&& archive_format != GNU_FORMAT) |
256 |
{ |
{ |
257 |
negative = v < 0; |
negative = v < 0; |
330 |
} |
} |
331 |
|
|
332 |
|
|
333 |
/* Writing routines. */ |
/* A file is not dumpable if |
334 |
|
a) it is empty *and* world-readable, or |
335 |
/* Zero out the buffer so we don't confuse ourselves with leftover |
b) current archive is /dev/null */ |
336 |
data. */ |
|
337 |
static void |
bool |
338 |
clear_buffer (char *buffer) |
file_dumpable_p (struct tar_stat_info *stat) |
339 |
{ |
{ |
340 |
memset (buffer, 0, BLOCKSIZE); |
return !(dev_null_output |
341 |
|
|| (stat->archive_file_size == 0 |
342 |
|
&& (stat->stat.st_mode & MODE_R) == MODE_R)); |
343 |
} |
} |
344 |
|
|
345 |
|
|
346 |
|
/* Writing routines. */ |
347 |
|
|
348 |
/* Write the EOT block(s). Zero at least two blocks, through the end |
/* Write the EOT block(s). Zero at least two blocks, through the end |
349 |
of the record. Old tar, as previous versions of GNU tar, writes |
of the record. Old tar, as previous versions of GNU tar, writes |
350 |
garbage after two zeroed blocks. */ |
garbage after two zeroed blocks. */ |
406 |
|
|
407 |
/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block. */ |
/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block. */ |
408 |
static void |
static void |
409 |
write_gnu_long_link (const char *p, char type) |
write_gnu_long_link (struct tar_stat_info *st, const char *p, char type) |
410 |
{ |
{ |
411 |
size_t size = strlen (p) + 1; |
size_t size = strlen (p) + 1; |
412 |
size_t bufsize; |
size_t bufsize; |
414 |
|
|
415 |
header = start_private_header ("././@LongLink", size); |
header = start_private_header ("././@LongLink", size); |
416 |
header->header.typeflag = type; |
header->header.typeflag = type; |
417 |
finish_header (header, -1); |
finish_header (st, header, -1); |
418 |
|
|
419 |
header = find_next_block (); |
header = find_next_block (); |
420 |
|
|
463 |
} |
} |
464 |
|
|
465 |
i = split_long_name (name, length); |
i = split_long_name (name, length); |
466 |
if (i == 0 || length - i -1 > NAME_FIELD_SIZE) |
if (i == 0 || length - i - 1 > NAME_FIELD_SIZE) |
467 |
{ |
{ |
468 |
WARN ((0, 0, |
WARN ((0, 0, |
469 |
_("%s: file name is too long (cannot be split); not dumped"), |
_("%s: file name is too long (cannot be split); not dumped"), |
470 |
quotearg_colon (name), |
quotearg_colon (name))); |
|
PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)); |
|
471 |
return NULL; |
return NULL; |
472 |
} |
} |
473 |
|
|
499 |
|
|
500 |
case OLDGNU_FORMAT: |
case OLDGNU_FORMAT: |
501 |
case GNU_FORMAT: |
case GNU_FORMAT: |
502 |
write_gnu_long_link (st->link_name, GNUTYPE_LONGLINK); |
write_gnu_long_link (st, st->link_name, GNUTYPE_LONGLINK); |
503 |
break; |
break; |
504 |
|
|
505 |
default: |
default: |
523 |
|
|
524 |
case OLDGNU_FORMAT: |
case OLDGNU_FORMAT: |
525 |
case GNU_FORMAT: |
case GNU_FORMAT: |
526 |
write_gnu_long_link (st->file_name, GNUTYPE_LONGNAME); |
write_gnu_long_link (st, st->file_name, GNUTYPE_LONGNAME); |
527 |
break; |
break; |
528 |
|
|
529 |
default: |
default: |
533 |
} |
} |
534 |
|
|
535 |
static union block * |
static union block * |
536 |
write_extended (union block *old_header, char type) |
write_extended (struct tar_stat_info *st, union block *old_header, char type) |
537 |
{ |
{ |
538 |
union block *header, hp; |
union block *header, hp; |
|
struct tar_stat_info foo; |
|
539 |
size_t size; |
size_t size; |
540 |
char *p; |
char *p; |
541 |
|
|
550 |
header = start_private_header ("././@PaxHeader", size); |
header = start_private_header ("././@PaxHeader", size); |
551 |
header->header.typeflag = type; |
header->header.typeflag = type; |
552 |
|
|
553 |
finish_header (header, -1); |
finish_header (st, header, -1); |
554 |
|
|
555 |
p = extended_header.buffer; |
p = extended_header.buffer; |
556 |
|
|
592 |
/* Make a header block for the file whose stat info is st, |
/* Make a header block for the file whose stat info is st, |
593 |
and return its address. */ |
and return its address. */ |
594 |
|
|
595 |
static union block * |
union block * |
596 |
start_header (const char *name, struct tar_stat_info *st) |
start_header (struct tar_stat_info *st) |
597 |
{ |
{ |
598 |
union block *header; |
union block *header; |
599 |
|
|
|
name = safer_name_suffix (name, 0); |
|
|
assign_string (&st->file_name, name); |
|
|
|
|
600 |
header = write_header_name (st); |
header = write_header_name (st); |
601 |
if (!header) |
if (!header) |
602 |
return NULL; |
return NULL; |
603 |
|
|
|
assign_string (¤t_stat_info.file_name, name); |
|
|
|
|
604 |
/* Override some stat fields, if requested to do so. */ |
/* Override some stat fields, if requested to do so. */ |
605 |
|
|
606 |
if (owner_option != (uid_t) -1) |
if (owner_option != (uid_t) -1) |
699 |
break; |
break; |
700 |
|
|
701 |
case OLDGNU_FORMAT: |
case OLDGNU_FORMAT: |
702 |
|
case GNU_FORMAT: /*FIXME?*/ |
703 |
/* Overwrite header->header.magic and header.version in one blow. */ |
/* Overwrite header->header.magic and header.version in one blow. */ |
704 |
strcpy (header->header.magic, OLDGNU_MAGIC); |
strcpy (header->header.magic, OLDGNU_MAGIC); |
705 |
break; |
break; |
706 |
|
|
707 |
case POSIX_FORMAT: |
case POSIX_FORMAT: |
708 |
case USTAR_FORMAT: |
case USTAR_FORMAT: |
|
case GNU_FORMAT: /*FIXME?*/ |
|
709 |
strncpy (header->header.magic, TMAGIC, TMAGLEN); |
strncpy (header->header.magic, TMAGIC, TMAGLEN); |
710 |
strncpy (header->header.version, TVERSION, TVERSLEN); |
strncpy (header->header.version, TVERSION, TVERSLEN); |
711 |
break; |
break; |
744 |
is not negative, is the block ordinal of the first record for this |
is not negative, is the block ordinal of the first record for this |
745 |
file, which may be a preceding long name or long link record. */ |
file, which may be a preceding long name or long link record. */ |
746 |
void |
void |
747 |
finish_header (union block *header, off_t block_ordinal) |
finish_header (struct tar_stat_info *st, |
748 |
|
union block *header, off_t block_ordinal) |
749 |
{ |
{ |
750 |
size_t i; |
size_t i; |
751 |
int sum; |
int sum; |
762 |
/* These globals are parameters to print_header, sigh. */ |
/* These globals are parameters to print_header, sigh. */ |
763 |
|
|
764 |
current_header = header; |
current_header = header; |
|
/* current_stat_info is already set up. */ |
|
765 |
current_format = archive_format; |
current_format = archive_format; |
766 |
print_header (block_ordinal); |
print_header (st, block_ordinal); |
767 |
} |
} |
768 |
|
|
769 |
header = write_extended (header, XHDTYPE); |
header = write_extended (st, header, XHDTYPE); |
770 |
|
|
771 |
memcpy (header->header.chksum, CHKBLANKS, sizeof header->header.chksum); |
memcpy (header->header.chksum, CHKBLANKS, sizeof header->header.chksum); |
772 |
|
|
791 |
set_next_block_after (header); |
set_next_block_after (header); |
792 |
} |
} |
793 |
|
|
|
/* Sparse file processing. */ |
|
|
|
|
|
/* Takes a blockful of data and basically cruises through it to see if |
|
|
it's made *entirely* of zeros, returning a 0 the instant it finds |
|
|
something that is a nonzero, i.e., useful data. */ |
|
|
static int |
|
|
zero_block_p (char *buffer) |
|
|
{ |
|
|
int counter; |
|
|
|
|
|
for (counter = 0; counter < BLOCKSIZE; counter++) |
|
|
if (buffer[counter] != '\0') |
|
|
return 0; |
|
|
return 1; |
|
|
} |
|
794 |
|
|
795 |
void |
void |
796 |
init_sparsearray (void) |
pad_archive (off_t size_left) |
|
{ |
|
|
if (! sp_array_size) |
|
|
sp_array_size = SPARSES_IN_OLDGNU_HEADER; |
|
|
sparsearray = xmalloc (sp_array_size * sizeof *sparsearray); |
|
|
} |
|
|
|
|
|
static off_t |
|
|
find_new_file_size (int sparses) |
|
797 |
{ |
{ |
798 |
int i; |
union block *blk; |
799 |
off_t s = 0; |
while (size_left > 0) |
800 |
for (i = 0; i < sparses; i++) |
{ |
801 |
s += sparsearray[i].numbytes; |
save_sizeleft = size_left; |
802 |
return s; |
blk = find_next_block (); |
803 |
} |
memset (blk->buffer, 0, BLOCKSIZE); |
804 |
|
set_next_block_after (blk); |
805 |
/* Make one pass over the file NAME, studying where any non-zero data |
size_left -= BLOCKSIZE; |
806 |
is, that is, how far into the file each instance of data is, and |
} |
807 |
how many bytes are there. Save this information in the |
} |
|
sparsearray, which will later be translated into header |
|
|
information. */ |
|
|
|
|
|
/* There is little point in trimming small amounts of null data at the head |
|
|
and tail of blocks, only avoid dumping full null blocks. */ |
|
|
|
|
|
/* FIXME: this routine might accept bits of algorithmic cleanup, it is |
|
|
too kludgey for my taste... */ |
|
808 |
|
|
809 |
static int |
static enum dump_status |
810 |
deal_with_sparse (char *name, union block *header) |
dump_regular_file (int fd, struct tar_stat_info *stat) |
811 |
{ |
{ |
812 |
size_t numbytes = 0; |
off_t size_left = stat->stat.st_size; |
813 |
off_t offset = 0; |
off_t block_ordinal; |
814 |
int file; |
union block *blk; |
815 |
int sparses = 0; |
|
816 |
ssize_t count; |
block_ordinal = current_block_ordinal (); |
817 |
char buffer[BLOCKSIZE]; |
blk = start_header (stat); |
818 |
|
if (!blk) |
819 |
if (archive_format == OLDGNU_FORMAT) |
return dump_status_fail; |
|
header->oldgnu_header.isextended = 0; |
|
820 |
|
|
821 |
if (file = open (name, O_RDONLY), file < 0) |
/* Mark contiguous files, if we support them. */ |
822 |
/* This problem will be caught later on, so just return. */ |
if (archive_format != V7_FORMAT && S_ISCTG (stat->stat.st_mode)) |
823 |
return 0; |
blk->header.typeflag = CONTTYPE; |
824 |
|
|
825 |
init_sparsearray (); |
finish_header (stat, blk, block_ordinal); |
|
clear_buffer (buffer); |
|
826 |
|
|
827 |
for (;;) |
while (size_left > 0) |
828 |
{ |
{ |
829 |
/* Realloc the scratch area as necessary. FIXME: should reallocate |
size_t bufsize, count; |
830 |
only at beginning of a new instance of non-zero data. */ |
|
831 |
|
if (multi_volume_option) |
|
if (sp_array_size <= sparses) |
|
832 |
{ |
{ |
833 |
sparsearray = |
assign_string (&save_name, stat->file_name); |
834 |
xrealloc (sparsearray, |
save_sizeleft = size_left; |
835 |
2 * sp_array_size * sizeof (struct sp_array)); |
save_totsize = stat->stat.st_size; |
|
sp_array_size *= 2; |
|
836 |
} |
} |
837 |
|
blk = find_next_block (); |
838 |
|
|
839 |
count = safe_read (file, buffer, sizeof buffer); |
bufsize = available_space_after (blk); |
840 |
if (count <= 0) |
|
841 |
break; |
if (size_left < bufsize) |
842 |
|
{ |
843 |
/* Process one block. */ |
/* Last read -- zero out area beyond. */ |
844 |
|
bufsize = size_left; |
845 |
if (count == sizeof buffer) |
count = bufsize % BLOCKSIZE; |
846 |
|
if (count) |
847 |
if (zero_block_p (buffer)) |
memset (blk->buffer + size_left, 0, BLOCKSIZE - count); |
848 |
{ |
} |
849 |
if (numbytes) |
|
850 |
{ |
count = (fd < 0) ? bufsize : safe_read (fd, blk->buffer, bufsize); |
851 |
sparsearray[sparses++].numbytes = numbytes; |
if (count < 0) |
852 |
numbytes = 0; |
{ |
853 |
} |
read_diag_details (stat->orig_file_name, |
854 |
} |
stat->stat.st_size - size_left, bufsize); |
855 |
else |
pad_archive (size_left); |
856 |
{ |
return dump_status_short; |
857 |
if (!numbytes) |
} |
858 |
sparsearray[sparses].offset = offset; |
size_left -= count; |
|
numbytes += count; |
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
/* Since count < sizeof buffer, we have the last bit of the file. */ |
|
859 |
|
|
860 |
if (!zero_block_p (buffer)) |
set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); |
|
{ |
|
|
if (!numbytes) |
|
|
sparsearray[sparses].offset = offset; |
|
|
numbytes += count; |
|
|
} |
|
|
else |
|
|
/* The next two lines are suggested by Andreas Degert, who says |
|
|
they are required for trailing full blocks to be written to the |
|
|
archive, when all zeroed. Yet, it seems to me that the case |
|
|
does not apply. Further, at restore time, the file is not as |
|
|
sparse as it should. So, some serious cleanup is *also* needed |
|
|
in this area. Just one more... :-(. FIXME. */ |
|
|
if (numbytes) |
|
|
numbytes += count; |
|
|
|
|
|
/* Prepare for next block. */ |
|
|
|
|
|
offset += count; |
|
|
/* FIXME: do not clear unless necessary. */ |
|
|
clear_buffer (buffer); |
|
|
} |
|
861 |
|
|
862 |
if (numbytes) |
if (count != bufsize) |
863 |
sparsearray[sparses++].numbytes = numbytes; |
{ |
864 |
else |
char buf[UINTMAX_STRSIZE_BOUND]; |
865 |
{ |
memset (blk->buffer + count, 0, bufsize - count); |
866 |
sparsearray[sparses].offset = offset - 1; |
WARN ((0, 0, |
867 |
sparsearray[sparses++].numbytes = 1; |
ngettext ("%s: File shrank by %s byte; padding with zeros", |
868 |
|
"%s: File shrank by %s bytes; padding with zeros", |
869 |
|
size_left), |
870 |
|
quotearg_colon (stat->orig_file_name), |
871 |
|
STRINGIFY_BIGINT (size_left, buf))); |
872 |
|
if (! ignore_failed_read_option) |
873 |
|
exit_status = TAREXIT_FAILURE; |
874 |
|
pad_archive (size_left); |
875 |
|
return dump_status_short; |
876 |
|
} |
877 |
} |
} |
878 |
|
return dump_status_ok; |
|
return close (file) == 0 && 0 <= count ? sparses : 0; |
|
879 |
} |
} |
880 |
|
|
881 |
static int |
void |
882 |
finish_sparse_file (int file, off_t *sizeleft, off_t fullsize, char *name) |
dump_regular_finish (int fd, struct tar_stat_info *st, time_t original_ctime) |
883 |
{ |
{ |
884 |
union block *start; |
if (fd >= 0) |
|
size_t bufsize; |
|
|
int sparses = 0; |
|
|
ssize_t count; |
|
|
|
|
|
while (*sizeleft > 0) |
|
885 |
{ |
{ |
886 |
start = find_next_block (); |
struct stat final_stat; |
887 |
memset (start->buffer, 0, BLOCKSIZE); |
if (fstat (fd, &final_stat) != 0) |
|
bufsize = sparsearray[sparses].numbytes; |
|
|
if (! bufsize) |
|
|
abort (); |
|
|
|
|
|
if (lseek (file, sparsearray[sparses++].offset, SEEK_SET) < 0) |
|
888 |
{ |
{ |
889 |
(ignore_failed_read_option ? seek_warn_details : seek_error_details) |
stat_diag (st->orig_file_name); |
|
(name, sparsearray[sparses - 1].offset); |
|
|
break; |
|
890 |
} |
} |
891 |
|
else if (final_stat.st_ctime != original_ctime) |
892 |
|
{ |
893 |
|
WARN ((0, 0, _("%s: file changed as we read it"), |
894 |
|
quotearg_colon (st->orig_file_name))); |
895 |
|
} |
896 |
|
if (close (fd) != 0) |
897 |
|
{ |
898 |
|
close_diag (st->orig_file_name); |
899 |
|
} |
900 |
|
} |
901 |
|
if (remove_files_option) |
902 |
|
{ |
903 |
|
if (unlink (st->orig_file_name) == -1) |
904 |
|
unlink_error (st->orig_file_name); |
905 |
|
} |
906 |
|
} |
907 |
|
|
908 |
/* If the number of bytes to be written here exceeds the size of |
void |
909 |
the temporary buffer, do it in steps. */ |
dump_dir0 (char *directory, |
910 |
|
struct tar_stat_info *stat, int top_level, dev_t parent_device) |
911 |
while (bufsize > BLOCKSIZE) |
{ |
912 |
|
dev_t our_device = stat->stat.st_dev; |
913 |
|
|
914 |
|
if (!is_avoided_name (stat->orig_file_name)) |
915 |
|
{ |
916 |
|
union block *blk = NULL; |
917 |
|
off_t block_ordinal = current_block_ordinal (); |
918 |
|
stat->stat.st_size = 0; /* force 0 size on dir */ |
919 |
|
|
920 |
|
blk = start_header (stat); |
921 |
|
if (!blk) |
922 |
|
return; |
923 |
|
|
924 |
|
if (incremental_option) |
925 |
|
blk->header.typeflag = GNUTYPE_DUMPDIR; |
926 |
|
else /* if (standard_option) */ |
927 |
|
blk->header.typeflag = DIRTYPE; |
928 |
|
|
929 |
|
/* If we're gnudumping, we aren't done yet so don't close it. */ |
930 |
|
|
931 |
|
if (!incremental_option) |
932 |
|
finish_header (stat, blk, block_ordinal); |
933 |
|
else if (gnu_list_name->dir_contents) |
934 |
{ |
{ |
935 |
count = safe_read (file, start->buffer, BLOCKSIZE); |
off_t size_left; |
936 |
if (count < 0) |
off_t totsize; |
937 |
|
size_t bufsize; |
938 |
|
ssize_t count; |
939 |
|
const char *buffer, *p_buffer; |
940 |
|
off_t block_ordinal = current_block_ordinal (); |
941 |
|
|
942 |
|
buffer = gnu_list_name->dir_contents; /* FOO */ |
943 |
|
totsize = 0; |
944 |
|
if (buffer) |
945 |
|
for (p_buffer = buffer; *p_buffer; ) |
946 |
|
{ |
947 |
|
size_t size = strlen (p_buffer) + 1; |
948 |
|
totsize += size; |
949 |
|
p_buffer += size; |
950 |
|
} |
951 |
|
totsize++; |
952 |
|
OFF_TO_CHARS (totsize, blk->header.size); |
953 |
|
finish_header (stat, blk, block_ordinal); |
954 |
|
p_buffer = buffer; |
955 |
|
size_left = totsize; |
956 |
|
while (size_left > 0) |
957 |
{ |
{ |
958 |
(ignore_failed_read_option |
if (multi_volume_option) |
959 |
? read_warn_details |
{ |
960 |
: read_error_details) |
assign_string (&save_name, stat->orig_file_name); |
961 |
(name, fullsize - *sizeleft, bufsize); |
save_sizeleft = size_left; |
962 |
return 1; |
save_totsize = totsize; |
963 |
|
} |
964 |
|
blk = find_next_block (); |
965 |
|
bufsize = available_space_after (blk); |
966 |
|
if (size_left < bufsize) |
967 |
|
{ |
968 |
|
bufsize = size_left; |
969 |
|
count = bufsize % BLOCKSIZE; |
970 |
|
if (count) |
971 |
|
memset (blk->buffer + size_left, 0, BLOCKSIZE - count); |
972 |
|
} |
973 |
|
memcpy (blk->buffer, p_buffer, bufsize); |
974 |
|
size_left -= bufsize; |
975 |
|
p_buffer += bufsize; |
976 |
|
set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE); |
977 |
} |
} |
978 |
bufsize -= count; |
if (multi_volume_option) |
979 |
*sizeleft -= count; |
assign_string (&save_name, 0); |
980 |
set_next_block_after (start); |
return; |
|
start = find_next_block (); |
|
|
memset (start->buffer, 0, BLOCKSIZE); |
|
981 |
} |
} |
982 |
|
} |
983 |
|
else if (!recursion_option) |
984 |
|
return; |
985 |
|
else if (one_file_system_option |
986 |
|
&& !top_level |
987 |
|
&& parent_device != stat->stat.st_dev) |
988 |
|
{ |
989 |
|
if (verbose_option) |
990 |
|
WARN ((0, 0, |
991 |
|
_("%s: file is on a different filesystem; not dumped"), |
992 |
|
quotearg_colon (stat->orig_file_name))); |
993 |
|
return; |
994 |
|
} |
995 |
|
|
996 |
|
{ |
997 |
|
char const *entry; |
998 |
|
size_t entry_len; |
999 |
|
char *name_buf = strdup (stat->orig_file_name); |
1000 |
|
size_t name_size = strlen (name_buf); |
1001 |
|
size_t name_len = name_size; |
1002 |
|
|
1003 |
|
/* Now output all the files in the directory. */ |
1004 |
|
/* FIXME: Should speed this up by cd-ing into the dir. */ |
1005 |
|
|
1006 |
|
for (entry = directory; (entry_len = strlen (entry)) != 0; |
1007 |
|
entry += entry_len + 1) |
1008 |
{ |
{ |
1009 |
char buffer[BLOCKSIZE]; |
if (name_size < name_len + entry_len) |
1010 |
|
{ |
1011 |
clear_buffer (buffer); |
name_size = name_len + entry_len; |
1012 |
count = safe_read (file, buffer, bufsize); |
name_buf = xrealloc (name_buf, name_size + 1); |
1013 |
memcpy (start->buffer, buffer, BLOCKSIZE); |
} |
1014 |
|
strcpy (name_buf + name_len, entry); |
1015 |
|
if (!excluded_name (name_buf)) |
1016 |
|
dump_file (name_buf, 0, our_device); |
1017 |
} |
} |
1018 |
|
|
1019 |
|
free (name_buf); |
1020 |
|
} |
1021 |
|
} |
1022 |
|
|
1023 |
if (count < 0) |
/* Ensure exactly one trailing slash. */ |
1024 |
{ |
static void |
1025 |
(ignore_failed_read_option |
ensure_slash (char **pstr) |
1026 |
? read_warn_details |
{ |
1027 |
: read_error_details) |
size_t len = strlen (*pstr); |
1028 |
(name, fullsize - *sizeleft, bufsize); |
while (len >= 1 && ISSLASH ((*pstr)[len - 1])) |
1029 |
return 1; |
len--; |
1030 |
} |
if (!ISSLASH ((*pstr)[len])) |
1031 |
|
*pstr = xrealloc (*pstr, len + 2); |
1032 |
|
(*pstr)[len++] = '/'; |
1033 |
|
(*pstr)[len] = '\0'; |
1034 |
|
} |
1035 |
|
|
1036 |
*sizeleft -= count; |
bool |
1037 |
set_next_block_after (start); |
dump_dir (struct tar_stat_info *stat, int top_level, dev_t parent_device) |
1038 |
|
{ |
1039 |
|
char *directory; |
1040 |
|
|
1041 |
|
directory = savedir (stat->orig_file_name); |
1042 |
|
if (!directory) |
1043 |
|
{ |
1044 |
|
savedir_diag (stat->orig_file_name); |
1045 |
|
return false; |
1046 |
} |
} |
1047 |
free (sparsearray); |
|
1048 |
#if 0 |
ensure_slash (&stat->orig_file_name); |
1049 |
set_next_block_after (start + (count - 1) / BLOCKSIZE); |
ensure_slash (&stat->file_name); |
1050 |
#endif |
|
1051 |
return 0; |
dump_dir0 (directory, stat, top_level, parent_device); |
1052 |
|
|
1053 |
|
free (directory); |
1054 |
|
return true; |
1055 |
} |
} |
1056 |
|
|
1057 |
|
|
1058 |
/* Main functions of this module. */ |
/* Main functions of this module. */ |
1059 |
|
|
1072 |
|
|
1073 |
collect_and_sort_names (); |
collect_and_sort_names (); |
1074 |
|
|
1075 |
while (p = name_from_list (), p) |
while ((p = name_from_list ()) != NULL) |
1076 |
if (!excluded_name (p)) |
if (!excluded_name (p)) |
1077 |
dump_file (p, -1, (dev_t) 0); |
dump_file (p, -1, (dev_t) 0); |
1078 |
|
|
1079 |
blank_name_list (); |
blank_name_list (); |
1080 |
while (p = name_from_list (), p) |
while ((p = name_from_list ()) != NULL) |
1081 |
if (!excluded_name (p)) |
if (!excluded_name (p)) |
1082 |
{ |
{ |
1083 |
size_t plen = strlen (p); |
size_t plen = strlen (p); |
1102 |
while ((buffer_size *=2 ) < plen + qlen) |
while ((buffer_size *=2 ) < plen + qlen) |
1103 |
continue; |
continue; |
1104 |
buffer = xrealloc (buffer, buffer_size); |
buffer = xrealloc (buffer, buffer_size); |
1105 |
} |
} |
1106 |
strcpy (buffer + plen, q + 1); |
strcpy (buffer + plen, q + 1); |
1107 |
dump_file (buffer, -1, (dev_t) 0); |
dump_file (buffer, -1, (dev_t) 0); |
1108 |
} |
} |
1113 |
} |
} |
1114 |
else |
else |
1115 |
{ |
{ |
1116 |
while (p = name_next (1), p) |
while ((p = name_next (1)) != NULL) |
1117 |
if (!excluded_name (p)) |
if (!excluded_name (p)) |
1118 |
dump_file (p, 1, (dev_t) 0); |
dump_file (p, 1, (dev_t) 0); |
1119 |
} |
} |
1143 |
return ((link1->dev ^ link2->dev) | (link1->ino ^ link2->ino)) == 0; |
return ((link1->dev ^ link2->dev) | (link1->ino ^ link2->ino)) == 0; |
1144 |
} |
} |
1145 |
|
|
1146 |
|
static void |
1147 |
|
unknown_file_error (char *p) |
1148 |
|
{ |
1149 |
|
WARN ((0, 0, _("%s: Unknown file type; file ignored"), |
1150 |
|
quotearg_colon (p))); |
1151 |
|
if (!ignore_failed_read_option) |
1152 |
|
exit_status = TAREXIT_FAILURE; |
1153 |
|
} |
1154 |
|
|
1155 |
|
|
1156 |
|
/* Handling of hard links */ |
1157 |
|
|
1158 |
/* Table of all non-directories that we've written so far. Any time |
/* Table of all non-directories that we've written so far. Any time |
1159 |
we see another, we check the table and avoid dumping the data |
we see another, we check the table and avoid dumping the data |
1160 |
again if we've done it once already. */ |
again if we've done it once already. */ |
1161 |
static Hash_table *link_table; |
static Hash_table *link_table; |
1162 |
|
|
1163 |
|
/* Try to dump stat as a hard link to another file in the archive. If |
1164 |
|
succeeded returns true */ |
1165 |
|
static bool |
1166 |
|
dump_hard_link (struct tar_stat_info *stat) |
1167 |
|
{ |
1168 |
|
if (link_table && stat->stat.st_nlink > 1) |
1169 |
|
{ |
1170 |
|
struct link lp; |
1171 |
|
struct link *dup; |
1172 |
|
off_t block_ordinal; |
1173 |
|
union block *blk; |
1174 |
|
|
1175 |
|
lp.ino = stat->stat.st_ino; |
1176 |
|
lp.dev = stat->stat.st_dev; |
1177 |
|
|
1178 |
|
if ((dup = hash_lookup (link_table, &lp))) |
1179 |
|
{ |
1180 |
|
/* We found a link. */ |
1181 |
|
char const *link_name = safer_name_suffix (dup->name, 1); |
1182 |
|
|
1183 |
|
dup->nlink--; |
1184 |
|
|
1185 |
|
block_ordinal = current_block_ordinal (); |
1186 |
|
assign_string (&stat->link_name, link_name); |
1187 |
|
if (NAME_FIELD_SIZE < strlen (link_name)) |
1188 |
|
write_long_link (stat); |
1189 |
|
|
1190 |
|
stat->stat.st_size = 0; |
1191 |
|
blk = start_header (stat); |
1192 |
|
if (!blk) |
1193 |
|
return true; |
1194 |
|
tar_copy_str (blk->header.linkname, link_name, NAME_FIELD_SIZE); |
1195 |
|
|
1196 |
|
blk->header.typeflag = LNKTYPE; |
1197 |
|
finish_header (stat, blk, block_ordinal); |
1198 |
|
|
1199 |
|
if (remove_files_option && unlink (stat->orig_file_name) != 0) |
1200 |
|
unlink_error (stat->orig_file_name); |
1201 |
|
|
1202 |
|
return true; |
1203 |
|
} |
1204 |
|
} |
1205 |
|
return false; |
1206 |
|
} |
1207 |
|
|
1208 |
|
static void |
1209 |
|
file_count_links (struct tar_stat_info *stat) |
1210 |
|
{ |
1211 |
|
if (stat->stat.st_nlink > 1) |
1212 |
|
{ |
1213 |
|
struct link *dup; |
1214 |
|
struct link *lp = xmalloc (offsetof (struct link, name) |
1215 |
|
+ strlen (stat->orig_file_name) + 1); |
1216 |
|
lp->ino = stat->stat.st_ino; |
1217 |
|
lp->dev = stat->stat.st_dev; |
1218 |
|
lp->nlink = stat->stat.st_nlink; |
1219 |
|
strcpy (lp->name, stat->orig_file_name); |
1220 |
|
|
1221 |
|
if (! ((link_table |
1222 |
|
|| (link_table = hash_initialize (0, 0, hash_link, |
1223 |
|
compare_links, 0))) |
1224 |
|
&& (dup = hash_insert (link_table, lp)))) |
1225 |
|
xalloc_die (); |
1226 |
|
|
1227 |
|
if (dup != lp) |
1228 |
|
abort (); |
1229 |
|
lp->nlink--; |
1230 |
|
} |
1231 |
|
} |
1232 |
|
|
1233 |
|
/* For each dumped file, check if all its links were dumped. Emit |
1234 |
|
warnings if it is not so. */ |
1235 |
|
void |
1236 |
|
check_links () |
1237 |
|
{ |
1238 |
|
struct link *lp; |
1239 |
|
|
1240 |
|
if (!link_table) |
1241 |
|
return; |
1242 |
|
|
1243 |
|
for (lp = hash_get_first (link_table); lp; |
1244 |
|
lp = hash_get_next (link_table, lp)) |
1245 |
|
{ |
1246 |
|
if (lp->nlink) |
1247 |
|
{ |
1248 |
|
WARN ((0, 0, _("Missing links to '%s'.\n"), lp->name)); |
1249 |
|
} |
1250 |
|
} |
1251 |
|
} |
1252 |
|
|
1253 |
|
|
1254 |
/* Dump a single file, recursing on directories. P is the file name |
/* Dump a single file, recursing on directories. P is the file name |
1255 |
to dump. TOP_LEVEL tells whether this is a top-level call; zero |
to dump. TOP_LEVEL tells whether this is a top-level call; zero |
1256 |
means no, positive means yes, and negative means the top level |
means no, positive means yes, and negative means the top level |
1257 |
of an incremental dump. PARENT_DEVICE is the device of P's |
of an incremental dump. PARENT_DEVICE is the device of P's |
1258 |
parent directory; it is examined only if TOP_LEVEL is zero. |
parent directory; it is examined only if TOP_LEVEL is zero. */ |
|
|
|
|
Set global CURRENT_STAT_INFO to stat output for this file. */ |
|
1259 |
|
|
1260 |
/* FIXME: One should make sure that for *every* path leading to setting |
/* FIXME: One should make sure that for *every* path leading to setting |
1261 |
exit_status to failure, a clear diagnostic has been issued. */ |
exit_status to failure, a clear diagnostic has been issued. */ |
1262 |
|
|
1263 |
void |
void |
1264 |
dump_file (char *p, int top_level, dev_t parent_device) |
dump_file0 (struct tar_stat_info *stat, char *p, |
1265 |
|
int top_level, dev_t parent_device) |
1266 |
{ |
{ |
1267 |
union block *header; |
union block *header; |
1268 |
char type; |
char type; |
|
union block *exhdr; |
|
|
char save_typeflag; |
|
1269 |
time_t original_ctime; |
time_t original_ctime; |
1270 |
struct utimbuf restore_times; |
struct utimbuf restore_times; |
1271 |
off_t block_ordinal = -1; |
off_t block_ordinal = -1; |
1272 |
|
|
|
/* FIXME: `header' might be used uninitialized in this |
|
|
function. Reported by Bruno Haible. */ |
|
|
|
|
1273 |
if (interactive_option && !confirm ("add", p)) |
if (interactive_option && !confirm ("add", p)) |
1274 |
return; |
return; |
1275 |
|
|
1276 |
if (deref_stat (dereference_option, p, ¤t_stat_info.stat) != 0) |
assign_string (&stat->orig_file_name, p); |
1277 |
|
assign_string (&stat->file_name, safer_name_suffix (p, 0)); |
1278 |
|
|
1279 |
|
if (deref_stat (dereference_option, p, &stat->stat) != 0) |
1280 |
{ |
{ |
1281 |
if (ignore_failed_read_option) |
stat_diag (p); |
|
stat_warn (p); |
|
|
else |
|
|
stat_error (p); |
|
1282 |
return; |
return; |
1283 |
} |
} |
1284 |
|
stat->archive_file_size = stat->stat.st_size; |
1285 |
original_ctime = current_stat_info.stat.st_ctime; |
|
1286 |
restore_times.actime = current_stat_info.stat.st_atime; |
original_ctime = stat->stat.st_ctime; |
1287 |
restore_times.modtime = current_stat_info.stat.st_mtime; |
restore_times.actime = stat->stat.st_atime; |
1288 |
|
restore_times.modtime = stat->stat.st_mtime; |
1289 |
|
|
1290 |
#ifdef S_ISHIDDEN |
#ifdef S_ISHIDDEN |
1291 |
if (S_ISHIDDEN (current_stat_info.stat.st_mode)) |
if (S_ISHIDDEN (stat->stat.st_mode)) |
1292 |
{ |
{ |
1293 |
char *new = (char *) alloca (strlen (p) + 2); |
char *new = (char *) alloca (strlen (p) + 2); |
1294 |
if (new) |
if (new) |
1304 |
put in the archive. */ |
put in the archive. */ |
1305 |
|
|
1306 |
if ((0 < top_level || !incremental_option) |
if ((0 < top_level || !incremental_option) |
1307 |
&& !S_ISDIR (current_stat_info.stat.st_mode) |
&& !S_ISDIR (stat->stat.st_mode) |
1308 |
&& current_stat_info.stat.st_mtime < newer_mtime_option |
&& stat->stat.st_mtime < newer_mtime_option |
1309 |
&& (!after_date_option || current_stat_info.stat.st_ctime < newer_ctime_option)) |
&& (!after_date_option || stat->stat.st_ctime < newer_ctime_option)) |
1310 |
{ |
{ |
1311 |
if (0 < top_level) |
if (0 < top_level) |
1312 |
WARN ((0, 0, _("%s: file is unchanged; not dumped"), |
WARN ((0, 0, _("%s: file is unchanged; not dumped"), |
1316 |
} |
} |
1317 |
|
|
1318 |
/* See if we are trying to dump the archive. */ |
/* See if we are trying to dump the archive. */ |
1319 |
if (sys_file_is_archive (¤t_stat_info)) |
if (sys_file_is_archive (stat)) |
1320 |
{ |
{ |
1321 |
WARN ((0, 0, _("%s: file is the archive; not dumped"), |
WARN ((0, 0, _("%s: file is the archive; not dumped"), |
1322 |
quotearg_colon (p))); |
quotearg_colon (p))); |
1323 |
return; |
return; |
1324 |
} |
} |
1325 |
|
|
1326 |
if (S_ISDIR (current_stat_info.stat.st_mode)) |
if (S_ISDIR (stat->stat.st_mode)) |
1327 |
{ |
{ |
1328 |
char *directory; |
dump_dir (stat, top_level, parent_device); |
|
char const *entry; |
|
|
size_t entrylen; |
|
|
char *namebuf; |
|
|
size_t buflen; |
|
|
size_t len; |
|
|
dev_t our_device = current_stat_info.stat.st_dev; |
|
|
|
|
|
errno = 0; |
|
|
|
|
|
directory = savedir (p); |
|
|
if (! directory) |
|
|
{ |
|
|
if (ignore_failed_read_option) |
|
|
savedir_warn (p); |
|
|
else |
|
|
savedir_error (p); |
|
|
return; |
|
|
} |
|
|
|
|
|
/* Build new prototype name. Ensure exactly one trailing slash. */ |
|
|
|
|
|
len = strlen (p); |
|
|
buflen = len + NAME_FIELD_SIZE; |
|
|
namebuf = xmalloc (buflen + 1); |
|
|
memcpy (namebuf, p, len); |
|
|
while (len >= 1 && ISSLASH (namebuf[len - 1])) |
|
|
len--; |
|
|
namebuf[len++] = '/'; |
|
|
namebuf[len] = '\0'; |
|
|
|
|
|
if (! is_avoided_name (namebuf)) |
|
|
{ |
|
|
/* The condition above used to be "archive_format != V7_FORMAT". |
|
|
GNU tar was not writing directory blocks at all. Daniel Trinkle |
|
|
writes: ``All old versions of tar I have ever seen have |
|
|
correctly archived an empty directory. The really old ones I |
|
|
checked included HP-UX 7 and Mt. Xinu More/BSD. There may be |
|
|
some subtle reason for the exclusion that I don't know, but the |
|
|
current behavior is broken.'' I do not know those subtle |
|
|
reasons either, so until these are reported (anew?), just allow |
|
|
directory blocks to be written even with old archives. */ |
|
|
|
|
|
block_ordinal = current_block_ordinal (); |
|
|
current_stat_info.stat.st_size = 0; /* force 0 size on dir */ |
|
|
|
|
|
header = start_header (namebuf, ¤t_stat_info); |
|
|
if (!header) |
|
|
return; |
|
|
|
|
|
if (incremental_option) |
|
|
header->header.typeflag = GNUTYPE_DUMPDIR; |
|
|
else /* if (standard_option) */ |
|
|
header->header.typeflag = DIRTYPE; |
|
|
|
|
|
/* If we're gnudumping, we aren't done yet so don't close it. */ |
|
|
|
|
|
if (!incremental_option) |
|
|
finish_header (header, block_ordinal); |
|
|
} |
|
|
|
|
|
if (incremental_option && gnu_list_name->dir_contents) |
|
|
{ |
|
|
off_t sizeleft; |
|
|
off_t totsize; |
|
|
size_t bufsize; |
|
|
union block *start; |
|
|
ssize_t count; |
|
|
const char *buffer, *p_buffer; |
|
|
|
|
|
buffer = gnu_list_name->dir_contents; /* FOO */ |
|
|
totsize = 0; |
|
|
if (buffer) |
|
|
for (p_buffer = buffer; *p_buffer; ) |
|
|
{ |
|
|
size_t size = strlen (p_buffer) + 1; |
|
|
totsize += size; |
|
|
p_buffer += size; |
|
|
} |
|
|
totsize++; |
|
|
OFF_TO_CHARS (totsize, header->header.size); |
|
|
finish_header (header, block_ordinal); |
|
|
p_buffer = buffer; |
|
|
sizeleft = totsize; |
|
|
while (sizeleft > 0) |
|
|
{ |
|
|
if (multi_volume_option) |
|
|
{ |
|
|
assign_string (&save_name, p); |
|
|
save_sizeleft = sizeleft; |
|
|
save_totsize = totsize; |
|
|
} |
|
|
start = find_next_block (); |
|
|
bufsize = available_space_after (start); |
|
|
if (sizeleft < bufsize) |
|
|
{ |
|
|
bufsize = sizeleft; |
|
|
count = bufsize % BLOCKSIZE; |
|
|
if (count) |
|
|
memset (start->buffer + sizeleft, 0, BLOCKSIZE - count); |
|
|
} |
|
|
memcpy (start->buffer, p_buffer, bufsize); |
|
|
sizeleft -= bufsize; |
|
|
p_buffer += bufsize; |
|
|
set_next_block_after (start + (bufsize - 1) / BLOCKSIZE); |
|
|
} |
|
|
if (multi_volume_option) |
|
|
assign_string (&save_name, 0); |
|
|
goto finish_dir; |
|
|
} |
|
|
|
|
|
/* See if we are about to recurse into a directory, and avoid doing |
|
|
so if the user wants that we do not descend into directories. */ |
|
|
|
|
|
if (! recursion_option) |
|
|
goto finish_dir; |
|
|
|
|
|
/* See if we are crossing from one file system to another, and |
|
|
avoid doing so if the user only wants to dump one file system. */ |
|
|
|
|
|
if (one_file_system_option && !top_level |
|
|
&& parent_device != current_stat_info.stat.st_dev) |
|
|
{ |
|
|
if (verbose_option) |
|
|
WARN ((0, 0, |
|
|
_("%s: file is on a different filesystem; not dumped"), |
|
|
quotearg_colon (p))); |
|
|
goto finish_dir; |
|
|
} |
|
|
|
|
|
/* Now output all the files in the directory. */ |
|
|
|
|
|
/* FIXME: Should speed this up by cd-ing into the dir. */ |
|
|
|
|
|
for (entry = directory; |
|
|
(entrylen = strlen (entry)) != 0; |
|
|
entry += entrylen + 1) |
|
|
{ |
|
|
if (buflen < len + entrylen) |
|
|
{ |
|
|
buflen = len + entrylen; |
|
|
namebuf = xrealloc (namebuf, buflen + 1); |
|
|
} |
|
|
strcpy (namebuf + len, entry); |
|
|
if (!excluded_name (namebuf)) |
|
|
dump_file (namebuf, 0, our_device); |
|
|
} |
|
|
|
|
|
finish_dir: |
|
|
|
|
|
free (directory); |
|
|
free (namebuf); |
|
1329 |
if (atime_preserve_option) |
if (atime_preserve_option) |
1330 |
utime (p, &restore_times); |
utime (p, &restore_times); |
1331 |
return; |
return; |
1335 |
else |
else |
1336 |
{ |
{ |
1337 |
/* Check for multiple links. */ |
/* Check for multiple links. */ |
1338 |
|
if (dump_hard_link (stat)) |
1339 |
if (1 < current_stat_info.stat.st_nlink && link_table) |
return; |
1340 |
{ |
|
|
struct link lp; |
|
|
struct link *dup; |
|
|
lp.ino = current_stat_info.stat.st_ino; |
|
|
lp.dev = current_stat_info.stat.st_dev; |
|
|
|
|
|
if ((dup = hash_lookup (link_table, &lp))) |
|
|
{ |
|
|
/* We found a link. */ |
|
|
char const *link_name = safer_name_suffix (dup->name, 1); |
|
|
|
|
|
dup->nlink--; |
|
|
|
|
|
block_ordinal = current_block_ordinal (); |
|
|
assign_string (¤t_stat_info.link_name, link_name); |
|
|
if (NAME_FIELD_SIZE < strlen (link_name)) |
|
|
write_long_link (¤t_stat_info); |
|
|
|
|
|
current_stat_info.stat.st_size = 0; |
|
|
header = start_header (p, ¤t_stat_info); |
|
|
if (!header) |
|
|
return; |
|
|
tar_copy_str (header->header.linkname, link_name, |
|
|
NAME_FIELD_SIZE); |
|
|
|
|
|
header->header.typeflag = LNKTYPE; |
|
|
finish_header (header, block_ordinal); |
|
|
|
|
|
/* FIXME: Maybe remove from table after all links found? */ |
|
|
|
|
|
if (remove_files_option && unlink (p) != 0) |
|
|
unlink_error (p); |
|
|
|
|
|
/* We dumped it, and we don't need to put it in the |
|
|
table again. */ |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
1341 |
/* This is not a link to a previously dumped file, so dump it. */ |
/* This is not a link to a previously dumped file, so dump it. */ |
1342 |
|
|
1343 |
if (S_ISREG (current_stat_info.stat.st_mode) |
if (S_ISREG (stat->stat.st_mode) |
1344 |
|| S_ISCTG (current_stat_info.stat.st_mode)) |
|| S_ISCTG (stat->stat.st_mode)) |
1345 |
{ |
{ |
1346 |
int f; /* file descriptor */ |
int fd; |
1347 |
size_t bufsize; |
enum dump_status status; |
|
ssize_t count; |
|
|
off_t sizeleft; |
|
|
union block *start; |
|
|
int header_moved; |
|
|
char isextended = 0; |
|
|
int sparses = 0; |
|
1348 |
|
|
1349 |
header_moved = 0; |
if (file_dumpable_p (stat)) |
|
|
|
|
if (sparse_option) |
|
1350 |
{ |
{ |
1351 |
/* Check the size of the file against the number of blocks |
fd = open (stat->orig_file_name, |
1352 |
allocated for it, counting both data and indirect blocks. |
O_RDONLY | O_BINARY); |
1353 |
If there is a smaller number of blocks than would be |
if (fd < 0) |
|
necessary to accommodate a file of this size, this is safe |
|
|
to say that we have a sparse file: at least one of those |
|
|
blocks in the file is just a useless hole. For sparse |
|
|
files not having more hole blocks than indirect blocks, the |
|
|
sparseness will go undetected. */ |
|
|
|
|
|
/* Bruno Haible sent me these statistics for Linux. It seems |
|
|
that some filesystems count indirect blocks in st_blocks, |
|
|
while others do not seem to: |
|
|
|
|
|
minix-fs tar: size=7205, st_blocks=18 and ST_NBLOCKS=18 |
|
|
extfs tar: size=7205, st_blocks=18 and ST_NBLOCKS=18 |
|
|
ext2fs tar: size=7205, st_blocks=16 and ST_NBLOCKS=16 |
|
|
msdos-fs tar: size=7205, st_blocks=16 and ST_NBLOCKS=16 |
|
|
|
|
|
Dick Streefland reports the previous numbers as misleading, |
|
|
because ext2fs use 12 direct blocks, while minix-fs uses only |
|
|
6 direct blocks. Dick gets: |
|
|
|
|
|
ext2 size=20480 ls listed blocks=21 |
|
|
minix size=20480 ls listed blocks=21 |
|
|
msdos size=20480 ls listed blocks=20 |
|
|
|
|
|
It seems that indirect blocks *are* included in st_blocks. |
|
|
The minix filesystem does not account for phantom blocks in |
|
|
st_blocks, so `du' and `ls -s' give wrong results. So, the |
|
|
--sparse option would not work on a minix filesystem. */ |
|
|
|
|
|
if (ST_NBLOCKS (current_stat_info.stat) |
|
|
< (current_stat_info.stat.st_size / ST_NBLOCKSIZE |
|
|
+ (current_stat_info.stat.st_size % ST_NBLOCKSIZE != 0))) |
|
1354 |
{ |
{ |
1355 |
int counter; |
if (!top_level && errno == ENOENT) |
|
|
|
|
block_ordinal = current_block_ordinal (); |
|
|
header = start_header (p, ¤t_stat_info); |
|
|
if (!header) |
|
|
return; |
|
|
header->header.typeflag = GNUTYPE_SPARSE; |
|
|
header_moved = 1; |
|
|
|
|
|
/* Call the routine that figures out the layout of the |
|
|
sparse file in question. SPARSES is the index of the |
|
|
first unused element of the "sparsearray," i.e., |
|
|
the number of elements it needed to describe the file. */ |
|
|
|
|
|
sparses = deal_with_sparse (p, header); |
|
|
|
|
|
/* See if we'll need an extended header later. */ |
|
|
|
|
|
if (SPARSES_IN_OLDGNU_HEADER < sparses) |
|
|
header->oldgnu_header.isextended = 1; |
|
|
|
|
|
/* We store the "real" file size so we can show that in |
|
|
case someone wants to list the archive, i.e., tar tvf |
|
|
<file>. It might be kind of disconcerting if the |
|
|
shrunken file size was the one that showed up. */ |
|
|
|
|
|
OFF_TO_CHARS (current_stat_info.stat.st_size, |
|
|
header->oldgnu_header.realsize); |
|
|
|
|
|
/* This will be the new "size" of the file, i.e., the size |
|
|
of the file minus the blocks of holes that we're |
|
|
skipping over. */ |
|
|
|
|
|
current_stat_info.stat.st_size = find_new_file_size (sparses); |
|
|
OFF_TO_CHARS (current_stat_info.stat.st_size, header->header.size); |
|
|
|
|
|
for (counter = 0; |
|
|
counter < sparses && counter < SPARSES_IN_OLDGNU_HEADER; |
|
|
counter++) |
|
|
{ |
|
|
OFF_TO_CHARS (sparsearray[counter].offset, |
|
|
header->oldgnu_header.sp[counter].offset); |
|
|
SIZE_TO_CHARS (sparsearray[counter].numbytes, |
|
|
header->oldgnu_header.sp[counter].numbytes); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
sizeleft = current_stat_info.stat.st_size; |
|
|
|
|
|
/* Don't bother opening empty, world readable files. Also do not |
|
|
open files when archive is meant for /dev/null. */ |
|
|
|
|
|
if (dev_null_output |
|
|
|| (sizeleft == 0 |
|
|
&& MODE_R == (MODE_R & current_stat_info.stat.st_mode))) |
|
|
f = -1; |
|
|
else |
|
|
{ |
|
|
f = open (p, O_RDONLY | O_BINARY); |
|
|
if (f < 0) |
|
|
{ |
|
|
if (! top_level && errno == ENOENT) |
|
1356 |
WARN ((0, 0, _("%s: File removed before we read it"), |
WARN ((0, 0, _("%s: File removed before we read it"), |
1357 |
quotearg_colon (p))); |
quotearg_colon (stat->orig_file_name))); |
1358 |
else |
else |
1359 |
(ignore_failed_read_option ? open_warn : open_error) (p); |
open_diag (stat->orig_file_name); |
1360 |
return; |
return; |
1361 |
} |
} |
1362 |
} |
} |
|
|
|
|
/* If the file is sparse, we've already taken care of this. */ |
|
|
|
|
|
if (!header_moved) |
|
|
{ |
|
|
block_ordinal = current_block_ordinal (); |
|
|
header = start_header (p, ¤t_stat_info); |
|
|
if (!header) |
|
|
return; |
|
|
} |
|
|
|
|
|
/* Mark contiguous files, if we support them. */ |
|
|
|
|
|
if (archive_format != V7_FORMAT |
|
|
&& S_ISCTG (current_stat_info.stat.st_mode)) |
|
|
header->header.typeflag = CONTTYPE; |
|
|
|
|
|
if (archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT) |
|
|
isextended = header->oldgnu_header.isextended; |
|
1363 |
else |
else |
1364 |
isextended = 0; |
fd = -1; |
1365 |
|
|
1366 |
save_typeflag = header->header.typeflag; |
if (sparse_option && sparse_file_p (stat)) |
|
finish_header (header, block_ordinal); |
|
|
if (isextended) |
|
|
{ |
|
|
int sparses_emitted = SPARSES_IN_OLDGNU_HEADER; |
|
|
|
|
|
for (;;) |
|
|
{ |
|
|
int i; |
|
|
exhdr = find_next_block (); |
|
|
memset (exhdr->buffer, 0, BLOCKSIZE); |
|
|
for (i = 0; |
|
|
(i < SPARSES_IN_SPARSE_HEADER |
|
|
&& sparses_emitted + i < sparses); |
|
|
i++) |
|
|
{ |
|
|
SIZE_TO_CHARS (sparsearray[sparses_emitted + i].numbytes, |
|
|
exhdr->sparse_header.sp[i].numbytes); |
|
|
OFF_TO_CHARS (sparsearray[sparses_emitted + i].offset, |
|
|
exhdr->sparse_header.sp[i].offset); |
|
|
} |
|
|
set_next_block_after (exhdr); |
|
|
sparses_emitted += i; |
|
|
if (sparses == sparses_emitted) |
|
|
break; |
|
|
exhdr->sparse_header.isextended = 1; |
|
|
} |
|
|
} |
|
|
if (save_typeflag == GNUTYPE_SPARSE) |
|
1367 |
{ |
{ |
1368 |
if (f < 0 |
status = sparse_dump_file (fd, stat); |
1369 |
|| finish_sparse_file (f, &sizeleft, |
if (status == dump_status_not_implemented) |
1370 |
current_stat_info.stat.st_size, p)) |
status = dump_regular_file (fd, stat); |
|
goto padit; |
|
1371 |
} |
} |
1372 |
else |
else |
1373 |
while (sizeleft > 0) |
status = dump_regular_file (fd, stat); |
|
{ |
|
|
if (multi_volume_option) |
|
|
{ |
|
|
assign_string (&save_name, p); |
|
|
save_sizeleft = sizeleft; |
|
|
save_totsize = current_stat_info.stat.st_size; |
|
|
} |
|
|
start = find_next_block (); |
|
|
|
|
|
bufsize = available_space_after (start); |
|
|
|
|
|
if (sizeleft < bufsize) |
|
|
{ |
|
|
/* Last read -- zero out area beyond. */ |
|
|
|
|
|
bufsize = sizeleft; |
|
|
count = bufsize % BLOCKSIZE; |
|
|
if (count) |
|
|
memset (start->buffer + sizeleft, 0, BLOCKSIZE - count); |
|
|
} |
|
|
if (f < 0) |
|
|
count = bufsize; |
|
|
else |
|
|
count = safe_read (f, start->buffer, bufsize); |
|
|
if (count < 0) |
|
|
{ |
|
|
(ignore_failed_read_option |
|
|
? read_warn_details |
|
|
: read_error_details) |
|
|
(p, current_stat_info.stat.st_size - sizeleft, bufsize); |
|
|
goto padit; |
|
|
} |
|
|
sizeleft -= count; |
|
|
|
|
|
/* This is nonportable (the type of set_next_block_after's arg). */ |
|
|
|
|
|
set_next_block_after (start + (bufsize - 1) / BLOCKSIZE); |
|
|
|
|
|
|
|
|
if (count != bufsize) |
|
|
{ |
|
|
char buf[UINTMAX_STRSIZE_BOUND]; |
|
|
memset (start->buffer + count, 0, bufsize - count); |
|
|
WARN ((0, 0, |
|
|
ngettext ("%s: File shrank by %s byte; padding with zeros", |
|
|
"%s: File shrank by %s bytes; padding with zeros", |
|
|
sizeleft), |
|
|
quotearg_colon (p), |
|
|
STRINGIFY_BIGINT (sizeleft, buf))); |
|
|
if (! ignore_failed_read_option) |
|
|
exit_status = TAREXIT_FAILURE; |
|
|
goto padit; /* short read */ |
|
|
} |
|
|
} |
|
1374 |
|
|
1375 |
if (multi_volume_option) |
switch (status) |
|
assign_string (&save_name, 0); |
|
|
|
|
|
if (f >= 0) |
|
1376 |
{ |
{ |
1377 |
struct stat final_stat; |
case dump_status_ok: |
1378 |
if (fstat (f, &final_stat) != 0) |
if (multi_volume_option) |
1379 |
{ |
assign_string (&save_name, 0); |
1380 |
if (ignore_failed_read_option) |
dump_regular_finish (fd, stat, original_ctime); |
1381 |
stat_warn (p); |
break; |
|
else |
|
|
stat_error (p); |
|
|
} |
|
|
else if (final_stat.st_ctime != original_ctime) |
|
|
{ |
|
|
char const *qp = quotearg_colon (p); |
|
|
WARN ((0, 0, _("%s: file changed as we read it"), qp)); |
|
|
} |
|
|
if (close (f) != 0) |
|
|
{ |
|
|
if (ignore_failed_read_option) |
|
|
close_warn (p); |
|
|
else |
|
|
close_error (p); |
|
|
} |
|
|
if (atime_preserve_option) |
|
|
utime (p, &restore_times); |
|
|
} |
|
|
if (remove_files_option) |
|
|
{ |
|
|
if (unlink (p) == -1) |
|
|
unlink_error (p); |
|
|
} |
|
|
goto file_was_dumped; |
|
1382 |
|
|
1383 |
/* File shrunk or gave error, pad out tape to match the size we |
case dump_status_short: |
1384 |
specified in the header. */ |
if (multi_volume_option) |
1385 |
|
assign_string (&save_name, 0); |
1386 |
|
close (fd); |
1387 |
|
break; |
1388 |
|
|
1389 |
|
case dump_status_fail: |
1390 |
|
close (fd); |
1391 |
|
return; |
1392 |
|
|
1393 |
padit: |
case dump_status_not_implemented: |
1394 |
while (sizeleft > 0) |
abort (); |
|
{ |
|
|
save_sizeleft = sizeleft; |
|
|
start = find_next_block (); |
|
|
memset (start->buffer, 0, BLOCKSIZE); |
|
|
set_next_block_after (start); |
|
|
sizeleft -= BLOCKSIZE; |
|
1395 |
} |
} |
1396 |
if (multi_volume_option) |
|
1397 |
assign_string (&save_name, 0); |
if (atime_preserve_option) |
1398 |
if (f >= 0) |
utime (stat->orig_file_name, &restore_times); |
1399 |
{ |
file_count_links (stat); |
1400 |
close (f); |
return; |
|
if (atime_preserve_option) |
|
|
utime (p, &restore_times); |
|
|
} |
|
|
goto file_was_dumped; |
|
1401 |
} |
} |
1402 |
#ifdef HAVE_READLINK |
#ifdef HAVE_READLINK |
1403 |
else if (S_ISLNK (current_stat_info.stat.st_mode)) |
else if (S_ISLNK (stat->stat.st_mode)) |
1404 |
{ |
{ |
1405 |
char *buffer; |
char *buffer; |
1406 |
int size; |
int size; |
1407 |
size_t linklen = current_stat_info.stat.st_size; |
size_t linklen = stat->stat.st_size; |
1408 |
if (linklen != current_stat_info.stat.st_size || linklen + 1 == 0) |
if (linklen != stat->stat.st_size || linklen + 1 == 0) |
1409 |
xalloc_die (); |
xalloc_die (); |
1410 |
buffer = (char *) alloca (linklen + 1); |
buffer = (char *) alloca (linklen + 1); |
1411 |
size = readlink (p, buffer, linklen + 1); |
size = readlink (p, buffer, linklen + 1); |
1412 |
if (size < 0) |
if (size < 0) |
1413 |
{ |
{ |
1414 |
if (ignore_failed_read_option) |
readlink_diag (p); |
|
readlink_warn (p); |
|
|
else |
|
|
readlink_error (p); |
|
1415 |
return; |
return; |
1416 |
} |
} |
1417 |
buffer[size] = '\0'; |
buffer[size] = '\0'; |
1418 |
assign_string (¤t_stat_info.link_name, buffer); |
assign_string (&stat->link_name, buffer); |
1419 |
if (size > NAME_FIELD_SIZE) |
if (size > NAME_FIELD_SIZE) |
1420 |
write_long_link (¤t_stat_info); |
write_long_link (stat); |
1421 |
|
|
1422 |
block_ordinal = current_block_ordinal (); |
block_ordinal = current_block_ordinal (); |
1423 |
current_stat_info.stat.st_size = 0; /* force 0 size on symlink */ |
stat->stat.st_size = 0; /* force 0 size on symlink */ |
1424 |
header = start_header (p, ¤t_stat_info); |
header = start_header (stat); |
1425 |
if (!header) |
if (!header) |
1426 |
return; |
return; |
1427 |
tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE); |
tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE); |
1428 |
header->header.typeflag = SYMTYPE; |
header->header.typeflag = SYMTYPE; |
1429 |
finish_header (header, block_ordinal); |
finish_header (stat, header, block_ordinal); |
1430 |
/* nothing more to do to it */ |
/* nothing more to do to it */ |
1431 |
|
|
1432 |
if (remove_files_option) |
if (remove_files_option) |
1434 |
if (unlink (p) == -1) |
if (unlink (p) == -1) |
1435 |
unlink_error (p); |
unlink_error (p); |
1436 |
} |
} |
1437 |
goto file_was_dumped; |
file_count_links (stat); |
1438 |
|
return; |
1439 |
} |
} |
1440 |
#endif |
#endif |
1441 |
else if (S_ISCHR (current_stat_info.stat.st_mode)) |
else if (S_ISCHR (stat->stat.st_mode)) |
1442 |
type = CHRTYPE; |
type = CHRTYPE; |
1443 |
else if (S_ISBLK (current_stat_info.stat.st_mode)) |
else if (S_ISBLK (stat->stat.st_mode)) |
1444 |
type = BLKTYPE; |
type = BLKTYPE; |
1445 |
else if (S_ISFIFO (current_stat_info.stat.st_mode)) |
else if (S_ISFIFO (stat->stat.st_mode)) |
1446 |
type = FIFOTYPE; |
type = FIFOTYPE; |
1447 |
else if (S_ISSOCK (current_stat_info.stat.st_mode)) |
else if (S_ISSOCK (stat->stat.st_mode)) |
1448 |
{ |
{ |
1449 |
WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p))); |
WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p))); |
1450 |
return; |
return; |
1451 |
} |
} |
1452 |
else if (S_ISDOOR (current_stat_info.stat.st_mode)) |
else if (S_ISDOOR (stat->stat.st_mode)) |
1453 |
{ |
{ |
1454 |
WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p))); |
WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p))); |
1455 |
return; |
return; |
1456 |
} |
} |
1457 |
else |
else |
1458 |
goto unknown; |
{ |
1459 |
|
unknown_file_error (p); |
1460 |
|
return; |
1461 |
|
} |
1462 |
} |
} |
1463 |
|
|
1464 |
if (archive_format == V7_FORMAT) |
if (archive_format == V7_FORMAT) |
1465 |
goto unknown; |
{ |
1466 |
|
unknown_file_error (p); |
1467 |
|
return; |
1468 |
|
} |
1469 |
|
|
1470 |
block_ordinal = current_block_ordinal (); |
block_ordinal = current_block_ordinal (); |
1471 |
current_stat_info.stat.st_size = 0; /* force 0 size */ |
stat->stat.st_size = 0; /* force 0 size */ |
1472 |
header = start_header (p, ¤t_stat_info); |
header = start_header (stat); |
1473 |
if (!header) |
if (!header) |
1474 |
return; |
return; |
1475 |
header->header.typeflag = type; |
header->header.typeflag = type; |
1476 |
|
|
1477 |
if (type != FIFOTYPE) |
if (type != FIFOTYPE) |
1478 |
{ |
{ |
1479 |
MAJOR_TO_CHARS (major (current_stat_info.stat.st_rdev), |
MAJOR_TO_CHARS (major (stat->stat.st_rdev), |
1480 |
header->header.devmajor); |
header->header.devmajor); |
1481 |
MINOR_TO_CHARS (minor (current_stat_info.stat.st_rdev), |
MINOR_TO_CHARS (minor (stat->stat.st_rdev), |
1482 |
header->header.devminor); |
header->header.devminor); |
1483 |
} |
} |
1484 |
|
|
1485 |
finish_header (header, block_ordinal); |
finish_header (stat, header, block_ordinal); |
1486 |
if (remove_files_option) |
if (remove_files_option) |
1487 |
{ |
{ |
1488 |
if (unlink (p) == -1) |
if (unlink (p) == -1) |
1489 |
unlink_error (p); |
unlink_error (p); |
1490 |
} |
} |
|
goto file_was_dumped; |
|
|
|
|
|
unknown: |
|
|
WARN ((0, 0, _("%s: Unknown file type; file ignored"), |
|
|
quotearg_colon (p))); |
|
|
if (! ignore_failed_read_option) |
|
|
exit_status = TAREXIT_FAILURE; |
|
|
return; |
|
|
|
|
|
file_was_dumped: |
|
|
if (1 < current_stat_info.stat.st_nlink) |
|
|
{ |
|
|
struct link *dup; |
|
|
struct link *lp = xmalloc (offsetof (struct link, name) |
|
|
+ strlen (p) + 1); |
|
|
lp->ino = current_stat_info.stat.st_ino; |
|
|
lp->dev = current_stat_info.stat.st_dev; |
|
|
lp->nlink = current_stat_info.stat.st_nlink; |
|
|
strcpy (lp->name, p); |
|
|
|
|
|
if (! ((link_table |
|
|
|| (link_table = hash_initialize (0, 0, hash_link, |
|
|
compare_links, 0))) |
|
|
&& (dup = hash_insert (link_table, lp)))) |
|
|
xalloc_die (); |
|
|
|
|
|
if (dup != lp) |
|
|
abort (); |
|
|
lp->nlink--; |
|
|
} |
|
|
|
|
1491 |
} |
} |
1492 |
|
|
|
/* For each dumped file, check if all its links were dumped. Emit |
|
|
warnings if it is not so. */ |
|
1493 |
void |
void |
1494 |
check_links () |
dump_file (char *p, int top_level, dev_t parent_device) |
1495 |
{ |
{ |
1496 |
struct link *lp; |
struct tar_stat_info stat; |
1497 |
|
tar_stat_init (&stat); |
1498 |
if (!link_table) |
dump_file0 (&stat, p, top_level, parent_device); |
1499 |
return; |
tar_stat_destroy (&stat); |
|
|
|
|
for (lp = hash_get_first (link_table); lp; |
|
|
lp = hash_get_next (link_table, lp)) |
|
|
{ |
|
|
if (lp->nlink) |
|
|
{ |
|
|
WARN ((0, 0, _("Missing links to '%s'.\n"), lp->name)); |
|
|
} |
|
|
} |
|
1500 |
} |
} |