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);
}
Example #5
0
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;
}
Example #6
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);
}
Example #10
0
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);
}
Example #11
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);
}
Example #12
0
/*
 * 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");
}
Example #13
0
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);
	}
Example #15
0
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));
}
Example #16
0
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;
}
Example #17
0
/*
 * 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));
}
Example #18
0
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;
}
Example #19
0
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;
}
Example #20
0
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);
}
Example #21
0
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);
}
Example #22
0
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);
}
Example #24
0
/**
 * 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);
}
Example #27
0
/*
 * 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);
		}
	}
}
Example #28
0
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);
}