/* * Note: if text was provided, this just returns that text. If you * really need the text to be rebuilt in a canonical form, set the * text, ask for the bitmaps, then set the bitmaps. (Setting the * bitmaps clears any stored text.) This design is deliberate: if * we're editing archives, we don't want to discard flags just because * they aren't supported on the current system. The bitmap<->text * conversions are platform-specific (see below). */ const char * archive_entry_fflags_text(struct archive_entry *entry) { const char *f; char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) { if (f != NULL) return (f); } else if (errno == ENOMEM) __archive_errx(1, "No memory"); if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) return (NULL); p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); if (p == NULL) return (NULL); archive_mstring_copy_mbs(&entry->ae_fflags_text, p); free(p); if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) return (f); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); }
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry) { struct links_entry *le; dev_t dev; int64_t ino; if (tk_archive_entry_nlink(entry) <= 1) return; dev = tk_archive_entry_dev(entry); ino = tk_archive_entry_ino64(entry); /* * First look in the list of multiply-linked files. If we've * already dumped it, convert this entry to a hard link entry. */ for (le = cpio->links_head; le; le = le->next) { if (le->dev == dev && le->ino == ino) { tk_archive_entry_copy_hardlink(entry, le->name); if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (cpio->links_head == le) cpio->links_head = le->next; free(le->name); free(le); } return; } } le = (struct links_entry *)malloc(sizeof(struct links_entry)); if (le == NULL) __archive_errx(1, "Out of memory adding file to list"); if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; le->dev = dev; le->ino = ino; le->links = tk_archive_entry_nlink(entry) - 1; le->name = strdup(tk_archive_entry_pathname(entry)); if (le->name == NULL) __archive_errx(1, "Out of memory adding file to list"); }
/* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { const wchar_t *r; r = archive_acl_text_w(entry->archive, &entry->acl, flags); if (r == NULL && errno == ENOMEM) __archive_errx(1, "No memory"); return (r); }
const char * archive_entry_acl_text(struct archive_entry *entry, int flags) { const char *p; if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0 && errno == ENOMEM) __archive_errx(1, "No memory"); return (p); }
static char * utf8_encode(const wchar_t *wval) { int utf8len; const wchar_t *wp; unsigned long wc; char *utf8_value, *p; utf8len = 0; for (wp = wval; *wp != L'\0'; ) { wc = *wp++; if (wc >= 0xd800 && wc <= 0xdbff && *wp >= 0xdc00 && *wp <= 0xdfff) { /* This is a surrogate pair. Combine into a * full Unicode value before encoding into * UTF-8. */ wc = (wc - 0xd800) << 10; /* High 10 bits */ wc += (*wp++ - 0xdc00); /* Low 10 bits */ wc += 0x10000; /* Skip BMP */ } if (wc <= 0x7f) utf8len++; else if (wc <= 0x7ff) utf8len += 2; else if (wc <= 0xffff) utf8len += 3; else if (wc <= 0x1fffff) utf8len += 4; else if (wc <= 0x3ffffff) utf8len += 5; else if (wc <= 0x7fffffff) utf8len += 6; /* Ignore larger values; UTF-8 can't encode them. */ } utf8_value = (char *)malloc(utf8len + 1); if (utf8_value == NULL) { __archive_errx(1, "Not enough memory for attributes"); return (NULL); } for (wp = wval, p = utf8_value; *wp != L'\0'; ) { wc = *wp++; if (wc >= 0xd800 && wc <= 0xdbff && *wp >= 0xdc00 && *wp <= 0xdfff) { /* Combine surrogate pair. */ wc = (wc - 0xd800) << 10; wc += *wp++ - 0xdc00 + 0x10000; } if (wc <= 0x7f) { *p++ = (char)wc; } else if (wc <= 0x7ff) { p[0] = 0xc0 | ((wc >> 6) & 0x1f); p[1] = 0x80 | (wc & 0x3f); p += 2; } else if (wc <= 0xffff) {
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); }
const char * archive_entry_gname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); }
const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); }
int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_gname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); }
struct archive_string * __archive_string_append(struct archive_string *as, const char *p, size_t s) { if (__archive_string_ensure(as, as->length + s + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(as->s + as->length, p, s); as->s[as->length + s] = 0; as->length += s; return (as); }
/* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { int r; r = archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name); if (r == ARCHIVE_FATAL && errno == ENOMEM) __archive_errx(1, "No memory"); return (r); }
const char * archive_entry_sourcepath(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); }
void __archive_string_concat(struct archive_string *dest, struct archive_string *src) { if (src->length > 0) { if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(dest->s + dest->length, src->s, src->length); dest->length += src->length; dest->s[dest->length] = 0; } }
void archive_entry_xattr_add_entry(struct archive_entry *entry, const char *name, const void *value, size_t size) { struct ae_xattr *xp; if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL) __archive_errx(1, "Out of memory"); if ((xp->name = strdup(name)) == NULL) __archive_errx(1, "Out of memory"); if ((xp->value = malloc(size)) != NULL) { memcpy(xp->value, value, size); xp->size = size; } else xp->size = 0; xp->next = entry->xattr_head; entry->xattr_head = xp; }
/* * Mark a previously-returned block of data as read. */ static ssize_t read_consume(struct archive_read *a, size_t n) { struct private_data *state; state = (struct private_data *)a->decompressor->data; a->archive.file_position += n; state->read_next += n; if (state->read_next > state->stream.next_out) __archive_errx(1, "Request to consume too many " "bytes from bzip2 decompressor"); return (n); }
void __archive_string_copy(struct archive_string *dest, struct archive_string *src) { if (src->length == 0) dest->length = 0; else { if (__archive_string_ensure(dest, src->length + 1) == NULL) __archive_errx(1, "Out of memory"); memcpy(dest->s, src->s, src->length); dest->length = src->length; dest->s[dest->length] = 0; } }
const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); }
const char * archive_entry_hardlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); }
int archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target) { if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); }
int archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname) { if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, linkname) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); }
static int64_t client_skip_proxy(struct archive_read_filter *self, int64_t request) { if (request < 0) __archive_errx(1, "Negative skip requested."); if (request == 0) return 0; if (self->archive->client.skipper != NULL) { /* Seek requests over 1GiB are broken down into * multiple seeks. This avoids overflows when the * requests get passed through 32-bit arguments. */ int64_t skip_limit = (int64_t)1 << 30; int64_t total = 0; for (;;) { int64_t get, ask = request; if (ask > skip_limit) ask = skip_limit; get = (self->archive->client.skipper) (&self->archive->archive, self->data, ask); total += get; if (get == 0 || get == request) return (total); if (get > request) return ARCHIVE_FATAL; request -= get; } } else if (self->archive->client.seeker != NULL && request > 64 * 1024) { /* If the client provided a seeker but not a skipper, * we can use the seeker to skip forward. * * Note: This isn't always a good idea. The client * skipper is allowed to skip by less than requested * if it needs to maintain block alignment. The * seeker is not allowed to play such games, so using * the seeker here may be a performance loss compared * to just reading and discarding. That's why we * only do this for skips of over 64k. */ int64_t before = self->position; int64_t after = (self->archive->client.seeker) (&self->archive->archive, self->data, request, SEEK_CUR); if (after != before + request) return ARCHIVE_FATAL; return after - before; } return 0; }
int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, target); else r = archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target); if (r == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); }
/* * Allow each registered format to bid on whether it wants to handle * the next entry. Return index of winning bidder. */ static int choose_format(struct archive_read *a) { int slots; int i; int bid, best_bid; int best_bid_slot; slots = sizeof(a->formats) / sizeof(a->formats[0]); best_bid = -1; best_bid_slot = -1; /* Set up a->format and a->pformat_data for convenience of bidders. */ a->format = &(a->formats[0]); for (i = 0; i < slots; i++, a->format++) { if (a->format->bid) { bid = (a->format->bid)(a); if (bid == ARCHIVE_FATAL) return (ARCHIVE_FATAL); if ((bid > best_bid) || (best_bid_slot < 0)) { best_bid = bid; best_bid_slot = i; } } } /* * There were no bidders; this is a serious programmer error * and demands a quick and definitive abort. */ if (best_bid_slot < 0) __archive_errx(1, "No formats were registered; you must " "invoke at least one archive_read_support_format_XXX " "function in order to successfully read an archive."); /* * There were bidders, but no non-zero bids; this means we * can't support this stream. */ if (best_bid < 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized archive format"); return (ARCHIVE_FATAL); } return (best_bid_slot); }
/* * Used internally by decompression routines to register their bid and * initialization functions. */ struct archive_read_filter_bidder * __archive_read_get_bidder(struct archive_read *a) { int i, number_slots; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_get_bidder"); number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]); for (i = 0; i < number_slots; i++) { if (a->bidders[i].bid == NULL) { memset(a->bidders + i, 0, sizeof(a->bidders[0])); return (a->bidders + i); } } __archive_errx(1, "Not enough slots for compression registration"); return (NULL); /* Never actually executed. */ }
/* * Used internally by read format handlers to register their bid and * initialization functions. */ int __archive_read_register_format(struct archive_read *a, void *format_data, const char *name, int (*bid)(struct archive_read *), int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), int (*read_data_skip)(struct archive_read *), int (*cleanup)(struct archive_read *)) { int i, number_slots; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_format"); number_slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < number_slots; i++) { if (a->formats[i].bid == bid) return (ARCHIVE_WARN); /* We've already installed */ if (a->formats[i].bid == NULL) { a->formats[i].bid = bid; a->formats[i].options = options; a->formats[i].read_header = read_header; a->formats[i].read_data = read_data; a->formats[i].read_data_skip = read_data_skip; a->formats[i].cleanup = cleanup; a->formats[i].data = format_data; a->formats[i].name = name; return (ARCHIVE_OK); } } __archive_errx(1, "Not enough slots for format registration"); return (ARCHIVE_FATAL); /* Never actually called. */ }
static void add_entry(struct iso9660 *iso9660, struct file_info *file) { /* Expand our pending files list as necessary. */ if (iso9660->pending_files_used >= iso9660->pending_files_allocated) { struct file_info **new_pending_files; int new_size = iso9660->pending_files_allocated * 2; if (new_size < 1024) new_size = 1024; new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) __archive_errx(1, "Out of memory"); memcpy(new_pending_files, iso9660->pending_files, iso9660->pending_files_allocated * sizeof(new_pending_files[0])); if (iso9660->pending_files != NULL) free(iso9660->pending_files); iso9660->pending_files = new_pending_files; iso9660->pending_files_allocated = new_size; } iso9660->pending_files[iso9660->pending_files_used++] = file; }
/* * Like 'vsprintf', but ensures the target is big enough, resizing if * necessary. */ void archive_string_vsprintf(struct archive_string *as, const char *fmt, va_list ap) { char long_flag; intmax_t s; /* Signed integer temp. */ uintmax_t u; /* Unsigned integer temp. */ const char *p, *p2; const wchar_t *pw; if (archive_string_ensure(as, 64) == NULL) __archive_errx(1, "Out of memory"); if (fmt == NULL) { as->s[0] = 0; return; } for (p = fmt; *p != '\0'; p++) { const char *saved_p = p; if (*p != '%') { archive_strappend_char(as, *p); continue; } p++; long_flag = '\0'; switch(*p) { case 'j': case 'l': case 'z': long_flag = *p; p++; break; } switch (*p) { case '%': archive_strappend_char(as, '%'); break; case 'c': s = va_arg(ap, int); archive_strappend_char(as, (char)s); break; case 'd': switch(long_flag) { case 'j': s = va_arg(ap, intmax_t); break; case 'l': s = va_arg(ap, long); break; case 'z': s = va_arg(ap, ssize_t); break; default: s = va_arg(ap, int); break; } append_int(as, s, 10); break; case 's': switch(long_flag) { case 'l': pw = va_arg(ap, wchar_t *); if (pw == NULL) pw = L"(null)"; if (archive_string_append_from_wcs(as, pw, wcslen(pw)) != 0 && errno == ENOMEM) __archive_errx(1, "Out of memory"); break; default: p2 = va_arg(ap, char *); if (p2 == NULL) p2 = "(null)"; archive_strcat(as, p2); break; } break; case 'S': pw = va_arg(ap, wchar_t *); if (pw == NULL) pw = L"(null)"; if (archive_string_append_from_wcs(as, pw, wcslen(pw)) != 0 && errno == ENOMEM) __archive_errx(1, "Out of memory"); break; case 'o': case 'u': case 'x': case 'X': /* Common handling for unsigned integer formats. */ switch(long_flag) { case 'j': u = va_arg(ap, uintmax_t); break; case 'l': u = va_arg(ap, unsigned long); break; case 'z': u = va_arg(ap, size_t); break; default: u = va_arg(ap, unsigned int); break; } /* Format it in the correct base. */ switch (*p) { case 'o': append_uint(as, u, 8); break; case 'u': append_uint(as, u, 10); break; default: append_uint(as, u, 16); break; } break; default: /* Rewind and print the initial '%' literally. */ p = saved_p; archive_strappend_char(as, *p); } } }
/* * Like 'vsprintf', but ensures the target is big enough, resizing if * necessary. */ void __archive_string_vsprintf(struct archive_string *as, const char *fmt, va_list ap) { char long_flag; intmax_t s; /* Signed integer temp. */ uintmax_t u; /* Unsigned integer temp. */ const char *p, *p2; if (__archive_string_ensure(as, 64) == NULL) __archive_errx(1, "Out of memory"); if (fmt == NULL) { as->s[0] = 0; return; } long_flag = '\0'; for (p = fmt; *p != '\0'; p++) { const char *saved_p = p; if (*p != '%') { archive_strappend_char(as, *p); continue; } p++; switch(*p) { case 'j': long_flag = 'j'; p++; break; case 'l': long_flag = 'l'; p++; break; } switch (*p) { case '%': __archive_strappend_char(as, '%'); break; case 'c': s = va_arg(ap, int); __archive_strappend_char(as, s); break; case 'd': switch(long_flag) { case 'j': s = va_arg(ap, intmax_t); break; case 'l': s = va_arg(ap, long); break; default: s = va_arg(ap, int); break; } archive_strappend_int(as, s, 10); break; case 's': p2 = va_arg(ap, char *); archive_strcat(as, p2); break; case 'o': case 'u': case 'x': case 'X': /* Common handling for unsigned integer formats. */ switch(long_flag) { case 'j': u = va_arg(ap, uintmax_t); break; case 'l': u = va_arg(ap, unsigned long); break; default: u = va_arg(ap, unsigned int); break; } /* Format it in the correct base. */ switch (*p) { case 'o': archive_strappend_int(as, u, 8); break; case 'u': archive_strappend_int(as, u, 10); break; default: archive_strappend_int(as, u, 16); break; } break; default: /* Rewind and print the initial '%' literally. */ p = saved_p; archive_strappend_char(as, *p); } } }
static int zip_read_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const struct zip_file_header *p; const void *h; if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) { tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } zip->version = p->version[0]; zip->system = p->version[1]; zip->flags = tk_archive_le16dec(p->flags); zip->compression = tk_archive_le16dec(p->compression); if (zip->compression < sizeof(compression_names)/sizeof(compression_names[0])) zip->compression_name = compression_names[zip->compression]; else zip->compression_name = "??"; zip->mtime = zip_time(p->timedate); zip->ctime = 0; zip->atime = 0; zip->mode = 0; zip->uid = 0; zip->gid = 0; zip->crc32 = tk_archive_le32dec(p->crc32); zip->filename_length = tk_archive_le16dec(p->filename_length); zip->extra_length = tk_archive_le16dec(p->extra_length); zip->uncompressed_size = tk_archive_le32dec(p->uncompressed_size); zip->compressed_size = tk_archive_le32dec(p->compressed_size); __archive_read_consume(a, sizeof(struct zip_file_header)); /* Read the filename. */ if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) { tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (tk_archive_string_ensure(&zip->pathname, zip->filename_length) == NULL) __archive_errx(1, "Out of memory"); tk_archive_strncpy(&zip->pathname, h, zip->filename_length); __archive_read_consume(a, zip->filename_length); tk_archive_entry_set_pathname(entry, zip->pathname.s); if (zip->pathname.s[tk_archive_strlen(&zip->pathname) - 1] == '/') zip->mode = AE_IFDIR | 0777; else zip->mode = AE_IFREG | 0777; /* Read the extra data. */ if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) { tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } process_extra(h, zip); __archive_read_consume(a, zip->extra_length); /* Populate some additional entry fields: */ tk_archive_entry_set_mode(entry, zip->mode); tk_archive_entry_set_uid(entry, zip->uid); tk_archive_entry_set_gid(entry, zip->gid); tk_archive_entry_set_mtime(entry, zip->mtime, 0); tk_archive_entry_set_ctime(entry, zip->ctime, 0); tk_archive_entry_set_atime(entry, zip->atime, 0); /* Set the size only if it's meaningful. */ if (0 == (zip->flags & ZIP_LENGTH_AT_END)) tk_archive_entry_set_size(entry, zip->uncompressed_size); zip->entry_bytes_remaining = zip->compressed_size; zip->entry_offset = 0; /* If there's no body, force read_data() to return EOF immediately. */ if (0 == (zip->flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ sprintf(zip->format_name, "ZIP %d.%d (%s)", zip->version / 10, zip->version % 10, zip->compression_name); a->archive.archive_format_name = zip->format_name; return (ARCHIVE_OK); }