write.c revision 315432
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 315432 2017-03-16 23:07:35Z 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 { 506228753Smm if (*arg != '/' && (arg[0] != '@' || arg[1] != '/')) 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; 543238856Smm archive_entry_free(entry); 544238856Smm continue; 545238856Smm } 546238856Smm 547238856Smm /* 548238856Smm * Invoke archive_read_next_header2() to work 549238856Smm * archive_read_data_block(), which is called via write_file(), 550238856Smm * without failure. 551238856Smm */ 552238856Smm entry2 = archive_entry_new(); 553238856Smm r = archive_read_next_header2(disk, entry2); 554238856Smm archive_entry_free(entry2); 555238856Smm if (r != ARCHIVE_OK) { 556238856Smm lafe_warnc(archive_errno(disk), 557238856Smm "%s", archive_error_string(disk)); 558238856Smm if (r == ARCHIVE_FATAL) 559238856Smm bsdtar->return_value = 1; 560238856Smm else 561238856Smm archive_read_close(disk); 562238856Smm archive_entry_free(entry); 563238856Smm continue; 564238856Smm } 565238856Smm 566232153Smm write_file(bsdtar, a, entry); 567228753Smm archive_entry_free(entry); 568238856Smm archive_read_close(disk); 569228753Smm entry = NULL; 570228753Smm archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); 571228753Smm } 572228753Smm 573228753Smm if (archive_write_close(a)) { 574228753Smm lafe_warnc(0, "%s", archive_error_string(a)); 575228753Smm bsdtar->return_value = 1; 576228753Smm } 577228753Smm 578228753Smmcleanup: 579228753Smm /* Free file data buffer. */ 580228753Smm free(bsdtar->buff); 581228753Smm archive_entry_linkresolver_free(bsdtar->resolver); 582228753Smm bsdtar->resolver = NULL; 583232153Smm archive_read_free(bsdtar->diskreader); 584228753Smm bsdtar->diskreader = NULL; 585228753Smm 586315432Smm if (bsdtar->flags & OPTFLAG_TOTALS) { 587228753Smm fprintf(stderr, "Total bytes written: %s\n", 588248616Smm tar_i64toa(archive_filter_bytes(a, -1))); 589228753Smm } 590228753Smm 591232153Smm archive_write_free(a); 592228753Smm} 593228753Smm 594228753Smm/* 595228753Smm * Archive names specified in file. 596228753Smm * 597228753Smm * Unless --null was specified, a line containing exactly "-C" will 598228753Smm * cause the next line to be a directory to pass to chdir(). If 599228753Smm * --null is specified, then a line "-C" is just another filename. 600228753Smm */ 601228753Smmstatic void 602228753Smmarchive_names_from_file(struct bsdtar *bsdtar, struct archive *a) 603228753Smm{ 604228753Smm struct lafe_line_reader *lr; 605228753Smm const char *line; 606228753Smm 607228753Smm bsdtar->next_line_is_dir = 0; 608228753Smm 609315432Smm lr = lafe_line_reader(bsdtar->names_from_file, 610315432Smm (bsdtar->flags & OPTFLAG_NULL)); 611228753Smm while ((line = lafe_line_reader_next(lr)) != NULL) { 612228753Smm if (bsdtar->next_line_is_dir) { 613232153Smm if (*line != '\0') 614232153Smm set_chdir(bsdtar, line); 615232153Smm else { 616232153Smm lafe_warnc(0, 617232153Smm "Meaningless argument for -C: ''"); 618232153Smm bsdtar->return_value = 1; 619232153Smm } 620228753Smm bsdtar->next_line_is_dir = 0; 621315432Smm } else if (((bsdtar->flags & OPTFLAG_NULL) == 0) && 622315432Smm strcmp(line, "-C") == 0) 623228753Smm bsdtar->next_line_is_dir = 1; 624228753Smm else { 625228753Smm if (*line != '/') 626228753Smm do_chdir(bsdtar); /* Handle a deferred -C */ 627228753Smm write_hierarchy(bsdtar, a, line); 628228753Smm } 629228753Smm } 630228753Smm lafe_line_reader_free(lr); 631228753Smm if (bsdtar->next_line_is_dir) 632228753Smm lafe_errc(1, errno, 633228753Smm "Unexpected end of filename list; " 634228753Smm "directory expected after -C"); 635228753Smm} 636228753Smm 637228753Smm/* 638228753Smm * Copy from specified archive to current archive. Returns non-zero 639228753Smm * for write errors (which force us to terminate the entire archiving 640228753Smm * operation). If there are errors reading the input archive, we set 641228753Smm * bsdtar->return_value but return zero, so the overall archiving 642228753Smm * operation will complete and return non-zero. 643228753Smm */ 644228753Smmstatic int 645228753Smmappend_archive_filename(struct bsdtar *bsdtar, struct archive *a, 646232153Smm const char *raw_filename) 647228753Smm{ 648228753Smm struct archive *ina; 649232153Smm const char *filename = raw_filename; 650228753Smm int rc; 651228753Smm 652228753Smm if (strcmp(filename, "-") == 0) 653228753Smm filename = NULL; /* Library uses NULL for stdio. */ 654228753Smm 655228753Smm ina = archive_read_new(); 656228753Smm archive_read_support_format_all(ina); 657232153Smm archive_read_support_filter_all(ina); 658299529Smm set_reader_options(bsdtar, ina); 659299529Smm archive_read_set_options(ina, "mtree:checkfs"); 660299529Smm if (bsdtar->passphrase != NULL) 661299529Smm rc = archive_read_add_passphrase(a, bsdtar->passphrase); 662299529Smm else 663299529Smm rc = archive_read_set_passphrase_callback(ina, bsdtar, 664299529Smm &passphrase_callback); 665299529Smm if (rc != ARCHIVE_OK) 666299529Smm lafe_errc(1, 0, "%s", archive_error_string(a)); 667248616Smm if (archive_read_open_filename(ina, filename, 668248616Smm bsdtar->bytes_per_block)) { 669228753Smm lafe_warnc(0, "%s", archive_error_string(ina)); 670228753Smm bsdtar->return_value = 1; 671228753Smm return (0); 672228753Smm } 673228753Smm 674228753Smm rc = append_archive(bsdtar, a, ina); 675228753Smm 676228753Smm if (rc != ARCHIVE_OK) { 677228753Smm lafe_warnc(0, "Error reading archive %s: %s", 678232153Smm raw_filename, archive_error_string(ina)); 679228753Smm bsdtar->return_value = 1; 680228753Smm } 681232153Smm archive_read_free(ina); 682228753Smm 683228753Smm return (rc); 684228753Smm} 685228753Smm 686228753Smmstatic int 687228753Smmappend_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) 688228753Smm{ 689228753Smm struct archive_entry *in_entry; 690228753Smm int e; 691228753Smm 692228753Smm while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) { 693238856Smm if (archive_match_excluded(bsdtar->matching, in_entry)) 694228753Smm continue; 695315432Smm if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && 696228753Smm !yes("copy '%s'", archive_entry_pathname(in_entry))) 697228753Smm continue; 698299529Smm if (bsdtar->verbose > 1) { 699299529Smm safe_fprintf(stderr, "a "); 700299529Smm list_item_verbose(bsdtar, stderr, in_entry); 701299529Smm } else if (bsdtar->verbose > 0) 702228753Smm safe_fprintf(stderr, "a %s", 703228753Smm archive_entry_pathname(in_entry)); 704228753Smm if (need_report()) 705228753Smm report_write(bsdtar, a, in_entry, 0); 706228753Smm 707228753Smm e = archive_write_header(a, in_entry); 708228753Smm if (e != ARCHIVE_OK) { 709228753Smm if (!bsdtar->verbose) 710228753Smm lafe_warnc(0, "%s: %s", 711228753Smm archive_entry_pathname(in_entry), 712228753Smm archive_error_string(a)); 713228753Smm else 714228753Smm fprintf(stderr, ": %s", archive_error_string(a)); 715228753Smm } 716228753Smm if (e == ARCHIVE_FATAL) 717228753Smm exit(1); 718228753Smm 719228753Smm if (e >= ARCHIVE_WARN) { 720228753Smm if (archive_entry_size(in_entry) == 0) 721228753Smm archive_read_data_skip(ina); 722238856Smm else if (copy_file_data_block(bsdtar, a, ina, in_entry)) 723228753Smm exit(1); 724228753Smm } 725228753Smm 726228753Smm if (bsdtar->verbose) 727228753Smm fprintf(stderr, "\n"); 728228753Smm } 729228753Smm 730228753Smm return (e == ARCHIVE_EOF ? ARCHIVE_OK : e); 731228753Smm} 732228753Smm 733238856Smm/* Helper function to copy file to archive. */ 734228753Smmstatic int 735238856Smmcopy_file_data_block(struct bsdtar *bsdtar, struct archive *a, 736238856Smm struct archive *in_a, struct archive_entry *entry) 737228753Smm{ 738238856Smm size_t bytes_read; 739228753Smm ssize_t bytes_written; 740238856Smm int64_t offset, progress = 0; 741238856Smm char *null_buff = NULL; 742238856Smm const void *buff; 743238856Smm int r; 744228753Smm 745238856Smm while ((r = archive_read_data_block(in_a, &buff, 746238856Smm &bytes_read, &offset)) == ARCHIVE_OK) { 747228753Smm if (need_report()) 748228753Smm report_write(bsdtar, a, entry, progress); 749228753Smm 750238856Smm if (offset > progress) { 751238856Smm int64_t sparse = offset - progress; 752238856Smm size_t ns; 753238856Smm 754238856Smm if (null_buff == NULL) { 755238856Smm null_buff = bsdtar->buff; 756238856Smm memset(null_buff, 0, bsdtar->buff_size); 757238856Smm } 758238856Smm 759238856Smm while (sparse > 0) { 760238856Smm if (sparse > (int64_t)bsdtar->buff_size) 761238856Smm ns = bsdtar->buff_size; 762238856Smm else 763238856Smm ns = (size_t)sparse; 764238856Smm bytes_written = 765238856Smm archive_write_data(a, null_buff, ns); 766238856Smm if (bytes_written < 0) { 767238856Smm /* Write failed; this is bad */ 768238856Smm lafe_warnc(0, "%s", 769238856Smm archive_error_string(a)); 770238856Smm return (-1); 771238856Smm } 772238856Smm if ((size_t)bytes_written < ns) { 773238856Smm /* Write was truncated; warn but 774238856Smm * continue. */ 775238856Smm lafe_warnc(0, 776238856Smm "%s: Truncated write; file may " 777238856Smm "have grown while being archived.", 778238856Smm archive_entry_pathname(entry)); 779238856Smm return (0); 780238856Smm } 781238856Smm progress += bytes_written; 782238856Smm sparse -= bytes_written; 783238856Smm } 784238856Smm } 785238856Smm 786238856Smm bytes_written = archive_write_data(a, buff, bytes_read); 787238856Smm if (bytes_written < 0) { 788238856Smm /* Write failed; this is bad */ 789228753Smm lafe_warnc(0, "%s", archive_error_string(a)); 790228753Smm return (-1); 791228753Smm } 792238856Smm if ((size_t)bytes_written < bytes_read) { 793238856Smm /* Write was truncated; warn but continue. */ 794238856Smm lafe_warnc(0, 795238856Smm "%s: Truncated write; file may have grown " 796238856Smm "while being archived.", 797238856Smm archive_entry_pathname(entry)); 798238856Smm return (0); 799238856Smm } 800228753Smm progress += bytes_written; 801228753Smm } 802238856Smm if (r < ARCHIVE_WARN) { 803238856Smm lafe_warnc(archive_errno(a), "%s", archive_error_string(a)); 804238856Smm return (-1); 805238856Smm } 806228753Smm return (0); 807228753Smm} 808228753Smm 809238856Smmstatic void 810238856Smmexcluded_callback(struct archive *a, void *_data, struct archive_entry *entry) 811238856Smm{ 812238856Smm struct bsdtar *bsdtar = (struct bsdtar *)_data; 813238856Smm 814315432Smm if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) 815238856Smm return; 816238856Smm if (!archive_read_disk_can_descend(a)) 817238856Smm return; 818315432Smm if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && 819238856Smm !yes("add '%s'", archive_entry_pathname(entry))) 820238856Smm return; 821238856Smm archive_read_disk_descend(a); 822238856Smm} 823238856Smm 824238856Smmstatic int 825238856Smmmetadata_filter(struct archive *a, void *_data, struct archive_entry *entry) 826238856Smm{ 827238856Smm struct bsdtar *bsdtar = (struct bsdtar *)_data; 828238856Smm 829238856Smm /* XXX TODO: check whether this filesystem is 830238856Smm * synthetic and/or local. Add a new 831238856Smm * --local-only option to skip non-local 832238856Smm * filesystems. Skip synthetic filesystems 833238856Smm * regardless. 834238856Smm * 835238856Smm * The results should be cached, since 836238856Smm * tree.c doesn't usually visit a directory 837238856Smm * and the directory contents together. A simple 838238856Smm * move-to-front list should perform quite well. 839238856Smm * 840238856Smm * Use archive_read_disk_current_filesystem_is_remote(). 841238856Smm */ 842238856Smm 843238856Smm /* 844238856Smm * If the user vetoes this file/directory, skip it. 845238856Smm * We want this to be fairly late; if some other 846238856Smm * check would veto this file, we shouldn't bother 847238856Smm * the user with it. 848238856Smm */ 849315432Smm if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && 850238856Smm !yes("add '%s'", archive_entry_pathname(entry))) 851238856Smm return (0); 852238856Smm 853238856Smm /* Note: if user vetoes, we won't descend. */ 854315432Smm if (((bsdtar->flags & OPTFLAG_NO_SUBDIRS) == 0) && 855315432Smm archive_read_disk_can_descend(a)) 856238856Smm archive_read_disk_descend(a); 857238856Smm 858238856Smm return (1); 859238856Smm} 860238856Smm 861228753Smm/* 862228753Smm * Add the file or dir hierarchy named by 'path' to the archive 863228753Smm */ 864228753Smmstatic void 865228753Smmwrite_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) 866228753Smm{ 867238856Smm struct archive *disk = bsdtar->diskreader; 868228753Smm struct archive_entry *entry = NULL, *spare_entry = NULL; 869238856Smm int r; 870228753Smm 871238856Smm r = archive_read_disk_open(disk, path); 872238856Smm if (r != ARCHIVE_OK) { 873238856Smm lafe_warnc(archive_errno(disk), 874238856Smm "%s", archive_error_string(disk)); 875228753Smm bsdtar->return_value = 1; 876228753Smm return; 877228753Smm } 878238856Smm bsdtar->first_fs = -1; 879228753Smm 880238856Smm for (;;) { 881238856Smm archive_entry_free(entry); 882238856Smm entry = archive_entry_new(); 883238856Smm r = archive_read_next_header2(disk, entry); 884238856Smm if (r == ARCHIVE_EOF) 885228753Smm break; 886238856Smm else if (r != ARCHIVE_OK) { 887238856Smm lafe_warnc(archive_errno(disk), 888238856Smm "%s", archive_error_string(disk)); 889302075Smm if (r == ARCHIVE_FATAL || r == ARCHIVE_FAILED) { 890238856Smm bsdtar->return_value = 1; 891307138Smm archive_entry_free(entry); 892307138Smm archive_read_close(disk); 893238856Smm return; 894238856Smm } else if (r < ARCHIVE_WARN) 895228776Smm continue; 896228776Smm } 897228753Smm 898228753Smm if (bsdtar->uid >= 0) { 899228753Smm archive_entry_set_uid(entry, bsdtar->uid); 900228753Smm if (!bsdtar->uname) 901228753Smm archive_entry_set_uname(entry, 902228753Smm archive_read_disk_uname(bsdtar->diskreader, 903228753Smm bsdtar->uid)); 904228753Smm } 905228753Smm if (bsdtar->gid >= 0) { 906228753Smm archive_entry_set_gid(entry, bsdtar->gid); 907228753Smm if (!bsdtar->gname) 908228753Smm archive_entry_set_gname(entry, 909228753Smm archive_read_disk_gname(bsdtar->diskreader, 910228753Smm bsdtar->gid)); 911228753Smm } 912228753Smm if (bsdtar->uname) 913228753Smm archive_entry_set_uname(entry, bsdtar->uname); 914228753Smm if (bsdtar->gname) 915228753Smm archive_entry_set_gname(entry, bsdtar->gname); 916228753Smm 917228753Smm /* 918228753Smm * Rewrite the pathname to be archived. If rewrite 919228753Smm * fails, skip the entry. 920228753Smm */ 921228753Smm if (edit_pathname(bsdtar, entry)) 922228753Smm continue; 923228753Smm 924299529Smm /* Display entry as we process it. */ 925299529Smm if (bsdtar->verbose > 1) { 926299529Smm safe_fprintf(stderr, "a "); 927299529Smm list_item_verbose(bsdtar, stderr, entry); 928299529Smm } else if (bsdtar->verbose > 0) { 929299529Smm /* This format is required by SUSv2. */ 930228753Smm safe_fprintf(stderr, "a %s", 931228753Smm archive_entry_pathname(entry)); 932299529Smm } 933228753Smm 934228753Smm /* Non-regular files get archived with zero size. */ 935228753Smm if (archive_entry_filetype(entry) != AE_IFREG) 936228753Smm archive_entry_set_size(entry, 0); 937228753Smm 938228753Smm archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); 939228753Smm 940228753Smm while (entry != NULL) { 941232153Smm write_file(bsdtar, a, entry); 942228753Smm archive_entry_free(entry); 943228753Smm entry = spare_entry; 944228753Smm spare_entry = NULL; 945228753Smm } 946228753Smm 947228753Smm if (bsdtar->verbose) 948228753Smm fprintf(stderr, "\n"); 949228753Smm } 950228753Smm archive_entry_free(entry); 951238856Smm archive_read_close(disk); 952228753Smm} 953228753Smm 954228753Smm/* 955232153Smm * Write a single file (or directory or other filesystem object) to 956232153Smm * the archive. 957228753Smm */ 958228753Smmstatic void 959232153Smmwrite_file(struct bsdtar *bsdtar, struct archive *a, 960228753Smm struct archive_entry *entry) 961228753Smm{ 962232153Smm write_entry(bsdtar, a, entry); 963232153Smm} 964232153Smm 965232153Smm/* 966232153Smm * Write a single entry to the archive. 967232153Smm */ 968232153Smmstatic void 969232153Smmwrite_entry(struct bsdtar *bsdtar, struct archive *a, 970232153Smm struct archive_entry *entry) 971232153Smm{ 972228753Smm int e; 973228753Smm 974228753Smm e = archive_write_header(a, entry); 975228753Smm if (e != ARCHIVE_OK) { 976299529Smm if (bsdtar->verbose > 1) { 977299529Smm safe_fprintf(stderr, "a "); 978299529Smm list_item_verbose(bsdtar, stderr, entry); 979299529Smm lafe_warnc(0, ": %s", archive_error_string(a)); 980299529Smm } else if (bsdtar->verbose > 0) { 981228753Smm lafe_warnc(0, "%s: %s", 982228753Smm archive_entry_pathname(entry), 983228753Smm archive_error_string(a)); 984299529Smm } else 985228753Smm fprintf(stderr, ": %s", archive_error_string(a)); 986228753Smm } 987228753Smm 988228753Smm if (e == ARCHIVE_FATAL) 989228753Smm exit(1); 990228753Smm 991228753Smm /* 992228753Smm * If we opened a file earlier, write it out now. Note that 993228753Smm * the format handler might have reset the size field to zero 994228753Smm * to inform us that the archive body won't get stored. In 995228753Smm * that case, just skip the write. 996228753Smm */ 997238856Smm if (e >= ARCHIVE_WARN && archive_entry_size(entry) > 0) { 998238856Smm if (copy_file_data_block(bsdtar, a, bsdtar->diskreader, entry)) 999228753Smm exit(1); 1000228753Smm } 1001228753Smm} 1002228753Smm 1003228753Smmstatic void 1004228753Smmreport_write(struct bsdtar *bsdtar, struct archive *a, 1005228753Smm struct archive_entry *entry, int64_t progress) 1006228753Smm{ 1007228753Smm uint64_t comp, uncomp; 1008228776Smm int compression; 1009228776Smm 1010228753Smm if (bsdtar->verbose) 1011228753Smm fprintf(stderr, "\n"); 1012248616Smm comp = archive_filter_bytes(a, -1); 1013248616Smm uncomp = archive_filter_bytes(a, 0); 1014228753Smm fprintf(stderr, "In: %d files, %s bytes;", 1015228753Smm archive_file_count(a), tar_i64toa(uncomp)); 1016315432Smm if (comp >= uncomp) 1017228776Smm compression = 0; 1018228776Smm else 1019228776Smm compression = (int)((uncomp - comp) * 100 / uncomp); 1020228753Smm fprintf(stderr, 1021228753Smm " Out: %s bytes, compression %d%%\n", 1022228776Smm tar_i64toa(comp), compression); 1023228753Smm /* Can't have two calls to tar_i64toa() pending, so split the output. */ 1024228753Smm safe_fprintf(stderr, "Current: %s (%s", 1025228753Smm archive_entry_pathname(entry), 1026228753Smm tar_i64toa(progress)); 1027228753Smm fprintf(stderr, "/%s bytes)\n", 1028228753Smm tar_i64toa(archive_entry_size(entry))); 1029228753Smm} 1030228753Smm 1031228753Smmstatic void 1032228753Smmtest_for_append(struct bsdtar *bsdtar) 1033228753Smm{ 1034228753Smm struct stat s; 1035228753Smm 1036228753Smm if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) 1037228753Smm lafe_errc(1, 0, "no files or directories specified"); 1038228753Smm if (bsdtar->filename == NULL) 1039228753Smm lafe_errc(1, 0, "Cannot append to stdout."); 1040228753Smm 1041228753Smm if (stat(bsdtar->filename, &s) != 0) 1042228753Smm return; 1043228753Smm 1044228753Smm if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) 1045228753Smm lafe_errc(1, 0, 1046228753Smm "Cannot append to %s: not a regular file.", 1047228753Smm bsdtar->filename); 1048228753Smm 1049228753Smm/* Is this an appropriate check here on Windows? */ 1050228753Smm/* 1051228753Smm if (GetFileType(handle) != FILE_TYPE_DISK) 1052228753Smm lafe_errc(1, 0, "Cannot append"); 1053228753Smm*/ 1054228753Smm 1055228753Smm} 1056