| 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 |
} |
} |