1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3238856Smm * Copyright (c) 2012 Michihiro NAKAJIMA 4228753Smm * All rights reserved. 5228753Smm * 6228753Smm * Redistribution and use in source and binary forms, with or without 7228753Smm * modification, are permitted provided that the following conditions 8228753Smm * are met: 9228753Smm * 1. Redistributions of source code must retain the above copyright 10228753Smm * notice, this list of conditions and the following disclaimer. 11228753Smm * 2. Redistributions in binary form must reproduce the above copyright 12228753Smm * notice, this list of conditions and the following disclaimer in the 13228753Smm * documentation and/or other materials provided with the distribution. 14228753Smm * 15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25228753Smm */ 26228753Smm 27228753Smm#include "bsdtar_platform.h" 28228763Smm__FBSDID("$FreeBSD: stable/11/contrib/libarchive/tar/write.c 342360 2018-12-21 23:33:05Z mm $"); 29228753Smm 30228753Smm#ifdef HAVE_SYS_TYPES_H 31228753Smm#include <sys/types.h> 32228753Smm#endif 33228753Smm#ifdef HAVE_SYS_STAT_H 34228753Smm#include <sys/stat.h> 35228753Smm#endif 36228753Smm#ifdef HAVE_ATTR_XATTR_H 37228753Smm#include <attr/xattr.h> 38228753Smm#endif 39228753Smm#ifdef HAVE_ERRNO_H 40228753Smm#include <errno.h> 41228753Smm#endif 42228753Smm#ifdef HAVE_FCNTL_H 43228753Smm#include <fcntl.h> 44228753Smm#endif 45228753Smm#ifdef HAVE_GRP_H 46228753Smm#include <grp.h> 47228753Smm#endif 48228753Smm#ifdef HAVE_IO_H 49228753Smm#include <io.h> 50228753Smm#endif 51232153Smm#ifdef HAVE_LIBGEN_H 52232153Smm#include <libgen.h> 53232153Smm#endif 54228753Smm#ifdef HAVE_LIMITS_H 55228753Smm#include <limits.h> 56228753Smm#endif 57232153Smm#ifdef HAVE_PATHS_H 58232153Smm#include <paths.h> 59232153Smm#endif 60228753Smm#ifdef HAVE_PWD_H 61228753Smm#include <pwd.h> 62228753Smm#endif 63228753Smm#ifdef HAVE_STDINT_H 64228753Smm#include <stdint.h> 65228753Smm#endif 66228753Smm#include <stdio.h> 67228753Smm#ifdef HAVE_STDLIB_H 68228753Smm#include <stdlib.h> 69228753Smm#endif 70228753Smm#ifdef HAVE_STRING_H 71228753Smm#include <string.h> 72228753Smm#endif 73228753Smm#ifdef HAVE_UNISTD_H 74228753Smm#include <unistd.h> 75228753Smm#endif 76228753Smm 77228753Smm#include "bsdtar.h" 78228753Smm#include "err.h" 79228753Smm#include "line_reader.h" 80228753Smm 81228753Smm#ifndef O_BINARY 82232153Smm#define O_BINARY 0 83228753Smm#endif 84228753Smm 85228753Smmstruct archive_dir_entry { 86228753Smm struct archive_dir_entry *next; 87228753Smm time_t mtime_sec; 88228753Smm int mtime_nsec; 89228753Smm char *name; 90228753Smm}; 91228753Smm 92228753Smmstruct archive_dir { 93228753Smm struct archive_dir_entry *head, *tail; 94228753Smm}; 95228753Smm 96228753Smmstatic int append_archive(struct bsdtar *, struct archive *, 97228753Smm struct archive *ina); 98228753Smmstatic int append_archive_filename(struct bsdtar *, 99228753Smm struct archive *, const char *fname); 100228753Smmstatic void archive_names_from_file(struct bsdtar *bsdtar, 101228753Smm struct archive *a); 102238856Smmstatic int copy_file_data_block(struct bsdtar *, 103238856Smm struct archive *a, struct archive *, 104238856Smm struct archive_entry *); 105238856Smmstatic void excluded_callback(struct archive *, void *, 106238856Smm struct archive_entry *); 107228753Smmstatic void report_write(struct bsdtar *, struct archive *, 108228753Smm struct archive_entry *, int64_t progress); 109228753Smmstatic void test_for_append(struct bsdtar *); 110238856Smmstatic int metadata_filter(struct archive *, void *, 111238856Smm struct archive_entry *); 112228753Smmstatic void write_archive(struct archive *, struct bsdtar *); 113232153Smmstatic void write_entry(struct bsdtar *, struct archive *, 114228753Smm struct archive_entry *); 115232153Smmstatic void write_file(struct bsdtar *, struct archive *, 116232153Smm struct archive_entry *); 117228753Smmstatic void write_hierarchy(struct bsdtar *, struct archive *, 118228753Smm const char *); 119228753Smm 120228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 121228753Smm/* Not a full lseek() emulation, but enough for our needs here. */ 122228753Smmstatic int 123228753Smmseek_file(int fd, int64_t offset, int whence) 124228753Smm{ 125228753Smm LARGE_INTEGER distance; 126228753Smm (void)whence; /* UNUSED */ 127228753Smm distance.QuadPart = offset; 128228753Smm return (SetFilePointerEx((HANDLE)_get_osfhandle(fd), 129228753Smm distance, NULL, FILE_BEGIN) ? 1 : -1); 130228753Smm} 131232153Smm#define open _open 132232153Smm#define close _close 133232153Smm#define read _read 134248616Smm#ifdef lseek 135248616Smm#undef lseek 136248616Smm#endif 137232153Smm#define lseek seek_file 138228753Smm#endif 139228753Smm 140248616Smmstatic void 141248616Smmset_writer_options(struct bsdtar *bsdtar, struct archive *a) 142248616Smm{ 143248616Smm const char *writer_options; 144248616Smm int r; 145248616Smm 146248616Smm writer_options = getenv(ENV_WRITER_OPTIONS); 147248616Smm if (writer_options != NULL) { 148311041Smm size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; 149311041Smm size_t opt_len = strlen(writer_options) + 1; 150248616Smm char *p; 151248616Smm /* Set default write options. */ 152311041Smm if ((p = malloc(module_len + opt_len)) == NULL) 153248616Smm lafe_errc(1, errno, "Out of memory"); 154248616Smm /* Prepend magic code to ignore options for 155248616Smm * a format or filters which are not added to 156248616Smm * the archive write object. */ 157311041Smm memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); 158311041Smm memcpy(p, writer_options, opt_len); 159248616Smm r = archive_write_set_options(a, p); 160248616Smm free(p); 161248616Smm if (r < ARCHIVE_WARN) 162248616Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 163248616Smm else 164248616Smm archive_clear_error(a); 165248616Smm } 166248616Smm if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) 167248616Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 168248616Smm} 169248616Smm 170248616Smmstatic void 171248616Smmset_reader_options(struct bsdtar *bsdtar, struct archive *a) 172248616Smm{ 173248616Smm const char *reader_options; 174248616Smm int r; 175248616Smm 176248616Smm (void)bsdtar; /* UNUSED */ 177248616Smm 178248616Smm reader_options = getenv(ENV_READER_OPTIONS); 179248616Smm if (reader_options != NULL) { 180311041Smm size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1; 181311041Smm size_t opt_len = strlen(reader_options) + 1; 182248616Smm char *p; 183248616Smm /* Set default write options. */ 184311041Smm if ((p = malloc(module_len + opt_len)) == NULL) 185248616Smm if (p == NULL) 186248616Smm lafe_errc(1, errno, "Out of memory"); 187248616Smm /* Prepend magic code to ignore options for 188248616Smm * a format or filters which are not added to 189248616Smm * the archive write object. */ 190311041Smm memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len); 191311041Smm memcpy(p, reader_options, opt_len); 192248616Smm r = archive_read_set_options(a, p); 193248616Smm free(p); 194248616Smm if (r < ARCHIVE_WARN) 195248616Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 196248616Smm else 197248616Smm archive_clear_error(a); 198248616Smm } 199248616Smm} 200248616Smm 201228753Smmvoid 202228753Smmtar_mode_c(struct bsdtar *bsdtar) 203228753Smm{ 204228753Smm struct archive *a; 205248616Smm const void *filter_name; 206228753Smm int r; 207228753Smm 208228753Smm if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) 209228753Smm lafe_errc(1, 0, "no files or directories specified"); 210228753Smm 211228753Smm a = archive_write_new(); 212228753Smm 213228753Smm /* Support any format that the library supports. */ 214248616Smm if (cset_get_format(bsdtar->cset) == NULL) { 215228753Smm r = archive_write_set_format_pax_restricted(a); 216248616Smm cset_set_format(bsdtar->cset, "pax restricted"); 217228753Smm } else { 218248616Smm r = archive_write_set_format_by_name(a, 219248616Smm cset_get_format(bsdtar->cset)); 220228753Smm } 221228753Smm if (r != ARCHIVE_OK) { 222228753Smm fprintf(stderr, "Can't use format %s: %s\n", 223248616Smm cset_get_format(bsdtar->cset), 224228753Smm archive_error_string(a)); 225228753Smm usage(); 226228753Smm } 227228753Smm 228232153Smm archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); 229232153Smm archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block); 230228753Smm 231248616Smm r = cset_write_add_filters(bsdtar->cset, a, &filter_name); 232248616Smm if (r < ARCHIVE_WARN) { 233248616Smm lafe_errc(1, 0, "Unsupported compression option --%s", 234248616Smm (const char *)filter_name); 235228753Smm } 236228753Smm 237248616Smm set_writer_options(bsdtar, a); 238299529Smm if (bsdtar->passphrase != NULL) 239299529Smm r = archive_write_set_passphrase(a, bsdtar->passphrase); 240299529Smm else 241299529Smm r = archive_write_set_passphrase_callback(a, bsdtar, 242299529Smm &passphrase_callback); 243299529Smm if (r != ARCHIVE_OK) 244299529Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 245248616Smm if (ARCHIVE_OK != archive_write_open_filename(a, bsdtar->filename)) 246228753Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 247228753Smm write_archive(a, bsdtar); 248228753Smm} 249228753Smm 250228753Smm/* 251228753Smm * Same as 'c', except we only support tar or empty formats in 252228753Smm * uncompressed files on disk. 253228753Smm */ 254228753Smmvoid 255228753Smmtar_mode_r(struct bsdtar *bsdtar) 256228753Smm{ 257228753Smm int64_t end_offset; 258228753Smm int format; 259228753Smm struct archive *a; 260228753Smm struct archive_entry *entry; 261228753Smm int r; 262228753Smm 263228753Smm /* Sanity-test some arguments and the file. */ 264228753Smm test_for_append(bsdtar); 265228753Smm 266228753Smm format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; 267228753Smm 268228753Smm#if defined(__BORLANDC__) 269228753Smm bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY); 270228753Smm#else 271228753Smm bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT | O_BINARY, 0666); 272228753Smm#endif 273228753Smm if (bsdtar->fd < 0) 274228753Smm lafe_errc(1, errno, 275228753Smm "Cannot open %s", bsdtar->filename); 276228753Smm 277228753Smm a = archive_read_new(); 278232153Smm archive_read_support_filter_all(a); 279239622Smm archive_read_support_format_empty(a); 280228753Smm archive_read_support_format_tar(a); 281228753Smm archive_read_support_format_gnutar(a); 282248616Smm set_reader_options(bsdtar, a); 283228753Smm r = archive_read_open_fd(a, bsdtar->fd, 10240); 284228753Smm if (r != ARCHIVE_OK) 285228753Smm lafe_errc(1, archive_errno(a), 286228753Smm "Can't read archive %s: %s", bsdtar->filename, 287228753Smm archive_error_string(a)); 288228753Smm while (0 == archive_read_next_header(a, &entry)) { 289248616Smm if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { 290232153Smm archive_read_free(a); 291228753Smm close(bsdtar->fd); 292228753Smm lafe_errc(1, 0, 293228753Smm "Cannot append to compressed archive."); 294228753Smm } 295228753Smm /* Keep going until we hit end-of-archive */ 296228753Smm format = archive_format(a); 297228753Smm } 298228753Smm 299228753Smm end_offset = archive_read_header_position(a); 300232153Smm archive_read_free(a); 301228753Smm 302228753Smm /* Re-open archive for writing */ 303228753Smm a = archive_write_new(); 304228753Smm /* 305228753Smm * Set the format to be used for writing. To allow people to 306228753Smm * extend empty files, we need to allow them to specify the format, 307228753Smm * which opens the possibility that they will specify a format that 308228753Smm * doesn't match the existing format. Hence, the following bit 309228753Smm * of arcane ugliness. 310228753Smm */ 311228753Smm 312248616Smm if (cset_get_format(bsdtar->cset) != NULL) { 313228753Smm /* If the user requested a format, use that, but ... */ 314228753Smm archive_write_set_format_by_name(a, 315248616Smm cset_get_format(bsdtar->cset)); 316228753Smm /* ... complain if it's not compatible. */ 317228753Smm format &= ARCHIVE_FORMAT_BASE_MASK; 318228753Smm if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK) 319228753Smm && format != ARCHIVE_FORMAT_EMPTY) { 320228753Smm lafe_errc(1, 0, 321228753Smm "Format %s is incompatible with the archive %s.", 322248616Smm cset_get_format(bsdtar->cset), bsdtar->filename); 323228753Smm } 324228753Smm } else { 325228753Smm /* 326228753Smm * Just preserve the current format, with a little care 327228753Smm * for formats that libarchive can't write. 328228753Smm */ 329228753Smm if (format == ARCHIVE_FORMAT_EMPTY) 330228753Smm format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; 331228753Smm archive_write_set_format(a, format); 332228753Smm } 333228753Smm if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) 334228753Smm lafe_errc(1, errno, "Could not seek to archive end"); 335248616Smm set_writer_options(bsdtar, a); 336228753Smm if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) 337228753Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 338228753Smm 339228753Smm write_archive(a, bsdtar); /* XXX check return val XXX */ 340228753Smm 341228753Smm close(bsdtar->fd); 342228753Smm bsdtar->fd = -1; 343228753Smm} 344228753Smm 345228753Smmvoid 346228753Smmtar_mode_u(struct bsdtar *bsdtar) 347228753Smm{ 348228753Smm int64_t end_offset; 349228753Smm struct archive *a; 350228753Smm struct archive_entry *entry; 351228753Smm int format; 352228753Smm struct archive_dir_entry *p; 353228753Smm struct archive_dir archive_dir; 354228753Smm 355228753Smm bsdtar->archive_dir = &archive_dir; 356228753Smm memset(&archive_dir, 0, sizeof(archive_dir)); 357228753Smm 358228753Smm format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; 359228753Smm 360228753Smm /* Sanity-test some arguments and the file. */ 361228753Smm test_for_append(bsdtar); 362228753Smm 363228753Smm bsdtar->fd = open(bsdtar->filename, O_RDWR | O_BINARY); 364228753Smm if (bsdtar->fd < 0) 365228753Smm lafe_errc(1, errno, 366228753Smm "Cannot open %s", bsdtar->filename); 367228753Smm 368228753Smm a = archive_read_new(); 369232153Smm archive_read_support_filter_all(a); 370228753Smm archive_read_support_format_tar(a); 371228753Smm archive_read_support_format_gnutar(a); 372248616Smm set_reader_options(bsdtar, a); 373232153Smm if (archive_read_open_fd(a, bsdtar->fd, bsdtar->bytes_per_block) 374232153Smm != ARCHIVE_OK) { 375228753Smm lafe_errc(1, 0, 376228753Smm "Can't open %s: %s", bsdtar->filename, 377228753Smm archive_error_string(a)); 378228753Smm } 379228753Smm 380228753Smm /* Build a list of all entries and their recorded mod times. */ 381228753Smm while (0 == archive_read_next_header(a, &entry)) { 382248616Smm if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { 383232153Smm archive_read_free(a); 384228753Smm close(bsdtar->fd); 385228753Smm lafe_errc(1, 0, 386228753Smm "Cannot append to compressed archive."); 387228753Smm } 388238856Smm if (archive_match_exclude_entry(bsdtar->matching, 389238856Smm ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER | 390238856Smm ARCHIVE_MATCH_EQUAL, entry) != ARCHIVE_OK) 391238856Smm lafe_errc(1, 0, "Error : %s", 392238856Smm archive_error_string(bsdtar->matching)); 393228753Smm /* Record the last format determination we see */ 394228753Smm format = archive_format(a); 395228753Smm /* Keep going until we hit end-of-archive */ 396228753Smm } 397228753Smm 398228753Smm end_offset = archive_read_header_position(a); 399232153Smm archive_read_free(a); 400228753Smm 401228753Smm /* Re-open archive for writing. */ 402228753Smm a = archive_write_new(); 403228753Smm /* 404232153Smm * Set format to same one auto-detected above. 405228753Smm */ 406228753Smm archive_write_set_format(a, format); 407232153Smm archive_write_set_bytes_per_block(a, bsdtar->bytes_per_block); 408232153Smm archive_write_set_bytes_in_last_block(a, bsdtar->bytes_in_last_block); 409232153Smm 410228753Smm if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) 411228753Smm lafe_errc(1, errno, "Could not seek to archive end"); 412248616Smm set_writer_options(bsdtar, a); 413228753Smm if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) 414228753Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 415228753Smm 416228753Smm write_archive(a, bsdtar); 417228753Smm 418228753Smm close(bsdtar->fd); 419228753Smm bsdtar->fd = -1; 420228753Smm 421228753Smm while (bsdtar->archive_dir->head != NULL) { 422228753Smm p = bsdtar->archive_dir->head->next; 423228753Smm free(bsdtar->archive_dir->head->name); 424228753Smm free(bsdtar->archive_dir->head); 425228753Smm bsdtar->archive_dir->head = p; 426228753Smm } 427228753Smm bsdtar->archive_dir->tail = NULL; 428228753Smm} 429228753Smm 430228753Smm 431228753Smm/* 432228753Smm * Write user-specified files/dirs to opened archive. 433228753Smm */ 434228753Smmstatic void 435228753Smmwrite_archive(struct archive *a, struct bsdtar *bsdtar) 436228753Smm{ 437228753Smm const char *arg; 438228753Smm struct archive_entry *entry, *sparse_entry; 439228753Smm 440232153Smm /* Choose a suitable copy buffer size */ 441232153Smm bsdtar->buff_size = 64 * 1024; 442232153Smm while (bsdtar->buff_size < (size_t)bsdtar->bytes_per_block) 443232153Smm bsdtar->buff_size *= 2; 444232153Smm /* Try to compensate for space we'll lose to alignment. */ 445232153Smm bsdtar->buff_size += 16 * 1024; 446232153Smm 447228753Smm /* Allocate a buffer for file data. */ 448232153Smm if ((bsdtar->buff = malloc(bsdtar->buff_size)) == NULL) 449228753Smm lafe_errc(1, 0, "cannot allocate memory"); 450228753Smm 451228753Smm if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) 452228753Smm lafe_errc(1, 0, "cannot create link resolver"); 453228753Smm archive_entry_linkresolver_set_strategy(bsdtar->resolver, 454228753Smm archive_format(a)); 455238856Smm 456238856Smm /* Create a read_disk object. */ 457228753Smm if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) 458228753Smm lafe_errc(1, 0, "Cannot create read_disk object"); 459238856Smm /* Tell the read_disk how handle symlink. */ 460238856Smm switch (bsdtar->symlink_mode) { 461238856Smm case 'H': 462238856Smm archive_read_disk_set_symlink_hybrid(bsdtar->diskreader); 463238856Smm break; 464238856Smm case 'L': 465238856Smm archive_read_disk_set_symlink_logical(bsdtar->diskreader); 466238856Smm break; 467238856Smm default: 468238856Smm archive_read_disk_set_symlink_physical(bsdtar->diskreader); 469238856Smm break; 470238856Smm } 471238856Smm /* Register entry filters. */ 472238856Smm archive_read_disk_set_matching(bsdtar->diskreader, 473238856Smm bsdtar->matching, excluded_callback, bsdtar); 474238856Smm archive_read_disk_set_metadata_filter_callback( 475238856Smm bsdtar->diskreader, metadata_filter, bsdtar); 476238856Smm /* Set the behavior of archive_read_disk. */ 477238856Smm archive_read_disk_set_behavior(bsdtar->diskreader, 478238856Smm bsdtar->readdisk_flags); 479228753Smm archive_read_disk_set_standard_lookup(bsdtar->diskreader); 480228753Smm 481228753Smm if (bsdtar->names_from_file != NULL) 482228753Smm archive_names_from_file(bsdtar, a); 483228753Smm 484228753Smm while (*bsdtar->argv) { 485228753Smm arg = *bsdtar->argv; 486228753Smm if (arg[0] == '-' && arg[1] == 'C') { 487228753Smm arg += 2; 488228753Smm if (*arg == '\0') { 489228753Smm bsdtar->argv++; 490228753Smm arg = *bsdtar->argv; 491228753Smm if (arg == NULL) { 492228753Smm lafe_warnc(0, "%s", 493228753Smm "Missing argument for -C"); 494228753Smm bsdtar->return_value = 1; 495228753Smm goto cleanup; 496228753Smm } 497232153Smm if (*arg == '\0') { 498232153Smm lafe_warnc(0, 499232153Smm "Meaningless argument for -C: ''"); 500232153Smm bsdtar->return_value = 1; 501232153Smm goto cleanup; 502232153Smm } 503228753Smm } 504228753Smm set_chdir(bsdtar, arg); 505228753Smm } else { 506337351Smm if (*arg != '/') 507228753Smm do_chdir(bsdtar); /* Handle a deferred -C */ 508228753Smm if (*arg == '@') { 509228753Smm if (append_archive_filename(bsdtar, a, 510228753Smm arg + 1) != 0) 511228753Smm break; 512228753Smm } else 513228753Smm write_hierarchy(bsdtar, a, arg); 514228753Smm } 515228753Smm bsdtar->argv++; 516228753Smm } 517228753Smm 518238856Smm archive_read_disk_set_matching(bsdtar->diskreader, NULL, NULL, NULL); 519238856Smm archive_read_disk_set_metadata_filter_callback( 520238856Smm bsdtar->diskreader, NULL, NULL); 521228753Smm entry = NULL; 522228753Smm archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); 523228753Smm while (entry != NULL) { 524238856Smm int r; 525238856Smm struct archive_entry *entry2; 526238856Smm struct archive *disk = bsdtar->diskreader; 527238856Smm 528238856Smm /* 529311041Smm * This tricky code here is to correctly read the contents 530238856Smm * of the entry because the disk reader bsdtar->diskreader 531238856Smm * is pointing at does not have any information about the 532238856Smm * entry by this time and using archive_read_data_block() 533238856Smm * with the disk reader consequently must fail. And we 534238856Smm * have to re-open the entry to read the contents. 535238856Smm */ 536238856Smm /* TODO: Work with -C option as well. */ 537238856Smm r = archive_read_disk_open(disk, 538238856Smm archive_entry_sourcepath(entry)); 539238856Smm if (r != ARCHIVE_OK) { 540238856Smm lafe_warnc(archive_errno(disk), 541238856Smm "%s", archive_error_string(disk)); 542238856Smm bsdtar->return_value = 1; 543342360Smm goto next_entry; 544238856Smm } 545238856Smm 546238856Smm /* 547238856Smm * Invoke archive_read_next_header2() to work 548238856Smm * archive_read_data_block(), which is called via write_file(), 549238856Smm * without failure. 550238856Smm */ 551238856Smm entry2 = archive_entry_new(); 552238856Smm r = archive_read_next_header2(disk, entry2); 553238856Smm archive_entry_free(entry2); 554238856Smm if (r != ARCHIVE_OK) { 555238856Smm lafe_warnc(archive_errno(disk), 556238856Smm "%s", archive_error_string(disk)); 557238856Smm if (r == ARCHIVE_FATAL) 558238856Smm bsdtar->return_value = 1; 559342360Smm archive_read_close(disk); 560342360Smm goto next_entry; 561238856Smm } 562238856Smm 563232153Smm write_file(bsdtar, a, entry); 564342360Smm archive_read_close(disk); 565342360Smmnext_entry: 566228753Smm archive_entry_free(entry); 567228753Smm entry = NULL; 568228753Smm archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); 569228753Smm } 570228753Smm 571228753Smm if (archive_write_close(a)) { 572228753Smm lafe_warnc(0, "%s", archive_error_string(a)); 573228753Smm bsdtar->return_value = 1; 574228753Smm } 575228753Smm 576228753Smmcleanup: 577228753Smm /* Free file data buffer. */ 578228753Smm free(bsdtar->buff); 579228753Smm archive_entry_linkresolver_free(bsdtar->resolver); 580228753Smm bsdtar->resolver = NULL; 581232153Smm archive_read_free(bsdtar->diskreader); 582228753Smm bsdtar->diskreader = NULL; 583228753Smm 584315432Smm if (bsdtar->flags & OPTFLAG_TOTALS) { 585228753Smm fprintf(stderr, "Total bytes written: %s\n", 586248616Smm tar_i64toa(archive_filter_bytes(a, -1))); 587228753Smm } 588228753Smm 589232153Smm archive_write_free(a); 590228753Smm} 591228753Smm 592228753Smm/* 593228753Smm * Archive names specified in file. 594228753Smm * 595228753Smm * Unless --null was specified, a line containing exactly "-C" will 596228753Smm * cause the next line to be a directory to pass to chdir(). If 597228753Smm * --null is specified, then a line "-C" is just another filename. 598228753Smm */ 599228753Smmstatic void 600228753Smmarchive_names_from_file(struct bsdtar *bsdtar, struct archive *a) 601228753Smm{ 602228753Smm struct lafe_line_reader *lr; 603228753Smm const char *line; 604228753Smm 605228753Smm bsdtar->next_line_is_dir = 0; 606228753Smm 607315432Smm lr = lafe_line_reader(bsdtar->names_from_file, 608315432Smm (bsdtar->flags & OPTFLAG_NULL)); 609228753Smm while ((line = lafe_line_reader_next(lr)) != NULL) { 610228753Smm if (bsdtar->next_line_is_dir) { 611232153Smm if (*line != '\0') 612232153Smm set_chdir(bsdtar, line); 613232153Smm else { 614232153Smm lafe_warnc(0, 615232153Smm "Meaningless argument for -C: ''"); 616232153Smm bsdtar->return_value = 1; 617232153Smm } 618228753Smm bsdtar->next_line_is_dir = 0; 619315432Smm } else if (((bsdtar->flags & OPTFLAG_NULL) == 0) && 620315432Smm strcmp(line, "-C") == 0) 621228753Smm bsdtar->next_line_is_dir = 1; 622228753Smm else { 623228753Smm if (*line != '/') 624228753Smm do_chdir(bsdtar); /* Handle a deferred -C */ 625228753Smm write_hierarchy(bsdtar, a, line); 626228753Smm } 627228753Smm } 628228753Smm lafe_line_reader_free(lr); 629228753Smm if (bsdtar->next_line_is_dir) 630228753Smm lafe_errc(1, errno, 631228753Smm "Unexpected end of filename list; " 632228753Smm "directory expected after -C"); 633228753Smm} 634228753Smm 635228753Smm/* 636228753Smm * Copy from specified archive to current archive. Returns non-zero 637228753Smm * for write errors (which force us to terminate the entire archiving 638228753Smm * operation). If there are errors reading the input archive, we set 639228753Smm * bsdtar->return_value but return zero, so the overall archiving 640228753Smm * operation will complete and return non-zero. 641228753Smm */ 642228753Smmstatic int 643228753Smmappend_archive_filename(struct bsdtar *bsdtar, struct archive *a, 644232153Smm const char *raw_filename) 645228753Smm{ 646228753Smm struct archive *ina; 647232153Smm const char *filename = raw_filename; 648228753Smm int rc; 649228753Smm 650228753Smm if (strcmp(filename, "-") == 0) 651228753Smm filename = NULL; /* Library uses NULL for stdio. */ 652228753Smm 653228753Smm ina = archive_read_new(); 654228753Smm archive_read_support_format_all(ina); 655232153Smm archive_read_support_filter_all(ina); 656299529Smm set_reader_options(bsdtar, ina); 657299529Smm archive_read_set_options(ina, "mtree:checkfs"); 658299529Smm if (bsdtar->passphrase != NULL) 659299529Smm rc = archive_read_add_passphrase(a, bsdtar->passphrase); 660299529Smm else 661299529Smm rc = archive_read_set_passphrase_callback(ina, bsdtar, 662299529Smm &passphrase_callback); 663299529Smm if (rc != ARCHIVE_OK) 664299529Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 665248616Smm if (archive_read_open_filename(ina, filename, 666248616Smm bsdtar->bytes_per_block)) { 667228753Smm lafe_warnc(0, "%s", archive_error_string(ina)); 668228753Smm bsdtar->return_value = 1; 669228753Smm return (0); 670228753Smm } 671228753Smm 672228753Smm rc = append_archive(bsdtar, a, ina); 673228753Smm 674228753Smm if (rc != ARCHIVE_OK) { 675228753Smm lafe_warnc(0, "Error reading archive %s: %s", 676232153Smm raw_filename, archive_error_string(ina)); 677228753Smm bsdtar->return_value = 1; 678228753Smm } 679232153Smm archive_read_free(ina); 680228753Smm 681228753Smm return (rc); 682228753Smm} 683228753Smm 684228753Smmstatic int 685228753Smmappend_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) 686228753Smm{ 687228753Smm struct archive_entry *in_entry; 688228753Smm int e; 689228753Smm 690228753Smm while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) { 691238856Smm if (archive_match_excluded(bsdtar->matching, in_entry)) 692228753Smm continue; 693315432Smm if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && 694228753Smm !yes("copy '%s'", archive_entry_pathname(in_entry))) 695228753Smm continue; 696299529Smm if (bsdtar->verbose > 1) { 697299529Smm safe_fprintf(stderr, "a "); 698299529Smm list_item_verbose(bsdtar, stderr, in_entry); 699299529Smm } else if (bsdtar->verbose > 0) 700228753Smm safe_fprintf(stderr, "a %s", 701228753Smm archive_entry_pathname(in_entry)); 702228753Smm if (need_report()) 703228753Smm report_write(bsdtar, a, in_entry, 0); 704228753Smm 705228753Smm e = archive_write_header(a, in_entry); 706228753Smm if (e != ARCHIVE_OK) { 707228753Smm if (!bsdtar->verbose) 708228753Smm lafe_warnc(0, "%s: %s", 709228753Smm archive_entry_pathname(in_entry), 710228753Smm archive_error_string(a)); 711228753Smm else 712228753Smm fprintf(stderr, ": %s", archive_error_string(a)); 713228753Smm } 714228753Smm if (e == ARCHIVE_FATAL) 715228753Smm exit(1); 716228753Smm 717228753Smm if (e >= ARCHIVE_WARN) { 718228753Smm if (archive_entry_size(in_entry) == 0) 719228753Smm archive_read_data_skip(ina); 720238856Smm else if (copy_file_data_block(bsdtar, a, ina, in_entry)) 721228753Smm exit(1); 722228753Smm } 723228753Smm 724228753Smm if (bsdtar->verbose) 725228753Smm fprintf(stderr, "\n"); 726228753Smm } 727228753Smm 728228753Smm return (e == ARCHIVE_EOF ? ARCHIVE_OK : e); 729228753Smm} 730228753Smm 731238856Smm/* Helper function to copy file to archive. */ 732228753Smmstatic int 733238856Smmcopy_file_data_block(struct bsdtar *bsdtar, struct archive *a, 734238856Smm struct archive *in_a, struct archive_entry *entry) 735228753Smm{ 736238856Smm size_t bytes_read; 737228753Smm ssize_t bytes_written; 738238856Smm int64_t offset, progress = 0; 739238856Smm char *null_buff = NULL; 740238856Smm const void *buff; 741238856Smm int r; 742228753Smm 743238856Smm while ((r = archive_read_data_block(in_a, &buff, 744238856Smm &bytes_read, &offset)) == ARCHIVE_OK) { 745228753Smm if (need_report()) 746228753Smm report_write(bsdtar, a, entry, progress); 747228753Smm 748238856Smm if (offset > progress) { 749238856Smm int64_t sparse = offset - progress; 750238856Smm size_t ns; 751238856Smm 752238856Smm if (null_buff == NULL) { 753238856Smm null_buff = bsdtar->buff; 754238856Smm memset(null_buff, 0, bsdtar->buff_size); 755238856Smm } 756238856Smm 757238856Smm while (sparse > 0) { 758238856Smm if (sparse > (int64_t)bsdtar->buff_size) 759238856Smm ns = bsdtar->buff_size; 760238856Smm else 761238856Smm ns = (size_t)sparse; 762238856Smm bytes_written = 763238856Smm archive_write_data(a, null_buff, ns); 764238856Smm if (bytes_written < 0) { 765238856Smm /* Write failed; this is bad */ 766238856Smm lafe_warnc(0, "%s", 767238856Smm archive_error_string(a)); 768238856Smm return (-1); 769238856Smm } 770238856Smm if ((size_t)bytes_written < ns) { 771238856Smm /* Write was truncated; warn but 772238856Smm * continue. */ 773238856Smm lafe_warnc(0, 774238856Smm "%s: Truncated write; file may " 775238856Smm "have grown while being archived.", 776238856Smm archive_entry_pathname(entry)); 777238856Smm return (0); 778238856Smm } 779238856Smm progress += bytes_written; 780238856Smm sparse -= bytes_written; 781238856Smm } 782238856Smm } 783238856Smm 784238856Smm bytes_written = archive_write_data(a, buff, bytes_read); 785238856Smm if (bytes_written < 0) { 786238856Smm /* Write failed; this is bad */ 787228753Smm lafe_warnc(0, "%s", archive_error_string(a)); 788228753Smm return (-1); 789228753Smm } 790238856Smm if ((size_t)bytes_written < bytes_read) { 791238856Smm /* Write was truncated; warn but continue. */ 792238856Smm lafe_warnc(0, 793238856Smm "%s: Truncated write; file may have grown " 794238856Smm "while being archived.", 795238856Smm archive_entry_pathname(entry)); 796238856Smm return (0); 797238856Smm } 798228753Smm progress += bytes_written; 799228753Smm } 800238856Smm if (r < ARCHIVE_WARN) { 801238856Smm lafe_warnc(archive_errno(a), "%s", archive_error_string(a)); 802238856Smm return (-1); 803238856Smm } 804228753Smm return (0); 805228753Smm} 806228753Smm 807238856Smmstatic void 808238856Smmexcluded_callback(struct archive *a, void *_data, struct archive_entry *entry) 809238856Smm{ 810238856Smm struct bsdtar *bsdtar = (struct bsdtar *)_data; 811238856Smm 812315432Smm if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) 813238856Smm return; 814238856Smm if (!archive_read_disk_can_descend(a)) 815238856Smm return; 816315432Smm if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && 817238856Smm !yes("add '%s'", archive_entry_pathname(entry))) 818238856Smm return; 819238856Smm archive_read_disk_descend(a); 820238856Smm} 821238856Smm 822238856Smmstatic int 823238856Smmmetadata_filter(struct archive *a, void *_data, struct archive_entry *entry) 824238856Smm{ 825238856Smm struct bsdtar *bsdtar = (struct bsdtar *)_data; 826238856Smm 827238856Smm /* XXX TODO: check whether this filesystem is 828238856Smm * synthetic and/or local. Add a new 829238856Smm * --local-only option to skip non-local 830238856Smm * filesystems. Skip synthetic filesystems 831238856Smm * regardless. 832238856Smm * 833238856Smm * The results should be cached, since 834238856Smm * tree.c doesn't usually visit a directory 835238856Smm * and the directory contents together. A simple 836238856Smm * move-to-front list should perform quite well. 837238856Smm * 838238856Smm * Use archive_read_disk_current_filesystem_is_remote(). 839238856Smm */ 840238856Smm 841238856Smm /* 842238856Smm * If the user vetoes this file/directory, skip it. 843238856Smm * We want this to be fairly late; if some other 844238856Smm * check would veto this file, we shouldn't bother 845238856Smm * the user with it. 846238856Smm */ 847315432Smm if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && 848238856Smm !yes("add '%s'", archive_entry_pathname(entry))) 849238856Smm return (0); 850238856Smm 851238856Smm /* Note: if user vetoes, we won't descend. */ 852315432Smm if (((bsdtar->flags & OPTFLAG_NO_SUBDIRS) == 0) && 853315432Smm archive_read_disk_can_descend(a)) 854238856Smm archive_read_disk_descend(a); 855238856Smm 856238856Smm return (1); 857238856Smm} 858238856Smm 859228753Smm/* 860228753Smm * Add the file or dir hierarchy named by 'path' to the archive 861228753Smm */ 862228753Smmstatic void 863228753Smmwrite_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) 864228753Smm{ 865238856Smm struct archive *disk = bsdtar->diskreader; 866228753Smm struct archive_entry *entry = NULL, *spare_entry = NULL; 867238856Smm int r; 868228753Smm 869238856Smm r = archive_read_disk_open(disk, path); 870238856Smm if (r != ARCHIVE_OK) { 871238856Smm lafe_warnc(archive_errno(disk), 872238856Smm "%s", archive_error_string(disk)); 873228753Smm bsdtar->return_value = 1; 874228753Smm return; 875228753Smm } 876238856Smm bsdtar->first_fs = -1; 877228753Smm 878238856Smm for (;;) { 879238856Smm archive_entry_free(entry); 880238856Smm entry = archive_entry_new(); 881238856Smm r = archive_read_next_header2(disk, entry); 882238856Smm if (r == ARCHIVE_EOF) 883228753Smm break; 884238856Smm else if (r != ARCHIVE_OK) { 885238856Smm lafe_warnc(archive_errno(disk), 886238856Smm "%s", archive_error_string(disk)); 887302075Smm if (r == ARCHIVE_FATAL || r == ARCHIVE_FAILED) { 888238856Smm bsdtar->return_value = 1; 889307138Smm archive_entry_free(entry); 890307138Smm archive_read_close(disk); 891238856Smm return; 892238856Smm } else if (r < ARCHIVE_WARN) 893228776Smm continue; 894228776Smm } 895228753Smm 896228753Smm if (bsdtar->uid >= 0) { 897228753Smm archive_entry_set_uid(entry, bsdtar->uid); 898228753Smm if (!bsdtar->uname) 899228753Smm archive_entry_set_uname(entry, 900228753Smm archive_read_disk_uname(bsdtar->diskreader, 901228753Smm bsdtar->uid)); 902228753Smm } 903228753Smm if (bsdtar->gid >= 0) { 904228753Smm archive_entry_set_gid(entry, bsdtar->gid); 905228753Smm if (!bsdtar->gname) 906228753Smm archive_entry_set_gname(entry, 907228753Smm archive_read_disk_gname(bsdtar->diskreader, 908228753Smm bsdtar->gid)); 909228753Smm } 910228753Smm if (bsdtar->uname) 911228753Smm archive_entry_set_uname(entry, bsdtar->uname); 912228753Smm if (bsdtar->gname) 913228753Smm archive_entry_set_gname(entry, bsdtar->gname); 914228753Smm 915228753Smm /* 916228753Smm * Rewrite the pathname to be archived. If rewrite 917228753Smm * fails, skip the entry. 918228753Smm */ 919228753Smm if (edit_pathname(bsdtar, entry)) 920228753Smm continue; 921228753Smm 922299529Smm /* Display entry as we process it. */ 923299529Smm if (bsdtar->verbose > 1) { 924299529Smm safe_fprintf(stderr, "a "); 925299529Smm list_item_verbose(bsdtar, stderr, entry); 926299529Smm } else if (bsdtar->verbose > 0) { 927299529Smm /* This format is required by SUSv2. */ 928228753Smm safe_fprintf(stderr, "a %s", 929228753Smm archive_entry_pathname(entry)); 930299529Smm } 931228753Smm 932228753Smm /* Non-regular files get archived with zero size. */ 933228753Smm if (archive_entry_filetype(entry) != AE_IFREG) 934228753Smm archive_entry_set_size(entry, 0); 935228753Smm 936228753Smm archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); 937228753Smm 938228753Smm while (entry != NULL) { 939232153Smm write_file(bsdtar, a, entry); 940228753Smm archive_entry_free(entry); 941228753Smm entry = spare_entry; 942228753Smm spare_entry = NULL; 943228753Smm } 944228753Smm 945228753Smm if (bsdtar->verbose) 946228753Smm fprintf(stderr, "\n"); 947228753Smm } 948228753Smm archive_entry_free(entry); 949238856Smm archive_read_close(disk); 950228753Smm} 951228753Smm 952228753Smm/* 953232153Smm * Write a single file (or directory or other filesystem object) to 954232153Smm * the archive. 955228753Smm */ 956228753Smmstatic void 957232153Smmwrite_file(struct bsdtar *bsdtar, struct archive *a, 958228753Smm struct archive_entry *entry) 959228753Smm{ 960232153Smm write_entry(bsdtar, a, entry); 961232153Smm} 962232153Smm 963232153Smm/* 964232153Smm * Write a single entry to the archive. 965232153Smm */ 966232153Smmstatic void 967232153Smmwrite_entry(struct bsdtar *bsdtar, struct archive *a, 968232153Smm struct archive_entry *entry) 969232153Smm{ 970228753Smm int e; 971228753Smm 972228753Smm e = archive_write_header(a, entry); 973228753Smm if (e != ARCHIVE_OK) { 974299529Smm if (bsdtar->verbose > 1) { 975299529Smm safe_fprintf(stderr, "a "); 976299529Smm list_item_verbose(bsdtar, stderr, entry); 977299529Smm lafe_warnc(0, ": %s", archive_error_string(a)); 978299529Smm } else if (bsdtar->verbose > 0) { 979228753Smm lafe_warnc(0, "%s: %s", 980228753Smm archive_entry_pathname(entry), 981228753Smm archive_error_string(a)); 982299529Smm } else 983228753Smm fprintf(stderr, ": %s", archive_error_string(a)); 984228753Smm } 985228753Smm 986228753Smm if (e == ARCHIVE_FATAL) 987228753Smm exit(1); 988228753Smm 989228753Smm /* 990228753Smm * If we opened a file earlier, write it out now. Note that 991228753Smm * the format handler might have reset the size field to zero 992228753Smm * to inform us that the archive body won't get stored. In 993228753Smm * that case, just skip the write. 994228753Smm */ 995238856Smm if (e >= ARCHIVE_WARN && archive_entry_size(entry) > 0) { 996238856Smm if (copy_file_data_block(bsdtar, a, bsdtar->diskreader, entry)) 997228753Smm exit(1); 998228753Smm } 999228753Smm} 1000228753Smm 1001228753Smmstatic void 1002228753Smmreport_write(struct bsdtar *bsdtar, struct archive *a, 1003228753Smm struct archive_entry *entry, int64_t progress) 1004228753Smm{ 1005228753Smm uint64_t comp, uncomp; 1006228776Smm int compression; 1007228776Smm 1008228753Smm if (bsdtar->verbose) 1009228753Smm fprintf(stderr, "\n"); 1010248616Smm comp = archive_filter_bytes(a, -1); 1011248616Smm uncomp = archive_filter_bytes(a, 0); 1012228753Smm fprintf(stderr, "In: %d files, %s bytes;", 1013228753Smm archive_file_count(a), tar_i64toa(uncomp)); 1014315432Smm if (comp >= uncomp) 1015228776Smm compression = 0; 1016228776Smm else 1017228776Smm compression = (int)((uncomp - comp) * 100 / uncomp); 1018228753Smm fprintf(stderr, 1019228753Smm " Out: %s bytes, compression %d%%\n", 1020228776Smm tar_i64toa(comp), compression); 1021228753Smm /* Can't have two calls to tar_i64toa() pending, so split the output. */ 1022228753Smm safe_fprintf(stderr, "Current: %s (%s", 1023228753Smm archive_entry_pathname(entry), 1024228753Smm tar_i64toa(progress)); 1025228753Smm fprintf(stderr, "/%s bytes)\n", 1026228753Smm tar_i64toa(archive_entry_size(entry))); 1027228753Smm} 1028228753Smm 1029228753Smmstatic void 1030228753Smmtest_for_append(struct bsdtar *bsdtar) 1031228753Smm{ 1032228753Smm struct stat s; 1033228753Smm 1034228753Smm if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) 1035228753Smm lafe_errc(1, 0, "no files or directories specified"); 1036228753Smm if (bsdtar->filename == NULL) 1037228753Smm lafe_errc(1, 0, "Cannot append to stdout."); 1038228753Smm 1039228753Smm if (stat(bsdtar->filename, &s) != 0) 1040228753Smm return; 1041228753Smm 1042228753Smm if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) 1043228753Smm lafe_errc(1, 0, 1044228753Smm "Cannot append to %s: not a regular file.", 1045228753Smm bsdtar->filename); 1046228753Smm 1047228753Smm/* Is this an appropriate check here on Windows? */ 1048228753Smm/* 1049228753Smm if (GetFileType(handle) != FILE_TYPE_DISK) 1050228753Smm lafe_errc(1, 0, "Cannot append"); 1051228753Smm*/ 1052228753Smm 1053228753Smm} 1054