/* * Open the archive using the current settings. */ int archive_write_open(struct archive *_a, void *client_data, archive_open_callback *opener, archive_write_callback *writer, archive_close_callback *closer) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *client_filter; int ret, r1; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open"); archive_clear_error(&a->archive); a->client_writer = writer; a->client_opener = opener; a->client_closer = closer; a->client_data = client_data; client_filter = __archive_write_allocate_filter(_a); client_filter->open = archive_write_client_open; client_filter->write = archive_write_client_write; client_filter->close = archive_write_client_close; ret = __archive_write_open_filter(a->filter_first); if (ret < ARCHIVE_WARN) { r1 = __archive_write_close_filter(a->filter_first); return (r1 < ret ? r1 : ret); } a->archive.state = ARCHIVE_STATE_HEADER; if (a->format_init) ret = (a->format_init)(a); return (ret); }
static void set_writer_options(struct bsdtar *bsdtar, struct archive *a) { const char *writer_options; int r; writer_options = getenv(ENV_WRITER_OPTIONS); if (writer_options != NULL) { char *p; /* Set default write options. */ p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + strlen(writer_options) + 1); if (p == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or filters which are not added to * the archive write object. */ strncpy(p, IGNORE_WRONG_MODULE_NAME, sizeof(IGNORE_WRONG_MODULE_NAME) -1); strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, writer_options); r = archive_write_set_options(a, p); free(p); if (r < ARCHIVE_WARN) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); }
/* * Note that the compressor is responsible for blocking. */ static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t s) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); archive_clear_error(&a->archive); return ((a->format_write_data)(a, buff, s)); }
int archive_read_open2(struct archive *_a, void *client_data, archive_open_callback *client_opener, archive_read_callback *client_reader, archive_skip_callback *client_skipper, archive_close_callback *client_closer) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter; int e; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); archive_clear_error(&a->archive); if (client_reader == NULL) __archive_errx(1, "No reader function provided to archive_read_open"); /* Open data source. */ if (client_opener != NULL) { e =(client_opener)(&a->archive, client_data); if (e != 0) { /* If the open failed, call the closer to clean up. */ if (client_closer) (client_closer)(&a->archive, client_data); return (e); } } /* Save the client functions and mock up the initial source. */ a->client.reader = client_reader; a->client.skipper = client_skipper; a->client.closer = client_closer; filter = calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = NULL; filter->upstream = NULL; filter->archive = a; filter->data = client_data; filter->read = client_read_proxy; filter->skip = client_skip_proxy; filter->close = client_close_proxy; filter->name = "none"; filter->code = ARCHIVE_COMPRESSION_NONE; a->filter = filter; /* Build out the input pipeline. */ e = build_stream(a); if (e == ARCHIVE_OK) a->archive.state = ARCHIVE_STATE_HEADER; return (e); }
int archive_read_support_format_all(struct archive *a) { archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_all"); /* TODO: It would be nice to compute the ordering * here automatically so that people who enable just * a few formats can still get the benefits. That * may just require the format registration to include * a "maximum read-ahead" value (anything that uses seek * would be essentially infinite read-ahead). The core * bid management can then sort the bidders before calling * them. * * If you implement the above, please return the list below * to alphabetic order. */ /* * These bidders are all pretty cheap; they just examine a * small initial part of the archive. If one of these bids * high, we can maybe avoid running any of the more expensive * bidders below. */ archive_read_support_format_ar(a); archive_read_support_format_cpio(a); archive_read_support_format_empty(a); archive_read_support_format_lha(a); archive_read_support_format_mtree(a); archive_read_support_format_tar(a); archive_read_support_format_xar(a); /* * Install expensive bidders last. By doing them last, we * increase the chance that a high bid from someone else will * make it unnecessary for these to do anything at all. */ /* These three have potentially large look-ahead. */ archive_read_support_format_7zip(a); archive_read_support_format_cab(a); archive_read_support_format_rar(a); archive_read_support_format_iso9660(a); /* Seek is really bad, since it forces the read-ahead * logic to discard buffered data. */ archive_read_support_format_zip(a); /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable * "as much as possible." Clients who need specific * compression should enable those individually so they can * verify the level of support. */ /* Clear any warning messages set by the above functions. */ archive_clear_error(a); return (ARCHIVE_OK); }
/* * Write the appropriate header. */ static int _archive_write_header(struct archive *_a, struct archive_entry *entry) { struct archive_write *a = (struct archive_write *)_a; int ret, r2; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header"); archive_clear_error(&a->archive); if (a->format_write_header == NULL) { archive_set_error(&(a->archive), -1, "Format must be set before you can write to an archive."); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* In particular, "retry" and "fatal" get returned immediately. */ ret = archive_write_finish_entry(&a->archive); if (ret == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN) return (ret); if (a->skip_file_set && archive_entry_dev_is_set(entry) && archive_entry_ino_is_set(entry) && archive_entry_dev(entry) == (dev_t)a->skip_file_dev && archive_entry_ino64(entry) == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Can't add archive to itself"); return (ARCHIVE_FAILED); } /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); if (r2 == ARCHIVE_FAILED) { return (ARCHIVE_FAILED); } if (r2 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } if (r2 < ret) ret = r2; a->archive.state = ARCHIVE_STATE_DATA; return (ret); }
/* * Note that the compressor is responsible for blocking. */ static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t s) { struct archive_write *a = (struct archive_write *)_a; const size_t max_write = INT_MAX; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); /* In particular, this catches attempts to pass negative values. */ if (s > max_write) s = max_write; archive_clear_error(&a->archive); return ((a->format_write_data)(a, buff, s)); }
/* * Set read options for the format and the filter. */ int archive_read_set_options(struct archive *_a, const char *s) { int r; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_set_options"); archive_clear_error(_a); r = archive_read_set_format_options(_a, s); if (r != ARCHIVE_OK) return (r); r = archive_read_set_filter_options(_a, s); if (r != ARCHIVE_OK) return (r); return (ARCHIVE_OK); }
int archive_read_open_fd(struct archive *a, int fd, size_t block_size) { struct stat st; struct read_fd_data *mine; void *b; archive_clear_error(a); if (fstat(fd, &st) != 0) { archive_set_error(a, errno, "Can't stat fd %d", fd); return (ARCHIVE_FATAL); } mine = (struct read_fd_data *)calloc(1, sizeof(*mine)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } mine->block_size = block_size; mine->buffer = b; mine->fd = fd; /* * Skip support is a performance optimization for anything * that supports lseek(). On FreeBSD, only regular files and * raw disk devices support lseek() and there's no portable * way to determine if a device is a raw disk device, so we * only enable this optimization for regular files. */ if (S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); mine->use_lseek = 1; } #if defined(__CYGWIN__) || defined(_WIN32) setmode(mine->fd, O_BINARY); #endif archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); archive_read_set_close_callback(a, file_close); archive_read_set_callback_data(a, mine); return (archive_read_open1(a)); }
int archive_read_support_filter_all(struct archive *a) { archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_filter_all"); /* Bzip falls back to "bunzip2" command-line */ archive_read_support_filter_bzip2(a); /* The decompress code doesn't use an outside library. */ archive_read_support_filter_compress(a); /* Gzip decompress falls back to "gzip -d" command-line. */ archive_read_support_filter_gzip(a); /* Lzip falls back to "unlzip" command-line program. */ archive_read_support_filter_lzip(a); /* The LZMA file format has a very weak signature, so it * may not be feasible to keep this here, but we'll try. * This will come back out if there are problems. */ /* Lzma falls back to "unlzma" command-line program. */ archive_read_support_filter_lzma(a); /* Xz falls back to "unxz" command-line program. */ archive_read_support_filter_xz(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_uu(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_rpm(a); /* The decode code always uses "lrzip -q -d" command-line. */ archive_read_support_filter_lrzip(a); /* Lzop decompress falls back to "lzop -d" command-line. */ archive_read_support_filter_lzop(a); /* The decode code always uses "grzip -d" command-line. */ archive_read_support_filter_grzip(a); /* Lz4 falls back to "lz4 -d" command-line program. */ archive_read_support_filter_lz4(a); /* Zstd falls back to "zstd -d" command-line program. */ archive_read_support_filter_zstd(a); /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable * "as much as possible." Clients who need specific * compression should enable those individually so they can * verify the level of support. */ /* Clear any warning messages set by the above functions. */ archive_clear_error(a); return (ARCHIVE_OK); }
int archive_read_open_filenames(struct archive *a, const char **filenames, size_t block_size) { struct read_file_data *mine; const char *filename = NULL; if (filenames) filename = *(filenames++); archive_clear_error(a); do { if (filename == NULL) filename = ""; mine = (struct read_file_data *)calloc(1, sizeof(*mine) + strlen(filename)); if (mine == NULL) goto no_memory; strcpy(mine->filename.m, filename); mine->block_size = block_size; mine->fd = -1; mine->buffer = NULL; mine->st_mode = mine->use_lseek = 0; if (filename == NULL || filename[0] == '\0') { mine->filename_type = FNT_STDIN; } else mine->filename_type = FNT_MBS; if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) return (ARCHIVE_FATAL); if (filenames == NULL) break; filename = *(filenames++); } while (filename != NULL && filename[0] != '\0'); archive_read_set_open_callback(a, file_open); archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); archive_read_set_close_callback(a, file_close); archive_read_set_switch_callback(a, file_switch); archive_read_set_seek_callback(a, file_seek); return (archive_read_open1(a)); no_memory: archive_set_error(a, ENOMEM, "No memory"); return (ARCHIVE_FATAL); }
int archive_read_support_compression_uu(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder; bidder = __archive_read_get_bidder(a); archive_clear_error(_a); if (bidder == NULL) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->bid = uudecode_bidder_bid; bidder->init = uudecode_bidder_init; bidder->options = NULL; bidder->free = NULL; return (ARCHIVE_OK); }
static ssize_t write_write(struct archive * a, void * cookie, const void * buffer, size_t nbytes) { struct multitape_write_internal * d = cookie; ssize_t writelen; writelen = writetape_write(d, buffer, nbytes); if (writelen < 0) { archive_set_error(a, errno, "Error writing archive"); return (ARCHIVE_FATAL); } else if (writelen == 0) { archive_clear_error(a); archive_set_error(a, 0, "Archive truncated"); return (ARCHIVE_WARN); } else return (nbytes); }
/** * archive_read_open_multitape(a, machinenum, tapename): * Open the multitape tape ${tapename} for reading (and skipping) and * associate it with the archive $a$. Return a cookie which can be passed * to the multitape layer. */ void * archive_read_open_multitape(struct archive * a, uint64_t machinenum, const char * tapename) { struct multitape_read_internal * d; /* Clear any error messages from the archive. */ archive_clear_error(a); if ((d = readtape_open(machinenum, tapename)) == NULL) { archive_set_error(a, errno, "Error opening archive"); return (NULL); } if (archive_read_open2(a, d, NULL, read_read, read_skip, read_close)) return (NULL); else return (d); }
/* * Set write options for the compressor. Returns 0 if successful. */ int archive_write_set_compressor_options(struct archive *_a, const char *s) { struct archive_write *a = (struct archive_write *)_a; char key[64], val[64]; int len, r; int ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compressor_options"); archive_clear_error(&a->archive); if (s == NULL || *s == '\0') return (ARCHIVE_OK); if (a->compressor.options == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported option ``%s''", s); /* This compressor does not support option. */ return (ARCHIVE_WARN); } while ((len = __archive_parse_options(s, a->archive.compression_name, sizeof(key), key, sizeof(val), val)) > 0) { if (val[0] == '\0') r = a->compressor.options(a, key, NULL); else r = a->compressor.options(a, key, val); if (r == ARCHIVE_FATAL) return (r); if (r < ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported option ``%s''", key); ret = ARCHIVE_WARN; } s += len; } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Illegal format options."); return (ARCHIVE_WARN); } return (ret); }
int archive_read_open_FILE(struct archive *a, FILE *f) { struct stat st; struct read_FILE_data *mine; size_t block_size = 128 * 1024; void *b; archive_clear_error(a); mine = (struct read_FILE_data *)malloc(sizeof(*mine)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } mine->block_size = block_size; mine->buffer = b; mine->f = f; /* * If we can't fstat() the file, it may just be that it's not * a file. (On some platforms, FILE * objects can wrap I/O * streams that don't support fileno()). As a result, fileno() * should be used cautiously.) */ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* Enable the seek optimization only for regular files. */ mine->can_skip = 1; } else mine->can_skip = 0; #if defined(__CYGWIN__) || defined(_WIN32) setmode(fileno(mine->f), O_BINARY); #endif archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); archive_read_set_close_callback(a, file_close); archive_read_set_callback_data(a, mine); return (archive_read_open1(a)); }
/* * Open the archive using the current settings. */ int archive_write_open(struct archive *_a, void *client_data, archive_open_callback *opener, archive_write_callback *writer, archive_close_callback *closer) { struct archive_write *a = (struct archive_write *)_a; int ret; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open"); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_HEADER; a->client_data = client_data; a->client_writer = writer; a->client_opener = opener; a->client_closer = closer; ret = (a->compressor.init)(a); if (a->format_init && ret == ARCHIVE_OK) ret = (a->format_init)(a); return (ret); }
/** * archive_write_open_multitape(a, machinenum, cachedir, tapename, argc, * argv, printstats, dryrun): * Open the multitape tape ${tapename} for writing and associate it with the * archive $a$. If ${printstats} is non-zero, print archive statistics when * the tape is closed. If ${dryrun} is non-zero, perform a dry run. Return * a cookie which can be passed to the multitape layer. */ void * archive_write_open_multitape(struct archive * a, uint64_t machinenum, const char * cachedir, const char * tapename, int argc, char ** argv, int printstats, int dryrun) { struct multitape_write_internal * d; /* Clear any error messages from the archive. */ archive_clear_error(a); if ((d = writetape_open(machinenum, cachedir, tapename, argc, argv, printstats, dryrun)) == NULL) { archive_set_error(a, errno, "Error creating new archive"); return (NULL); } if (archive_write_open(a, d, NULL, write_write, write_close)) { writetape_free(d); return (NULL); } else return (d); }
/* * Set write options for the format. Returns 0 if successful. */ int archive_write_set_format_options(struct archive *_a, const char *s) { struct archive_write *a = (struct archive_write *)_a; char key[64], val[64]; int len, r, ret = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_options"); archive_clear_error(&a->archive); if (s == NULL || *s == '\0') return (ARCHIVE_OK); if (a->format_options == NULL) /* This format does not support option. */ return (ARCHIVE_OK); while ((len = __archive_parse_options(s, a->format_name, sizeof(key), key, sizeof(val), val)) > 0) { if (val[0] == '\0') r = a->format_options(a, key, NULL); else r = a->format_options(a, key, val); if (r == ARCHIVE_FATAL) return (r); if (r < ARCHIVE_OK) { /* This key was not handled. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported option ``%s''", key); ret = ARCHIVE_WARN; } s += len; } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed options string."); return (ARCHIVE_WARN); } return (ret); }
/* * Close the file and all I/O. */ static int _archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state == ARCHIVE_STATE_CLOSED) return (ARCHIVE_OK); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; /* TODO: Clean up the formatters. */ /* Release the filter objects. */ r1 = close_filters(a); if (r1 < r) r = r1; return (r); }
int archive_read_support_compression_xz(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); archive_clear_error(_a); if (bidder == NULL) return (ARCHIVE_FATAL); bidder->data = NULL; bidder->bid = xz_bidder_bid; bidder->init = xz_bidder_init; bidder->options = NULL; bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external unxz program for xz decompression"); return (ARCHIVE_WARN); #endif }
/* * Close out the archive. */ static int _archive_write_close(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_close"); if (a->archive.state == ARCHIVE_STATE_NEW || a->archive.state == ARCHIVE_STATE_CLOSED) return (ARCHIVE_OK); /* Okay to close() when not open. */ archive_clear_error(&a->archive); /* Finish the last entry if a finish callback is specified */ if (a->archive.state == ARCHIVE_STATE_DATA && a->format_finish_entry != NULL) r = ((a->format_finish_entry)(a)); /* Finish off the archive. */ /* TODO: have format closers invoke compression close. */ if (a->format_close != NULL) { r1 = (a->format_close)(a); if (r1 < r) r = r1; } /* Finish the compression and close the stream. */ r1 = __archive_write_close_filter(a->filter_first); if (r1 < r) r = r1; if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; return (r); }
/* * Close the file and release most resources. * * Be careful: client might just call read_new and then read_finish. * Don't assume we actually read anything or performed any non-trivial * initialization. */ static int _archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; size_t i, n; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_close"); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; /* Call cleanup functions registered by optional components. */ if (a->cleanup_archive_extract != NULL) r = (a->cleanup_archive_extract)(a); /* TODO: Clean up the formatters. */ /* Release the filter objects. */ r1 = cleanup_filters(a); if (r1 < r) r = r1; /* Release the bidder objects. */ n = sizeof(a->bidders)/sizeof(a->bidders[0]); for (i = 0; i < n; i++) { if (a->bidders[i].free != NULL) { r1 = (a->bidders[i].free)(&a->bidders[i]); if (r1 < r) r = r1; } } return (r); }
static int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; const void *h, *hl; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; int r; cpio = (struct cpio *)(a->format->data); sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); if (r < ARCHIVE_WARN) return (r); /* Read name from buffer. */ h = __archive_read_ahead(a, namelength + name_pad, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, (const char *)h, namelength, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname can't be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } cpio->entry_offset = 0; __archive_read_consume(a, namelength + name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { if (cpio->entry_bytes_remaining > 1024 * 1024) { archive_set_error(&a->archive, ENOMEM, "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); return (ARCHIVE_FATAL); } hl = __archive_read_ahead(a, (size_t)cpio->entry_bytes_remaining, NULL); if (hl == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_symlink_l(entry, (const char *)hl, (size_t)cpio->entry_bytes_remaining, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname can't be converted from %s to " "current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } __archive_read_consume(a, cpio->entry_bytes_remaining); cpio->entry_bytes_remaining = 0; } /* XXX TODO: If the full mode is 0160200, then this is a Solaris * ACL description for the following entry. Read this body * and parse it as a Solaris-style ACL, then read the next * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", 11) == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* Detect and record hardlinks to previously-extracted entries. */ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } return (r); }
int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if (st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size + 1; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = setup_acls(a, entry, &fd); r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; if (a->enable_copyfile) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); }
static int file_open(struct archive *a, void *client_data) { struct stat st; struct read_file_data *mine = (struct read_file_data *)client_data; void *buffer; const char *filename = NULL; const wchar_t *wfilename = NULL; int fd; int is_disk_like = 0; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */ #elif defined(__NetBSD__) || defined(__OpenBSD__) struct disklabel dl; #elif defined(__DragonFly__) struct partinfo pi; #endif archive_clear_error(a); if (mine->filename_type == FNT_STDIN) { /* We used to delegate stdin support by * directly calling archive_read_open_fd(a,0,block_size) * here, but that doesn't (and shouldn't) handle the * end-of-file flush when reading stdout from a pipe. * Basically, read_open_fd() is intended for folks who * are willing to handle such details themselves. This * API is intended to be a little smarter for folks who * want easy handling of the common case. */ fd = 0; #if defined(__CYGWIN__) || defined(_WIN32) setmode(0, O_BINARY); #endif filename = ""; } else if (mine->filename_type == FNT_MBS) { filename = mine->filename.m; fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(a, errno, "Failed to open '%s'", filename); return (ARCHIVE_FATAL); } } else { #if defined(_WIN32) && !defined(__CYGWIN__) wfilename = mine->filename.w; fd = _wopen(wfilename, O_RDONLY | O_BINARY); if (fd < 0 && errno == ENOENT) { wchar_t *fullpath; fullpath = __la_win_permissive_name_w(wfilename); if (fullpath != NULL) { fd = _wopen(fullpath, O_RDONLY | O_BINARY); free(fullpath); } } if (fd < 0) { archive_set_error(a, errno, "Failed to open '%S'", wfilename); return (ARCHIVE_FATAL); } #else archive_set_error(a, ARCHIVE_ERRNO_MISC, "Unexpedted operation in archive_read_open_filename"); return (ARCHIVE_FATAL); #endif } if (fstat(fd, &st) != 0) { if (mine->filename_type == FNT_WCS) archive_set_error(a, errno, "Can't stat '%S'", wfilename); else archive_set_error(a, errno, "Can't stat '%s'", filename); return (ARCHIVE_FATAL); } /* * Determine whether the input looks like a disk device or a * tape device. The results are used below to select an I/O * strategy: * = "disk-like" devices support arbitrary lseek() and will * support I/O requests of any size. So we get easy skipping * and can cheat on block sizes to get better performance. * = "tape-like" devices require strict blocking and use * specialized ioctls for seeking. * = "socket-like" devices cannot seek at all but can improve * performance by using nonblocking I/O to read "whatever is * available right now". * * Right now, we only specially recognize disk-like devices, * but it should be straightforward to add probes and strategy * here for tape-like and socket-like devices. */ if (S_ISREG(st.st_mode)) { /* Safety: Tell the extractor not to overwrite the input. */ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* Regular files act like disks. */ is_disk_like = 1; } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) /* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */ else if (S_ISCHR(st.st_mode) && ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 && mediasize > 0) { is_disk_like = 1; } #elif defined(__NetBSD__) || defined(__OpenBSD__) /* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */ else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) && ioctl(fd, DIOCGDINFO, &dl) == 0 && dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) { is_disk_like = 1; } #elif defined(__DragonFly__) /* DragonFly BSD: if it supports DIOCGPART ioctl, it's disk-like. */ else if (S_ISCHR(st.st_mode) && ioctl(fd, DIOCGPART, &pi) == 0 && pi.media_size > 0) { is_disk_like = 1; } #elif defined(__linux__) /* Linux: All block devices are disk-like. */ else if (S_ISBLK(st.st_mode) && lseek(fd, 0, SEEK_CUR) == 0 && lseek(fd, 0, SEEK_SET) == 0 && lseek(fd, 0, SEEK_END) > 0 && lseek(fd, 0, SEEK_SET) == 0) { is_disk_like = 1; } #endif /* TODO: Add an "is_tape_like" variable and appropriate tests. */ /* Disk-like devices prefer power-of-two block sizes. */ /* Use provided block_size as a guide so users have some control. */ if (is_disk_like) { size_t new_block_size = 64 * 1024; while (new_block_size < mine->block_size && new_block_size < 64 * 1024 * 1024) new_block_size *= 2; mine->block_size = new_block_size; } buffer = malloc(mine->block_size); if (mine == NULL || buffer == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(buffer); return (ARCHIVE_FATAL); } mine->buffer = buffer; mine->fd = fd; /* Remember mode so close can decide whether to flush. */ mine->st_mode = st.st_mode; /* Disk-like inputs can use lseek(). */ if (is_disk_like) mine->use_lseek = 1; return (ARCHIVE_OK); }
int archive_read_open1(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter; int slot, e; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); archive_clear_error(&a->archive); if (a->client.reader == NULL) { archive_set_error(&a->archive, EINVAL, "No reader function provided to archive_read_open"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* Open data source. */ if (a->client.opener != NULL) { e =(a->client.opener)(&a->archive, a->client.data); if (e != 0) { /* If the open failed, call the closer to clean up. */ if (a->client.closer) (a->client.closer)(&a->archive, a->client.data); return (e); } } filter = calloc(1, sizeof(*filter)); if (filter == NULL) return (ARCHIVE_FATAL); filter->bidder = NULL; filter->upstream = NULL; filter->archive = a; filter->data = a->client.data; filter->read = client_read_proxy; filter->skip = client_skip_proxy; filter->seek = client_seek_proxy; filter->close = client_close_proxy; filter->name = "none"; filter->code = ARCHIVE_COMPRESSION_NONE; a->filter = filter; /* Build out the input pipeline. */ e = choose_filters(a); if (e < ARCHIVE_WARN) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } slot = choose_format(a); if (slot < 0) { close_filters(a); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->format = &(a->formats[slot]); a->archive.state = ARCHIVE_STATE_HEADER; return (e); }
/* * Read header of next entry. */ static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read *a = (struct archive_read *)_a; int r1 = ARCHIVE_OK, r2; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header"); archive_entry_clear(entry); archive_clear_error(&a->archive); /* * If client didn't consume entire data, skip any remainder * (This is especially important for GNU incremental directories.) */ if (a->archive.state == ARCHIVE_STATE_DATA) { r1 = archive_read_data_skip(&a->archive); if (r1 == ARCHIVE_EOF) archive_set_error(&a->archive, EIO, "Premature end-of-file."); if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } /* Record start-of-header offset in uncompressed stream. */ a->header_position = a->filter->position; ++_a->file_count; r2 = (a->format->read_header)(a, entry); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r2) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; --_a->file_count;/* Revert a file counter. */ break; case ARCHIVE_OK: a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_WARN: a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } a->read_data_output_offset = 0; a->read_data_remaining = 0; /* EOF always wins; otherwise return the worst error. */ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1; }
int archive_read_open_filename(struct archive *a, const char *filename, size_t block_size) { struct stat st; struct read_file_data *mine; void *b; int fd; archive_clear_error(a); if (filename == NULL || filename[0] == '\0') { /* We used to invoke archive_read_open_fd(a,0,block_size) * here, but that doesn't (and shouldn't) handle the * end-of-file flush when reading stdout from a pipe. * Basically, read_open_fd() is intended for folks who * are willing to handle such details themselves. This * API is intended to be a little smarter for folks who * want easy handling of the common case. */ filename = ""; /* Normalize NULL to "" */ fd = 0; #if defined(__CYGWIN__) || defined(_WIN32) setmode(0, O_BINARY); #endif } else { fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) { archive_set_error(a, errno, "Failed to open '%s'", filename); return (ARCHIVE_FATAL); } } if (fstat(fd, &st) != 0) { archive_set_error(a, errno, "Can't stat '%s'", filename); return (ARCHIVE_FATAL); } mine = (struct read_file_data *)calloc(1, sizeof(*mine) + strlen(filename)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } strcpy(mine->filename, filename); mine->block_size = block_size; mine->buffer = b; mine->fd = fd; /* Remember mode so close can decide whether to flush. */ mine->st_mode = st.st_mode; /* If we're reading a file from disk, ensure that we don't overwrite it with an extracted file. */ if (S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* * Enabling skip here is a performance optimization * for anything that supports lseek(). On FreeBSD * (and probably many other systems), only regular * files and raw disk devices support lseek() (on * other input types, lseek() returns success but * doesn't actually change the file pointer, which * just completely screws up the position-tracking * logic). In addition, I've yet to find a portable * way to determine if a device is a raw disk device. * So I don't see a way to do much better than to only * enable this optimization for regular files. */ mine->can_skip = 1; } return (archive_read_open2(a, mine, NULL, file_read, file_skip, file_close)); }
/* * Handle 'x' and 't' modes. */ static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) { struct progress_data progress_data; FILE *out; struct archive *a; struct archive_entry *entry; const char *reader_options; int r; while (*bsdtar->argv) { if (archive_match_include_pattern(bsdtar->matching, *bsdtar->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) if (archive_match_include_pattern_from_file( bsdtar->matching, bsdtar->names_from_file, bsdtar->option_null) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); a = archive_read_new(); if (cset_read_support_filter_program(bsdtar->cset, a) == 0) archive_read_support_filter_all(a); archive_read_support_format_all(a); reader_options = getenv(ENV_READER_OPTIONS); if (reader_options != NULL) { char *p; /* Set default read options. */ p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + strlen(reader_options) + 1); if (p == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or modules which are not added to * the archive read object. */ strncpy(p, IGNORE_WRONG_MODULE_NAME, sizeof(IGNORE_WRONG_MODULE_NAME) -1); strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options); r = archive_read_set_options(a, p); free(p); if (r == ARCHIVE_FATAL) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, bsdtar->filename, bsdtar->bytes_per_block)) lafe_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); if (mode == 'x') { /* Set an extract callback so that we can handle SIGINFO. */ progress_data.bsdtar = bsdtar; progress_data.archive = a; archive_read_extract_set_progress_callback(a, progress_func, &progress_data); } if (mode == 'x' && bsdtar->option_chroot) { #if HAVE_CHROOT if (chroot(".") != 0) lafe_errc(1, errno, "Can't chroot to \".\""); #else lafe_errc(1, 0, "chroot isn't supported on this platform"); #endif } for (;;) { /* Support --fast-read option */ if (bsdtar->option_fast_read && archive_match_path_unmatched_inclusions(bsdtar->matching) == 0) break; r = archive_read_next_header(a, &entry); progress_data.entry = entry; if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ lafe_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) break; if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); archive_entry_set_uname(entry, NULL); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); archive_entry_set_gname(entry, NULL); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more * control over exclusions, since rewrites always lose * information. (For example, consider a rewrite * s/foo[0-9]/foo/. If we check exclusions after the * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ if (archive_match_excluded(bsdtar->matching, entry)) continue; /* Excluded by a pattern test. */ if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ out = bsdtar->option_stdout ? stderr : stdout; /* * TODO: Provide some reasonable way to * preview rewrites. gtar always displays * the unedited path in -t output, which means * you cannot easily preview rewrites. */ if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); else list_item_verbose(bsdtar, out, entry); fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } fprintf(out, "\n"); } else { /* Note: some rewrite failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) continue; /* Excluded by a rewrite failure. */ if (bsdtar->option_interactive && !yes("extract '%s'", archive_entry_pathname(entry))) continue; /* * Format here is from SUSv2, including the * deferred '\n'. */ if (bsdtar->verbose) { safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); } /* TODO siginfo_printinfo(bsdtar, 0); */ if (bsdtar->option_stdout) r = archive_read_data_into_fd(a, 1); else r = archive_read_extract2(a, entry, writer); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) fprintf(stderr, "\n"); bsdtar->return_value = 1; } if (bsdtar->verbose) fprintf(stderr, "\n"); if (r == ARCHIVE_FATAL) break; } } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_filter_name(a, 0)); archive_read_free(a); }