static int archive_write_mtree_header(struct archive_write *a, struct archive_entry *entry) { struct mtree_writer *mtree= a->format_data; struct archive_string *str; const char *path; mtree->entry = archive_entry_clone(entry); path = archive_entry_pathname(mtree->entry); if (mtree->first) { mtree->first = 0; archive_strcat(&mtree->buf, "#mtree\n"); } if (mtree->set.output) set_global(mtree, entry); archive_string_empty(&mtree->ebuf); str = (mtree->indent)? &mtree->ebuf : &mtree->buf; if (!mtree->dironly || archive_entry_filetype(entry) == AE_IFDIR) mtree_quote(str, path); mtree->entry_bytes_remaining = archive_entry_size(entry); if ((mtree->keys & F_CKSUM) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_CKSUM; mtree->crc = 0; mtree->crc_len = 0; } else mtree->compute_sum &= ~F_CKSUM; #ifdef ARCHIVE_HAS_MD5 if ((mtree->keys & F_MD5) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_MD5; archive_md5_init(&mtree->md5ctx); } else mtree->compute_sum &= ~F_MD5; #endif #ifdef ARCHIVE_HAS_RMD160 if ((mtree->keys & F_RMD160) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_RMD160; archive_rmd160_init(&mtree->rmd160ctx); } else mtree->compute_sum &= ~F_RMD160; #endif #ifdef ARCHIVE_HAS_SHA1 if ((mtree->keys & F_SHA1) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA1; archive_sha1_init(&mtree->sha1ctx); } else mtree->compute_sum &= ~F_SHA1; #endif #ifdef ARCHIVE_HAS_SHA256 if ((mtree->keys & F_SHA256) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA256; archive_sha256_init(&mtree->sha256ctx); } else mtree->compute_sum &= ~F_SHA256; #endif #ifdef ARCHIVE_HAS_SHA384 if ((mtree->keys & F_SHA384) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA384; archive_sha384_init(&mtree->sha384ctx); } else mtree->compute_sum &= ~F_SHA384; #endif #ifdef ARCHIVE_HAS_SHA512 if ((mtree->keys & F_SHA512) != 0 && archive_entry_filetype(entry) == AE_IFREG) { mtree->compute_sum |= F_SHA512; archive_sha512_init(&mtree->sha512ctx); } else mtree->compute_sum &= ~F_SHA512; #endif return (ARCHIVE_OK); }
static void test_format(int (*set_format)(struct archive *)) { char filedata[64]; struct archive_entry *ae; struct archive *a; char *p; size_t used; size_t buffsize = 1000000; char *buff; int damaged = 0; buff = malloc(buffsize); /* Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertA(0 == (*set_format)(a)); assertA(0 == archive_write_add_filter_none(a)); assertA(0 == archive_write_open_memory(a, buff, buffsize, &used)); /* * Write a file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_mtime(ae, 1, 10); assert(1 == archive_entry_mtime(ae)); assert(10 == archive_entry_mtime_nsec(ae)); p = strdup("file"); archive_entry_copy_pathname(ae, p); strcpy(p, "XXXX"); free(p); assertEqualString("file", archive_entry_pathname(ae)); archive_entry_set_mode(ae, S_IFREG | 0755); assert((S_IFREG | 0755) == archive_entry_mode(ae)); archive_entry_set_size(ae, 8); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); assertA(8 == archive_write_data(a, "12345678", 9)); /* * Write another file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_mtime(ae, 1, 10); assert(1 == archive_entry_mtime(ae)); assert(10 == archive_entry_mtime_nsec(ae)); p = strdup("file2"); archive_entry_copy_pathname(ae, p); strcpy(p, "XXXX"); free(p); assertEqualString("file2", archive_entry_pathname(ae)); archive_entry_set_mode(ae, S_IFREG | 0755); assert((S_IFREG | 0755) == archive_entry_mode(ae)); archive_entry_set_size(ae, 4); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); assertA(4 == archive_write_data(a, "1234", 5)); /* * Write a file with a name, filetype, and size. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "name"); archive_entry_set_size(ae, 0); archive_entry_set_filetype(ae, AE_IFREG); assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae)); assert(archive_error_string(a) == NULL); archive_entry_free(ae); /* * Write a file with a name and filetype but no size. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "name"); archive_entry_unset_size(ae); archive_entry_set_filetype(ae, AE_IFREG); assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); assert(archive_error_string(a) != NULL); archive_entry_free(ae); /* * Write a file with a name and size but no filetype. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "name"); archive_entry_set_size(ae, 0); assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); assert(archive_error_string(a) != NULL); archive_entry_free(ae); /* * Write a file with a size and filetype but no name. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_size(ae, 0); archive_entry_set_filetype(ae, AE_IFREG); assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); assert(archive_error_string(a) != NULL); archive_entry_free(ae); /* * Write a directory to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_mtime(ae, 11, 110); archive_entry_copy_pathname(ae, "dir"); archive_entry_set_mode(ae, S_IFDIR | 0755); archive_entry_set_size(ae, 512); assertA(0 == archive_write_header(a, ae)); assertEqualInt(0, archive_entry_size(ae)); archive_entry_free(ae); assertEqualIntA(a, 0, archive_write_data(a, "12345678", 9)); /* Close out the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* * Damage the second entry to test the search-ahead recovery. * TODO: Move the damage-recovery checking to a separate test; * it doesn't really belong in this write test. */ { int i; for (i = 80; i < 150; i++) { if (memcmp(buff + i, "07070", 5) == 0) { damaged = 1; buff[i] = 'X'; break; } } } failure("Unable to locate the second header for damage-recovery test."); assert(damaged == 1); /* * Now, read the data back. */ assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_open_memory(a, buff, used)); if (!assertEqualIntA(a, 0, archive_read_next_header(a, &ae))) { archive_read_free(a); return; } assertEqualInt(1, archive_entry_mtime(ae)); /* Not the same as above: cpio doesn't store hi-res times. */ assert(0 == archive_entry_mtime_nsec(ae)); assert(0 == archive_entry_atime(ae)); assert(0 == archive_entry_ctime(ae)); assertEqualString("file", archive_entry_pathname(ae)); assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); assertEqualInt(8, archive_entry_size(ae)); assertA(8 == archive_read_data(a, filedata, 10)); assertEqualMem(filedata, "12345678", 8); /* * The second file can't be read because we damaged its header. */ /* * Read the third file back. * ARCHIVE_WARN here because the damaged entry was skipped. */ assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae)); assertEqualString("name", archive_entry_pathname(ae)); /* * Read the dir entry back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(11, archive_entry_mtime(ae)); assert(0 == archive_entry_mtime_nsec(ae)); assert(0 == archive_entry_atime(ae)); assert(0 == archive_entry_ctime(ae)); assertEqualString("dir", archive_entry_pathname(ae)); assertEqualInt((S_IFDIR | 0755), archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualIntA(a, 0, archive_read_data(a, filedata, 10)); /* Verify the end of the archive. */ assertEqualIntA(a, 1, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); free(buff); }
static int archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) { int ret, append_fn; char buff[60]; char *ss, *se; struct ar_w *ar; const char *pathname; const char *filename; int64_t size; append_fn = 0; ar = (struct ar_w *)a->format_data; ar->is_strtab = 0; filename = NULL; size = archive_entry_size(entry); /* * Reject files with empty name. */ pathname = archive_entry_pathname(entry); if (pathname == NULL || *pathname == '\0') { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } /* * If we are now at the beginning of the archive, * we need first write the ar global header. */ if (!ar->wrote_global_header) { __archive_write_output(a, "!<arch>\n", 8); ar->wrote_global_header = 1; } memset(buff, ' ', 60); strncpy(&buff[AR_fmag_offset], "`\n", 2); if (strcmp(pathname, "/") == 0 ) { /* Entry is archive symbol table in GNU format */ buff[AR_name_offset] = '/'; goto stat; } if (strcmp(pathname, "__.SYMDEF") == 0) { /* Entry is archive symbol table in BSD format */ strncpy(buff + AR_name_offset, "__.SYMDEF", 9); goto stat; } if (strcmp(pathname, "//") == 0) { /* * Entry is archive filename table, inform that we should * collect strtab in next _data call. */ ar->is_strtab = 1; buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; /* * For archive string table, only ar_size field should * be set. */ goto size; } /* * Otherwise, entry is a normal archive member. * Strip leading paths from filenames, if any. */ if ((filename = ar_basename(pathname)) == NULL) { /* Reject filenames with trailing "/" */ archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) { /* * SVR4/GNU variant use a "/" to mark then end of the filename, * make it possible to have embedded spaces in the filename. * So, the longest filename here (without extension) is * actually 15 bytes. */ if (strlen(filename) <= 15) { strncpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = '/'; } else { /* * For filename longer than 15 bytes, GNU variant * makes use of a string table and instead stores the * offset of the real filename to in the ar_name field. * The string table should have been written before. */ if (ar->has_strtab <= 0) { archive_set_error(&a->archive, EINVAL, "Can't find string table"); return (ARCHIVE_WARN); } se = (char *)malloc(strlen(filename) + 3); if (se == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate filename buffer"); return (ARCHIVE_FATAL); } strncpy(se, filename, strlen(filename)); strcpy(se + strlen(filename), "/\n"); ss = strstr(ar->strtab, se); free(se); if (ss == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid string table"); return (ARCHIVE_WARN); } /* * GNU variant puts "/" followed by digits into * ar_name field. These digits indicates the real * filename string's offset to the string table. */ buff[AR_name_offset] = '/'; if (format_decimal(ss - ar->strtab, buff + AR_name_offset + 1, AR_name_size - 1)) { archive_set_error(&a->archive, ERANGE, "string table offset too large"); return (ARCHIVE_WARN); } } } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) { /* * BSD variant: for any file name which is more than * 16 chars or contains one or more embedded space(s), the * string "#1/" followed by the ASCII length of the name is * put into the ar_name field. The file size (stored in the * ar_size field) is incremented by the length of the name. * The name is then written immediately following the * archive header. */ if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { strncpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = ' '; } else { strncpy(buff + AR_name_offset, "#1/", 3); if (format_decimal(strlen(filename), buff + AR_name_offset + 3, AR_name_size - 3)) { archive_set_error(&a->archive, ERANGE, "File name too long"); return (ARCHIVE_WARN); } append_fn = 1; size += strlen(filename); } } stat: if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); return (ARCHIVE_WARN); } if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); return (ARCHIVE_WARN); } if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); return (ARCHIVE_WARN); } if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); return (ARCHIVE_WARN); } /* * Sanity Check: A non-pseudo archive member should always be * a regular file. */ if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) { archive_set_error(&a->archive, EINVAL, "Regular file required for non-pseudo member"); return (ARCHIVE_WARN); } size: if (format_decimal(size, buff + AR_size_offset, AR_size_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); return (ARCHIVE_WARN); } ret = __archive_write_output(a, buff, 60); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining = size; ar->entry_padding = ar->entry_bytes_remaining % 2; if (append_fn > 0) { ret = __archive_write_output(a, filename, strlen(filename)); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining -= strlen(filename); } return (ARCHIVE_OK); }
static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int r, ret, ret2 = ARCHIVE_OK; int tartype; struct gnutar *gnutar; struct archive_string_conv *sconv; struct archive_entry *entry_main; gnutar = (struct gnutar *)a->format_data; /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { gnutar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); gnutar->init_default_conversion = 1; } sconv = gnutar->sconv_default; } else sconv = gnutar->opt_sconv; /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; size_t path_length; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w(entry, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname(entry, as.s); archive_string_free(&as); } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathame"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", archive_entry_uname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_gname_l(entry, &(gnutar->gname), &(gnutar->gname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", archive_entry_gname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } /* If linkname is longer than 100 chars we need to add a 'K' header. */ r = archive_entry_hardlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (gnutar->linkname_length == 0) { r = archive_entry_symlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } } if (gnutar->linkname_length > GNUTAR_linkname_size) { size_t length = gnutar->linkname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'K'); archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if (ret < ARCHIVE_WARN) goto exit_write_header; /* Write name and trailing null byte. */ ret = __archive_write_output(a, gnutar->linkname, length); if (ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to 512 bytes */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } /* If pathname is longer than 100 chars we need to add an 'L' header. */ if (gnutar->pathname_length > GNUTAR_name_size) { const char *pathname = gnutar->pathname; size_t length = gnutar->pathname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'L'); archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) goto exit_write_header; /* Write pathname + trailing null byte. */ ret = __archive_write_output(a, pathname, length); if(ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to multiple of 512 bytes. */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } if (archive_entry_hardlink(entry) != NULL) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { case AE_IFREG: tartype = '0' ; break; case AE_IFLNK: tartype = '2' ; break; case AE_IFCHR: tartype = '3' ; break; case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); ret = ARCHIVE_FAILED; goto exit_write_header; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); ret = ARCHIVE_FAILED; goto exit_write_header; } ret = archive_format_gnutar_header(a, buff, entry, tartype); if (ret < ARCHIVE_WARN) goto exit_write_header; if (ret2 < ret) ret = ret2; ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { ret = ret2; goto exit_write_header; } if (ret2 < ret) ret = ret2; gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); exit_write_header: archive_entry_free(entry_main); return (ret); }
int main(int argc, char* argv[]){ struct archive* ar; struct archive_entry* aren; int arnum; if(argc < 3){ fprintf(stderr, "Usage: %s infile outfile\n", argv[0]); return 255; } #ifdef ENABLE_STAGE_1 puts("==> Stage 1: Listing"); ar = archive_read_new(); archive_read_support_filter_all(ar); archive_read_support_format_all(ar); arnum = archive_read_open_filename(ar, argv[1], 16384); if(arnum != ARCHIVE_OK){ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], archive_error_string(ar)); return 1; } while(archive_read_next_header(ar, &aren) == ARCHIVE_OK){ const char *hardlink, *symlink; printf("%s format: %s, pathname: %s, size: %"PRId64", links: %d," "username: %s, uid: %d", argv[1], archive_format_name(ar), archive_entry_pathname(aren), archive_entry_size(aren), archive_entry_nlink(aren), archive_entry_uname(aren), archive_entry_uid(aren)); hardlink = archive_entry_hardlink(aren); symlink = archive_entry_symlink(aren); if(hardlink != NULL){ printf(", hardlink: %s", hardlink); } if(symlink != NULL){ printf(", symlink: %s", symlink); } putchar('\n'); } archive_read_close(ar); archive_read_free(ar); #endif #ifdef ENABLE_STAGE_2 puts("==> Stage 2: Displaying"); ar = archive_read_new(); archive_read_support_filter_all(ar); archive_read_support_format_all(ar); arnum = archive_read_open_filename(ar, argv[1], 16384); if(arnum != ARCHIVE_OK){ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], archive_error_string(ar)); return 2; } while(archive_read_next_header(ar, &aren) == ARCHIVE_OK){ printf("<<< %s >>>\n", archive_entry_pathname(aren)); for(;;){ size_t size; off_t offset; const void* buffer; switch(archive_read_data_block(ar, &buffer, &size, &offset)){ case ARCHIVE_OK: puts(":: Block reading succeeded"); fwrite(buffer, size, 1, stdout); break; case ARCHIVE_WARN: puts(":: Block reading succeeded, warning exists"); fwrite(buffer, size, 1, stdout); break; case ARCHIVE_EOF: goto loop_outside; case ARCHIVE_RETRY: puts(":: Block reading failed, retrying"); break; case ARCHIVE_FATAL: puts(":: Fatal error! STOP!"); return 2; } } loop_outside: puts("@@ Extract OK @@"); } archive_read_close(ar); archive_read_free(ar); #endif #ifdef ENABLE_STAGE_3 puts("==> Stage 3: Extracting"); struct archive* arext; ar = archive_read_new(); archive_read_support_format_all(ar); archive_read_support_filter_all(ar); arext = archive_write_disk_new(); archive_write_disk_set_options(arext, ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR ); archive_write_disk_set_standard_lookup(arext); if(archive_read_open_filename(ar, argv[1], 16384)){ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], archive_error_string(ar)); return 3; } while((arnum = archive_read_next_header(ar, &aren)) == ARCHIVE_OK){ int filesize, accsize; printf("<<< %s >>>\n", archive_entry_pathname(aren)); if(archive_write_header(arext, aren) != ARCHIVE_OK){ puts(":: Write header not OK ..."); }else if((filesize = archive_entry_size(aren)) > 0){ accsize = 0; for(;;){ size_t size; off_t offset; const void* buffer; arnum = archive_read_data_block(ar, &buffer, &size, &offset); if(arnum != ARCHIVE_OK){ break; } arnum = archive_write_data(arext, buffer, size); if(arnum >= 0){ accsize += arnum; printf(":: %d of %d bytes written\n", accsize, filesize); } } } if(archive_write_finish_entry(arext) != ARCHIVE_OK){ return 3; } } archive_read_close(ar); archive_read_free(ar); archive_write_close(arext); archive_write_free(arext); #endif return 0; }
/*- * The logic here for -C <dir> attempts to avoid * chdir() as long as possible. For example: * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") * "-C /foo -C bar file" needs chdir("/foo/bar") * "-C /foo -C bar /file1" does not need chdir() * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 * * The only correct way to handle this is to record a "pending" chdir * request and combine multiple requests intelligently until we * need to process a non-absolute file. set_chdir() adds the new dir * to the pending list; do_chdir() actually executes any pending chdir. * * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. * */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) { #if defined(_WIN32) && !defined(__CYGWIN__) if (newdir[0] == '/' || newdir[0] == '\\' || /* Detect this type, for example, "C:\" or "C:/" */ (((newdir[0] >= 'a' && newdir[0] <= 'z') || (newdir[0] >= 'A' && newdir[0] <= 'Z')) && newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { #else if (newdir[0] == '/') { #endif /* The -C /foo -C /bar case; dump first one. */ free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } if (bsdtar->pending_chdir == NULL) /* Easy case: no previously-saved dir. */ bsdtar->pending_chdir = strdup(newdir); else { /* The -C /foo -C bar case; concatenate */ char *old_pending = bsdtar->pending_chdir; size_t old_len = strlen(old_pending); bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2); if (old_pending[old_len - 1] == '/') old_pending[old_len - 1] = '\0'; if (bsdtar->pending_chdir != NULL) sprintf(bsdtar->pending_chdir, "%s/%s", old_pending, newdir); free(old_pending); } if (bsdtar->pending_chdir == NULL) lafe_errc(1, errno, "No memory"); } void do_chdir(struct bsdtar *bsdtar) { if (bsdtar->pending_chdir == NULL) return; if (chdir(bsdtar->pending_chdir) != 0) { lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } static const char * strip_components(const char *p, int elements) { /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif elements--; break; case '\0': /* Path is too short, skip it. */ return (NULL); } } /* Skip any / characters. This handles short paths that have * additional / termination. This also handles the case where * the logic above stops in the middle of a duplicate // * sequence (which would otherwise get converted to an * absolute path). */ for (;;) { switch (*p) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif ++p; break; case '\0': return (NULL); default: return (p); } } } static void warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading '%c' from member names", c[0]); bsdtar->warned_lead_slash = 1; } } static void warn_strip_drive_letter(struct bsdtar *bsdtar) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; } } /* * Convert absolute path to non-absolute path by skipping leading * absolute path prefixes. */ static const char* strip_absolute_path(struct bsdtar *bsdtar, const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; warn_strip_drive_letter(bsdtar); } /* Remove multiple leading slashes and Windows drive letters. */ do { rp = p; if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; warn_strip_drive_letter(bsdtar); } /* Remove leading "/../", "/./", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" for next pass. */ } else if (p[1] == '.' && (p[2] == '/' || p[2] == '\\')) { p += 2; /* Remove "/.", leave "/" for next pass. */ } else p += 1; /* Remove "/". */ warn_strip_leading_char(bsdtar, rp); } } while (rp != p); return (p); } /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * * Note: The rewrites are applied uniformly to pathnames and hardlink * names but not to symlink bodies. This is deliberate: Symlink * bodies are not necessarily filenames. Even when they are, they * need to be interpreted relative to the directory containing them, * so simple rewrites like this are rarely appropriate. * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); const char *original_name = name; const char *hardlinkname = archive_entry_hardlink(entry); const char *original_hardlinkname = hardlinkname; #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; /* Apply user-specified substitution to pathname. */ r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_pathname(entry, subst_name); if (*subst_name == '\0') { free(subst_name); return -1; } else free(subst_name); name = archive_entry_pathname(entry); original_name = name; } /* Apply user-specified substitution to hardlink target. */ if (hardlinkname != NULL) { r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } hardlinkname = archive_entry_hardlink(entry); original_hardlinkname = hardlinkname; } /* Apply user-specified substitution to symlink body. */ if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_symlink(entry, subst_name); free(subst_name); } } #endif /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); if (hardlinkname != NULL) { hardlinkname = strip_components(hardlinkname, bsdtar->strip_components); if (hardlinkname == NULL) return (1); } } if (!bsdtar->option_absolute_paths) { /* By default, don't write or restore absolute pathnames. */ name = strip_absolute_path(bsdtar, name); if (*name == '\0') name = "."; if (hardlinkname != NULL) { hardlinkname = strip_absolute_path(bsdtar, hardlinkname); if (*hardlinkname == '\0') return (1); } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } /* Replace name in archive_entry. */ if (name != original_name) { archive_entry_copy_pathname(entry, name); } if (hardlinkname != original_hardlinkname) { archive_entry_copy_hardlink(entry, hardlinkname); } return (0); } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are quite a headache. Hence the * following simple utility function. */ const char * tar_i64toa(int64_t n0) { static char buff[24]; uint64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); } while (n /= 10); if (n0 < 0) *--p = '-'; return p; } /* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" * * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" * TODO: After this works, push it down into libarchive. * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. * * Note: This is currently only used within write.c, so should * not handle \ path separators. */ int pathcmp(const char *a, const char *b) { /* Skip leading './' */ if (a[0] == '.' && a[1] == '/' && a[2] != '\0') a += 2; if (b[0] == '.' && b[1] == '/' && b[2] != '\0') b += 2; /* Find the first difference, or return (0) if none. */ while (*a == *b) { if (*a == '\0') return (0); a++; b++; } /* * If one ends in '/' and the other one doesn't, * they're the same. */ if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') return (0); if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') return (0); /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); } #define PPBUFF_SIZE 1024 const char * passphrase_callback(struct archive *a, void *_client_data) { struct bsdtar *bsdtar = (struct bsdtar *)_client_data; (void)a; /* UNUSED */ if (bsdtar->ppbuff == NULL) { bsdtar->ppbuff = malloc(PPBUFF_SIZE); if (bsdtar->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", bsdtar->ppbuff, PPBUFF_SIZE); } void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { sprintf(tmp, "%lu ", (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { sprintf(tmp, "%lu", (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { sprintf(tmp, "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); #define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); }
/* * Extract a mixed archive file which has both LZMA and LZMA2 encoded files. * LZMA: file1, file2, file3, file4 * LZMA2: zfile1, zfile2, zfile3, zfile4 */ static void test_extract_all_files2(const char *refname) { struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("dir1/file1", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(13, archive_entry_size(ae)); assertEqualInt(13, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\n", 13); /* Verify regular file2. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(26, archive_entry_size(ae)); assertEqualInt(26, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26); /* Verify regular file3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file3", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(39, archive_entry_size(ae)); assertEqualInt(39, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39); /* Verify regular file4. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file4", archive_entry_pathname(ae)); assertEqualInt(86401, archive_entry_mtime(ae)); assertEqualInt(52, archive_entry_size(ae)); assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); /* Verify regular zfile1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("dir1/zfile1", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(13, archive_entry_size(ae)); assertEqualInt(13, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\n", 13); /* Verify regular zfile2. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("zfile2", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(26, archive_entry_size(ae)); assertEqualInt(26, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\n", 26); /* Verify regular zfile3. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("zfile3", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(39, archive_entry_size(ae)); assertEqualInt(39, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\n", 39); /* Verify regular zfile4. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("zfile4", archive_entry_pathname(ae)); assertEqualInt(5184001, archive_entry_mtime(ae)); assertEqualInt(52, archive_entry_size(ae)); assertEqualInt(52, archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "aaaaaaaaaaaa\nbbbbbbbbbbbb\ncccccccccccc\ndddddddddddd\n", 52); /* Verify directory dir1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFDIR | 0755), archive_entry_mode(ae)); assertEqualString("dir1/", archive_entry_pathname(ae)); assertEqualInt(2764801, archive_entry_mtime(ae)); assertEqualInt(9, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) { struct zip *zip; struct zip_local_file_header h; struct zip_extra_data_local e; struct zip_data_descriptor *d; struct zip_file_header_link *l; struct archive_string_conv *sconv; int ret, ret2 = ARCHIVE_OK; int64_t size; mode_t type; /* Entries other than a regular file or a folder are skipped. */ type = archive_entry_filetype(entry); if ((type != AE_IFREG) & (type != AE_IFDIR)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filetype not supported"); return ARCHIVE_FAILED; }; /* Directory entries should have a size of 0. */ if (type == AE_IFDIR) archive_entry_set_size(entry, 0); zip = a->format_data; /* Setup default conversion. */ if (zip->opt_sconv == NULL && !zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_write(&(a->archive)); zip->init_default_conversion = 1; } if (zip->flags == 0) { /* Initialize the general purpose flags. */ zip->flags = ZIP_FLAGS; if (zip->opt_sconv != NULL) { if (strcmp(archive_string_conversion_charset_name( zip->opt_sconv), "UTF-8") == 0) zip->flags |= ZIP_FLAGS_UTF8_NAME; #if HAVE_NL_LANGINFO } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) { zip->flags |= ZIP_FLAGS_UTF8_NAME; #endif } } d = &zip->data_descriptor; size = archive_entry_size(entry); zip->remaining_data_bytes = size; /* Append archive entry to the central directory data. */ l = (struct zip_file_header_link *) malloc(sizeof(*l)); if (l == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data"); return (ARCHIVE_FATAL); } l->entry = archive_entry_clone(entry); l->flags = zip->flags; if (zip->opt_sconv != NULL) sconv = zip->opt_sconv; else sconv = zip->sconv_default; if (sconv != NULL) { const char *p; size_t len; if (archive_entry_pathname_l(entry, &p, &len, 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, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (len > 0) archive_entry_set_pathname(l->entry, p); } /* If all character of a filename is ASCII, Reset UTF-8 Name flag. */ if ((l->flags & ZIP_FLAGS_UTF8_NAME) != 0 && is_all_ascii(archive_entry_pathname(l->entry))) l->flags &= ~ZIP_FLAGS_UTF8_NAME; /* Initialize the CRC variable and potentially the local crc32(). */ l->crc32 = crc32(0, NULL, 0); l->compression = zip->compression; l->compressed_size = 0; l->next = NULL; if (zip->central_directory == NULL) { zip->central_directory = l; } else { zip->central_directory_end->next = l; } zip->central_directory_end = l; /* Store the offset of this header for later use in central directory. */ l->offset = zip->written_bytes; memset(&h, 0, sizeof(h)); archive_le32enc(&h.signature, ZIP_SIGNATURE_LOCAL_FILE_HEADER); archive_le16enc(&h.version, ZIP_VERSION_EXTRACT); archive_le16enc(&h.flags, l->flags); archive_le16enc(&h.compression, zip->compression); archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(entry))); archive_le16enc(&h.filename_length, (uint16_t)path_length(l->entry)); switch (zip->compression) { case COMPRESSION_STORE: /* Setting compressed and uncompressed sizes even when specification says * to set to zero when using data descriptors. Otherwise the end of the * data for an entry is rather difficult to find. */ archive_le32enc(&h.compressed_size, size); archive_le32enc(&h.uncompressed_size, size); break; #ifdef HAVE_ZLIB_H case COMPRESSION_DEFLATE: archive_le32enc(&h.uncompressed_size, size); zip->stream.zalloc = Z_NULL; zip->stream.zfree = Z_NULL; zip->stream.opaque = Z_NULL; zip->stream.next_out = zip->buf; zip->stream.avail_out = zip->len_buf; if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor"); return (ARCHIVE_FATAL); } break; #endif } /* Formatting extra data. */ archive_le16enc(&h.extra_length, sizeof(e)); archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP); archive_le16enc(&e.time_size, sizeof(e.time_flag) + sizeof(e.mtime) + sizeof(e.atime) + sizeof(e.ctime)); e.time_flag[0] = 0x07; archive_le32enc(&e.mtime, archive_entry_mtime(entry)); archive_le32enc(&e.atime, archive_entry_atime(entry)); archive_le32enc(&e.ctime, archive_entry_ctime(entry)); archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_NEW_UNIX); archive_le16enc(&e.unix_size, sizeof(e.unix_version) + sizeof(e.unix_uid_size) + sizeof(e.unix_uid) + sizeof(e.unix_gid_size) + sizeof(e.unix_gid)); e.unix_version = 1; e.unix_uid_size = 4; archive_le32enc(&e.unix_uid, archive_entry_uid(entry)); e.unix_gid_size = 4; archive_le32enc(&e.unix_gid, archive_entry_gid(entry)); archive_le32enc(&d->uncompressed_size, size); ret = __archive_write_output(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(h); ret = write_path(l->entry, a); if (ret <= ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += ret; ret = __archive_write_output(a, &e, sizeof(e)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(e); if (ret2 != ARCHIVE_OK) return (ret2); return (ARCHIVE_OK); }
static int archive_write_zip_close(struct archive_write *a) { struct zip *zip; struct zip_file_header_link *l; struct zip_file_header h; struct zip_central_directory_end end; struct zip_extra_data_central e; int64_t offset_start, offset_end; int entries; int ret; zip = a->format_data; l = zip->central_directory; /* * Formatting central directory file header fields that are fixed for all entries. * Fields not used (and therefor 0) are: * * - comment_length * - disk_number * - attributes_internal */ memset(&h, 0, sizeof(h)); archive_le32enc(&h.signature, ZIP_SIGNATURE_FILE_HEADER); archive_le16enc(&h.version_by, ZIP_VERSION_BY); archive_le16enc(&h.version_extract, ZIP_VERSION_EXTRACT); entries = 0; offset_start = zip->written_bytes; /* Formatting individual header fields per entry and * writing each entry. */ while (l != NULL) { archive_le16enc(&h.flags, l->flags); archive_le16enc(&h.compression, l->compression); archive_le32enc(&h.timedate, dos_time(archive_entry_mtime(l->entry))); archive_le32enc(&h.crc32, l->crc32); archive_le32enc(&h.compressed_size, l->compressed_size); archive_le32enc(&h.uncompressed_size, archive_entry_size(l->entry)); archive_le16enc(&h.filename_length, (uint16_t)path_length(l->entry)); archive_le16enc(&h.extra_length, sizeof(e)); archive_le16enc(&h.attributes_external[2], archive_entry_mode(l->entry)); archive_le32enc(&h.offset, l->offset); /* Formatting extra data. */ archive_le16enc(&e.time_id, ZIP_SIGNATURE_EXTRA_TIMESTAMP); archive_le16enc(&e.time_size, sizeof(e.mtime) + sizeof(e.time_flag)); e.time_flag[0] = 0x07; archive_le32enc(&e.mtime, archive_entry_mtime(l->entry)); archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_NEW_UNIX); archive_le16enc(&e.unix_size, 0x0000); ret = __archive_write_output(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(h); ret = write_path(l->entry, a); if (ret <= ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += ret; ret = __archive_write_output(a, &e, sizeof(e)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(e); l = l->next; entries++; } offset_end = zip->written_bytes; /* Formatting end of central directory. */ memset(&end, 0, sizeof(end)); archive_le32enc(&end.signature, ZIP_SIGNATURE_CENTRAL_DIRECTORY_END); archive_le16enc(&end.entries_disk, entries); archive_le16enc(&end.entries, entries); archive_le32enc(&end.size, offset_end - offset_start); archive_le32enc(&end.offset, offset_start); /* Writing end of central directory. */ ret = __archive_write_output(a, &end, sizeof(end)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(end); return (ARCHIVE_OK); }
static int entry_to_archive(struct cpio *cpio, struct archive_entry *entry) { const char *destpath = archive_entry_pathname(entry); const char *srcpath = archive_entry_sourcepath(entry); int fd = -1; ssize_t bytes_read; int r; /* Print out the destination name to the user. */ if (cpio->verbose) fprintf(stderr,"%s", destpath); if (cpio->dot) fprintf(stderr, "."); /* * Option_link only makes sense in pass mode and for * regular files. Also note: if a link operation fails * because of cross-device restrictions, we'll fall back * to copy mode for that entry. * * TODO: Test other cpio implementations to see if they * hard-link anything other than regular files here. */ if (cpio->option_link && archive_entry_filetype(entry) == AE_IFREG) { struct archive_entry *t; /* Save the original entry in case we need it later. */ t = archive_entry_clone(entry); if (t == NULL) lafe_errc(1, ENOMEM, "Can't create link"); /* Note: link(2) doesn't create parent directories, * so we use archive_write_header() instead as a * convenience. */ archive_entry_set_hardlink(t, srcpath); /* This is a straight link that carries no data. */ archive_entry_set_size(t, 0); r = archive_write_header(cpio->archive, t); archive_entry_free(t); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); #ifdef EXDEV if (r != ARCHIVE_OK && archive_errno(cpio->archive) == EXDEV) { /* Cross-device link: Just fall through and use * the original entry to copy the file over. */ lafe_warnc(0, "Copying file instead"); } else #endif return (0); } /* * Make sure we can open the file (if necessary) before * trying to write the header. */ if (archive_entry_filetype(entry) == AE_IFREG) { if (archive_entry_size(entry) > 0) { fd = open(srcpath, O_RDONLY | O_BINARY); if (fd < 0) { lafe_warnc(errno, "%s: could not open file", srcpath); goto cleanup; } } } else { archive_entry_set_size(entry, 0); } r = archive_write_header(cpio->archive, entry); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s: %s", srcpath, archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); if (r >= ARCHIVE_WARN && archive_entry_size(entry) > 0 && fd >= 0) { bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); while (bytes_read > 0) { ssize_t bytes_write; bytes_write = archive_write_data(cpio->archive, cpio->buff, bytes_read); if (bytes_write < 0) lafe_errc(1, archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (bytes_write < bytes_read) { lafe_warnc(0, "Truncated write; file may have " "grown while being archived."); } bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); } } fd = restore_time(cpio, entry, srcpath, fd); cleanup: if (cpio->verbose) fprintf(stderr,"\n"); if (fd >= 0) close(fd); return (0); }
static void mode_in(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; struct archive *ext; const char *destpath; int r; ext = archive_write_disk_new(); if (ext == NULL) lafe_errc(1, 0, "Couldn't allocate restore object"); r = archive_write_disk_set_options(ext, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->option_rename) { destpath = cpio_rename(archive_entry_pathname(entry)); archive_entry_set_pathname(entry, destpath); } else destpath = archive_entry_pathname(entry); if (destpath == NULL) continue; if (cpio->verbose) fprintf(stderr, "%s\n", destpath); if (cpio->dot) fprintf(stderr, "."); if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) archive_entry_set_gid(entry, cpio->gid_override); r = archive_write_header(ext, entry); if (r != ARCHIVE_OK) { fprintf(stderr, "%s: %s\n", archive_entry_pathname(entry), archive_error_string(ext)); } else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) { r = extract_data(a, ext); if (r != ARCHIVE_OK) cpio->return_value = 1; } } r = archive_read_close(a); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); r = archive_write_close(ext); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); archive_write_free(ext); exit(cpio->return_value); }
/* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct cpio *cpio, struct archive_entry *entry) { char size[32]; char date[32]; char uids[16], gids[16]; const char *uname, *gname; FILE *out = stdout; const char *fmt; time_t mtime; static time_t now; if (!now) time(&now); if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); uname = uids; strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ uname = archive_entry_uname(entry); if (uname == NULL) uname = lookup_uname(cpio, (uid_t)archive_entry_uid(entry)); /* Use gname if it's present, else lookup name from gid. */ gname = archive_entry_gname(entry); if (gname == NULL) gname = lookup_gname(cpio, (uid_t)archive_entry_gid(entry)); } /* Print device number or file size. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(size, sizeof(size), "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(size, cpio_i64toa(archive_entry_size(entry))); } /* Format the time using 'ls -l' conventions. */ mtime = archive_entry_mtime(entry); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ if (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%d %b %Y" : "%b %d %Y"; else fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; #else if (abs(mtime - now) > (365/2)*86400) fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; #endif strftime(date, sizeof(date), fmt, localtime(&mtime)); fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", archive_entry_strmode(entry), archive_entry_nlink(entry), uname, gname, size, date, archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ fprintf(out, " -> %s", archive_entry_symlink(entry)); fprintf(out, "\n"); }
int AccessOpen(vlc_object_t *p_object) { access_t *p_access = (access_t*)p_object; const char *sep = strchr(p_access->psz_location, ARCHIVE_SEP_CHAR); if (sep == NULL) return VLC_EGENERIC; char *psz_base = strdup(p_access->psz_location); if (unlikely(psz_base == NULL)) return VLC_ENOMEM; char *psz_name = psz_base + (sep - p_access->psz_location); *(psz_name++) = '\0'; if (decode_URI(psz_base) == NULL) { free(psz_base); return VLC_EGENERIC; } access_sys_t *p_sys = p_access->p_sys = calloc(1, sizeof(access_sys_t)); p_sys->p_archive = archive_read_new(); if (!p_sys->p_archive) { msg_Err(p_access, "can't create libarchive instance: %s", archive_error_string(p_sys->p_archive)); free(psz_base); goto error; } EnableArchiveFormats(p_sys->p_archive); /* Set up the switch callback for multiple volumes handling */ archive_read_set_switch_callback(p_sys->p_archive, SwitchCallback); /* !Warn: sucks because libarchive can't guess format without reading 1st header * and it can't tell either if volumes are missing neither set following * volumes after the first Open(). * We need to know volumes uri in advance then :/ */ /* Try to list existing volumes */ char **ppsz_files = NULL; unsigned int i_files = 0; FindVolumes(p_access, p_sys->p_archive, psz_base, &ppsz_files, &i_files); p_sys->i_callback_data = 1 + i_files; p_sys->p_callback_data = malloc(sizeof(callback_data_t) * p_sys->i_callback_data); if (!p_sys->p_callback_data) { for(unsigned int i=0; i<i_files; i++) free(ppsz_files[i]); free(ppsz_files); free(psz_base); AccessClose(p_object); return VLC_ENOMEM; } /* set up our callback struct for our main uri */ p_sys->p_callback_data[0].psz_uri = psz_base; p_sys->p_callback_data[0].p_access = p_access; archive_read_append_callback_data(p_sys->p_archive, &p_sys->p_callback_data[0]); /* and register other volumes */ for(unsigned int i=0; i<i_files; i++) { p_sys->p_callback_data[1+i].psz_uri = ppsz_files[i]; p_sys->p_callback_data[1+i].p_access = p_access; archive_read_append_callback_data(p_sys->p_archive, &p_sys->p_callback_data[1+i]); } free(ppsz_files); if (archive_read_open2(p_sys->p_archive, &p_sys->p_callback_data[0], OpenCallback, ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK) { msg_Err(p_access, "can't open archive: %s", archive_error_string(p_sys->p_archive)); AccessClose(p_object); return VLC_EGENERIC; } bool b_present = false; while(archive_read_next_header(p_sys->p_archive, &p_sys->p_entry) == ARCHIVE_OK) { if (!strcmp(archive_entry_pathname(p_sys->p_entry), psz_name)) { b_present = true; break; } msg_Dbg(p_access, "skipping entry %s != %s", archive_entry_pathname(p_sys->p_entry), psz_name); } if (!b_present) { msg_Err(p_access, "entry '%s' not found in archive", psz_name); /* entry not found */ goto error; } msg_Dbg(p_access, "reading entry %s %"PRId64, archive_entry_pathname(p_sys->p_entry), archive_entry_size(p_sys->p_entry)); /* try to guess if it is seekable or not (does not depend on backend) */ p_sys->b_seekable = (archive_seek_data(p_sys->p_archive, 0, SEEK_SET) >= 0); p_access->pf_read = Read; p_access->pf_block = NULL; /* libarchive's zerocopy keeps owning block :/ */ p_access->pf_control = Control; p_access->pf_seek = Seek; access_InitFields(p_access); return VLC_SUCCESS; error: AccessClose(p_object); return VLC_EGENERIC; }
static int archive_write_mtree_finish_entry(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; struct archive_entry *entry; struct archive_string *str; const char *name; int keys, ret; entry = mtree->entry; if (entry == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Finished entry without being open first."); return (ARCHIVE_FATAL); } mtree->entry = NULL; if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_free(entry); return (ARCHIVE_OK); } str = (mtree->indent)? &mtree->ebuf : &mtree->buf; keys = get_keys(mtree, entry); if ((keys & F_NLINK) != 0 && archive_entry_nlink(entry) != 1 && archive_entry_filetype(entry) != AE_IFDIR) archive_string_sprintf(str, " nlink=%u", archive_entry_nlink(entry)); if ((keys & F_GNAME) != 0 && (name = archive_entry_gname(entry)) != NULL) { archive_strcat(str, " gname="); mtree_quote(str, name); } if ((keys & F_UNAME) != 0 && (name = archive_entry_uname(entry)) != NULL) { archive_strcat(str, " uname="); mtree_quote(str, name); } if ((keys & F_FLAGS) != 0 && (name = archive_entry_fflags_text(entry)) != NULL) { archive_strcat(str, " flags="); mtree_quote(str, name); } if ((keys & F_TIME) != 0) archive_string_sprintf(str, " time=%jd.%jd", (intmax_t)archive_entry_mtime(entry), (intmax_t)archive_entry_mtime_nsec(entry)); if ((keys & F_MODE) != 0) archive_string_sprintf(str, " mode=%o", archive_entry_mode(entry) & 07777); if ((keys & F_GID) != 0) archive_string_sprintf(str, " gid=%jd", (intmax_t)archive_entry_gid(entry)); if ((keys & F_UID) != 0) archive_string_sprintf(str, " uid=%jd", (intmax_t)archive_entry_uid(entry)); switch (archive_entry_filetype(entry)) { case AE_IFLNK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=link"); if ((keys & F_SLINK) != 0) { archive_strcat(str, " link="); mtree_quote(str, archive_entry_symlink(entry)); } break; case AE_IFSOCK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=socket"); break; case AE_IFCHR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=char"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%d,%d", archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); } break; case AE_IFBLK: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=block"); if ((keys & F_DEV) != 0) { archive_string_sprintf(str, " device=native,%d,%d", archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); } break; case AE_IFDIR: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=dir"); break; case AE_IFIFO: if ((keys & F_TYPE) != 0) archive_strcat(str, " type=fifo"); break; case AE_IFREG: default: /* Handle unknown file types as regular files. */ if ((keys & F_TYPE) != 0) archive_strcat(str, " type=file"); if ((keys & F_SIZE) != 0) archive_string_sprintf(str, " size=%jd", (intmax_t)archive_entry_size(entry)); break; } if (mtree->compute_sum & F_CKSUM) { uint64_t len; /* Include the length of the file. */ for (len = mtree->crc_len; len != 0; len >>= 8) COMPUTE_CRC(mtree->crc, len & 0xff); mtree->crc = ~mtree->crc; archive_string_sprintf(str, " cksum=%ju", (uintmax_t)mtree->crc); }
static void verify_contents(struct archive *a, int expect_details) { char filedata[64]; struct archive_entry *ae; /* * Read and verify first file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); /* Zip doesn't store high-resolution mtime. */ assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("file", archive_entry_pathname(ae)); if (expect_details) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(8, archive_entry_size(ae)); } else { assertEqualInt(0, archive_entry_size(ae)); } assertEqualIntA(a, 8, archive_read_data(a, filedata, sizeof(filedata))); assertEqualMem(filedata, "12345678", 8); /* * Read the second file back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("file2", archive_entry_pathname(ae)); if (expect_details) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(4, archive_entry_size(ae)); } else { assertEqualInt(0, archive_entry_size(ae)); } assertEqualIntA(a, 4, archive_read_data(a, filedata, sizeof(filedata))); assertEqualMem(filedata, "1234", 4); /* * Read the third file back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("symlink", archive_entry_pathname(ae)); if (expect_details) { assertEqualInt(AE_IFLNK | 0755, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualString("file1", archive_entry_symlink(ae)); } else { assertEqualInt(AE_IFREG | 0666, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); } /* * Read the dir entry back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(11, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("dir/", archive_entry_pathname(ae)); if (expect_details) assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualIntA(a, 0, archive_read_data(a, filedata, 10)); /* Verify the end of the archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int unpack_archive(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod, const char *pkgver, const char *fname, struct archive *ar) { xbps_dictionary_t propsd, filesd, old_filesd; xbps_array_t array, obsoletes; xbps_object_t obj; void *instbuf = NULL, *rembuf = NULL; struct stat st; struct xbps_unpack_cb_data xucd; struct archive_entry *entry; size_t i, entry_idx = 0, instbufsiz = 0, rembufsiz = 0; ssize_t entry_size; const char *file, *entry_pname, *transact, *tgtlnk; char *pkgname, *dname, *buf, *buf2, *p, *p2; int ar_rv, rv, entry_type, flags; bool preserve, update, conf_file, file_exists, skip_obsoletes; bool softreplace, skip_extract, force, metafile; uid_t euid; assert(xbps_object_type(pkg_repod) == XBPS_TYPE_DICTIONARY); assert(ar != NULL); propsd = filesd = old_filesd = NULL; force = preserve = update = conf_file = file_exists = false; skip_obsoletes = softreplace = metafile = false; xbps_dictionary_get_bool(pkg_repod, "preserve", &preserve); xbps_dictionary_get_bool(pkg_repod, "skip-obsoletes", &skip_obsoletes); xbps_dictionary_get_bool(pkg_repod, "softreplace", &softreplace); xbps_dictionary_get_cstring_nocopy(pkg_repod, "transaction", &transact); euid = geteuid(); pkgname = xbps_pkg_name(pkgver); assert(pkgname); if (xhp->flags & XBPS_FLAG_FORCE_UNPACK) force = true; if (xhp->unpack_cb != NULL) { /* initialize data for unpack cb */ memset(&xucd, 0, sizeof(xucd)); } if (access(xhp->rootdir, R_OK) == -1) { if (errno != ENOENT) { rv = errno; goto out; } if (xbps_mkpath(xhp->rootdir, 0750) == -1) { rv = errno; goto out; } } if (chdir(xhp->rootdir) == -1) { xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, errno, pkgver, "%s: [unpack] failed to chdir to rootdir `%s': %s", pkgver, xhp->rootdir, strerror(errno)); rv = errno; goto out; } if (strcmp(transact, "update") == 0) update = true; /* * Process the archive files. */ flags = set_extract_flags(euid); for (;;) { ar_rv = archive_read_next_header(ar, &entry); if (ar_rv == ARCHIVE_EOF || ar_rv == ARCHIVE_FATAL) break; else if (ar_rv == ARCHIVE_RETRY) continue; entry_pname = archive_entry_pathname(entry); entry_size = archive_entry_size(entry); entry_type = archive_entry_filetype(entry); /* * Ignore directories from archive. */ if (entry_type == AE_IFDIR) { archive_read_data_skip(ar); continue; } if (strcmp("./INSTALL", entry_pname) == 0) { /* * Store file in a buffer and execute * the "pre" action from it. */ instbufsiz = entry_size; instbuf = malloc(entry_size); assert(instbuf); if (archive_read_data(ar, instbuf, entry_size) != entry_size) { rv = EINVAL; goto out; } rv = xbps_pkg_exec_buffer(xhp, instbuf, instbufsiz, pkgver, "pre", update); if (rv != 0) { xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver, "%s: [unpack] INSTALL script failed " "to execute pre ACTION: %s", pkgver, strerror(rv)); goto out; } continue; } else if (strcmp("./REMOVE", entry_pname) == 0) { /* store file in a buffer */ rembufsiz = entry_size; rembuf = malloc(entry_size); assert(rembuf); if (archive_read_data(ar, rembuf, entry_size) != entry_size) { rv = EINVAL; goto out; } continue; } else if (strcmp("./files.plist", entry_pname) == 0) { filesd = xbps_archive_get_dictionary(ar, entry); if (filesd == NULL) { rv = errno; goto out; } continue; } else if (strcmp("./props.plist", entry_pname) == 0) { propsd = xbps_archive_get_dictionary(ar, entry); if (propsd == NULL) { rv = errno; goto out; } continue; } /* * XXX: duplicate code. * Create the metaplist file before unpacking any real file. */ if (propsd && filesd && !metafile) { rv = create_pkg_metaplist(xhp, pkgname, pkgver, propsd, filesd, instbuf, instbufsiz, rembuf, rembufsiz); if (rv != 0) { xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver, "%s: [unpack] failed to create metaplist file: %s", pkgver, strerror(rv)); goto out; } metafile = true; } /* * If XBPS_PKGFILES or XBPS_PKGPROPS weren't found * in the archive at this phase, skip all data. */ if (propsd == NULL || filesd == NULL) { archive_read_data_skip(ar); /* * If we have processed 4 entries and the two * required metadata files weren't found, bail out. * This is not an XBPS binary package. */ if (entry_idx >= 3) { xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, ENODEV, pkgver, "%s: [unpack] invalid binary package `%s'.", pkgver, fname); rv = ENODEV; goto out; } entry_idx++; continue; } /* * Prepare unpack callback ops. */ if (xhp->unpack_cb != NULL) { xucd.xhp = xhp; xucd.pkgver = pkgver; xucd.entry = entry_pname; xucd.entry_size = entry_size; xucd.entry_is_conf = false; } /* * Compute total entries in progress data, if set. * total_entries = files + conf_files + links. */ if (xhp->unpack_cb != NULL) { xucd.entry_total_count = 0; array = xbps_dictionary_get(filesd, "files"); xucd.entry_total_count += (ssize_t)xbps_array_count(array); array = xbps_dictionary_get(filesd, "conf_files"); xucd.entry_total_count += (ssize_t)xbps_array_count(array); array = xbps_dictionary_get(filesd, "links"); xucd.entry_total_count += (ssize_t)xbps_array_count(array); } /* * Always check that extracted file exists and hash * doesn't match, in that case overwrite the file. * Otherwise skip extracting it. */ conf_file = skip_extract = file_exists = false; if (lstat(entry_pname, &st) == 0) file_exists = true; if (!force && (entry_type == AE_IFREG)) { buf = strchr(entry_pname, '.') + 1; assert(buf != NULL); if (file_exists) { /* * Handle configuration files. Check if current * entry is a configuration file and take action * if required. Skip packages that don't have * "conf_files" array on its XBPS_PKGPROPS * dictionary. */ if (xbps_entry_is_a_conf_file(propsd, buf)) { conf_file = true; if (xhp->unpack_cb != NULL) xucd.entry_is_conf = true; rv = xbps_entry_install_conf_file(xhp, filesd, entry, entry_pname, pkgver, pkgname); if (rv == -1) { /* error */ goto out; } else if (rv == 0) { /* * Keep curfile as is. */ skip_extract = true; } } else { rv = xbps_file_hash_check_dictionary( xhp, filesd, "files", buf); if (rv == -1) { /* error */ xbps_dbg_printf(xhp, "%s: failed to check" " hash for `%s': %s\n", pkgver, entry_pname, strerror(errno)); goto out; } else if (rv == 0) { /* * hash match, skip extraction. */ xbps_dbg_printf(xhp, "%s: file %s " "matches existing SHA256, " "skipping...\n", pkgver, entry_pname); skip_extract = true; } } } } else if (!force && (entry_type == AE_IFLNK)) { /* * Check if current link from binpkg hasn't been * modified, otherwise extract new link. */ buf = realpath(entry_pname, NULL); if (buf) { if (strcmp(xhp->rootdir, "/")) { p = buf; p += strlen(xhp->rootdir); } else p = buf; tgtlnk = find_pkg_symlink_target(filesd, entry_pname); assert(tgtlnk); if (strncmp(tgtlnk, "./", 2) == 0) { buf2 = strdup(entry_pname); assert(buf2); dname = dirname(buf2); p2 = xbps_xasprintf("%s/%s", dname, tgtlnk); free(buf2); } else { p2 = strdup(tgtlnk); assert(p2); } xbps_dbg_printf(xhp, "%s: symlink %s cur: %s " "new: %s\n", pkgver, entry_pname, p, p2); if (strcmp(p, p2) == 0) { xbps_dbg_printf(xhp, "%s: symlink " "%s matched, skipping...\n", pkgver, entry_pname); skip_extract = true; } free(buf); free(p2); } } /* * Check if current file mode differs from file mode * in binpkg and apply perms if true. */ if (!force && file_exists && skip_extract && (archive_entry_mode(entry) != st.st_mode)) { if (chmod(entry_pname, archive_entry_mode(entry)) != 0) { xbps_dbg_printf(xhp, "%s: failed " "to set perms %s to %s: %s\n", pkgver, archive_entry_strmode(entry), entry_pname, strerror(errno)); rv = EINVAL; goto out; } xbps_dbg_printf(xhp, "%s: entry %s changed file " "mode to %s.\n", pkgver, entry_pname, archive_entry_strmode(entry)); } /* * Check if current uid/gid differs from file in binpkg, * and change permissions if true. */ if ((!force && file_exists && skip_extract && (euid == 0)) && (((archive_entry_uid(entry) != st.st_uid)) || ((archive_entry_gid(entry) != st.st_gid)))) { if (lchown(entry_pname, archive_entry_uid(entry), archive_entry_gid(entry)) != 0) { xbps_dbg_printf(xhp, "%s: failed " "to set uid/gid to %u:%u (%s)\n", pkgver, archive_entry_uid(entry), archive_entry_gid(entry), strerror(errno)); } else { xbps_dbg_printf(xhp, "%s: entry %s changed " "uid/gid to %u:%u.\n", pkgver, entry_pname, archive_entry_uid(entry), archive_entry_gid(entry)); } } if (!update && conf_file && file_exists && !skip_extract) { /* * If installing new package preserve old configuration * file but renaming it to <file>.old. */ buf = xbps_xasprintf("%s.old", entry_pname); (void)rename(entry_pname, buf); free(buf); buf = NULL; xbps_set_cb_state(xhp, XBPS_STATE_CONFIG_FILE, 0, pkgver, "Renamed old configuration file " "`%s' to `%s.old'.", entry_pname, entry_pname); } if (!force && skip_extract) { archive_read_data_skip(ar); continue; } /* * Reset entry_pname again because if entry's pathname * has been changed it will become a dangling pointer. */ entry_pname = archive_entry_pathname(entry); /* * Extract entry from archive. */ if (archive_read_extract(ar, entry, flags) != 0) { rv = archive_errno(ar); xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver, "%s: [unpack] failed to extract file `%s': %s", pkgver, entry_pname, strerror(rv)); } else { if (xhp->unpack_cb != NULL) { xucd.entry_extract_count++; (*xhp->unpack_cb)(&xucd, xhp->unpack_cb_data); } } } /* * XXX: duplicate code. * Create the metaplist file if it wasn't created before. */ if (propsd && filesd && !metafile) { rv = create_pkg_metaplist(xhp, pkgname, pkgver, propsd, filesd, instbuf, instbufsiz, rembuf, rembufsiz); if (rv != 0) { xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver, "%s: [unpack] failed to create metaplist file: %s", pkgver, strerror(rv)); goto out; } } /* * If there was any error extracting files from archive, error out. */ if ((rv = archive_errno(ar)) != 0) { xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL, rv, pkgver, NULL, "%s: [unpack] failed to extract files: %s", pkgver, fname, archive_error_string(ar)); goto out; } /* * Skip checking for obsolete files on: * - New package installation without "softreplace" keyword. * - Package with "preserve" keyword. * - Package with "skip-obsoletes" keyword. */ if (skip_obsoletes || preserve || (!softreplace && !update)) goto out; /* * Check and remove obsolete files on: * - Package upgrade. * - Package with "softreplace" keyword. */ old_filesd = xbps_pkgdb_get_pkg_metadata(xhp, pkgname); if (old_filesd == NULL) goto out; obsoletes = xbps_find_pkg_obsoletes(xhp, old_filesd, filesd); for (i = 0; i < xbps_array_count(obsoletes); i++) { obj = xbps_array_get(obsoletes, i); file = xbps_string_cstring_nocopy(obj); if (remove(file) == -1) { xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL, errno, pkgver, "%s: failed to remove obsolete entry `%s': %s", pkgver, file, strerror(errno)); continue; } xbps_set_cb_state(xhp, XBPS_STATE_REMOVE_FILE_OBSOLETE, 0, pkgver, "%s: removed obsolete entry: %s", pkgver, file); xbps_object_release(obj); } xbps_object_release(old_filesd); out: if (xbps_object_type(filesd) == XBPS_TYPE_DICTIONARY) xbps_object_release(filesd); if (xbps_object_type(propsd) == XBPS_TYPE_DICTIONARY) xbps_object_release(propsd); if (pkgname != NULL) free(pkgname); if (instbuf != NULL) free(instbuf); if (rembuf != NULL) free(rembuf); return rv; }
/* * Handle read modes: 'x', 't' and 'p'. */ static void read_archive(struct bsdar *bsdar, char mode) { struct archive *a; struct archive_entry *entry; struct stat sb; struct tm *tp; const char *bname; const char *name; mode_t md; size_t size; time_t mtime; uid_t uid; gid_t gid; char **av; char buf[25]; char find; int flags, r, i; if ((a = archive_read_new()) == NULL) bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed"); archive_read_support_format_ar(a); AC(archive_read_open_filename(a, bsdar->filename, DEF_BLKSZ)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY || r == ARCHIVE_FATAL) bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); if (r == ARCHIVE_EOF || r == ARCHIVE_FATAL) break; if (r == ARCHIVE_RETRY) { bsdar_warnc(bsdar, 0, "Retrying..."); continue; } if ((name = archive_entry_pathname(entry)) == NULL) break; /* Skip pseudo members. */ if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0) continue; if (bsdar->argc > 0) { find = 0; for(i = 0; i < bsdar->argc; i++) { av = &bsdar->argv[i]; if (*av == NULL) continue; if ((bname = basename(*av)) == NULL) bsdar_errc(bsdar, EX_SOFTWARE, errno, "basename failed"); if (strcmp(bname, name) != 0) continue; *av = NULL; find = 1; break; } if (!find) continue; } if (mode == 't') { if (bsdar->options & AR_V) { md = archive_entry_mode(entry); uid = archive_entry_uid(entry); gid = archive_entry_gid(entry); size = archive_entry_size(entry); mtime = archive_entry_mtime(entry); (void)strmode(md, buf); (void)fprintf(stdout, "%s %6d/%-6d %8ju ", buf + 1, uid, gid, (uintmax_t)size); tp = localtime(&mtime); (void)strftime(buf, sizeof(buf), "%b %e %H:%M %Y", tp); (void)fprintf(stdout, "%s %s", buf, name); } else (void)fprintf(stdout, "%s", name); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY || r == ARCHIVE_FATAL) { (void)fprintf(stdout, "\n"); bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) break; (void)fprintf(stdout, "\n"); } else { /* mode == 'x' || mode = 'p' */ if (mode == 'p') { if (bsdar->options & AR_V) { (void)fprintf(stdout, "\n<%s>\n\n", name); fflush(stdout); } r = archive_read_data_into_fd(a, 1); } else { /* mode == 'x' */ if (stat(name, &sb) != 0) { if (errno != ENOENT) { bsdar_warnc(bsdar, 0, "stat %s failed", bsdar->filename); continue; } } else { /* stat success, file exist */ if (bsdar->options & AR_CC) continue; if (bsdar->options & AR_U && archive_entry_mtime(entry) <= sb.st_mtime) continue; } if (bsdar->options & AR_V) (void)fprintf(stdout, "x - %s\n", name); /* Disallow absolute paths. */ if (name[0] == '/') { bsdar_warnc(bsdar, 0, "Absolute path '%s'", name); continue; } /* Basic path security flags. */ flags = ARCHIVE_EXTRACT_SECURE_SYMLINKS | \ ARCHIVE_EXTRACT_SECURE_NODOTDOT; if (bsdar->options & AR_O) flags |= ARCHIVE_EXTRACT_TIME; r = archive_read_extract(a, entry, flags); } if (r) bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); } } AC(archive_read_close(a)); AC(archive_read_free(a)); }
int archive_extract_all( char *arch_file, char *dest_dir, char *suffix ) { int ret, flags; char *pwd = NULL, *filename = NULL, *filename_new = NULL, *hardlink = NULL; struct archive *arch_r = NULL, *arch_w = NULL; struct archive_entry *entry = NULL; if( !arch_file ) return -1; arch_r = archive_read_new(); archive_read_support_format_all( arch_r ); archive_read_support_compression_all( arch_r ); if( archive_read_open_filename( arch_r, arch_file, 10240 ) != ARCHIVE_OK ) goto errout; if( dest_dir ) { if( util_mkdir( dest_dir ) == -1 ) { if( errno == EEXIST ) { if( access( dest_dir, R_OK | W_OK | X_OK ) == -1 ) { goto errout; } } else { goto errout; } } pwd = getcwd( NULL, 0 ); chdir( dest_dir ); } flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_OWNER; arch_w = archive_write_disk_new(); archive_write_disk_set_options( arch_w, flags ); archive_write_disk_set_standard_lookup( arch_w ); while( archive_read_next_header( arch_r, &entry ) == ARCHIVE_OK ) { if( suffix ) filename = (char *)archive_entry_pathname( entry ); #ifdef DEBUG if( !filename ) filename = (char *)archive_entry_pathname( entry ); printf("extract:%s\n", filename ); #endif if( suffix && archive_entry_filetype( entry ) != AE_IFDIR ) { filename_new = util_strcat( filename, suffix, NULL ); archive_entry_set_pathname( entry, filename_new ); free( filename_new ); if( archive_entry_nlink( entry ) > 0 ) { hardlink = (char *)archive_entry_hardlink( entry ); if( hardlink ) { filename_new = util_strcat( hardlink, suffix, NULL ); archive_entry_set_hardlink( entry, filename_new ); free( filename_new ); } } } ret = archive_read_extract2( arch_r, entry, arch_w ); if( ret != ARCHIVE_OK && ret != ARCHIVE_WARN ) { goto errout; } #ifdef DEBUG if( ret != ARCHIVE_OK) { printf("ret:%d, file:%s, link:%d, size:%d, read_err:%s, write_err:%s\n", ret, filename, archive_entry_nlink(entry), archive_entry_size(entry), archive_error_string(arch_r), archive_error_string(arch_w)); } #endif } archive_read_finish( arch_r ); archive_write_finish( arch_w ); if( pwd ) { chdir( pwd ); free( pwd ); } return 0; errout: if( arch_r ) archive_read_finish( arch_r ); if( arch_w ) archive_write_finish( arch_w ); if( pwd ) { chdir( pwd ); free( pwd ); } return -1; }
static Eina_Bool _etui_epub_file_unzip(Etui_Provider_Data *pd) { struct archive *a; struct archive_entry *entry; int r; if (!eina_file_mkdtemp("etui-epub-tmp-XXXXXX", &pd->doc.path)) return EINA_FALSE; a = archive_read_new(); if (!a) goto free_path; if (archive_read_support_filter_all(a) != ARCHIVE_OK) goto free_path; if (archive_read_support_format_zip(a) != ARCHIVE_OK) goto free_path; r = archive_read_open_filename(a, pd->doc.filename, 16384); if (r != ARCHIVE_OK) goto free_path; while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { if (archive_entry_filetype(entry) == AE_IFREG) { char buf[PATH_MAX]; const char *name; char *dir; char *base; size_t size; void *data; name = archive_entry_pathname(entry); dir = strdup(name); base = strdup(name); if (dir && base && (strcmp(dir, ".") != 0)) { snprintf(buf, sizeof(buf), "%s/%s", pd->doc.path, dirname(dir)); buf[sizeof(buf) - 1] = '\0'; ecore_file_mkdir(buf); printf(" * %s %s %s\n", name, dirname(dir), basename(base)); } if (base) free(base); if (dir) free(dir); size = archive_entry_size(entry); data = malloc(size); if (data) { size_t res; res = archive_read_data(a, data, size); if (res > 0) { FILE *f; snprintf(buf, sizeof(buf), "%s/%s", pd->doc.path, name); buf[sizeof(buf) - 1] = '\0'; printf(" $ %s\n", buf); f = fopen(buf, "wb"); if (f) { fwrite(data, 1, size, f); fclose(f); } } free(data); } } archive_read_data_skip(a); } archive_read_free(a); return EINA_TRUE; free_path: eina_tmpstr_del(pd->doc.path); return EINA_FALSE; }
static void test_linkify_new_cpio(void) { struct archive_entry *entry, *e2; struct archive_entry_linkresolver *resolver; /* Initialize the resolver. */ assert(NULL != (resolver = archive_entry_linkresolver_new())); archive_entry_linkresolver_set_strategy(resolver, ARCHIVE_FORMAT_CPIO_SVR4_NOCRC); /* Create an entry with only 1 link and try to linkify it. */ assert(NULL != (entry = archive_entry_new())); archive_entry_set_pathname(entry, "test1"); archive_entry_set_ino(entry, 1); archive_entry_set_dev(entry, 2); archive_entry_set_nlink(entry, 1); archive_entry_set_size(entry, 10); archive_entry_linkify(resolver, &entry, &e2); /* Shouldn't have been changed. */ assert(e2 == NULL); assertEqualInt(10, archive_entry_size(entry)); assertEqualString("test1", archive_entry_pathname(entry)); /* Now, try again with an entry that has 3 links. */ archive_entry_set_pathname(entry, "test2"); archive_entry_set_nlink(entry, 3); archive_entry_set_ino(entry, 2); archive_entry_linkify(resolver, &entry, &e2); /* First time, it just gets swallowed. */ assert(entry == NULL); assert(e2 == NULL); /* Match again. */ assert(NULL != (entry = archive_entry_new())); archive_entry_set_pathname(entry, "test3"); archive_entry_set_ino(entry, 2); archive_entry_set_dev(entry, 2); archive_entry_set_nlink(entry, 2); archive_entry_set_size(entry, 10); archive_entry_linkify(resolver, &entry, &e2); /* Should get back "test2" and nothing else. */ assertEqualString("test2", archive_entry_pathname(entry)); assertEqualInt(0, archive_entry_size(entry)); archive_entry_free(entry); assert(NULL == e2); archive_entry_free(e2); /* This should be a no-op. */ /* Match a third time. */ assert(NULL != (entry = archive_entry_new())); archive_entry_set_pathname(entry, "test4"); archive_entry_set_ino(entry, 2); archive_entry_set_dev(entry, 2); archive_entry_set_nlink(entry, 3); archive_entry_set_size(entry, 10); archive_entry_linkify(resolver, &entry, &e2); /* Should get back "test3". */ assertEqualString("test3", archive_entry_pathname(entry)); assertEqualInt(0, archive_entry_size(entry)); /* Since "test4" was the last link, should get it back also. */ assertEqualString("test4", archive_entry_pathname(e2)); assertEqualInt(10, archive_entry_size(e2)); archive_entry_free(entry); archive_entry_free(e2); archive_entry_linkresolver_free(resolver); }
int packing_append_file_attr(struct packing *pack, const char *filepath, const char *newpath, const char *uname, const char *gname, mode_t perm) { int fd; int len; char buf[BUFSIZ]; int retcode = EPKG_OK; int ret; struct stat st; struct archive_entry *entry, *sparse_entry; /* ugly hack for python and emacs */ /*char *p;*/ /*bool unset_timestamp = true;*/ entry = archive_entry_new(); archive_entry_copy_sourcepath(entry, filepath); if (lstat(filepath, &st) != 0) { pkg_emit_errno("lstat", filepath); retcode = EPKG_FATAL; goto cleanup; } ret = archive_read_disk_entry_from_file(pack->aread, entry, -1, &st); if (ret != ARCHIVE_OK) { pkg_emit_error("%s: %s", filepath, archive_error_string(pack->aread)); retcode = EPKG_FATAL; goto cleanup; } if (newpath != NULL) archive_entry_set_pathname(entry, newpath); if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } if (uname != NULL && uname[0] != '\0') archive_entry_set_uname(entry, uname); if (gname != NULL && gname[0] != '\0') archive_entry_set_gname(entry, gname); if (perm != 0) archive_entry_set_perm(entry, perm); /* XXX ugly hack for python and emacs */ /* p = strrchr(filepath, '.'); if (p != NULL && (strcmp(p, ".pyc") == 0 || strcmp(p, ".py") == 0 || strcmp(p, ".pyo") == 0 || strcmp(p, ".elc") == 0 || strcmp(p, ".el") == 0 )) unset_timestamp = false; if (unset_timestamp) { archive_entry_unset_atime(entry); archive_entry_unset_ctime(entry); archive_entry_unset_mtime(entry); archive_entry_unset_birthtime(entry); }*/ archive_entry_linkify(pack->resolver, &entry, &sparse_entry); if (sparse_entry != NULL && entry == NULL) entry = sparse_entry; archive_write_header(pack->awrite, entry); if (archive_entry_size(entry) > 0) { if ((fd = open(filepath, O_RDONLY)) < 0) { pkg_emit_errno("open", filepath); retcode = EPKG_FATAL; goto cleanup; } while ((len = read(fd, buf, sizeof(buf))) > 0) archive_write_data(pack->awrite, buf, len); close(fd); } cleanup: archive_entry_free(entry); return (retcode); }
static void test_linkify_tar(void) { struct archive_entry *entry, *e2; struct archive_entry_linkresolver *resolver; /* Initialize the resolver. */ assert(NULL != (resolver = archive_entry_linkresolver_new())); archive_entry_linkresolver_set_strategy(resolver, ARCHIVE_FORMAT_TAR_USTAR); /* Create an entry with only 1 link and try to linkify it. */ assert(NULL != (entry = archive_entry_new())); archive_entry_set_pathname(entry, "test1"); archive_entry_set_ino(entry, 1); archive_entry_set_dev(entry, 2); archive_entry_set_nlink(entry, 1); archive_entry_set_size(entry, 10); archive_entry_linkify(resolver, &entry, &e2); /* Shouldn't have been changed. */ assert(e2 == NULL); assertEqualInt(10, archive_entry_size(entry)); assertEqualString("test1", archive_entry_pathname(entry)); /* Now, try again with an entry that has 2 links. */ archive_entry_set_pathname(entry, "test2"); archive_entry_set_nlink(entry, 2); archive_entry_set_ino(entry, 2); archive_entry_linkify(resolver, &entry, &e2); /* Shouldn't be altered, since it wasn't seen before. */ assert(e2 == NULL); assertEqualString("test2", archive_entry_pathname(entry)); assertEqualString(NULL, archive_entry_hardlink(entry)); assertEqualInt(10, archive_entry_size(entry)); /* Match again and make sure it does get altered. */ archive_entry_linkify(resolver, &entry, &e2); assert(e2 == NULL); assertEqualString("test2", archive_entry_pathname(entry)); assertEqualString("test2", archive_entry_hardlink(entry)); assertEqualInt(0, archive_entry_size(entry)); /* Dirs should never be matched as hardlinks, regardless. */ archive_entry_set_pathname(entry, "test3"); archive_entry_set_nlink(entry, 2); archive_entry_set_filetype(entry, AE_IFDIR); archive_entry_set_ino(entry, 3); archive_entry_set_hardlink(entry, NULL); archive_entry_linkify(resolver, &entry, &e2); /* Shouldn't be altered, since it wasn't seen before. */ assert(e2 == NULL); assertEqualString("test3", archive_entry_pathname(entry)); assertEqualString(NULL, archive_entry_hardlink(entry)); /* Dir, so it shouldn't get matched. */ archive_entry_linkify(resolver, &entry, &e2); assert(e2 == NULL); assertEqualString("test3", archive_entry_pathname(entry)); assertEqualString(NULL, archive_entry_hardlink(entry)); archive_entry_free(entry); archive_entry_linkresolver_free(resolver); }
static int archive_format_gnutar_header(struct archive_write *a, char h[512], struct archive_entry *entry, int tartype) { unsigned int checksum; int i, ret; size_t copy_length; const char *p; struct gnutar *gnutar; gnutar = (struct gnutar *)a->format_data; ret = 0; /* * The "template header" already includes the signature, * various end-of-field markers, and other required elements. */ memcpy(h, &template_header, 512); /* * Because the block is already null-filled, and strings * are allowed to exactly fill their destination (without null), * I use memcpy(dest, src, strlen()) here a lot to copy strings. */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_pathname(entry); copy_length = strlen(p); } else { p = gnutar->pathname; copy_length = gnutar->pathname_length; } if (copy_length > GNUTAR_name_size) copy_length = GNUTAR_name_size; memcpy(h + GNUTAR_name_offset, p, copy_length); if ((copy_length = gnutar->linkname_length) > 0) { if (copy_length > GNUTAR_linkname_size) copy_length = GNUTAR_linkname_size; memcpy(h + GNUTAR_linkname_offset, gnutar->linkname, copy_length); } /* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_uname(entry); copy_length = strlen(p); } else { p = gnutar->uname; copy_length = gnutar->uname_length; } if (copy_length > 0) { if (copy_length > GNUTAR_uname_size) copy_length = GNUTAR_uname_size; memcpy(h + GNUTAR_uname_offset, p, copy_length); } /* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_gname(entry); copy_length = strlen(p); } else { p = gnutar->gname; copy_length = gnutar->gname_length; } if (copy_length > 0) { if (strlen(p) > GNUTAR_gname_size) copy_length = GNUTAR_gname_size; memcpy(h + GNUTAR_gname_offset, p, copy_length); } /* By truncating the mode here, we ensure it always fits. */ format_octal(archive_entry_mode(entry) & 07777, h + GNUTAR_mode_offset, GNUTAR_mode_size); /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset, GNUTAR_uid_size, GNUTAR_uid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID %jd too large", (intmax_t)archive_entry_uid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset, GNUTAR_gid_size, GNUTAR_gid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID %jd too large", (intmax_t)archive_entry_gid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset, GNUTAR_size_size, GNUTAR_size_max_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } /* Shouldn't overflow before 2106, since mtime field is 33 bits. */ format_octal(archive_entry_mtime(entry), h + GNUTAR_mtime_offset, GNUTAR_mtime_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { if (format_octal(archive_entry_rdevmajor(entry), h + GNUTAR_rdevmajor_offset, GNUTAR_rdevmajor_size)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } if (format_octal(archive_entry_rdevminor(entry), h + GNUTAR_rdevminor_offset, GNUTAR_rdevminor_size)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } h[GNUTAR_typeflag_offset] = tartype; checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ /* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ format_octal(checksum, h + GNUTAR_checksum_offset, 6); return (ret); }
/** * Issue 152: A file generated by a tool that doesn't really * believe in populating local file headers at all. This * is only readable with the seeking reader. */ static void test_compat_zip_5(void) { const char *refname = "test_compat_zip_5.zip"; struct archive_entry *ae; struct archive *a; void *p; size_t s; extract_reference_file(refname); p = slurpfile(&s, refname); /* Verify with seek support. * Everything works correctly here. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 18)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Metadata/Job_PT.xml", archive_entry_pathname(ae)); assertEqualInt(3559, archive_entry_size(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(0666, archive_entry_perm(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Metadata/MXDC_Empty_PT.xml", archive_entry_pathname(ae)); assertEqualInt(456, archive_entry_size(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(0666, archive_entry_perm(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Metadata/Page1_Thumbnail.JPG", archive_entry_pathname(ae)); assertEqualInt(1495, archive_entry_size(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(0666, archive_entry_perm(ae)); /* TODO: Read some of the file data and verify it. The code to read uncompressed Zip entries with "file at end" semantics is tricky and should be verified more carefully. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Pages/_rels/1.fpage.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Pages/1.fpage", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Resources/Fonts/3DFDBC8B-4514-41F1-A808-DEA1C79BAC2B.odttf", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/_rels/FixedDocument.fdoc.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/FixedDocument.fdoc", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("_rels/FixedDocumentSequence.fdseq.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("FixedDocumentSequence.fdseq", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("_rels/.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("[Content_Types].xml", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); /* Try reading without seek support. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 3)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Metadata/Job_PT.xml", archive_entry_pathname(ae)); assertEqualInt(0, archive_entry_size(ae)); assert(!archive_entry_size_is_set(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(0666, archive_entry_perm(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Metadata/MXDC_Empty_PT.xml", archive_entry_pathname(ae)); assertEqualInt(0, archive_entry_size(ae)); assert(!archive_entry_size_is_set(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(0666, archive_entry_perm(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Metadata/Page1_Thumbnail.JPG", archive_entry_pathname(ae)); assertEqualInt(0, archive_entry_size(ae)); assert(!archive_entry_size_is_set(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(0666, archive_entry_perm(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Pages/_rels/1.fpage.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Pages/1.fpage", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/Resources/Fonts/3DFDBC8B-4514-41F1-A808-DEA1C79BAC2B.odttf", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/_rels/FixedDocument.fdoc.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("Documents/1/FixedDocument.fdoc", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("_rels/FixedDocumentSequence.fdseq.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("FixedDocumentSequence.fdseq", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("_rels/.rels", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("[Content_Types].xml", archive_entry_pathname(ae)); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); free(p); }
/* * A single file can have multiple lines contribute specifications. * Parse as many lines as necessary, then pull additional information * from a backing file on disk as necessary. */ static int parse_file(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mentry, int *use_next) { const char *path; struct stat st_storage, *st; struct mtree_entry *mp; struct archive_entry *sparse_entry; int r = ARCHIVE_OK, r1, parsed_kws; mentry->used = 1; /* Initialize reasonable defaults. */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_size(entry, 0); archive_string_empty(&mtree->contents_name); /* Parse options from this line. */ parsed_kws = 0; r = parse_line(a, entry, mtree, mentry, &parsed_kws); if (mentry->full) { archive_entry_copy_pathname(entry, mentry->name); /* * "Full" entries are allowed to have multiple lines * and those lines aren't required to be adjacent. We * don't support multiple lines for "relative" entries * nor do we make any attempt to merge data from * separate "relative" and "full" entries. (Merging * "relative" and "full" entries would require dealing * with pathname canonicalization, which is a very * tricky subject.) */ for (mp = mentry->next; mp != NULL; mp = mp->next) { if (mp->full && !mp->used && strcmp(mentry->name, mp->name) == 0) { /* Later lines override earlier ones. */ mp->used = 1; r1 = parse_line(a, entry, mtree, mp, &parsed_kws); if (r1 < r) r = r1; } } } else { /* * Relative entries require us to construct * the full path and possibly update the * current directory. */ size_t n = archive_strlen(&mtree->current_dir); if (n > 0) archive_strcat(&mtree->current_dir, "/"); archive_strcat(&mtree->current_dir, mentry->name); archive_entry_copy_pathname(entry, mtree->current_dir.s); if (archive_entry_filetype(entry) != AE_IFDIR) mtree->current_dir.length = n; } /* * Try to open and stat the file to get the real size * and other file info. It would be nice to avoid * this here so that getting a listing of an mtree * wouldn't require opening every referenced contents * file. But then we wouldn't know the actual * contents size, so I don't see a really viable way * around this. (Also, we may want to someday pull * other unspecified info from the contents file on * disk.) */ mtree->fd = -1; if (archive_strlen(&mtree->contents_name) > 0) path = mtree->contents_name.s; else path = archive_entry_pathname(entry); if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { mtree->fd = open(path, O_RDONLY | O_BINARY); if (mtree->fd == -1 && (errno != ENOENT || archive_strlen(&mtree->contents_name) > 0)) { archive_set_error(&a->archive, errno, "Can't open %s", path); r = ARCHIVE_WARN; } } st = &st_storage; if (mtree->fd >= 0) { if (fstat(mtree->fd, st) == -1) { archive_set_error(&a->archive, errno, "Could not fstat %s", path); r = ARCHIVE_WARN; /* If we can't stat it, don't keep it open. */ close(mtree->fd); mtree->fd = -1; st = NULL; } } else if (lstat(path, st) == -1) { st = NULL; } /* * Check for a mismatch between the type in the specification and * the type of the contents object on disk. */ if (st != NULL) { if ( ((st->st_mode & S_IFMT) == S_IFREG && archive_entry_filetype(entry) == AE_IFREG) #ifdef S_IFLNK || ((st->st_mode & S_IFMT) == S_IFLNK && archive_entry_filetype(entry) == AE_IFLNK) #endif #ifdef S_IFSOCK || ((st->st_mode & S_IFSOCK) == S_IFSOCK && archive_entry_filetype(entry) == AE_IFSOCK) #endif #ifdef S_IFCHR || ((st->st_mode & S_IFMT) == S_IFCHR && archive_entry_filetype(entry) == AE_IFCHR) #endif #ifdef S_IFBLK || ((st->st_mode & S_IFMT) == S_IFBLK && archive_entry_filetype(entry) == AE_IFBLK) #endif || ((st->st_mode & S_IFMT) == S_IFDIR && archive_entry_filetype(entry) == AE_IFDIR) #ifdef S_IFIFO || ((st->st_mode & S_IFMT) == S_IFIFO && archive_entry_filetype(entry) == AE_IFIFO) #endif ) { /* Types match. */ } else { /* Types don't match; bail out gracefully. */ if (mtree->fd >= 0) close(mtree->fd); mtree->fd = -1; if (parsed_kws & MTREE_HAS_OPTIONAL) { /* It's not an error for an optional entry to not match disk. */ *use_next = 1; } else if (r == ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "mtree specification has different type for %s", archive_entry_pathname(entry)); r = ARCHIVE_WARN; } return r; } } /* * If there is a contents file on disk, pick some of the metadata * from that file. For most of these, we only set it from the contents * if it wasn't already parsed from the specification. */ if (st != NULL) { if ((parsed_kws & MTREE_HAS_DEVICE) == 0 && (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK)) archive_entry_set_rdev(entry, st->st_rdev); if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0) archive_entry_set_gid(entry, st->st_gid); if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0) archive_entry_set_uid(entry, st->st_uid); if ((parsed_kws & MTREE_HAS_MTIME) == 0) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime*1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec*1000); #else archive_entry_set_mtime(entry, st->st_mtime, 0); #endif } if ((parsed_kws & MTREE_HAS_NLINK) == 0) archive_entry_set_nlink(entry, st->st_nlink); if ((parsed_kws & MTREE_HAS_PERM) == 0) archive_entry_set_perm(entry, st->st_mode); if ((parsed_kws & MTREE_HAS_SIZE) == 0) archive_entry_set_size(entry, st->st_size); archive_entry_set_ino(entry, st->st_ino); archive_entry_set_dev(entry, st->st_dev); archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); } else if (parsed_kws & MTREE_HAS_OPTIONAL) { /* * Couldn't open the entry, stat it or the on-disk type * didn't match. If this entry is optional, just ignore it * and read the next header entry. */ *use_next = 1; return ARCHIVE_OK; } mtree->cur_size = archive_entry_size(entry); mtree->offset = 0; return r; }
static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int r, ret, ret2 = ARCHIVE_OK; int tartype; struct gnutar *gnutar; struct archive_string_conv *sconv; gnutar = (struct gnutar *)a->format_data; /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { gnutar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); gnutar->init_default_conversion = 1; } sconv = gnutar->sconv_default; } else sconv = gnutar->opt_sconv; /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; char *t; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ p = archive_entry_pathname(entry); if (p[strlen(p) - 1] != '/') { t = (char *)malloc(strlen(p) + 2); if (t == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate gnutar data"); return(ARCHIVE_FATAL); } strcpy(t, p); strcat(t, "/"); archive_entry_copy_pathname(entry, t); free(t); } } r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathame"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", archive_entry_uname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_gname_l(entry, &(gnutar->gname), &(gnutar->gname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", archive_entry_gname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } /* If linkname is longer than 100 chars we need to add a 'K' header. */ r = archive_entry_hardlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 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, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (gnutar->linkname_length == 0) { r = archive_entry_symlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 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, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } } if (gnutar->linkname_length > GNUTAR_linkname_size) { size_t todo = gnutar->linkname_length; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, gnutar->linkname_length + 1); ret = archive_format_gnutar_header(a, buff, temp, 'K'); if (ret < ARCHIVE_WARN) return (ret); ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) return (ret); archive_entry_free(temp); /* Write as many 512 bytes blocks as needed to write full name. */ ret = __archive_write_output(a, gnutar->linkname, todo); if(ret < ARCHIVE_WARN) return (ret); ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo)); if (ret < ARCHIVE_WARN) return (ret); } /* If pathname is longer than 100 chars we need to add an 'L' header. */ if (gnutar->pathname_length > GNUTAR_name_size) { const char *pathname = gnutar->pathname; size_t todo = gnutar->pathname_length; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, gnutar->pathname_length + 1); ret = archive_format_gnutar_header(a, buff, temp, 'L'); if (ret < ARCHIVE_WARN) return (ret); ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) return (ret); archive_entry_free(temp); /* Write as many 512 bytes blocks as needed to write full name. */ ret = __archive_write_output(a, pathname, todo); if(ret < ARCHIVE_WARN) return (ret); ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo)); if (ret < ARCHIVE_WARN) return (ret); } if (archive_entry_hardlink(entry) != NULL) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { case AE_IFREG: tartype = '0' ; break; case AE_IFLNK: tartype = '2' ; break; case AE_IFCHR: tartype = '3' ; break; case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); return (ARCHIVE_FAILED); } ret = archive_format_gnutar_header(a, buff, entry, tartype); if (ret < ARCHIVE_WARN) return (ret); if (ret2 < ret) ret = ret2; ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) return (ret2); if (ret2 < ret) ret = ret2; gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); return (ret); }
/* * Main loop: open the zipfile, iterate over its contents and decide what * to do with each entry. */ static void unzip(const char *fn) { struct archive *a; struct archive_entry *e; int fd, ret; uintmax_t total_size, file_count, error_count; if (strcmp(fn, "-") == 0) fd = STDIN_FILENO; else if ((fd = open(fn, O_RDONLY)) < 0) error("%s", fn); if ((a = archive_read_new()) == NULL) error("archive_read_new failed"); ac(archive_read_support_format_zip(a)); ac(archive_read_open_fd(a, fd, 8192)); if (!zipinfo_mode) { if (!p_opt && !q_opt) printf("Archive: %s\n", fn); if (v_opt == 1) { printf(" Length Date Time Name\n"); printf(" -------- ---- ---- ----\n"); } else if (v_opt == 2) { printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); printf("-------- ------ ------- ----- ---- ---- ------ ----\n"); } } total_size = 0; file_count = 0; error_count = 0; for (;;) { ret = archive_read_next_header(a, &e); if (ret == ARCHIVE_EOF) break; ac(ret); if (!zipinfo_mode) { if (t_opt) error_count += test(a, e); else if (v_opt) list(a, e); else if (p_opt || c_opt) extract_stdout(a, e); else extract(a, e); } else { if (Z1_opt) list(a, e); } total_size += archive_entry_size(e); ++file_count; } if (zipinfo_mode) { if (v_opt == 1) { printf(" -------- -------\n"); printf(" %8ju %ju file%s\n", total_size, file_count, file_count != 1 ? "s" : ""); } else if (v_opt == 2) { printf("-------- ------- --- -------\n"); printf("%8ju %7ju 0%% %ju file%s\n", total_size, total_size, file_count, file_count != 1 ? "s" : ""); } } ac(archive_read_close(a)); (void)archive_read_finish(a); if (fd != STDIN_FILENO && close(fd) != 0) error("%s", fn); if (t_opt) { if (error_count > 0) { errorx("%d checksum error(s) found.", error_count); } else { printf("No errors detected in compressed data of %s.\n", fn); } } }
bool vesKiwiArchiveUtils::extractArchive(const std::string& filename, const std::string& destDir) { this->mEntries.clear(); struct archive *a; struct archive *ext; struct archive_entry *entry; int flags; int r; flags = ARCHIVE_EXTRACT_TIME; flags |= ARCHIVE_EXTRACT_PERM; flags |= ARCHIVE_EXTRACT_ACL; flags |= ARCHIVE_EXTRACT_FFLAGS; a = archive_read_new(); archive_read_support_format_all(a); archive_read_support_compression_all(a); ext = archive_write_disk_new(); archive_write_disk_set_options(ext, flags); archive_write_disk_set_standard_lookup(ext); if ((r = archive_read_open_file(a, filename.c_str(), 10240))) { this->setError("Error Opening File", "Failed to open file: " + filename); return false; } for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(a)); if (r < ARCHIVE_WARN) { this->setError("Error Reading Archive", archive_error_string(a)); return false; } std::string destPath = archive_entry_pathname(entry); //printf("entry: %s\n", destPath.c_str()); if (destDir.size()) { destPath = destDir + "/" + destPath; } archive_entry_set_pathname(entry, destPath.c_str()); r = archive_write_header(ext, entry); if (r != ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); else if (archive_entry_size(entry) > 0) { copy_data(a, ext); if (r != ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); if (r < ARCHIVE_WARN) { this->setError("Error Reading Archive", archive_error_string(ext)); return false; } } r = archive_write_finish_entry(ext); if (r != ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); if (r < ARCHIVE_WARN) { this->setError("Error Reading Archive", archive_error_string(ext)); return false; } this->mEntries.push_back(destPath); } archive_read_close(a); archive_read_free(a); archive_write_close(ext); archive_write_free(ext); return true; }
static void test_open_filename_mbs(void) { char buff[64]; struct archive_entry *ae; struct archive *a; /* Write an archive through this FILE *. */ assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_compression_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_write_open_filename(a, "test.tar")); /* * Write a file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_set_mtime(ae, 1, 0); archive_entry_copy_pathname(ae, "file"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, 8); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9)); /* * Write a second file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "file2"); archive_entry_set_mode(ae, S_IFREG | 0755); archive_entry_set_size(ae, 819200); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* * Now, read the data back. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, "test.tar", 512)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("file", archive_entry_pathname(ae)); assert((S_IFREG | 0755) == archive_entry_mode(ae)); assertEqualInt(8, archive_entry_size(ae)); assertEqualIntA(a, 8, archive_read_data(a, buff, 10)); assertEqualMem(buff, "12345678", 8); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("file2", archive_entry_pathname(ae)); assert((S_IFREG | 0755) == archive_entry_mode(ae)); assertEqualInt(819200, archive_entry_size(ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_data_skip(a)); /* Verify the end of the archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); /* * Verify some of the error handling. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_FATAL, archive_read_open_filename(a, "nonexistent.tar", 512)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int _warc_header(struct archive_write *a, struct archive_entry *entry) { struct warc_s *w = a->format_data; struct archive_string hdr; #define MAX_HDR_SIZE 512 /* check whether warcinfo record needs outputting */ if (!w->omit_warcinfo) { warc_essential_hdr_t wi = { WT_INFO, /*uri*/NULL, /*urn*/NULL, /*rtm*/w->now, /*mtm*/w->now, /*cty*/"application/warc-fields", /*len*/sizeof(warcinfo) - 1U, }; ssize_t r; archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi); if (r >= 0) { /* jackpot! */ /* now also use HDR buffer for the actual warcinfo */ archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1); /* append end-of-record indicator */ archive_strncat(&hdr, "\r\n\r\n", 4); /* write to output stream */ __archive_write_output(a, hdr.s, archive_strlen(&hdr)); } /* indicate we're done with file header writing */ w->omit_warcinfo = 1U; archive_string_free(&hdr); } if (archive_entry_pathname(entry) == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } w->typ = archive_entry_filetype(entry); w->populz = 0U; if (w->typ == AE_IFREG) { warc_essential_hdr_t rh = { WT_RSRC, /*uri*/archive_entry_pathname(entry), /*urn*/NULL, /*rtm*/w->now, /*mtm*/archive_entry_mtime(entry), /*cty*/NULL, /*len*/archive_entry_size(entry), }; ssize_t r; archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh); if (r < 0) { /* don't bother */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cannot archive file"); return (ARCHIVE_WARN); } /* otherwise append to output stream */ __archive_write_output(a, hdr.s, r); /* and let subsequent calls to _data() know about the size */ w->populz = rh.cntlen; archive_string_free(&hdr); return (ARCHIVE_OK); } /* just resort to erroring as per Tim's advice */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "WARC can only process regular files"); return (ARCHIVE_FAILED); }